<?php
# Copyright (C) 2009 José Manuel Carnero <hide@address.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
# http://www.gnu.org/copyleft/gpl.html
/**
* Clase para conexion y querys a bases de datos MySQL >= 4.1.3
* (MySQL Improved Extension)
*
* PHP 5
*/
class ddbb_mysqli extends ddbb{
//variables privadas
private $bSeleccion; //estado de seleccion de base de datos (true, false)
private $oResultados; //objeto mysqli_result, resultante de $this->oMysqli->store_result()
private $oConsulta; //objeto mysqli_stmt, se usa solo cuando se pasan parametros en array
private $oUltimaQuery; //ultima query lanzada, sin sustituir parametros (para uso con mysqli_stmt)
private $oMysqli; //objeto mysqli
//variables publicas
public $sCharset; //codificacion en que se lanzaran las querys; a vacio usara la que tenga la base de datos; valores: utf-8,
public $sCollation; //collation en que se lanzaran las querys; vacio usara la que tenga la base de datos; util para evitar problemas en las comparaciones de cadenas de distintas tablas
public $sQueryTipoArray; //define que tipo de array devolvera una query: dos (MYSQL_BOTH) -> ambos tipos de array, aso (MYSQL_ASSOC) -> asociativo, num (MYSQL_NUM) -> numerico; por defecto (si no se define explicitamente valor): ambos; es valido asignandole las constantes entre parentesis (naturales de PHP)
/**
* Constructor
* Los parametros por defecto conectan a un servidor de MYSQL local,
* con el usuario administrador (sin clave) y a la base de datos "test"
* (que existe por defecto)
*
*/
function __construct($servidor = 'localhost', $usuario = 'root', $password = '', $ddbb = 'test'){
parent::ddbb($servidor, $usuario, $password, $ddbb);
//$this->oMysqli = mysqli_init(); //nueva instancia de la clase mysqli (propia de PHP)
$this->oMysqli = & new mysqli(); //nueva instancia de la clase mysqli (propia de PHP)
$this->oMysqli->init(); //inicializar (crea un recurso necesario para metodos como mysqli::real_connect
$this->oResultados = false;
//$this->oResultados = & new mysqli_result(); //nueva instancia de la clase mysqli_result (propia de PHP)
$this->oConsulta = false;
$this->oUltimaQuery = '';
$this->sMotor = 'mysqli';
$this->sQueryTipoArray = 'dos';
$this->sCharset = $this->codificacion('charset');
$this->sCollation = $this->codificacion('collation');
$this->aErrorMensajes = array_merge($this->aErrorMensajes, array('conectar1' => 'No se puede conectar a base de datos. Extension MYSQL para PHP no instalada.',
'conectar2' => 'No se puede conectar a la base de datos <em>%s</em>, error: [%s] %s',
'desconectar1' => 'No se puede cerrar la conexion a la base de datos <em>%s</em>, error: [%s] %s',
'seleccionarDDBB1' => 'No se puede seleccionar la base de datos <em>%s</em>, error: [%s] %s',
'query1' => 'La preparación de la consulta ha generado el error: [%s] %s',
'query2' => 'La asignación de parametros a la consulta ha generado el error: [%s] %s',
'consulta1' => 'La consulta <em>%s</em> ha generado el error: [%s] %s',
'consulta2' => 'Tipo de consulta no soportada: %s',
'numResultados1' => 'No es posible contar el numero de registros, no se reconoce el tipo de query: %s',
'recPuntero1' => 'No se ha podido recolocar el puntero debido al error: [%s] %s',
'leeFila' => 'Intenta obtener un array de resultados de una consulta no "SELECT"',
'leeFilasTodos' => 'Intenta obtener un array de resultados de una consulta no "SELECT"',
'tipoQuery' => 'El tipo de array (<em>%s</em>) que debe devolverse es desconocido'
));
}
/**
* Conexion al servidor.
* Selecciona automaticamente la base de datos,
* llamar a "fSeleccionarDDBB()" cambiando la propiedad "$this->sDDBB"
* para usar otra base de datos con esta misma conexion.
* #required#
*
* @param $servidor Servidor
* @param $usuario Usuario
* @param $clave Clave
* @param $db Schema (base de datos)
*
* @return boolean
* @access public
*/
public function conectar($servidor = false, $usuario = false, $clave = false, $db = false){
if(!class_exists('mysqli')){
$this->sError .= sprintf($this->aErrorMensajes['conectar1'])."\n";
$this->bConectado = false;
return(false);
}
ddbb::instCount();
if($servidor) $this->sDDBBServidor = $servidor;
if($usuario) $this->sDDBBUsuario = $usuario;
if($clave) $this->sDDBBPassword = $clave;
//medicion de tiempo
$iTiempoIni = $this->microtimeSeg();
$this->oMysqli->real_connect($this->sDDBBServidor, $this->sDDBBUsuario, $this->sDDBBPassword, false, 3306);
$iTiempoFin = $this->microtimeSeg();
$this->aTiempos['Conectar'] = $iTiempoFin - $iTiempoIni;
//PHP >= 5.2.9
if($this->oMysqli->connect_error){
$this->sError .= sprintf($this->aErrorMensajes['conectar2'], $this->sDDBB, $this->oMysqli->connect_errno, $this->oMysqli->connect_error)."\n";
$this->bConectado = false;
return(false);
}
//PHP < 5.2.9
elseif(mysqli_connect_error()){
$this->sError .= sprintf($this->aErrorMensajes['conectar2'], $this->sDDBB, mysqli_connect_errno(), mysqli_connect_error())."\n";
$this->bConectado = false;
return(false);
}
$this->bConectado = true;
return($this->seleccionarDDBB($db));
}
/**
* Desconexion del servidor.
*
* @return boolean
* @access public
*/
public function desconectar(){
if(!empty($this->oResultados)) $this->oResultados->free(); //fuerza liberacion de memoria, solo sirve para "SELECT" (devuelve booleano)
if(ddbb::instCount(-1) == 0){
//PHP >= 5.2.9
if(!$this->oMysqli->close()){
if(!$this->bSeleccion) $this->sError .= sprintf($this->aErrorMensajes['desconectar1'], $this->sDDBB, $this->oMysqli->connect_errno, $this->oMysqli->connect_error)."\n";
$this->bConectado = true;
$sReturn = false;
}
//PHP < 5.2.9
/*elseif(!mysqli_close()){
if(!$this->bSeleccion) $this->sError .= sprintf($this->aErrorMensajes['desconectar1'], $this->sDDBB, mysqli_connect_errno(), mysqli_connect_error())."\n";
$sReturn = false;
}*/
else $sReturn = true;
}
else $sReturn = true;
$this->bConectado = false;
return($sReturn);
}
/**
* Seleccion de base de datos.
*
* @param $db Base de datos
* @return boolean
* @access public
*/
public function seleccionarDDBB($db = false){
if($db !== false){
//if($this->sDDBB == $db) return(true); //sale con cierto si ya esta seleccionada la base de datos
$this->sDDBB = $db;
}
else return(true); //devuelve cierto si no se requiere seleccionar una base de datos concreta, como cuando se desee crear una
//medicion de tiempo
$iTiempoIni = $this->microtimeSeg();
$this->bSeleccion = $this->oMysqli->select_db($this->sDDBB);
$iTiempoFin = $this->microtimeSeg();
$this->aTiempos['SeleccionarDDBB'] = $iTiempoFin - $iTiempoIni;
if(!$this->bSeleccion) $this->sError .= sprintf($this->aErrorMensajes['seleccionarDDBB1'], $this->sDDBB, $this->oMysqli->errno, $this->oMysqli->error)."\n";
//codificacion en que se lanzaran las consultas a la base de datos
//es equivalente a las tres sentencias:
/*SET character_set_client = x;
SET character_set_results = x;
SET character_set_connection = x;*/
/*estas muestran los valores de las anteriores
SHOW VARIABLES LIKE 'character_set%';
SHOW VARIABLES LIKE 'collation%';
*/
//if(ddbb::instCount(0) == 1 && $this->sCharset != '') $this->oMysqli->query("SET CHARACTER SET '".$this->sCharset."'");
if(ddbb::instCount(0) == 1 && $this->sCharset != '') $this->oMysqli->query("SET NAMES '$this->sCharset' COLLATE '$this->sCollation'");
return($this->bSeleccion);
}
/**
* Devuelve las banderas de los campos.
* Tomada de phpMyAdmin.
*
* @param object mysqli result $result
* @param integer $i field
* @return string field flags
* @access private
*/
private function fieldFlags($i){
// This is missing from PHP 5.2.5, see http://bugs.php.net/bug.php?id=44846
if(! defined('MYSQLI_ENUM_FLAG')) {
define('MYSQLI_ENUM_FLAG', 256); // see MySQL source include/mysql_com.h
}
$f = $this->oResultados->fetch_field_direct($i);
$type = $f->type;
$charsetnr = $f->charsetnr;
$f = $f->flags;
$flags = '';
if($f & MYSQLI_UNIQUE_KEY_FLAG) { $flags .= 'unique ';}
if($f & MYSQLI_NUM_FLAG) { $flags .= 'num ';}
if($f & MYSQLI_PART_KEY_FLAG) { $flags .= 'part_key ';}
if($f & MYSQLI_SET_FLAG) { $flags .= 'set ';}
if($f & MYSQLI_TIMESTAMP_FLAG) { $flags .= 'timestamp ';}
if($f & MYSQLI_AUTO_INCREMENT_FLAG) { $flags .= 'auto_increment ';}
if($f & MYSQLI_ENUM_FLAG) { $flags .= 'enum ';}
// See http://dev.mysql.com/doc/refman/6.0/en/c-api-datatypes.html:
// to determine if a string is binary, we should not use MYSQLI_BINARY_FLAG
// but instead the charsetnr member of the MYSQL_FIELD
// structure. Watch out: some types like DATE returns 63 in charsetnr
// so we have to check also the type.
// Unfortunately there is no equivalent in the mysql extension.
if(($type == MYSQLI_TYPE_TINY_BLOB || $type == MYSQLI_TYPE_BLOB || $type == MYSQLI_TYPE_MEDIUM_BLOB || $type == MYSQLI_TYPE_LONG_BLOB || $type == MYSQLI_TYPE_VAR_STRING || $type == MYSQLI_TYPE_STRING) && 63 == $charsetnr) { $flags .= 'binary ';}
if($f & MYSQLI_ZEROFILL_FLAG) { $flags .= 'zerofill ';}
if($f & MYSQLI_UNSIGNED_FLAG) { $flags .= 'unsigned ';}
if($f & MYSQLI_BLOB_FLAG) { $flags .= 'blob ';}
if($f & MYSQLI_MULTIPLE_KEY_FLAG) { $flags .= 'multiple_key ';}
if($f & MYSQLI_UNIQUE_KEY_FLAG) { $flags .= 'unique_key ';}
if($f & MYSQLI_PRI_KEY_FLAG) { $flags .= 'primary_key ';}
if($f & MYSQLI_NOT_NULL_FLAG) { $flags .= 'not_null ';}
return(trim($flags));
}
/**
* Lista de los campos de la consulta
* Devuelve tanto por posicion en el recordset de resultados como por nombre de campo.
*
* @return boolean
* @access private
*/
private function listaCampos(){
$this->aCampos = array(); //se vacia, por si se vuelve a llamar la funcion
// Build an associative array for a type look up
$typeAr = array();
$typeAr[MYSQLI_TYPE_DECIMAL] = 'real';
$typeAr[MYSQLI_TYPE_NEWDECIMAL] = 'real';
$typeAr[MYSQLI_TYPE_BIT] = 'int';
$typeAr[MYSQLI_TYPE_TINY] = 'int';
$typeAr[MYSQLI_TYPE_SHORT] = 'int';
$typeAr[MYSQLI_TYPE_LONG] = 'int';
$typeAr[MYSQLI_TYPE_FLOAT] = 'real';
$typeAr[MYSQLI_TYPE_DOUBLE] = 'real';
$typeAr[MYSQLI_TYPE_NULL] = 'null';
$typeAr[MYSQLI_TYPE_TIMESTAMP] = 'timestamp';
$typeAr[MYSQLI_TYPE_LONGLONG] = 'int';
$typeAr[MYSQLI_TYPE_INT24] = 'int';
$typeAr[MYSQLI_TYPE_DATE] = 'date';
$typeAr[MYSQLI_TYPE_TIME] = 'time';
$typeAr[MYSQLI_TYPE_DATETIME] = 'datetime';
$typeAr[MYSQLI_TYPE_YEAR] = 'year';
$typeAr[MYSQLI_TYPE_NEWDATE] = 'date';
$typeAr[MYSQLI_TYPE_ENUM] = 'unknown';
$typeAr[MYSQLI_TYPE_SET] = 'unknown';
$typeAr[MYSQLI_TYPE_TINY_BLOB] = 'blob';
$typeAr[MYSQLI_TYPE_MEDIUM_BLOB] = 'blob';
$typeAr[MYSQLI_TYPE_LONG_BLOB] = 'blob';
$typeAr[MYSQLI_TYPE_BLOB] = 'blob';
$typeAr[MYSQLI_TYPE_VAR_STRING] = 'string';
$typeAr[MYSQLI_TYPE_STRING] = 'string';
// MySQL returns MYSQLI_TYPE_STRING for CHAR
// and MYSQLI_TYPE_CHAR === MYSQLI_TYPE_TINY
// so this would override TINYINT and mark all TINYINT as string
// https://sf.net/tracker/?func=detail&aid=1532111&group_id=23067&atid=377408
//$typeAr[MYSQLI_TYPE_CHAR] = 'string';
$typeAr[MYSQLI_TYPE_GEOMETRY] = 'unknown';
$typeAr[MYSQLI_TYPE_BIT] = 'bit';
for($i=0;$i<$this->oMysqli->field_count;$i++){
$this->aCampos[$i]['nombre'] = $this->oResultados->fetch_field_direct($i)->name;
$this->aCampos[$this->aCampos[$i]['nombre']]['pos'] = $i;
$this->aCampos[$this->aCampos[$i]['nombre']]['tipo'] = $this->aCampos[$i]['tipo'] = $typeAr[$this->oResultados->fetch_field_direct($i)->type];
$this->aCampos[$this->aCampos[$i]['nombre']]['long'] = $this->aCampos[$i]['long'] = $this->oResultados->fetch_field_direct($i)->length;
$this->aCampos[$this->aCampos[$i]['nombre']]['flags'] = $this->aCampos[$i]['flags'] = $this->fieldFlags($i);
// Enhance the field objects for mysql-extension compatibilty
//$flags = explode(' ', $fields[$i]->flags);
//array_unshift($flags, 'dummy');
/*$this->aCampos[$i]->multiple_key = (int) (bool) ($fields[$i]->_flags & MYSQLI_MULTIPLE_KEY_FLAG);
$this->aCampos[$i]->primary_key = (int) (bool) ($fields[$i]->_flags & MYSQLI_PRI_KEY_FLAG);
$this->aCampos[$i]->unique_key = (int) (bool) ($fields[$i]->_flags & MYSQLI_UNIQUE_KEY_FLAG);
$this->aCampos[$i]->not_null = (int) (bool) ($fields[$i]->_flags & MYSQLI_NOT_NULL_FLAG);
$this->aCampos[$i]->unsigned = (int) (bool) ($fields[$i]->_flags & MYSQLI_UNSIGNED_FLAG);
$this->aCampos[$i]->zerofill = (int) (bool) ($fields[$i]->_flags & MYSQLI_ZEROFILL_FLAG);
$this->aCampos[$i]->numeric = (int) (bool) ($fields[$i]->_flags & MYSQLI_NUM_FLAG);
$this->aCampos[$i]->blob = (int) (bool) ($fields[$i]->_flags & MYSQLI_BLOB_FLAG);*/
}
return(true);
}
/**
* Construir query.
* si ya se ha asignado una query mediante la propiedad "sQuery" y no se desea cambiar NO llamar a esta funcion y llamar al metodo "consulta" sin parametros
* ej: $obj->fQuery("SELECT * FROM tabla WHERE campo=%s AND b=%s", array('1', '2'))
* cuando se requiera el comodin SQL "%" escribirlo doble "%%"; ej: $obj->fQuery("SELECT * FROM tabla WHERE campo='%%%s%%'", array('hola')) ->producira la salida: "SELECT * FROM tabla WHERE campo='%hola%'"
*
* @param $query Query SQL
* @param $pars Array de parametros de la query
* @param $cantidad Numero de registros a devolver
* @param $inicial Registro inicial a devolver
*
* @return void
* @access public
*/
public function query($query, $pars = array(), $cantidad = 0, $inicial = 0){
$this->oConsulta = false;
//elimina tags html y php de los parametros de la consulta
//si los parametros no van en el array "pars" no tiene efecto
//TODO ver mysqli::real_escape_string
if($this->sTagsPermitidosIns != 'todos'){
for($i=0;$i<count($pars);$i++){
$pars[$i] = strip_tags($pars[$i], $this->sTagsPermitidosIns);
}
}
//preparar las cadenas para que no den problemas SQL
if(!get_magic_quotes_gpc()){
$pars = array_map('addslashes', $pars);
}
//añade a la consulta "SQL_CALC_FOUND_ROWS", permite tener el total de filas en consultas limitadas con "SELECT FOUND_ROWS()", sin volver a lanzar la consulta
if($cantidad){
$iSelectPos = strpos(strtoupper($query), 'SELECT ');
if($iSelectPos !== false && strpos($query, 'SQL_CALC_FOUND_ROWS') === false) $query = substr($query, 0, $iSelectPos + 7).'SQL_CALC_FOUND_ROWS '.substr($query, $iSelectPos + 7);
//si "$inicial=0" devuelve "$cantidad" de registros desde el primero
//si se quieren devolver todos los que haya desde un "$inicial=x", dar como "$cantidad" un valor mayor al total de registros que pueda devolver la consulta (un numero al azar suficientemente grande, por ejemplo 123456789123456789123456789)
$query .= ' LIMIT '.$inicial.','.$cantidad;
}
$this->tipoQuery($query); //comprueba el tipo de query
$query = $this->sQuery;
//si se pasan parametros separados se construye la consulta con el objeto MySQLi_STMT
$this->oConsulta = false;
if(!empty($pars)){
$query = str_replace("'?'", '?', vsprintf($query, array_fill(0 ,count($pars) ,'?'))); //el formato correcto para objetos mysql_stmt indica parametros con ?, no con %s; tambien se retiran los apostrofes, mysql_stmt::bind_param se ocupara de ponerlos
if($this->oUltimaQuery != $query){
$this->oUltimaQuery = $query;
if(!($this->oConsulta = $this->oMysqli->prepare($this->oUltimaQuery))){
$this->sError .= sprintf($this->aErrorMensajes['query1'], $this->oMysqli->errno, $this->oMysqli->error)."\n";
return(false);
}
}
//TODO aun no se contemplan tipos de datos distintos de string
//TODO comprobar tipo de dato, si muy grande (text o blob) necesitara mysqli_stmt::send_long_data
if(!call_user_func_array(array($this->oConsulta, 'bind_param'), array_merge(array(str_repeat("s", count($pars))), $pars))){
ERROR -> call_user_func_array parece no funcionar bien con un objeto de objeto, ya que construye $this->oConsulta::bind_param; sustituir por eval
//if(!$this->oConsulta->bind_param(str_repeat("s", count($pars)), extract($pars))){
$this->sError .= sprintf($this->aErrorMensajes['query2'], $this->oConsulta->errno, $this->oConsulta->error)."\n";
if($cantidad){
$this->sQuery = str_replace('SQL_CALC_FOUND_ROWS ', '', $this->sQuery); //no es necesario en caso de error y no es algo añadido por el usuario
}
return(false);
}
}
$this->sQuery = vsprintf($this->sQuery, $pars); //se construye igual esta propiedad, ya que permite ver la consulta formada para depuracion y se utiliza igual cuando no se pasan parametros para la consulta
//var_dump($this->sQuery);
}
/**
* Consulta a la base de datos, (si "SELECT", necesita de "leeFila()" para empezar a devolver resultados).
* #required#
*
* @param $query Query SQL
* @param $pars Array de parametros de la query
* @param $cantidad Numero de registros a devolver
* @param $inicial Registro inicial a devolver
*
* @return boolean
* @access public
*/
public function consulta($query = false, $pars = array(), $cantidad = 0, $inicial = 0){
if(!$this->bConectado){
if(!$this->conectar($this->sDDBBServidor, $this->sDDBBUsuario, $this->sDDBBPassword, $this->sDDBB)) return(false);
}
if($query) $this->query($query, $pars, $cantidad, $inicial);
//medicion de tiempo
$iTiempoIni = $this->microtimeSeg();
/*si se va a recuperar gran cantidad de datos con mysqli::query se recomienda usar MYSQLI_USE_RESULT (por defecto MYSQLI_STORE_RESULT)
ej: $mysqli->query("SELECT * FROM datos", MYSQLI_USE_RESULT)
pero esto impedira lanzar funciones que interactuen con el servidor (devolviendo error 'out of sync'), como otra query*/
/*nota: mysqli::use_result recupera sin colocar en bufer (unbuffered) las filas de resultado, mas rapido pero bloquea las tablas implicadas y da problemas con el recuento de resultados*/
if(!empty($this->oResultados->num_rows)) $this->oResultados->free(); //importante cuando se usa mysqli::store_result
if(!empty($this->oConsulta)){
if($this->oConsulta->execute())
$this->oResultados = $this->oConsulta->result_metadata();
else{
$this->sError .= sprintf($this->aErrorMensajes['consulta1'], $this->sTipoQuery, $this->oMysqli->errno, $this->oMysqli->error)."\n";
if($cantidad){
$this->sQuery = str_replace('SQL_CALC_FOUND_ROWS ', '', $this->sQuery); //no es necesario en caso de error y no es algo añadido por el usuario
}
return(false);
}
}
else{
if($this->oMysqli->real_query($this->sQuery))
$this->oResultados = $this->oMysqli->store_result(); //recurso de resultados //mysqli_stmt::result_metadata
else{
$this->sError .= sprintf($this->aErrorMensajes['consulta1'], $this->sTipoQuery, $this->oMysqli->errno, $this->oMysqli->error)."\n";
if($cantidad){
$this->sQuery = str_replace('SQL_CALC_FOUND_ROWS ', '', $this->sQuery); //no es necesario en caso de error y no es algo añadido por el usuario
}
return(false);
}
}
//seleccionar una base de datos despues de crearla
if($this->sTipoQuery == 'create' && $this->sSubTipoQuery == 'database') $this->seleccionarDDBB($this->aTablas[0]); //despues de crear una base de datos la selecciona para las siguientes sentencias
if($this->sTipoQuery == 'use') $this->seleccionarDDBB($this->aTablas[0]); //use `database`
$iTiempoFin = $this->microtimeSeg();
$this->aTiempos[$this->sTipoQuery] = $iTiempoFin - $iTiempoIni;
switch($this->sTipoQuery){
case 'describe':
case 'explain':
case 'select':
case 'show':
if($cantidad){
//mysqli::num_rows
//medicion de tiempo
$iTiempoIni = $this->microtimeSeg();
//calculo de resultados para consultas con LIMIT
$oTempMysqli = new mysqli($this->sDDBBServidor, $this->sDDBBUsuario, $this->sDDBBPassword, $this->sDDBB);
$oResultados = $oTempMysqli->query('SELECT FOUND_ROWS() AS totalRows');
$aResultados = $oResultados->fetch_array();
$this->iTotalFilas = $aResultados['totalRows'];
$this->sQuery = str_replace('SQL_CALC_FOUND_ROWS ', '', $this->sQuery); //no es necesario despues de calcular el total de filas y no es algo añadido por el usuario
$oResultados->close();
$oTempMysqli->close();
$iTiempoFin = $this->microtimeSeg();
$this->aTiempos['totalLimit'] = $iTiempoFin - $iTiempoIni;
}
else $this->numResultados(); //calculo del total de resultados
$this->listaCampos(); //lista de campos de la consulta
break;
case 'insert':
$this->sUltimaId = $this->oMysqli->insert_id; //-> devuelve la ultima id insertada
case 'alter':
case 'create':
case 'delete':
case 'drop':
case 'use':
case 'update':
$this->numResultados(); //calculo del total de resultados
break;
case 'lock':
case 'set':
case 'unlock':
break;
default:
$this->sError .= sprintf($this->aErrorMensajes['consulta2'], $this->sTipoQuery)."\n";
return(false);
}
return(true);
}
/**
* Calcular el numero de filas devueltas o afectadas por la query.
*
* @return boolean
* @access public
*/
public function numResultados(){
switch($this->sTipoQuery){
case 'describe':
case 'explain':
case 'select':
case 'show':
//si el numero devuelto supera el rango de integer (segun maquina y sistema operativo) se devolvera como cadena
$this->iTotalFilas = $this->oResultados->num_rows;
return(true);
break;
case 'alter':
case 'create':
case 'drop':
case 'use':
case 'delete':
case 'insert':
case 'update':
//si el numero devuelto supera el rango de integer (segun maquina y sistema operativo) se devolvera como cadena
//entero > 0 indica el numero de filas afectadas (o recuperadas, que no es el caso)
//0 indica que un UPDATE no afecto a ninguna fila, ninguna fila coincide con un WHERE o que la query aun no ha sido ejecutada
//-1 indica error
$this->iTotalFilas = $this->oMysqli->affected_rows;
if($this->iTotalFilas < 1) $this->iTotalFilas = false;
return(true);
break;
case 'lock':
case 'set':
case 'unlock':
break;
default:
$this->sError .= sprintf($this->aErrorMensajes['numResultados1'], $this->sTipoQuery)."\n";
return(false);
}
}
/**
* Recolocar puntero en el array de resultados (solo para SELECT).
*
* @param PosicionPuntero $pos
* @return boolealn
* @access public
*/
function recPuntero($pos = 0){
//mysqli_result::data_seek solo puede usarse con resultados en buffer como los obtenidos con mysqli::store_result() o mysqli:query()
if($this->iTotalFilas)
if(!$this->oResultados->data_seek($pos)){
$this->sError .= sprintf($this->aErrorMensajes['recPuntero1'], $this->oMysqli->errno, $this->oMysqli->error)."\n";
return(false);
}
return(true);
}
/**
* Lee una fila de resultados.
*
* @return boolean
* @access public
*/
public function leeFila(){
//este metodo solo tiene sentido con consultas tipo "SELECT"
if($this->sTipoQuery == 'select' || $this->sTipoQuery == 'describe' || $this->sTipoQuery == 'explain' || $this->sTipoQuery == 'show'){
if(!$this->tipoArrayQuery($this->sQueryTipoArray)) return(false); //tipo de array que devolvera la query
//medicion de tiempo
$iTiempoIni = $this->microtimeSeg();
$aTemp = $this->aFila; //conserva el ultimo array de resultados (la ultima fila), que sera pisada en el siguiente if si llega al final del recordset
if($this->aFila = $this->oResultados->fetch_array($this->sQueryTipoArray)){
$iTiempoFin = $this->microtimeSeg();
$this->aTiempos['fila'][] = $iTiempoFin - $iTiempoIni;
//elimina tags html y php de los resultados
if($this->sTagsPermitidosSel != 'todos'){
foreach($this->aFila as $key => $valor){
$this->aFila[$key] = strip_tags($valor, $this->sTagsPermitidosSel);
}
}
return(true);
}
else{
$this->aFila = $aTemp;
return(false);
}
}
else{
$this->sError .= sprintf($this->aErrorMensajes['leeFila'])."\n";
return(false);
}
}
/**
* Crea un array con todas las filas de resultados.
*
* @return boolean
* @access public
*/
public function leeFilasTodos(){
//recoloca el puntero en el inicio para obtener todos los resultados
if($this->iTotalFilas > 0) $this->recPuntero();
//este metodo solo tiene sentido con consultas tipo "SELECT"
if($this->sTipoQuery == 'describe' || $this->sTipoQuery == 'explain' || $this->sTipoQuery == 'select' || $this->sTipoQuery == 'show'){
if(!$this->tipoArrayQuery($this->sQueryTipoArray)) return(false); //tipo de array que devolvera la query
//medicion de tiempo
$iTiempoIni = $this->microtimeSeg();
if(function_exists('mysqli_result::fetch_all')) $this->aFilaTodos = $this->oResultados->fetch_all($this->sQueryTipoArray);
else{
while($this->aFilaTodos[] = $this->oResultados->fetch_array($this->sQueryTipoArray));
}
if(empty($this->aFilaTodos[count($this->aFilaTodos)-1])) array_pop($this->aFilaTodos); //el anterior bucle (si todo es correcto) colocara un false en el ultimo elemento, sobra
//TODO
//elimina tags html y php de los resultados
/*if($this->sTagsPermitidosSel != 'todos'){
foreach($this->aFila as $key => $valor){
($this->aFila[$key] = strip_tags($valor, $this->sTagsPermitidosSel);
}
}*/
$iTiempoFin = $this->microtimeSeg();
$this->aTiempos['filasTodos'] = $iTiempoFin - $iTiempoIni;
return(true);
}
else{
$this->sError .= sprintf($this->aErrorMensajes['leeFilasTodos'])."\n";
return(false);
}
}
/**
* Tipo de array que devolvera la query.
*
* @param $tipo Tipo de array que devolvera la consulta
* @return boolean
* @access private
*/
private function tipoArrayQuery($tipo = ''){
if(!empty($tipo)) $this->sQueryTipoArray = $tipo;
//tambien admite las constantes de mysql (no i)
switch($this->sQueryTipoArray){
case 'dos': //devuelve array asociativo y numerico
case MYSQL_BOTH:
$this->sQueryTipoArray = MYSQLI_BOTH;
case MYSQLI_BOTH:
break;
case 'aso': //devuelve array asociativo
case MYSQL_ASSOC:
$this->sQueryTipoArray = MYSQLI_ASSOC;
case MYSQLI_ASSOC:
break;
case 'num': //devuelve array numerico
case MYSQL_NUM:
$this->sQueryTipoArray = MYSQLI_NUM;
case MYSQLI_NUM:
break;
default:
$this->sError .= sprintf($this->aErrorMensajes['tipoQuery'], $this->sQueryTipoArray)."\n";
return(false);
}
return(true);
}
}
/*
ejemplo de uso:
es preferible usar el metodo descrito en "class.ddbb.inc"
include_once("./inc/class.ddbb.inc");
include_once("./inc/class.ddbb_mysql.inc");
$oTest = new ddbb_mysqli(DB_SERVER, DB_USER, DB_PASSWORD, DB_DATABASE);
//$oTest->conectar(DB_SERVER, DB_USER, DB_PASSWORD, DB_DATABASE); //se puede omitir si se han pasado los parametros de conexion al constructor
$oTest->consulta("SELECT * FROM usuarios");
while($oTest->leeFila()){
echo($oTest->aFila['login']."\n");
}
//var_dump($oTest->aTiempos);
$oTest->desconectar();
*/
?>