Перед тем как перейти к PDO, я создал SQL-запросы в PHP путем объединения строк. Если я получил синтаксическую ошибку базы данных, я мог бы просто повторить последнюю строку запроса SQL, попробовать ее сам в базе данных и настроить ее, пока не исправлю ошибку, а затем вернуть ее в код.
Подготовленные операторы PDO работают быстрее, лучше и безопаснее, но меня беспокоит одна вещь: я никогда не вижу окончательный запрос, когда он отправляется в базу данных. Когда я получаю ошибки о синтаксисе в моем журнале Apache или моем пользовательском файле журнала (я регистрирую ошибки внутри catch
блока), я не вижу запроса, который их вызвал.
Есть ли способ захватить полный SQL-запрос, отправленный PDO в базу данных, и записать его в файл?
/var/log/mysql/*
. Параметры, связанные с PDO, не могут вызывать синтаксических ошибок, поэтому все, что вам нужно, - это подготовленный SQL-запрос.var_dump($pdo_instance->debugDumpParams())
Ответы:
Вы говорите это:
На самом деле, при использовании подготовленных операторов не бывает такого понятия, как « окончательный запрос » :
Итак, чтобы ответить на ваш вопрос:
Нет: поскольку нигде нет « полного SQL-запроса », нет и возможности его захватить.
Лучшее, что вы можете сделать для целей отладки, - это «воссоздать» «настоящий» SQL-запрос, вставив значения в строку SQL оператора.
Что я обычно делаю в таких ситуациях:
var_dump
(или эквивалент) сразу после, чтобы отобразить значения параметровЭто не очень хорошо, когда дело доходит до отладки, но это цена подготовленных операторов и преимущества, которые они приносят.
источник
Просмотр журнала базы данных
Хотя Паскаль МАРТИН прав в том, что PDO не отправляет полный запрос к базе данных сразу, предложение ryeguy использовать функцию регистрации базы данных фактически позволило мне увидеть полный запрос, собранный и выполненный базой данных.
Вот как: (Эти инструкции предназначены для MySQL на машине с Windows - ваш опыт может отличаться)
my.ini
под[mysqld]
разделом добавьтеlog
команду, напримерlog="C:\Program Files\MySQL\MySQL Server 5.1\data\mysql.log"
Этот файл будет быстро расти, поэтому обязательно удалите его и отключите ведение журнала, когда закончите тестирование.
источник
PDO::ATTR_EMULATE_PREPARES
. См. Этот ответ для получения дополнительной информации: stackoverflow.com/questions/10658865/#answer-10658929general_log
вместоlog
. См. Dev.mysql.com/doc/refman/5.5/en/query-log.htmlКонечно, вы можете отлаживать в этом режиме.
{{ PDO::ATTR_ERRMODE }}
Просто добавьте новую строку перед запросом, и вы увидите строки отладки.$db->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING ); $db->query('SELECT *******');
источник
->query
при использовании подготовленных операторов?Вероятно, вы хотите использовать debugDumpParams () в дескрипторе оператора. Вы можете запустить это в любое время после привязки значений к подготовленному запросу (
execute()
оператор не нужен ).Он не создает за вас подготовленный оператор, но он покажет ваши параметры.
источник
Старый пост, но, возможно, кому-то это пригодится;
function pdo_sql_debug($sql,$placeholders){ foreach($placeholders as $k => $v){ $sql = preg_replace('/:'.$k.'/',"'".$v."'",$sql); } return $sql; }
источник
Вот функция, позволяющая увидеть, каким будет эффективный SQL, из комментария «Mark» на 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('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; }
источник
str_replace(":$k" ....
? В ассоциативных индексах он уже есть в массиве $ params.:name_long
на:name
. По крайней мере, если:name
будет раньше:name_long
. Подготовленные операторы MySQL могут справиться с этим правильно, так что пусть это вас не смущает.Нет. Запросы PDO не подготавливаются на стороне клиента. PDO просто отправляет SQL-запрос и параметры на сервер базы данных. База данных - это то, что делает замену (из
?
). У вас есть два варианта:источник
Об отображении ошибок почти ничего не говорилось, кроме проверки журналов ошибок, но есть довольно полезная функция:
<?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"
источник
Вот функция, которую я сделал для возврата 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);
Эта функция НЕ добавляет кавычек в запросы, но выполняет свою работу за меня.
источник
if (is_string($value)) { $value = "'".$value."'"; }
Проблема, с которой я столкнулся с решением по перехвату исключений PDO для целей отладки, заключается в том, что он перехватывает только исключения PDO (да), но не перехватывает синтаксические ошибки, которые были зарегистрированы как ошибки 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; } } }
Таким образом, приведенный выше код перехватывает ОБЕИ исключения 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-debug
Найдите здесь GitHub-главную страницу проекта , посмотрите сообщение в блоге, объясняющее это здесь . Одна строка, которую нужно добавить в свой composer.json, а затем вы можете использовать ее следующим образом:
echo debugPDO($sql, $parameters);
$ sql - это необработанный оператор SQL, $ parameters - это массив ваших параметров: ключ - это имя-заполнитель (": user_id") или номер безымянного параметра ("?"), значение ... ну, значение.
Логика, лежащая в основе: этот сценарий просто градуирует параметры и заменяет их предоставленной строкой SQL. Супер-простой, но суперэффективный для 99% случаев использования. Примечание: это просто базовая эмуляция, а не настоящая отладка PDO (поскольку это невозможно, поскольку PHP отправляет необработанный SQL и параметры на сервер MySQL отдельно).
Большое спасибо к bigwebguy и Майку из нити StackOverflow Получения строки сырой SQL запроса из PDO для записи в основном все основные функции за этот сценарием. Большой!
источник
Как отлаживать PDO запросы к базе данных mysql в Ubuntu
TL; DR Регистрируйте все ваши запросы и следите за журналом mysql.
Эти инструкции относятся к моей установке Ubuntu 14.04. Выполните команду,
lsb_release -a
чтобы получить свою версию. Ваша установка может отличаться.Включите вход в mysql
cd /etc/mysql
. Вы должны увидеть файл с именемmy.cnf
. Это файл, который мы собираемся изменить.cat my.cnf | grep general_log
. Это фильтруетmy.cnf
файл за вас. Вы должны увидеть две записи:#general_log_file = /var/log/mysql/mysql.log
&&#general_log = 1
.sudo service mysql restart
.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
.Примечания
truncate --size 0 mysql.log
.Кредит и спасибо
Огромный привет ответу Натана Лонга выше за вдохновение, чтобы понять это на Ubuntu. Также к дикириллу за его комментарий к сообщению Натана, который привел меня к этому решению.
Люблю тебя, stackoverflow!
источник
В среде Debian NGINX я сделал следующее.
Перейти к
/etc/mysql/mysql.conf.d
редактированию,mysqld.cnf
если вы обнаружите, чтоlog-error = /var/log/mysql/error.log
добавьте следующие две строки под ним.general_log_file = /var/log/mysql/mysql.log general_log = 1
Чтобы увидеть журналы goto
/var/log/mysql
иtail -f mysql.log
Не забудьте закомментировать эти строки после завершения отладки, если вы находитесь в производственной среде, удалите, так
mysql.log
как этот файл журнала будет быстро расти и может быть огромным.источник