1. Objeto DBQuery
Ahora, nuestro objeto DBQuery simplemente emula un procedimiento almacenado: una vez ejecutado, devuelve un recurso de resultado que debe guardarse y si desea utilizar funciones en el conjunto de resultados (como num_rows() o fetch_row() ); ), debes pasar el objeto MySqlDB. Entonces, ¿cuál es el efecto si el objeto DBQuery implementa las funciones implementadas por el objeto MySqlDB (que está diseñado para operar sobre los resultados de una consulta ejecutada)? Sigamos usando el código del ejemplo anterior y supongamos que nuestros recursos de resultados ahora están administrados por un objeto DBQuery. El código fuente de la clase DBQuery se muestra en el Listado 1.
Listado 1. Usando la clase DBQuery.
requiere 'mysql_db.php';
require_once 'query.php';
$db = nuevo MySqlDb;
$db->connect('host', 'nombre de usuario', 'contraseña');
$db->query('usar content_management_system');
$consulta = nueva DBQuery($db);
$query->prepare('SELECT fname,sname FROM usuarios DONDE nombre de usuario=:1S AND pword=:2S AND expire_time<:3I');
intentar {
if($consulta->execute("visualad", "delantal", tiempo()))->num_rows() == 1) {
echo('Credenciales correctas');
} demás {
echo('Credenciales incorrectas/Sesión caducada');
}
} captura (QueryException $e) {
echo('Error al ejecutar la consulta: ' . $e);
}
Lo que más nos interesa en el código modificado anterior es la declaración de captura y la declaración de ejecución.
· La instrucción de ejecución ya no devuelve un recurso de resultado, ahora devuelve el propio objeto DBQuery.
· El objeto DBQuery ahora implementa la función num_rows(), con la que ya estamos familiarizados desde la interfaz DB.
· Si falla la ejecución de la consulta, genera una excepción de tipo QueryException. Cuando se convierte en una cadena, devuelve los detalles del error ocurrido.
Para hacer esto, necesita usar un proxy. De hecho, ya está utilizando servidores proxy en nuestro objeto DBQuery, pero ahora los utilizará con mayor profundidad para vincularlos estrechamente al objeto MySqlDB. El objeto DBQuery se ha inicializado con un objeto que implementa la interfaz DB y ya contiene una función miembro de ejecución, que llama al método query() del objeto DB para ejecutar la consulta. El objeto DBQuery en sí en realidad no consulta la base de datos, deja esta tarea al objeto DB. Este es un proxy, que es un proceso mediante el cual un objeto puede implementar un comportamiento específico enviando mensajes a otro objeto que implementa el mismo comportamiento o uno similar.
Para hacer esto, necesita modificar el objeto DBQuery para incluir todas las funciones que operan en un recurso de resultado del objeto DB. Debe utilizar los resultados almacenados al ejecutar una consulta para llamar a la función correspondiente del objeto DB y devolver sus resultados. Se agregarán las siguientes funciones:
Listado 2: Ampliar la clase DBQuery usando proxies.
claseDBQuery
{
.....
función pública fetch_array()
{
si (! is_resource($this->resultado)) {
lanzar nueva excepción ('Consulta no ejecutada');
}
devolver $this->db->fetch_array($this->resultado);
}
función pública fetch_row()
{
si (! is_resource($this->resultado)) {
lanzar nueva excepción ('Consulta no ejecutada');
}
return $this->db->fetch_row($this->resultado);
}
función pública fetch_assoc()
{
si (! is_resource($this->resultado)) {
lanzar nueva excepción ('Consulta no ejecutada');
}
return $this->db->fetch_assoc($this->resultado);
}
función pública fetch_object()
{
si (! is_resource($this->resultado)) {
lanzar nueva excepción ('Consulta no ejecutada');
}
return $this->db->fetch_object($this->resultado);
}
función pública num_rows()
{
si (! is_resource($this->resultado)) {
lanzar nueva excepción ('Consulta no ejecutada');
}
return $this->db->num_rows($this->resultado);
}
}
La implementación de cada función es bastante simple. Primero verifica que la consulta se haya ejecutado, luego delega la tarea al objeto de base de datos y devuelve sus resultados como si fuera el objeto de consulta en sí (lo que se denomina función básica de base de datos).
2. Sugerencias de tipo
Para que el proxy funcione, debemos asegurarnos de que la variable $db del objeto DBQuery sea una instancia de un objeto que implemente la interfaz DB. Las sugerencias de tipo son una característica nueva en PHP 5 que le permite convertir parámetros de funciones en objetos de un tipo específico. Antes de PHP 5, la única forma de garantizar que un parámetro de función fuera un tipo de objeto específico era utilizar la función de verificación de tipos proporcionada en PHP (es decir, is_a()). Ahora, puede simplemente convertir un tipo de objeto, anteponiendo al parámetro de función el nombre del tipo. Ya ha visto sugerencias de tipo de nuestro objeto DBQuery, que garantiza que un objeto que implementa la interfaz DB se pase al constructor del objeto.
función pública __construct(DB $db)
{
$this->db = $db;
}
Al utilizar sugerencias de tipos, puede especificar no sólo tipos de objetos, sino también clases e interfaces abstractas.
3. Lanzar excepciones
Es posible que hayas notado en el código anterior que lo que detectas es una excepción llamada QueryException (implementaremos este objeto más adelante). Una excepción es similar a un error, pero más general. La mejor manera de describir una excepción es utilizar emergencia. Aunque una emergencia puede no ser "mortal", aun así hay que abordarla. Cuando se lanza una excepción en PHP, el alcance de ejecución actual finaliza rápidamente, ya sea una función, un bloque try..catch o el script en sí. Luego, la excepción atraviesa la pila de llamadas (terminando cada alcance de ejecución) hasta que queda atrapada en un bloque try..catch o llega a la parte superior de la pila de llamadas, momento en el que genera un error fatal.
El manejo de excepciones es otra característica nueva en PHP 5. Cuando se usa junto con la programación orientada a objetos, puede lograr un buen control sobre el manejo y los informes de errores. Un bloque try..catch es un mecanismo importante para manejar excepciones. Una vez detectada, la ejecución del script continuará desde la siguiente línea de código donde se detectó y manejó la excepción.
Si la consulta falla, debe cambiar su función de ejecución para generar una excepción. Lanzará un objeto de excepción personalizado llamado QueryException: se le pasa el objeto DBQuery que causó el error.
Listado 3. Lanza una excepción.
/**
*Ejecutar consulta actual
*
* Ejecute la consulta actual, reemplazando los puntos con los argumentos proporcionados.
* .
*
* @parameters: mixto $queryParams,... parámetros de consulta
* @return: Recurso A: referencia que describe el recurso en el que se ejecuta la consulta.
*/
función pública ejecutar ($queryParams = '')
{
//Por ejemplo: SELECCIONAR * DE la tabla DONDE nombre=:1S Y tipo=:2I Y nivel=:3N
$args = func_get_args();
si ($this->procedimiento_almacenado) {
/*Llama a la función de compilación para obtener la consulta*/
$consulta = call_user_func_array(array($this, 'compilar'), $args);
} demás {
/*Un procedimiento almacenado no ha sido inicializado, por lo tanto, se ejecuta como una consulta estándar*/
$consulta = $queryParams;
}
$resultado = $this->db->query($consulta);
si (! $resultado) {
lanzar una nueva QueryException($this);
}
$este->resultado = $resultado;
/* Observe cómo ahora devolvemos el objeto en sí, lo que nos permite llamar a funciones miembro desde el resultado devuelto de esta función */
devolver $esto;
}
4. Utilice la herencia para generar excepciones personalizadas
En PHP, puede generar cualquier objeto como una excepción; sin embargo, primero, la excepción debe heredar de la clase de excepción incorporada de PHP; Al crear su propia excepción personalizada, puede registrar otra información sobre el error, crear una entrada en un archivo de registro o hacer lo que quiera. Su excepción personalizada hará lo siguiente:
· Registra el mensaje de error del objeto de base de datos generado por la consulta.
· Proporcione detalles precisos de la línea de código donde ocurrió el error de consulta, examinando la pila de llamadas.
· Muestra mensajes de error y texto de consulta, cuando se convierte en una cadena.
Para obtener el mensaje de error y el texto de la consulta, se deben realizar varios cambios en el objeto DBQuery.
1. Es necesario agregar un nuevo atributo protegido (compiledQuery) a la clase.
2. La función compile() actualiza la propiedad compiledQuery de la consulta con el texto de la consulta.
3. Se debe agregar una función para recuperar el texto de consulta compilado.
4. También se debe agregar una función: obtiene el objeto DB actual asociado con el objeto DBQuery.
Listado 4. Lanzar una excepción.
claseDBQuery
{
/**
*Almacena la versión compilada de la consulta después de llamar a compilar() o ejecutar()*
* @var cadena $compiledQuery
*/
protegido $consultacompilada;
/**
* Devuelve la consulta compilada sin ejecutarla.
* @parameters: $params mixtos,...parámetros de consulta* @return: cadena—consulta compilada*/
compilación de función pública ($params='')
{
si (! $this->procedimiento_almacenado) {
throw new Exception ("El procedimiento almacenado no se ha inicializado.");
}
/*reemplazando parámetros*/
$params = func_get_args(); //Obtener parámetros de función $query = preg_replace("/(?compile_callback($params, 1, "2")', $this->query);
return ($this->compiledQuery = $this->add_strings($query)); //Vuelve a colocar la cadena en la consulta}
función pública getDB()
{
devolver $this->db;
}
función pública getCompiledQuery()
{
devolver $this->compiledQuery;
}
}
Ahora puede implementar la clase QueryException. Observe cómo recorre la pila de llamadas para encontrar la ubicación en el script que realmente causó el error. Este es exactamente el caso cuando el objeto DBQuery que genera la excepción es una subclase que hereda del objeto DBQuery.
Listado 5: Clase QueryException.
/**
*Excepción de consulta
*
*Al intentar ejecutar una consulta, si se produce un error, el objeto {@link DBQuery} generará un error.
*/
clase QueryException extiende la excepción
{
/**
*Texto de consulta*
* @var cadena $QueryText;
*/
protegido $QueryText;
/**
*Número/código de error de la base de datos*
* @var cadena $Código de error
*/
protegido $NúmeroError;
/**
*Mensaje de error de la base de datos*
* @var cadena $Mensaje de error
*/
protegido $Mensaje de error;
/**
*Constructor de clases*
* @Parameter: DBQuery $db, que es el objeto de consulta que arroja la excepción */
función pública __construct(DBQuery $consulta)
{
/*Obtener la pila de llamadas*/
$retroceso = $this->GetTrace();
/*Establezca la línea y el archivo en la ubicación donde realmente ocurrió el error*/
si (cuenta ($ retroceso) > 0) {
$x = 1;
/*Si la clase de consulta se hereda, entonces debemos ignorar las llamadas realizadas por las subclases*/
while((! isset($backtrace[$x]['line'])) ||
(isset($backtrace[$x]['clase']) && is_subclass_of($backtrace[$x]['clase'], 'DBQuery')) ||
(strpos(strtolower(@$backtrace[$x]['función']), 'call_user_func')) !== false ) {
/*Ejecución en bucle, siempre que no haya un número de línea o la función llamada sea una subclase de la clase DBQuery*/
++$x;
/*Si llegamos al final de la pila, entonces usamos la primera persona que llama*/
si (($x) >= contar($rastreo inverso)) {
$x = contar($rastreo inverso);
romper;
}
}
/*Si el bucle anterior se ejecuta al menos una vez, entonces podemos disminuirlo en 1 para encontrar la línea de código real que causó el error*/
si ($x! = 1) {
$x-= 1;
}
/*Finalmente, podemos configurar los números de archivo y línea, que deberían reflejar la declaración SQL que causó el error*/
$this->line = $backtrace[$x]['line'];
$this->file = $backtrace[$x]['archivo'];
}
$this->QueryText = $consulta->getCompiledQuery();
$this->ErrorNumber = $consulta->getDB()->errno();
$this->ErrorMessage = $consulta->getDB()->error();
/*Llamar al constructor de excepciones de la superclase*/
parent::__construct('Error de consulta', 0);
}
/**
*obtener texto de consulta*
* @return texto de consulta de cadena */
función pública GetQueryText()
{
devolver $this->QueryText;
}
/**
*obtuve el número de error*
* @return número de error de cadena */
función pública GetErrorNumber()
{
devolver $this->NúmeroError;
}
/**
*recibí un mensaje de error*
* Mensaje de error de cadena @return */
función pública GetErrorMessage()
{
devolver $this->Mensaje de error;
}
/**
* Se llama cuando el objeto se convierte en una cadena.
* @cadena de retorno */
función pública __toString()
{
$output = "Error de consulta en {$this->file} en la línea {$this->line}nn";
$salida .= "Consulta: {$this->QueryText}n";
$
salida .= "Error: {$this->ErrorMessage} ({$this->ErrorNumber})nn";
}
}
Ahora el código que viste al principio de esta sección funciona.
5. Conclusión
En este artículo, vio cómo el agente asigna la interfaz de base de datos asociada con la consulta a operaciones en un resultado de consulta específico. Los objetos DBQuery exponen las mismas funciones, como fetch_assoc(), que los objetos DB. Sin embargo, todos estos funcionan para una sola consulta. También aprendió a utilizar excepciones personalizadas para brindar información detallada sobre cuándo y dónde ocurrió un error, y cómo pueden controlar mejor el manejo de errores.