Intereting Posts
как передать переменные var php в форму в скрытых полях? Слияние MySQL Два поля из двух таблиц в одно поле в наборе результатов Как я могу использовать один штрих-код Nav на нескольких страницах, но показывается текущая страница? Получить имя пользователя из URL в PHP Только показать кнопку для входа в систему в Laravel PHP Twitter OAuth использование jquery для публикации данных в zend framework Как эхо на консоли в Laravel и Artisan? PHP включает в себя вопрос о лучших практиках Bootstrap Отзывчивый редактор WYSWIG не отправляет входные данные в php Почему абсолютные константы пути __DIR__ и __FILE__ не должны использоваться в Symfony Laravel и Dropbox WebAuth: «Отсутствует токен CSRF в сеансе» Как изменить расширение в поле зрения с помощью codeigniter 3? Как отменить этот массив и использовать повторяющиеся значения в качестве ключа в новом массиве электронные письма покупателей woocommerce на frontend

Как отлаживать запросы базы данных PDO?

Прежде чем перейти к PDO, я создал SQL-запросы в PHP путем объединения строк. Если я получил ошибку синтаксиса базы данных, я мог бы просто повторить окончательную строку запроса SQL, попробовать ее сам в базе данных и настроить ее до тех пор, пока я не исправил ошибку, а затем вернул ее обратно в код.

Подготовленные отчеты PDO быстрее, лучше и безопаснее, но меня беспокоит одно: я никогда не вижу окончательный запрос, поскольку он отправляется в базу данных. Когда я получаю ошибки в синтаксисе в моем журнале Apache или в моем пользовательском файле журнала (я регистрирую ошибки внутри блока catch ), я не вижу запрос, который их вызвал.

Есть ли способ захватить полный SQL-запрос, отправленный PDO в базу данных, и записать его в файл?

Вы говорите так:

Я никогда не вижу окончательный запрос, поскольку он отправляется в базу данных

Ну, на самом деле, при использовании подготовленных операторов нет такой вещи, как « окончательный запрос » :

  • Во-первых, заявление отправляется в БД и готовится там
    • База данных анализирует запрос и строит внутреннее представление
  • И, когда вы связываете переменные и выполняете оператор, в базу данных отправляются только переменные
    • И база данных «вводит» значения во внутреннее представление оператора

Итак, чтобы ответить на ваш вопрос:

Есть ли способ захватить полный SQL-запрос, отправленный PDO в базу данных, и записать его в файл?

Нет: поскольку « полного SQL-запроса » нет, нет возможности его захватить.

Самое лучшее, что вы можете сделать для целей отладки, – это «перестроить» «реальный» SQL-запрос, введя значения в строку SQL инструкции.

То, что я обычно делаю, в таких ситуациях:

  • echo код SQL, соответствующий заявлению, с заполнителями
  • и используйте var_dump (или эквивалент) сразу после этого, чтобы отобразить значения параметров
  • Этого достаточно, чтобы увидеть возможную ошибку, даже если у вас нет «реального» запроса, который вы можете выполнить.

Это не здорово, когда дело доходит до отладки – но это цена подготовленных заявлений и преимуществ, которые они приносят.

Просмотр в журнале базы данных

Хотя Pascal MARTIN правильно, что PDO не отправляет полный запрос в базу данных сразу, предложение ryeguy использовать функцию ведения журнала БД фактически позволило мне увидеть полный запрос, собранный и выполненный базой данных.

Вот как это сделать: (Эти инструкции предназначены для MySQL на компьютере под управлением Windows – ваш пробег может отличаться)

  • В my.ini разделе [mysqld] добавьте команду log , например log="C:\Program Files\MySQL\MySQL Server 5.1\data\mysql.log"
  • Перезапустите MySQL.
  • Он начнет регистрировать каждый запрос в этом файле.

Этот файл будет быстро расти, поэтому обязательно удалите его и отключите ведение журнала, когда вы закончите тестирование.

Старая почта, но, возможно, кто-то найдет это полезным;

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

Вероятно, вы хотите использовать debugdumparams (). Он не создает для вас подготовленный оператор, но он покажет ваши параметры.

Конечно, вы можете отлаживать этот режим. {{ PDO::ATTR_ERRMODE }} Просто добавьте новую строку перед запросом, тогда вы увидите строки отладки.

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

Нет. Запросы PDO не готовы на стороне клиента. PDO просто отправляет SQL-запрос и параметры на сервер базы данных. База данных – это то, что делает подстановка (из ? ). У вас есть два варианта:

  • Используйте функцию ведения журнала своей базы данных (но даже тогда ее обычно показывают как два отдельных оператора (т. Е. «Не окончательный»), по крайней мере, с помощью Postgres)
  • Выведите SQL-запрос и параметры и соедините его самостоятельно

Вот функция, чтобы увидеть, какой эффективный SQL будет, добавлен из комментария «Марк» на php.net :

 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('Ymd 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; } 

почти ничего не было сказано об отображении ошибок, кроме журналов ошибок проверки, но есть довольно полезная функциональность:

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

( ссылка источника )

ясно, что этот код может быть изменен для использования в качестве сообщения об исключении или любого другого вида обработки ошибок

например, у вас есть это заявление 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, )); 

теперь вы можете получить выполненный запрос, задав такой массив:

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

Поиск в Интернете Я нашел это приемлемым решением. Вместо PDO используется другой класс, а функции PDO вызываются через вызовы магической функции. Я не уверен, что это создает серьезные проблемы с производительностью. Но его можно использовать до тех пор, пока в PDO не будет добавлена ​​разумная функция регистрации.

Таким образом, в соответствии с этим потоком вы можете написать обертку для своего PDO-соединения, которое может регистрировать и вызывать исключение при получении ошибки.

Вот простой пример:

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

поэтому вы можете использовать этот класс вместо PDOStatement:

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

Здесь упоминается реализация декоратора 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); } } 

Чтобы зарегистрировать MySQL в WAMP , вам нужно будет отредактировать my.ini (например, под wamp \ bin \ mysql \ mysql5.6.17 \ my.ini)

и добавьте в [mysqld] :

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

Как отлаживать запросы базы данных PDO mysql в Ubuntu

TL; DR Запишите все ваши запросы и закройте журнал mysql.

Эти инструкции предназначены для моей установки Ubuntu 14.04. lsb_release -a команду lsb_release -a чтобы получить свою версию. Ваша установка может отличаться.

Включить ведение журнала в mysql

  1. Перейдите на страницу cmd-сервера dev-сервера
  2. Измените каталоги cd /etc/mysql . Вы должны увидеть файл my.cnf . Это файл, который мы собираемся изменить.
  3. Убедитесь, что вы находитесь в нужном месте, набрав cat my.cnf | grep general_log cat my.cnf | grep general_log . Это фильтрует файл my.cnf для вас. Вы должны увидеть две записи: #general_log_file = /var/log/mysql/mysql.log && #general_log = 1 .
  4. Раскомментируйте эти две строки и сохраните их через свой редактор.
  5. Перезапустите mysql: sudo service mysql restart .
  6. Возможно, вам также потребуется перезагрузить веб-сервер. (Я не могу вспомнить последовательность, которую я использовал). Для моей установки это nginx: sudo service nginx restart .

Хорошая работа! Вы все настроены. Теперь все, что вам нужно сделать, это хвост файла журнала, чтобы вы могли видеть запросы PDO, которые делает ваше приложение в режиме реального времени.

Хвост журнала, чтобы просмотреть ваши запросы

Введите этот cmd- tail -f /var/log/mysql/mysql.log .

Ваш результат будет выглядеть примерно так:

 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='a@b.co' LIMIT 5 75 Close stmt 75 Quit 73 Quit 

Любые новые запросы, созданные вашим приложением, автоматически появятся на экране , если вы продолжаете отслеживать журнал. Чтобы выйти из хвоста, нажмите cmd/ctrl c .

Заметки

  1. Осторожно: этот файл журнала может стать огромным. Я использую это только на своем dev-сервере.
  2. Файл журнала становится слишком большим? Усекайте его. Это означает, что файл остается, но содержимое удаляется. truncate --size 0 mysql.log .
  3. Охладите, что в файле журнала перечислены соединения mysql. Я знаю, что один из них – это мой старый код mysqli, из которого я перехожу. Третий – из моего нового соединения PDO. Однако, не уверен, откуда приходит вторая. Если вы знаете быстрый способ найти его, дайте мне знать.

Кредит и благодарность

Огромный крик в ответ Натан Лонг выше, чтобы вдохновить его понять на Ubuntu. Также к дикирилю за его комментарий к сообщению Натана, который привел меня к этому решению.

Люблю тебя stackoverflow!

Проблема, с которой я столкнулась с решением уловить исключения PDO для целей отладки, состоит в том, что она только улавливала исключения PDO (duh), но не улавливала синтаксические ошибки, которые были зарегистрированы как ошибки php (я не уверен, почему это так, но " почему «не имеет никакого отношения к решению»). Все мои вызовы PDO исходят из одного класса модели таблицы, который я расширил для всех своих взаимодействий со всеми таблицами … это сложные вещи, когда я пытался отлаживать код, потому что ошибка регистрировала бы строку php-кода, где мой вызов вызова но не сказал мне, где был вызван звонок. Для решения этой проблемы я использовал следующий код:

 /** * 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; } } } 

Таким образом, приведенный выше код улавливает ошибки BOTH PDO и ошибки синтаксиса php и обрабатывает их одинаково. Мой обработчик ошибок выглядит примерно так:

 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); } } 

Если у кого-то есть лучшие идеи о том, как получить релевантную информацию для моего обработчика ошибок, чем установить модель таблицы в качестве глобальной переменной, я был бы рад услышать ее и отредактировать мой код.

этот код отлично работает для меня:

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

Не забудьте заменить $ data и $ query вашими именами

я использую этот класс для отладки PDO (с 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); } } 

Я создал современный проект / репозиторий, загруженный с помощью Composer, именно здесь:

PDO-отладка

Найдите здесь проект GitHub , см. Сообщение в блоге, объясняющее его здесь . Одна строка для добавления в ваш composer.json, а затем вы можете использовать ее следующим образом:

 echo debugPDO($sql, $parameters); 

$ sql – это исходный оператор SQL, $ parameters – это массив ваших параметров: ключ – это имя-заполнитель («: user_id») или номер неназванного параметра («?»), значение … ну, стоимость.

Логика: этот скрипт просто градирует параметры и заменяет их на предоставленную строку SQL. Супер-простой, но суперэффективный для 99% ваших прецедентов. Примечание. Это просто базовая эмуляция, а не реальная отладка PDO (поскольку это невозможно, поскольку PHP отправляет необработанный SQL и параметры на сервер MySQL отдельно).

Большое спасибо bigwebguy и Майку из потока StackOverflow Получение необработанной строки запроса SQL из PDO для записи в основном всей основной функции этого скрипта. Большой вверх!

Вот функция, которую я сделал, чтобы вернуть SQL-запрос с «разрешенными» параметрами.

 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); 

Предполагая, что вы выполняете это

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

Эта функция НЕ добавляет кавычки к запросам, но выполняет эту работу для меня.