una preparadas obtener insertar hacer example ejemplos datos consultas consulta con php sql pdo

preparadas - pdo php ejemplos



¿Cómo depurar las consultas de bases de datos PDO? (17)

Antes de pasar a PDO, creé consultas SQL en PHP concatenando cadenas. Si obtengo un error de sintaxis en la base de datos, podría hacer eco de la cadena de consulta SQL final, probarla yo mismo en la base de datos y modificarla hasta que haya corregido el error, y luego volver a ponerlo en el código.

Las declaraciones preparadas de PDO son más rápidas, mejores y más seguras, pero una cosa me molesta: nunca veo la consulta final, ya que se envía a la base de datos. Cuando recibo errores sobre la sintaxis en mi registro de Apache o en mi archivo de registro personalizado (registro de errores dentro de un bloque catch ), no puedo ver la consulta que los causó.

¿Hay alguna forma de capturar la consulta SQL completa enviada por PDO a la base de datos y registrarla en un archivo?


Cómo depurar las consultas de la base de datos de PDO mysql en Ubuntu

TL; DR Registre todas sus consultas y alinee el registro de mysql.

Estas instrucciones son para mi instalación de Ubuntu 14.04. Emita el comando lsb_release -a para obtener su versión. Tu instalación puede ser diferente.

Active el inicio de sesión en mysql

  1. Vaya a la línea de cmd del servidor de desarrollo
  2. Cambie los directorios cd /etc/mysql . Debería ver un archivo llamado my.cnf . Ese es el archivo que vamos a cambiar.
  3. Verifica que estás en el lugar correcto escribiendo cat my.cnf | grep general_log cat my.cnf | grep general_log . Esto filtra el archivo my.cnf por ti. Debería ver dos entradas: #general_log_file = /var/log/mysql/mysql.log && #general_log = 1 .
  4. Descomenta esas dos líneas y guárdalas a través de tu editor de elección.
  5. Reinicie mysql: sudo service mysql restart .
  6. Es posible que deba reiniciar su servidor web también. (No recuerdo la secuencia que utilicé). Para mi instalación, eso es nginx: sudo service nginx restart .

¡Buen trabajo! Ya estás listo. Ahora todo lo que tiene que hacer es alinear el archivo de registro para que pueda ver las consultas PDO que realiza su aplicación en tiempo real.

Siga el registro para ver sus consultas

Ingrese este cmd tail -f /var/log/mysql/mysql.log .

Su salida se verá algo como esto:

73 Connect xyz@localhost on your_db 73 Query SET NAMES utf8mb4 74 Connect xyz@localhost on your_db 75 Connect xyz@localhost on your_db 74 Quit 75 Prepare SELECT email FROM customer WHERE email=? LIMIT ? 75 Execute SELECT email FROM customer WHERE email=''[email protected]'' LIMIT 5 75 Close stmt 75 Quit 73 Quit

Las consultas nuevas que haga su aplicación aparecerán automáticamente en la vista , siempre que continúe siguiendo el registro. Para salir de la cola, presiona cmd/ctrl c .

Notas

  1. Cuidado: este archivo de registro puede ser enorme. Solo estoy ejecutando esto en mi servidor de desarrollo.
  2. ¿El archivo de registro se está haciendo demasiado grande? Truncarlo. Eso significa que el archivo permanece, pero los contenidos se eliminan. truncate --size 0 mysql.log .
  3. Enfriar que el archivo de registro enumera las conexiones de mysql. Sé que uno de esos es de mi código mysqli heredado del cual estoy haciendo la transición. El tercero es de mi nueva conexión PDO. Sin embargo, no estoy seguro de dónde viene el segundo. Si conoce una forma rápida de encontrarlo, hágamelo saber.

Crédito y gracias

Enormes palabras a la respuesta anterior de Nathan Long para que la inspo descubra esto en Ubuntu. También a dikirill por su comentario sobre la publicación de Nathan que me llevó a esta solución.

Te amo !


Buscando en el registro de la base de datos

Aunque Pascal MARTIN tiene razón en que PDO no envía la consulta completa a la base de datos de una vez, la sugerencia de ryeguy de usar la función de registro de la base de datos realmente me permitió ver la consulta completa como ensamblada y ejecutada por la base de datos.

He aquí cómo: (Estas instrucciones son para MySQL en una máquina con Windows, su millaje puede variar)

  • En my.ini , en la sección [mysqld] , agregue un comando de log , como log="C:/Program Files/MySQL/MySQL Server 5.1/data/mysql.log"
  • Reinicia MySQL.
  • Comenzará a registrar cada consulta en ese archivo.

Ese archivo crecerá rápidamente, así que asegúrese de eliminarlo y desactivar el registro cuando termine la prueba.


Aquí hay una función para ver cuál será el SQL efectivo, adaptado de un comentario de "Marca" en debugDumParams() :

function sql_debug($sql_string, array $params = null) { if (!empty($params)) { $indexed = $params == array_values($params); foreach($params as $k=>$v) { if (is_object($v)) { if ($v instanceof /DateTime) $v = $v->format(''Y-m-d H:i:s''); else continue; } elseif (is_string($v)) $v="''$v''"; elseif ($v === null) $v=''NULL''; elseif (is_array($v)) $v = implode('','', $v); if ($indexed) { $sql_string = preg_replace(''//?/'', $v, $sql_string, 1); } else { if ($k[0] != '':'') $k = '':''.$k; //add leading colon if it was left out $sql_string = str_replace($k,$v,$sql_string); } } } return $sql_string; }


Aquí hay una función que hice para devolver una consulta SQL con parámetros "resueltos".

function paramToString($query, $parameters) { if(!empty($parameters)) { foreach($parameters as $key => $value) { preg_match(''/(/?(?!=))/i'', $query, $match, PREG_OFFSET_CAPTURE); $query = substr_replace($query, $value, $match[0][1], 1); } } return $query; $query = "SELECT email FROM table WHERE id = ? AND username = ?"; $values = [1, ''Super'']; echo paramToString($query, $values);

Suponiendo que ejecutas así

$values = array(1, ''SomeUsername''); $smth->execute($values);

Esta función NO agrega presupuestos a las consultas, pero hace el trabajo por mí.


Buscando en internet encontré esto como una solución aceptable. Se usa una clase diferente en lugar de PDO y las funciones PDO se invocan a través de llamadas a funciones mágicas. No estoy seguro de que esto cree serios problemas de rendimiento. Pero puede usarse hasta que se agregue una característica de registro sensible a PDO.

Entonces, según este thread , puede escribir un contenedor para su conexión PDO que puede iniciar sesión y arroja una excepción cuando obtiene un error.

Aquí hay un ejemplo simple:

class LoggedPDOSTatement extends PDOStatement { function execute ($array) { parent::execute ($array); $errors = parent::errorInfo(); if ($errors[0] != ''00000''): throw new Exception ($errors[2]); endif; } }

para que pueda usar esa clase en lugar de PDOStatement:

$this->db->setAttribute (PDO::ATTR_STATEMENT_CLASS, array (''LoggedPDOStatement'', array()));

Aquí una implementación mencionada del decorador de PDO:

class LoggedPDOStatement { function __construct ($stmt) { $this->stmt = $stmt; } function execute ($params = null) { $result = $this->stmt->execute ($params); if ($this->stmt->errorCode() != PDO::ERR_NONE): $errors = $this->stmt->errorInfo(); $this->paint ($errors[2]); endif; return $result; } function bindValue ($key, $value) { $this->values[$key] = $value; return $this->stmt->bindValue ($key, $value); } function paint ($message = false) { echo ''<pre>''; echo ''<table cellpadding="5px">''; echo ''<tr><td colspan="2">Message: '' . $message . ''</td></tr>''; echo ''<tr><td colspan="2">Query: '' . $this->stmt->queryString . ''</td></tr>''; if (count ($this->values) > 0): foreach ($this->values as $key => $value): echo ''<tr><th align="left" style="background-color: #ccc;">'' . $key . ''</th><td>'' . $value . ''</td></tr>''; endforeach; endif; echo ''</table>''; echo ''</pre>''; } function __call ($method, $params) { return call_user_func_array (array ($this->stmt, $method), $params); } }


Claro que puede depurar usando este modo {{ PDO::ATTR_ERRMODE }} Simplemente agregue una nueva línea antes de su consulta y luego mostrará las líneas de depuración.

$db->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING ); $db->query(''SELECT *******'');


El problema que tuve con la solución para atrapar excepciones PDO con fines de depuración es que solo capturó las excepciones PDO (duh), pero no capturó errores de sintaxis que se registraron como errores php (no estoy seguro de por qué es así, pero " por qué "es irrelevante para la solución). Todas mis llamadas PDO provienen de una única clase de modelo de tabla que extendí para todas mis interacciones con todas las tablas ... esto fue complicado cuando estaba tratando de depurar código, porque el error registraba la línea de código php donde estaba mi llamada de ejecución llamé, pero no me dijo de dónde venía la llamada, de hecho, estaba hecha de. Usé el siguiente código para resolver este problema:

/** * Executes a line of sql with PDO. * * @param string $sql * @param array $params */ class TableModel{ var $_db; //PDO connection var $_query; //PDO query function execute($sql, $params) { //we''re saving this as a global, so it''s available to the error handler global $_tm; //setting these so they''re available to the error handler as well $this->_sql = $sql; $this->_paramArray = $params; $this->_db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $this->_query = $this->_db->prepare($sql); try { //set a custom error handler for pdo to catch any php errors set_error_handler(''pdoErrorHandler''); //save the table model object to make it available to the pdoErrorHandler $_tm = $this; $this->_query->execute($params); //now we restore the normal error handler restore_error_handler(); } catch (Exception $ex) { pdoErrorHandler(); return false; } } }

Por lo tanto, el código anterior detecta AMBAS excepciones PDO Y errores de sintaxis php y los trata de la misma manera. Mi controlador de errores se ve así:

function pdoErrorHandler() { //get all the stuff that we set in the table model global $_tm; $sql = $_tm->_sql; $params = $_tm->_params; $query = $tm->_query; $message = ''PDO error: '' . $sql . '' ('' . implode('', '', $params) . ") /n"; //get trace info, so we can know where the sql call originated from ob_start(); debug_backtrace(); //I have a custom method here that parses debug backtrace, but this will work as well $trace = ob_get_clean(); //log the error in a civilized manner error_log($message); if(admin(){ //print error to screen based on your environment, logged in credentials, etc. print_r($message); } }

Si alguien tiene mejores ideas sobre cómo obtener información relevante para mi controlador de errores que si configurara el modelo de tabla como una variable global, me complacería escucharlo y editar mi código.


He creado un proyecto / repositorio cargado con Composer moderno para exactamente esto aquí:

pdo-depuración

Encuentre el github.com/panique/pdo-debug del proyecto github.com/panique/pdo-debug , vea una publicación de blog que lo explica aquí . Una línea para agregar en tu composer.json, y luego puedes usarla así:

echo debugPDO($sql, $parameters);

$ sql es la instrucción SQL sin procesar, $ parameters es una matriz de sus parámetros: la clave es el nombre del marcador de posición (": user_id") o el número del parámetro sin nombre ("?"), el valor es ... bueno, el valor.

La lógica detrás: este script simplemente graduará los parámetros y los reemplazará en la cadena SQL provista. Súper simple, pero súper efectivo para el 99% de tus casos de uso. Nota: Esto es solo una emulación básica, no una verdadera depuración de PDO (ya que esto no es posible ya que PHP envía SQL sin procesar y los parámetros al servidor MySQL separados).

Muchas gracias a bigwebguy y a Mike por el hilo de Obtención de la cadena de consulta SQL sin formato desde PDO para escribir básicamente toda la función principal detrás de este script. ¡Grande!


No. Las consultas de PDO no están preparadas en el lado del cliente. PDO simplemente envía la consulta SQL y los parámetros al servidor de la base de datos. La base de datos es lo que hace la sustitución (de los ? ) S. Tienes dos opciones:

  • Use la función de registro de su base de datos (pero incluso entonces normalmente se muestra como dos declaraciones separadas (es decir, "no final") al menos con Postgres)
  • Envía la consulta SQL y los parámetros y compártala tú mismo

Para iniciar sesión en MySQL en WAMP , tendrá que editar my.ini (por ejemplo, en wamp / bin / mysql / mysql5.6.17 / my.ini)

y agregar a [mysqld] :

general_log = 1 general_log_file="c://tmp//mysql.log"


Probablemente lo que quiere hacer es usar debugDumParams() No construye la declaración preparada para usted, pero mostrará sus parámetros.


Tú dices esto :

Nunca veo la consulta final, ya que se envía a la base de datos

Bueno, en realidad, cuando se utilizan declaraciones preparadas, no existe una " consulta final " :

  • Primero, se envía una declaración al DB, y se prepara allí
    • La base de datos analiza la consulta y crea una representación interna de la misma
  • Y, cuando vincula variables y ejecuta la instrucción, solo las variables se envían a la base de datos
    • Y la base de datos "inyecta" los valores en su representación interna de la declaración


Por lo tanto, para responder a su pregunta:

¿Hay alguna forma de capturar la consulta SQL completa enviada por PDO a la base de datos y registrarla en un archivo?

No: como no existe una " consulta SQL completa " en ningún lado, no hay forma de capturarla.


Lo mejor que puede hacer, para fines de depuración, es "reconstruir" una consulta SQL "real", inyectando los valores en la cadena SQL de la instrucción.

Lo que suelo hacer, en este tipo de situaciones, es:

  • repetir el código SQL que corresponde a la instrucción, con marcadores de posición
  • y use var_dump (o un equivalente) justo después, para mostrar los valores de los parámetros
  • En general, esto es suficiente para ver un posible error, incluso si no tiene ninguna consulta "real" que pueda ejecutar.

Esto no es genial, cuando se trata de depuración, pero ese es el precio de las declaraciones preparadas y las ventajas que aportan.


Una publicación anterior, pero tal vez alguien lo encuentre útil;

function pdo_sql_debug($sql,$placeholders){ foreach($placeholders as $k => $v){ $sql = preg_replace(''/:''.$k.''/'',"''".$v."''",$sql); } return $sql; }


casi nada se dijo sobre la visualización de errores, excepto los registros de errores de verificación, pero hay una funcionalidad bastante útil:

<?php /* Provoke an error -- bogus SQL syntax */ $stmt = $dbh->prepare(''bogus sql''); if (!$stmt) { echo "/PDO::errorInfo():/n"; print_r($dbh->errorInfo()); } ?>

( enlace fuente )

Está claro que este código se puede modificar para usarlo como mensaje de excepción o cualquier otro tipo de manejo de errores.


este código funciona muy bien para mí:

echo str_replace(array_keys($data), array_values($data), $query->queryString);

No olvides reemplazar $ data y $ query por tus nombres


por ejemplo, tiene esta declaración de pdo:

$query="insert into tblTest (field1, field2, field3) values (:val1, :val2, :val3)"; $res=$db->prepare($query); $res->execute(array( '':val1''=>$val1, '':val2''=>$val2, '':val3''=>$val3, ));

ahora puede obtener la consulta ejecutada definiendo una matriz como esta:

$assoc=array( '':val1''=>$val1, '':val2''=>$val2, '':val3''=>$val3, ); $exQuery=str_replace(array_keys($assoc), array_values($assoc), $query); echo $exQuery;


utilizo esta clase para depurar PDO (con Log4PHP )

<?php /** * Extends PDO and logs all queries that are executed and how long * they take, including queries issued via prepared statements */ class LoggedPDO extends PDO { public static $log = array(); public function __construct($dsn, $username = null, $password = null, $options = null) { parent::__construct($dsn, $username, $password, $options); } public function query($query) { $result = parent::query($query); return $result; } /** * @return LoggedPDOStatement */ public function prepare($statement, $options = NULL) { if (!$options) { $options = array(); } return new /LoggedPDOStatement(parent::prepare($statement, $options)); } } /** * PDOStatement decorator that logs when a PDOStatement is * executed, and the time it took to run * @see LoggedPDO */ class LoggedPDOStatement { /** * The PDOStatement we decorate */ private $statement; protected $_debugValues = null; public function __construct(PDOStatement $statement) { $this->statement = $statement; } public function getLogger() { return /Logger::getLogger(''PDO sql''); } /** * When execute is called record the time it takes and * then log the query * @return PDO result set */ public function execute(array $params = array()) { $start = microtime(true); if (empty($params)) { $result = $this->statement->execute(); } else { foreach ($params as $key => $value) { $this->_debugValues[$key] = $value; } $result = $this->statement->execute($params); } $this->getLogger()->debug($this->_debugQuery()); $time = microtime(true) - $start; $ar = (int) $this->statement->rowCount(); $this->getLogger()->debug(''Affected rows: '' . $ar . '' Query took: '' . round($time * 1000, 3) . '' ms''); return $result; } public function bindValue($parameter, $value, $data_type = false) { $this->_debugValues[$parameter] = $value; return $this->statement->bindValue($parameter, $value, $data_type); } public function _debugQuery($replaced = true) { $q = $this->statement->queryString; if (!$replaced) { return $q; } return preg_replace_callback(''/:([0-9a-z_]+)/i'', array($this, ''_debugReplace''), $q); } protected function _debugReplace($m) { $v = $this->_debugValues[$m[0]]; if ($v === null) { return "NULL"; } if (!is_numeric($v)) { $v = str_replace("''", "''''", $v); } return "''" . $v . "''"; } /** * Other than execute pass all other calls to the PDOStatement object * @param string $function_name * @param array $parameters arguments */ public function __call($function_name, $parameters) { return call_user_func_array(array($this->statement, $function_name), $parameters); } }