Подтвердить что ты не робот

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

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

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

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

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

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

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

4b9b3361

Ответ 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;
}

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