Получить (или смоделировать) полный запрос из подготовленного оператора PDO - PullRequest
13 голосов
/ 20 сентября 2010

Я наткнулся на этот вопрос двухлетней давности.

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

Победивший ответ утверждает, что

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

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

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

Ответы [ 5 ]

13 голосов
/ 29 сентября 2010

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

PDOStatement::debugDumpParams

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

2 голосов
/ 23 сентября 2010

Если вы не можете получить его из самого 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() ;
2 голосов
/ 20 сентября 2010

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

1 голос
/ 30 декабря 2014

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

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

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

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

Посмотрите на таблицу mysql.general_log Если вы предпочитаете выводить вfile:

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

0 голосов
/ 04 апреля 2013

Следующий статический метод берет шаблон запроса 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;
}

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

...