artabro/wire/core/WireDatabasePDOStatement.php
2024-08-27 11:35:37 +02:00

212 lines
4.7 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php namespace ProcessWire;
/**
* ProcessWire PDO Statement
*
* Serves as a wrapper to PHPs 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;
}
}