"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 * */ 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 * */ 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; } }