Location: PHPKode > scripts > ddbb > class.ddbb_mysqli.inc
<?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();
*/
?>
Return current item: ddbb