Получить (или смоделировать) полный запрос из подготовленного оператора PDO
Я два раза назад наткнулся на этот вопрос.
Есть ли способ получить необработанную строку SQL, выполняемую при вызове PDOStatement:: execute() в подготовленном сообщении? Для целей отладки это было бы чрезвычайно полезно.
В выигрышном ответе говорится, что
[...] Вы также можете получить то, что хотите, если вы установить атрибут PDO PDO:: ATTR_EMULATE_PREPARES. В этом режим, PDO интерполировать параметры в SQL-запрос и отправляет целое запрос при выполнении().
Но он не упоминает, как получить полученную строку запроса. Я знаю, что это плохая идея, но это не беспокоит меня в режиме отладки. Кто-нибудь знает, как это сделать?
PS Если есть какой-то способ, я мог бы снова открыть/обратить внимание на оригинальную двухлетнюю тему вместо того, чтобы открывать новую, пожалуйста, дайте мне знать.
Ответы
Ответ 1
Я считаю, что это упоминается в первоначальном вопросе, который был ссылкой в этом. Однако
на самом деле предполагается, что это метод для извлечения этих данных.
PDOStatement::debugDumpParams
Однако он в настоящее время не работает как задокументированный. Здесь есть отчет об ошибке и исправление http://bugs.php.net/bug.php?id=52384, если кто-то заинтересован в голосовании по нему. До тех пор, пока он не будет исправлен, похоже, что вам осталось использовать ведение журнала запросов или настройку класса пользовательских операторов с использованием атрибута PDO:: ATTR_STATEMENT_CLASS.
Ответ 2
Afaik, PDO на самом деле не раскрывает его вам. На серверах разработки вы можете включить общий журнал запросов для MySQL (если это то, что вы используете), возможно, с большим контролем с sql_log_off, который требует привилегии SUPER.
Ответ 3
Если вы не можете получить его из самого 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() ;
Ответ 4
Самый лучший подход, на мой взгляд, заключается в использовании журнала mysql для отображения последних запросов, запущенных, поскольку получение их непосредственно в php является перетаскиванием.
С
Как показать последние запросы, выполненные в 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
Ответ 5
Следующий статический метод принимает шаблон запроса PDO (запрос SQL с заполнителями ?
и/или :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;
}
Как указано в имени метода, вы должны использовать это только для целей отладки.