La Página de DriverOp

Clase PHP para manejo de base de datos.

Descripción, guía de referencia de propiedades y métodos de la clase para acceso a base de datos MySQL, DBUTIL, en PHP 5.x. Ejemplos de uso.

Índice

Introducción.

Desde hace mucho tiempo, más del que puedo recordar, he reconocido ciertos patrones a la hora de programar acceso al motor de base de datos MySQL desde PHP5. Esto me ha permitido crear una clase que implementa las funciones más comunes. A lo largo del tiempo esta clase la he estado modificando para agregar funciones y reparar fallo o simplemente mejorando sus funciones. Llegado a un punto la clase se ha mantenido suficientemente estable debido a que hace lo que tiene que hacer y lo hace bien.

Debido a esto y siguiendo la lógica de que "si me es útil a mi, probablemente lo sea para alguien más", he decidido hoy dar libremente esta clase a todos los programadores que la necesiten y le encuentren la misma utilidad que le encuentro yo.

La clase está implementada en un archivo llamado "class.dbutil.inc.php", la versión que doy aquí es la última y está numerada como 1.2. Ha sido pensada para PHP 5.x y no funcionará en versiones anteriores (simplemente porque en versiones anteriores no existe el concepto de programación orientada a objetos), la he usado exitósamente en servidores MySQL versión 4.3, 5.0, 5.2 y 5.5 y probablemente en versiones futuras.

Eres libre de usar, modificar, hacer trabajos derivados, de acuerdo a la licencia LGPL, tal como se describe aquí: http://www.gnu.org/licenses/lgpl.html.

Con esta clase puedes conectar y desconectar el servidor MySQL, ejecutar cualquier instrucción SQL, navegar los resultados, insertar, modificar y borrar registros, y obtener información relacionada; todo esto usando la programación orientada a objetos de PHP.

Es responsabilidad del programador que usa esta clase, verificar los datos de entrada proporcionados por los usuarios de su aplicación o sitio web. Esta clase no contiene ningún mecanismo de seguridad para evitar ataques del tipo "SQL Injection".

Reportar errores y mejoras.

Si usando esta clase encuentras algún error en ella o haz implementado alguna mejora o extensión a la misma, me gustaría que me lo comuniques usando mi formulario de contacto.

En cambio, si tienes alguna consulta técnica, o comentario relativo al uso de esta clase, por favor usa el sistema de comentarios que está al pie de este mismo artículo.

Descarga.

Haz clic en el enlace de más abajo para descargar DBUtil Ver. 1.2 para PHP5. El archivo descargado "dbutil.zip" es un archivo comprimido ZIP que contiene los archivos "class.dbutil.inc.php", "LICENSE.TXT", siete programas PHP de ejemplos y unas tablas SQL listas para importar (usadas en los ejemplos), pesa aproximádamente 7,94 KB. Por favor no enlaces directamente el archivo sino a esta misma página que estás leyendo.

DBUtil.zipDBUtil ver 1.2 para PHP5 (7,94 KB)

Propiedades.

Estas son las propiedades públicas de la clase. Entre corchetes rectos se mencionan los métodos que modifican la propiedad.

Tipo: recurso MySQL.

Puntero al recurso MySQL. Es el vínculo entre la aplicación y el servidor MySQL que se estableció usando Connect(). Vale NULL si no hay conexión abierta o fue errónea.

errmsg

Tipo: string.

Mensaje literal del último error producido. El mensaje es propio de MySQL.

error

Tipo: boolean.

Bandera que se establece si hubo (true) o no hubo (false) un error al ejecutar la última operación sobre la base de datos.

errno

Tipo: int.

Código del último error detectado durante la última operación sobre la base de datos. Vale cero si no hubo error.

numrows

Tipo: int.

Cantidad de registros devueltos en la última operación SELECT [Query(), SeekBy()]

affectedrows

Tipo: int.

Cantidad de registros afectados por la última operación UPDATE, o cantidad de registros agregados en la última operación INSERT, o registros eliminados en la última operación DELETE [Update(), Insert(), Delete()]

result

Tipo: recurso MySQL.

Puntero al último conjunto de resultado devuelto por la última operación SELECT [Query(), SeekBy()]

last_id

Tipo: int.

Valor de la última clave primaria autoincremental que se generó luego de la última operación INSERT [Insert()].

lastsql

Tipo: string.

Contiene la instrucción SQL exacta, tal como se envió al servidor, de la última operación realizada [Query(), Update(), Insert(), Delete(), SeekBy()]

Métodos

(constructor)(dbhost, dbname, dbuser, dbpass)

Tipo devuelto: una instancia de la clase.

Parámetros:

  • dbhost: string. Nombre o dirección IP del servidor MySQL al que debe conectarse.
  • dbname: string. Nombre de la base de datos con la cual se debe trabajar.
  • dbuser: string. Nombre de usuario MySQL.
  • dbpass: string. Contraseña del usuario MySQL.

Los parámetros son opcionales, pero si se especifica uno, deben especificarte todos los demás. Internamente llama al método Connect() para establecer la conexión.

CheckError()

Tipo devuelto: boolean.

Parámetros: ninguno.

Establece las propiedades error, errno y errmsg según el estado de error de la última operación SQL. Devuelve el valor de la propiedad error.

Este método se usa internamente pero puede ser llamado públicamente en cualquier momento.

Connect(dbhost, dbname, dbuser, dbpass)

Tipo devuelto: boolean.

Parámetros:

  • dbhost: string. Nombre o dirección IP del servidor MySQL al que debe conectarse.
  • dbname: string. Nombre de la base de datos con la cual se debe trabajar.
  • dbuser: string. Nombre de usuario MySQL.
  • dbpass: string. Contraseña del usuario MySQL.

Los parámetros son obligatorios. Establece la conexión al servidor con los datos proporcionados en los parámetros, si fue exitosa, selecciona la base de datos.

La conexión es siempre una nueva conexión.

Si la conexión fue exitosa devuelve true, caso contrario false. Consultar el valor de errmsg para saber cuál fue el error.

ToDo: Poner un quinto parámetro para indicar si debe crearse una nueva conexión o usar una ya establecida. Esto podría ayudar con los hostings que tienen esta limitante.

Disconnect()

Tipo devuelto: ninguno.

Parámetros: ninguno.

Cierra la conexión con el servidor MySQL.

IsConnected()

Tipo devuelto: boolean.

Parámetros: ninguno.

Permite conocer el estado de la conexión. Devuelve true si la conexión está activa o bién false si la conexión está cerrada.

Tipo devuelto: recurso MySQL.

Parámetros: ninguno.

Devuelve el enlace de la conexión al servidor o NULL en caso que no esté conectado.

Query(sql)

Tipo devuelto: recurso MySQL.

Parámetros:

  • sql: string. Instrucción SQL a ser ejecutada.

Permite mandar a ejecutar cualquier instrucción SQL. Devuelve un puntero al conjunto de resultado si los hay. Se hace cargo de actualizar el estado de error y la cantidad de registros devueltos (numrows) o afectados (affectedrows) por la instrucción.

Es quizá, el método principal de la clase. Generalmente se usa para ejecutar la instrucción SELECT.

Update(tabla, lista, where = "")

Tipo devuelto: boolean.

Parámetros:

  • tabla: string. Nombre de la tabla a actualizar.
  • lista: array. Debe ser un array asociativo donde el o los índices deben ser nombres de campos de la tabla y los valores del array, los valores que se actualizarán para esos campos.
  • where: string. Lo que se pase en este parámetro se agregará a la cláusula WHERE. Este parámetro es opcional.

Este método no es más que un envoltorio para la instrucción UPDATE de SQL. Conveniente para no tener que recordar la sintáxis de esa instrucción.

El parámetro lista puede usarse como un array "al vuelo":

	$db->Update("clientes", array("nombre"=>"Diego"),"`id` = 1");

O pasar un array ya declarado:

	$cliente = array();
	$cliente['nombre'] = "Diego";
	$cliente['apellido'] = "Romero";
	$db->Update("clientes", $cliente,"`id` = 1");

Donde el índice del array es el nombre del campo en la tabla que se quiere actualizar su valor.

Este método devuelve true si no hubo errores al ejecutar la instrucción, o false en caso contrario. Consultar la propiedad errmsg para saber cuál fue el error, y affectedrows para saber cuántos registros se modificaron.

Este método genera el número de error -1 si el segundo parámetro no es un array.

Insert(tabla, lista)

Tipo devuelto: boolean.

Parámetros:

  • tabla: string. Nombre de la tabla donde se insertará el registro.
  • lista: array. Debe ser un array asociativo donde el o los índices deben ser nombres de campos de la tabla y los valores del array, los valores para esos campos.

Este método no es más que un envoltorio de la instrucción INSERT de SQL.

El parámetro lista debe ser un array que contenga todos los campos y sus valores. Se producirá un error si en ese array se omiten campos que en la tabla no tienen valores por omisión.

Al igual que en el método Update() el segundo parámetro puede ser un array "al vuelo" o uno ya declarado.

Este método devuelve true si no hubo errores al ejecutar la instrucción, o false en caso contrario. Consultar la propiedad errmsg para saber cuál fue el error, y affectedrows para saber cuántos registros se insertaron. Consultar la propiedad last_id para saber el valor generado para un campo AUTO_INCREMENT, usualmente la clave primaria.

Este método genera el número de error -1 si el segundo parámetro no es un array.

Delete(tabla, where)

Tipo devuelto: boolean.

Parámetros:

  • tabla: string. Nombre de la tabla donde se insertará el registro.
  • where: string. Condicionales para la cláusula WHERE.

Este método no es más que un envoltorio de la instrucción DELETE de SQL.

Aunque la instrucción DELETE de SQL no requiere de la cláusula WHERE, ejecutarla sin ella provoca que todos los registros de la tabla se eliminen. Como medida de seguridad, el parámetro where es obligatorio.

Este método devuelve true si no hubo errores al ejecutar la instrucción, o false en caso contrario. Consultar la propiedad errmsg para saber cuál fue el error, y affectedrows para saber cuántos registros se eliminaron.

First(res = NULL)

Tipo devuelto: array.

Parámetros:

  • res: puntero a un conjunto de resultados. Este parámetro es opcional.

Este método devuelve el primer registro del conjunto de resultados generado por una consulta SELECT y establece el índice de lectura a ese primer registro. Para extraer los siguientes registros se debe llamar al método Next().

La clase mantiene un puntero al último conjunto de resultados generado por la instrucción SELECT usando el método Query(). Si se omite el parámetro res, éste método usará el puntero de la clase (que es la propiedad result), en cambio, si se le pasa como parámetro otro puntero, usará ese.

El resultado de este método es un array asociativo donde los índices del array son los nombres de los campos extraidos de la consulta SQL, y sus valores, los valores de esos campos.

En caso que la consulta no haya regresado ningún registro, y por tanto el conjunto de resultados esté vacío, este método devuelve FALSE.

Next(res = NULL)

Tipo devuelto: array.

Parámetros:

  • res: puntero a un conjunto de resultados. Este parámetro es opcional.

Este método devuelve el siguiente registro del conjunto de resultados generado por una consulta SELECT. Generalmente pero no obligatoriamente, se usa después de ejecutar el método First(); avanza el índice del conjunto de resultado al siguiente registro.

La clase mantiene un puntero al último conjunto de resultados generado por la instrucción SELECT usando el método Query(). Si se omite el parámetro res, éste método usará el puntero de la clase (que es la propiedad result), en cambio, si se le pasa como parámetro otro puntero, usará ese.

El resultado de este método es un array asociativo donde los índices del array son los nombres de los campos extraidos de la consulta SQL, y sus valores, los valores de esos campos.

En caso que la consulta no haya regresado ningún registro, y por tanto el conjunto de resultados esté vacío, este método devuelve FALSE. Pero también retornará FALSE cuando se alcance el final del conjunto de resultados.

Last(res = NULL)

Tipo devuelto: array.

Parámetros:

  • res: puntero a un conjunto de resultados. Este parámetro es opcional.

Este método devuelve el último registro del conjunto de resultados generado por una consulta SELECT si existe.

La clase mantiene un puntero al último conjunto de resultados generado por la instrucción SELECT usando el método Query(). Si se omite el parámetro res, éste método usará el puntero de la clase (que es la propiedad result), en cambio, si se le pasa como parámetro otro puntero, usará ese.

El resultado de este método es un array asociativo donde los índices del array son los nombres de los campos extraidos de la consulta SQL, y sus valores, los valores de esos campos.

En caso que la consulta no haya regresado ningún registro, y por tanto el conjunto de resultados esté vacío, este método devuelve FALSE.

Seek(num, res = NULL)

Tipo devuelto: array.

Parámetros:

  • num: int. Índice absoluto en el conjunto de resultados.
  • res: puntero a un conjunto de resultados. Este parámetro es opcional.

Este método devuelve el "n"esimo registro del conjunto de resultados generado por una consulta SELECT si existe.

La clase mantiene un puntero al último conjunto de resultados generado por la instrucción SELECT usando el método Query(). Si se omite el parámetro res, éste método usará el puntero de la clase (que es la propiedad result), en cambio, si se le pasa como parámetro otro puntero, usará ese.

El resultado de este método es un array asociativo donde los índices del array son los nombres de los campos extraidos de la consulta SQL, y sus valores, los valores de esos campos.

El parámetro num debe ser un entero mayor o igual a cero y menor a la cantidad de registros en el conjunto de resultados. Es decir, debe estar en el rango 0 <= num <= (numrows - 1). Señala la posición absoluta o índice interno SQL para el conjunto de resultado siendo el primer registro el que tiene el índice 0.

En caso que la consulta no haya regresado ningún registro, y por tanto el conjunto de resultados esté vacío, este método devuelve FALSE. Pero también devuelve FALSE si con el parámetro num se intenta acceder a un registro que no existe en el conjunto de resultados (por estar fuera de rango).

SeekBy(tabla, campo, valor, altorden = null)

Tipo devuelto: array.

Parámetros:

  • tabla: string. Nombre de la tabla donde buscar.
  • campo: string. Nombre del campo que se está buscando.
  • valor: string o int. Valor en el campo que se está buscando.
  • altorden: string. Nombre del campo que se usará para ordenar la búsqueda. Este parámetro es opcional.

Este es un método de conveniencia. Básicamente busca en la tabla tabla, un registro cuyo campo campo tiene el valor valor y en caso de encontrarlo lo devuelve como un array asociativo (el registro completo). En caso contrario devuelve FALSE.

Este método devuelve el primer registro que cumpla la condición antes mencionada. Si hay más registros coincidentes se debe usar el método Next() para recuperar los siguientes. Para saber cuántos registros coincidieron consultar la propiedad numrows.

En el caso de que haya más de un registro coincidente, el primer registro estará determinado por el orden natural de la clave primaria de la tabla tabla. Si se quiere encontrar el primer registro pero ordenado por otro campo de la tabla tabla debe especificarse por cuál campo hacer la ordenación en el parámetro altorden.

Es posible usar comodines SQL % o ? dentro del parámetro valor cuando el campo campo es de tipo varchar o text ya que en estos casos el método usa el operador LIKE para hacer la comparación. La comparación, además, es insensible a mayúsculas y minúsculas.

Un ejemplo de uso sería:

	$db->SeekBy("clientes", "nombre", "Diego%");

Esto sería buscar en la tabla "clientes" los registros cuyo campo "nombre" comiencen con el literal "Diego".

Este método podría producir el error 1054 en caso que campo no sea un campo de la tabla tabla.

GetNumRows(res)

Tipo devuelto: int.

Parámetros:

  • res: puntero a un conjunto de resultados.

Devuelve la cantidad de registros en el conjunto de resultados cuyo puntero se pasa como parámetro.

Es útil para saber cuántos registros hay en un conjunto de resultados diferente al que actualmente está usando la clase (ver First(), Next(), Last() y Seek()).

ShowLastError()

Tipo devuelto: ninguno.

Parámetros: ninguno

Método de conveniencia. Al ser llamado y se ha producido un error en la última operación sobre la base de datos, este método imprime el mensaje de error que generó el servidor MySQL. Si no hubo error, este método no hace nada.

SetUTF8(valor = true)

Tipo devuelto: boolean.

Parámetros:

  • valor: boolean. Por omisión vale TRUE, por tanto la transmisión hacia y desde el servidor se hará en la codificación de caracteres UTF-8, caso contrario se hará en la codificación de caracteres LATIN1. Este parámetro es opcional.

Método de conveniencia. Sirve para forzar a que la tabla de codificación de caracteres que se use durante la transmisión de datos entre la aplicación y el servidor MySQL se haga en UTF-8. Esto es útil para solucionar problemas con los caracteres no ASCII (acentos, "ñ") debido a que aunque la base de datos, las tablas y los campos estén en UTF-8, el servidor MySQL convierta esos datos a una codificación diferente.

Alternativamente, si se le pasa el valor FALSE, la codificación se hará en LATIN1.

Este método puede ser llamado en cualquier momento pero es conveniente hacerlo justo después del método Connect().

Cómo se usa: Ejemplos prácticos.

A continuación de muestran algunos ejemplos de uso más comunes que se le puede dar a la clase DBUtil. Todos los ejemplos de código mostrados a continuación más las tablas SQL usadas en estos ejemplos pueden ser descargados aquí.

Inicializar el objeto.
Ejemplo 1
<?php
	require("class.dbutil.inc.php");
	
	$db = new cDB();
	$db->Connect("localhost", "test", "usuariomysql", "contrasenamysql");
	
	if ($db->IsConnected()) {
		echo "Estoy conectado.<br />";
		/*
			Trabajar con la base de datos.
		*/
		$db->Disconnect();
	} else {
		$db->ShowLastError();
	}
	
?>

Lo primero es incluir mediante require() el archivo que contiene la definición de la clase.

Luego debe instanciarse el objeto. La variable $db (el nombre de esta variable es arbitraria, puedes usar la que tú quieras) contendrá la instancia de la clase cDB, a partir de aquí, se debe usar esa variable para acceder a las propiedades y métodos de la clase.

Usando el método Connect() se intenta conectar con el servidor MySQL pasándole como parámetros la dirección del servidor, la base de datos que se va a usar, el nombre de usuario y la contraseña correspondiente.

Se pregunta si la conexión está activa. Si lo está, se trabaja con la base de datos.

En caso de no haberse podido realizar la conexión, mostrar un mensaje de error.

Aunque los pasos explicados aquí son correctos, en lo general no es necesario verificar que la conexión se hizo ya que uno, como programador, ya sabe que el servidor MySQL responderá (a menos, claro, que el servidor sea remoto, pero ese es otro tema).

Leer una tabla.

Vamos a leer el contenido de la tabla "regiones" mostrando todos sus registros.

Ejemplo 2
<?php
	require("class.dbutil.inc.php");
	
	$db = new cDB();
	$db->Connect("localhost", "test", "usuariomysql", "contrasenamysql");
	
	$sql = "SELECT * FROM `regiones`";
	
	$db->Query($sql);
	if ($db->numrows > 0) {
		while($fila = $db->Next()) {
			echo "<pre>";
			print_r($fila);
			echo "</pre>";
		}
	} else {
		echo "La tabla regiones está vacía.<br />";
	}
	
	$db->Disconnect();
?>

Se crea el objeto.

Se conecta al servidor MySQL.

Se arma la instrucción SQL SELECT.

Se ejecuta el método Query() pasándole como parámetro la instrucción SQL.

Luego preguntamos cuántos registros devolvió la consulta, si es mayor a cero, procedemos a imprimir los registros dentro de un ciclo while. Aquí el ciclo while funciona de la siguiente manera. Ingresa al ciclo y continua ejecutando mientras el método Next() devuelva un valor distinto de FALSE asigándoselo a la variable $fila. Next() devolverá FALSE cuando no haya más registros qué leer en el conjunto de resultados.

Por lo tanto lo que se ve como salida son todos los registros de la tabla.

En caso que la consulta no devuelva ningún registro, se muestra un mensaje acorde.

Finalmente desconectamos del servidor MySQL.

Sin embargo hay una forma más eficiente de hacer lo mismo usando el método First(), como se muestra a continuación.

Ejemplo 3
<?php
	require("class.dbutil.inc.php");
	
	$db = new cDB();
	$db->Connect("localhost", "test", "usuariomysql", "contrasenamysql");
	
	$sql = "SELECT * FROM `regiones`";
	
	$db->Query($sql);
	if ($fila = $db->First()) {
		do {
			echo "<pre>";
			print_r($fila);
			echo "</pre>";
		} while($fila = $db->Next());
	} else {
		echo "La tabla regiones está vacía.<br />";
	}
	
	$db->Disconnect();
?>

La diferencia aquí es que en vez de preguntar por el valor de numrows, usamos el método First() el cual devolverá FALSE en caso que no haya registros en el conjunto de resultados, pero si hay al menos uno, asignará a $fila el primer registro y el if será verdadero, por tanto se ejecutará el ciclo do .. while. Este ciclo se ejecutará hasta que Next() no lea más registros (porque alcanzó el final).

El por qué usar do .. while y no while como en el ejemplo anterior, se debe a que de lo contrario el registro leído por First() nunca se imprimiría; mientras que el ciclo do .. while se ejecuta al menos una vez y el primer registro será impreso.

Leer una tabla con control de errores.

El momento donde es más probable que se produzca un error, especiamente en la etapa de desarrollo del software, es cuando se ejecuta una instrucción SQL.

A continuación una forma de detectar ese tipo de errores.

Ejemplo 4
<?php
	require("class.dbutil.inc.php");
	
	$db = new cDB();
	$db->Connect("localhost", "test", "usuariomysql", "contrasenamysql");
	
	$sql = "SELECT * FROM `regio-nes`";
	
	$db->Query($sql);
	if ($db->error) { $db->ShowLastError(); }
	else {
		if ($fila = $db->First()) {
			do {
				echo "<pre>";
				print_r($fila);
				echo "</pre>";
			} while($fila = $db->Next());
		} else {
			echo "La tabla regiones está vacía.<br />";
		} // if
	} // else
	$db->Disconnect();
?>

Simplemente se pregunta por el estado de la propiedad error y, si es verdadero, mostrar un mensaje de error con el método ShowLastError().

Saber si un registro existe.

Es bastante común que en determinado momento se necesite saber si en una tabla existe un registro con cierto valor en cierto campo. Usando la clase DBUtil se puede hacer eso como sigue:

Ejemplo 5
<?php
	require("class.dbutil.inc.php");
	
	$db = new cDB();
	$db->Connect("localhost", "test", "usuariomysql", "contrasenamysql");

	$sql = "SELECT * FROM `regiones` WHERE `id_pais` = 1";
	
	$db->Query($sql);
	if ($fila = $db->First()) {
		echo "<pre>";
		print_r($fila);
		echo "</pre>";
	} else {
		echo "No existe región para el país con id = 1.<br />";
	}

	$db->Disconnect();
?>

Aquí se busca un registro en la tabla "regiones" cuyo campo "id_pais" es igual a 1, tal como se ve en la instrucción SELECT.

Pero como esta tarea es bastante común, la clase encapsula un método más directo de hacerlo, el método SeekBy().. Este es el ejemplo de uso para el mismo caso:

Ejemplo 6
<?php
	require("class.dbutil.inc.php");
	
	$db = new cDB();
	$db->Connect("localhost", "test", "usuariomysql", "contrasenamysql");

	if ($fila = $db->SeekBy("regiones", "id_pais", 1)) {
		echo "<pre>";
		print_r($fila);
		echo "</pre>";
	} else {
		echo "No existe región para el país con id = 1.<br />";
	}

	$db->Disconnect();
?>
Recorrer dos tablas al mismo tiempo en una sola instancia.

En este código se muestra un ejemplo de cómo usar el parámetro res de los métodos de navegación de resultados (First(), Next()) combinados usando la misma instancia de la clase.

La idea es ejecutar dos instrucciones SELECT para obtener los registros de las tablas "regiones" y "paises", y luego recorrer esos registros por separado.

Ejemplo 7
<?php
	require("class.dbutil.inc.php");
	
	$db = new cDB();
	$db->Connect("localhost", "test", "usuariomysql", "contrasenamysql");

	$sql = "SELECT * FROM `regiones`";
	$regiones = $db->Query($sql);
	
	$sql = "SELECT * FROM `paises`";
	$paises = $db->Query($sql);
	
	if ($fila = $db->First($paises)) {
		echo "<h1>Lista de paises.</h1>";
		do {
			echo "<pre>";
			print_r($fila);
			echo "</pre>";
		} while($fila = $db->Next($paises));
	} else {
		echo "<p>No hay paises.</p>";
	}
	echo "<hr />";
	
	if ($fila = $db->First($regiones)) {
		echo "<h1>Lista de regiones.</h1>";
		do {
			echo "<pre>";
			print_r($fila);
			echo "</pre>";
		} while($fila = $db->Next($regiones));
	} else {
		echo "<p>No hay regiones.</p>";
	}

	$db->Disconnect();
?>

Esto aprovecha el hecho de que el método Query devuelve un puntero al conjunto de resultados que la instrucción SQL genera. Ese puntero puede ser usado luego en otro momento por los métodos First() y Next(). En el código esos punteros se almacenan en las variables $paises y $regiones.

Por Diego Romero,