Rev 1 | Blame | Compare with Previous | Last modification | View Log | Download
<?php namespace ProcessWire;/*** ProcessWire PDO Statement** Serves as a wrapper to PHP’s PDOStatement class, purely for debugging purposes.* When ProcessWire is not in debug mode, this class is not used at present.** The primary thing this class does is log queries with the bind parameters* populated into the SQL query string, purely for readability purposes. These* populated queries are not ever used for actual database queries, just for logs.** Note that this class only tracks bindValue() and does not track bindParam().** ProcessWire 3.x, Copyright 2020 by Ryan Cramer* https://processwire.com**/class WireDatabasePDOStatement extends \PDOStatement {/*** @var WireDatabasePDO**/protected $database = null;/*** Debug params in format [ ":param_name" => "param value" ]** @var array**/protected $debugParams = array();/*** Debug params that require PCRE, in format [ "/:param_name\b/" => "param value" ]** @var array**/protected $debugParamsPCRE = array();/*** Quantity of debug params** @var int**/protected $debugParamsQty = 0;/*** Debug note** @var string**/protected $debugNote = '';/*** Debug mode?** @var bool**/protected $debugMode = false;/*** Construct** PDO requires the PDOStatement constructor to be protected for some reason** @param WireDatabasePDO $database**/protected function __construct(WireDatabasePDO $database) {$this->database = $database;$this->debugMode = $database->debugMode;}/*** Set debug note** @param string $note**/public function setDebugNote($note) {$this->debugNote = $note;}/*** Set a named debug parameter** @param string $parameter* @param int|string|null $value* @param int|null $data_type \PDO::PARAM_* type**/public function setDebugParam($parameter, $value, $data_type = null) {if($data_type === \PDO::PARAM_INT) {$value = (int) $value;} else if($data_type === \PDO::PARAM_NULL) {$value = 'NULL';} else {$value = $this->database->quote($value);}if($parameter[strlen($parameter)-1] !== 'X') {// user-specified param name: partial name collisions possible, so use boundary$this->debugParamsPCRE['/' . $parameter . '\b/'] = $value;} else {// auto-generated param name: already protected against partial name collisions$this->debugParams[$parameter] = $value;}$this->debugParamsQty++;}/*** Bind a value for this statement** @param string|int $parameter* @param mixed $value* @param int $data_type* @return bool**/#[\ReturnTypeWillChange]public function bindValue($parameter, $value, $data_type = \PDO::PARAM_STR) {$result = parent::bindValue($parameter, $value, $data_type);if($this->debugMode && strpos($parameter, ':') === 0) {$this->setDebugParam($parameter, $value, $data_type);} else {// note we do not handle index/question-mark parameters for debugging}return $result;}/*** Execute prepared statement** @param array|null $input_parameters* @return bool* @throws \PDOException**/#[\ReturnTypeWillChange]public function execute($input_parameters = NULL) {if($this->debugMode) {return $this->executeDebug($input_parameters);} else {return parent::execute($input_parameters);}}/*** Execute prepared statement when in debug mode only** @param array|null $input_parameters* @return bool* @throws \PDOException**/public function executeDebug($input_parameters = NULL) {$timer = Debug::startTimer();$exception = null;try {$result = parent::execute($input_parameters);} catch(\PDOException $e) {$exception = $e;$result = false;}$timer = Debug::stopTimer($timer, 'ms');if(!$this->database) {if($exception) throw $exception;return $result;}if(is_array($input_parameters)) {foreach($input_parameters as $key => $value) {if(is_string($key)) $this->setDebugParam($key, $value);}}$debugNote = trim("$this->debugNote [$timer]");if($exception) $debugNote .= ' FAIL SQLSTATE[' . $exception->getCode() . ']';if($this->debugParamsQty) {$sql = $this->queryString;if(count($this->debugParams)) {$sql = strtr($sql, $this->debugParams);}if(count($this->debugParamsPCRE)) {$sql = preg_replace(array_keys($this->debugParamsPCRE),array_values($this->debugParamsPCRE),$sql);}$this->database->queryLog($sql, $debugNote);} else {$this->database->queryLog($this->queryString, $debugNote);}if($exception) throw $exception;return $result;}}