Получить (или смоделировать) полный запрос из подготовленного оператора PDO

Я наткнулся на этот вопрос два года назад.

Есть ли способ получить необработанную строку SQL, выполняемую при вызове PDOStatement :: execute () в подготовленном заявлении? Для целей отладки это было бы чрезвычайно полезно.

В выигрышном ответе говорится, что

[…] Вы также можете получить то, что хотите, если вы установите атрибут PDO PDO :: ATTR_EMULATE_PREPARES. В этом режиме PDO интерполирует параметры в SQL-запрос и отправляет весь запрос при выполнении ().

Но он не упоминает, как получить полученную строку запроса. Я знаю, что это плохая идея, но это не мешает мне в режиме отладки. Кто-нибудь знает, как это сделать?

PS Если есть какой-то способ, я мог бы снова открыть / обратить внимание на оригинальную двухлетнюю тему вместо того, чтобы открывать новую, пожалуйста, дайте мне знать.

Я считаю, что это упоминается в первоначальном вопросе, который был ссылкой в ​​этом. Однако на самом деле предполагается, что это метод для извлечения этих данных.

PDOStatement::debugDumpParams

Однако в настоящее время он не документируется. Для этого есть сообщение об ошибке и патч, представленный здесь http://bugs.php.net/bug.php?id=52384, если кто-то заинтересован в голосовании по нему. Пока это не исправлено, кажется, что вам осталось использовать ведение журнала запросов или настройку класса пользовательских операторов с использованием атрибута PDO :: ATTR_STATEMENT_CLASS.

Afaik, PDO на самом деле не раскрывает его вам. На серверах разработки вы можете включить общий журнал запросов для MySQL (если это то, что вы используете), возможно, с большим контролем с sql_log_off , что требует привилегии SUPER.

Если вы не можете получить его из самого PDO, рассмотрите возможность использования класса-оболочки только для PDOStatement::execute() который будет регистрировать SQL-запрос и значения, а затем вызывать execute() в инструкции. Вам придется реорганизовать свой код, чтобы использовать новый класс.

В качестве побочного элемента я вижу, что в PDOStatement есть переменная класса $queryString которая содержит используемый запрос. Значения должны быть получены из того, что передано в execute() или bindParam() .

Сначала некоторые функции утилиты для ведения журнала:

 //removes newlines and extra spaces from print_r output function str_squeeze($str) { if (is_array($str)) { $str = print_r($str, true); } $str = preg_replace('/[(\r)?\n|\t]/', ' ', $str); $str = trim(ereg_replace(' +', ' ', $str)); return $str; } function logger($str) { //log it somewhere } 

Вариант 1: класс оболочки вокруг PDOStatement

 class My_PDO_Utils { public static function execute(PDOStatement &$stm, $values = array()) { logger("QUERY: " . $stm->queryString . ", values = " . str_squeeze($values)) ; return $stm->execute($values) ; } } 

Тогда ваш код должен быть:

 $stm = $db->prepare("SELECT * FROM table2 WHERE id = ?") ; $res = My_PDO_Utils::execute($stm, array(79)) ; 

вместо

 $res = $stm->execute(array(79)) ; 

Подумав еще об этом, вы могли бы принять это еще одно:

Вариант 2: Расширение PDO и PDOStatement

Если вы хотите быть предприимчивым, вы можете расширить PDOStatement, чтобы выполнить регистрацию для вас, и PDO, чтобы вернуть ваш расширенный класс PDOStatement. Это потребует наименьшего возможного рефакторинга, то есть просто измените new PDO() на new MY_PDO() , но может оказаться сложным в его реализации, поскольку вам нужно будет явно определить любую функциональность PDOStatement, которая вам нужна в MY_PDOStatement, чтобы она вызывалась должным образом.

 class My_PDO extends PDO { public function prepare($sql, $options = array()) { //do normal call $stm = parent::prepare($sql, $options) ; //encapsulate it in your pdostatement wrapper $myStm = new My_PDOStatement() ; $myStm->stm = $stm ; return $myStm ; } } class My_PDOStatement extends PDOStatement { /** * * @var PDOStatement */ public $stm ; public function execute($values) { logger("QUERY: " . $this->stm->queryString . ", values = " . str_squeeze($values)) ; return $this->stm->execute($values) ; } public function fetchAll($fetch_style = PDO::FETCH_BOTH, $column_index = 0, $ctor_args = array()) { return $this->stm->fetchAll($fetch_style, $column_index, $ctor_args) ; } } 

Но теперь ваш код может быть:

 $db = new My_PDO($dsn, $user, $pass) ; $stm = $db->prepare("SELECT * FROM table2 WHERE id = ?") ; $res = $stm->execute(array(79)) ; $row = $stm->fetchAll() ; 

Самый лучший подход, на мой взгляд, заключается в использовании журнала mysql для отображения последних запросов, поскольку получение их непосредственно в php является перетаскиванием.

From Как показать последние запросы, выполняемые в MySQL? первый ответ:

Кроме того, для тех, кто благословлен MySQL> = 5.1.12:

 SET GLOBAL log_output = 'TABLE'; SET GLOBAL general_log = 'ON'; 

Взгляните на таблицу mysql.general_log Если вы предпочитаете выводить в файл:

 SET GLOBAL log_output = "FILE"; which is set by default. SET GLOBAL general_log_file = "/path/to/your/logfile.log" SET GLOBAL general_log = 'ON'; 

Я предпочитаю этот метод, потому что:

вы не редактируете файл my.cnf и, возможно, постоянно включаете ведение журнала, вы не ловите рыбу вокруг файловой системы, которая ищет журнал запросов – или, что еще хуже, отвлекается на необходимость идеального назначения. / var / log / var / data / log / opt / home / mysql_savior / var перезагрузка сервера оставляет вас там, где вы начали (журнал отключен) Дополнительные сведения см. в Справочном руководстве MySQL 5.1 – Системные переменные сервера – general_log

Следующий статический метод принимает шаблон запроса PDO (SQL-запрос с :name ? И / или :name ) и интерполирует параметры:

 static public function getDebugFullQuery($query, $params = array()){ if(is_array($params) && count($params)){ $search = []; $replace = []; foreach($params as $k => $p){ $pos = strpos($query, ":{$k}"); if($pos !== false){ $query = substr($query, 0, $pos) . "%!-!{$k}!-!%" . substr($query, $pos + strlen($k) + 1); } else { $pos = strpos($query, "?"); if($pos !== false){ $query = substr($query, 0, $pos) . "%!-!{$k}!-!%" . substr($query, $pos + 1); } else { break; } } $search[] = "%!-!{$k}!-!%"; $replace[] = "'" . str_replace(array("\r", "\n", "'"), array("\\\\r", "\\\\n", "\\'"), $p) . "'"; } if(count($search)){ $query = str_replace($search, $replace, $query); } } return $query; } 

Как указано в имени метода, вы должны использовать это только для целей отладки.