update v1.0.7.9 R.C.

This is a Release Candidate. We are still testing.
This commit is contained in:
Sujit Prasad
2016-08-03 20:04:36 +05:30
parent 8b6b924d09
commit ffa56a43cb
3830 changed files with 181529 additions and 495353 deletions

View File

@@ -0,0 +1,182 @@
<?php
namespace DebugBar\DataCollector\PDO;
use DebugBar\DataCollector\AssetProvider;
use DebugBar\DataCollector\DataCollector;
use DebugBar\DataCollector\Renderable;
use DebugBar\DataCollector\TimeDataCollector;
/**
* Collects data about SQL statements executed with PDO
*/
class PDOCollector extends DataCollector implements Renderable, AssetProvider
{
protected $connections = array();
protected $timeCollector;
protected $renderSqlWithParams = false;
protected $sqlQuotationChar = '<>';
/**
* @param TraceablePDO $pdo
* @param TimeDataCollector $timeCollector
*/
public function __construct(TraceablePDO $pdo = null, TimeDataCollector $timeCollector = null)
{
$this->timeCollector = $timeCollector;
if ($pdo !== null) {
$this->addConnection($pdo, 'default');
}
}
/**
* Renders the SQL of traced statements with params embeded
*
* @param boolean $enabled
*/
public function setRenderSqlWithParams($enabled = true, $quotationChar = '<>')
{
$this->renderSqlWithParams = $enabled;
$this->sqlQuotationChar = $quotationChar;
}
public function isSqlRenderedWithParams()
{
return $this->renderSqlWithParams;
}
public function getSqlQuotationChar()
{
return $this->sqlQuotationChar;
}
/**
* Adds a new PDO instance to be collector
*
* @param TraceablePDO $pdo
* @param string $name Optional connection name
*/
public function addConnection(TraceablePDO $pdo, $name = null)
{
if ($name === null) {
$name = spl_object_hash($pdo);
}
$this->connections[$name] = $pdo;
}
/**
* Returns PDO instances to be collected
*
* @return array
*/
public function getConnections()
{
return $this->connections;
}
public function collect()
{
$data = array(
'nb_statements' => 0,
'nb_failed_statements' => 0,
'accumulated_duration' => 0,
'memory_usage' => 0,
'peak_memory_usage' => 0,
'statements' => array()
);
foreach ($this->connections as $name => $pdo) {
$pdodata = $this->collectPDO($pdo, $this->timeCollector);
$data['nb_statements'] += $pdodata['nb_statements'];
$data['nb_failed_statements'] += $pdodata['nb_failed_statements'];
$data['accumulated_duration'] += $pdodata['accumulated_duration'];
$data['memory_usage'] += $pdodata['memory_usage'];
$data['peak_memory_usage'] = max($data['peak_memory_usage'], $pdodata['peak_memory_usage']);
$data['statements'] = array_merge($data['statements'],
array_map(function ($s) use ($name) { $s['connection'] = $name; return $s; }, $pdodata['statements']));
}
$data['accumulated_duration_str'] = $this->getDataFormatter()->formatDuration($data['accumulated_duration']);
$data['memory_usage_str'] = $this->getDataFormatter()->formatBytes($data['memory_usage']);
$data['peak_memory_usage_str'] = $this->getDataFormatter()->formatBytes($data['peak_memory_usage']);
return $data;
}
/**
* Collects data from a single TraceablePDO instance
*
* @param TraceablePDO $pdo
* @param TimeDataCollector $timeCollector
* @return array
*/
protected function collectPDO(TraceablePDO $pdo, TimeDataCollector $timeCollector = null)
{
$stmts = array();
foreach ($pdo->getExecutedStatements() as $stmt) {
$stmts[] = array(
'sql' => $this->renderSqlWithParams ? $stmt->getSqlWithParams($this->sqlQuotationChar) : $stmt->getSql(),
'row_count' => $stmt->getRowCount(),
'stmt_id' => $stmt->getPreparedId(),
'prepared_stmt' => $stmt->getSql(),
'params' => (object) $stmt->getParameters(),
'duration' => $stmt->getDuration(),
'duration_str' => $this->getDataFormatter()->formatDuration($stmt->getDuration()),
'memory' => $stmt->getMemoryUsage(),
'memory_str' => $this->getDataFormatter()->formatBytes($stmt->getMemoryUsage()),
'end_memory' => $stmt->getEndMemory(),
'end_memory_str' => $this->getDataFormatter()->formatBytes($stmt->getEndMemory()),
'is_success' => $stmt->isSuccess(),
'error_code' => $stmt->getErrorCode(),
'error_message' => $stmt->getErrorMessage()
);
if ($timeCollector !== null) {
$timeCollector->addMeasure($stmt->getSql(), $stmt->getStartTime(), $stmt->getEndTime());
}
}
return array(
'nb_statements' => count($stmts),
'nb_failed_statements' => count($pdo->getFailedExecutedStatements()),
'accumulated_duration' => $pdo->getAccumulatedStatementsDuration(),
'accumulated_duration_str' => $this->getDataFormatter()->formatDuration($pdo->getAccumulatedStatementsDuration()),
'memory_usage' => $pdo->getMemoryUsage(),
'memory_usage_str' => $this->getDataFormatter()->formatBytes($pdo->getPeakMemoryUsage()),
'peak_memory_usage' => $pdo->getPeakMemoryUsage(),
'peak_memory_usage_str' => $this->getDataFormatter()->formatBytes($pdo->getPeakMemoryUsage()),
'statements' => $stmts
);
}
public function getName()
{
return 'pdo';
}
public function getWidgets()
{
return array(
"database" => array(
"icon" => "inbox",
"widget" => "PhpDebugBar.Widgets.SQLQueriesWidget",
"map" => "pdo",
"default" => "[]"
),
"database:badge" => array(
"map" => "pdo.nb_statements",
"default" => 0
)
);
}
public function getAssets()
{
return array(
'css' => 'widgets/sqlqueries/widget.css',
'js' => 'widgets/sqlqueries/widget.js'
);
}
}

View File

@@ -0,0 +1,196 @@
<?php
namespace DebugBar\DataCollector\PDO;
use PDO;
use PDOException;
/**
* A PDO proxy which traces statements
*/
class TraceablePDO extends PDO
{
protected $pdo;
protected $executedStatements = array();
public function __construct(PDO $pdo)
{
$this->pdo = $pdo;
$this->pdo->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('DebugBar\DataCollector\PDO\TraceablePDOStatement', array($this)));
}
public function beginTransaction()
{
return $this->pdo->beginTransaction();
}
public function commit()
{
return $this->pdo->commit();
}
public function errorCode()
{
return $this->pdo->errorCode();
}
public function errorInfo()
{
return $this->pdo->errorInfo();
}
public function exec($sql)
{
return $this->profileCall('exec', $sql, func_get_args());
}
public function getAttribute($attr)
{
return $this->pdo->getAttribute($attr);
}
public function inTransaction()
{
return $this->pdo->inTransaction();
}
public function lastInsertId($name = null)
{
return $this->pdo->lastInsertId($name);
}
public function prepare($sql, $driver_options = array())
{
return $this->pdo->prepare($sql, $driver_options);
}
public function query($sql)
{
return $this->profileCall('query', $sql, func_get_args());
}
public function quote($expr, $parameter_type = PDO::PARAM_STR)
{
return $this->pdo->quote($expr, $parameter_type);
}
public function rollBack()
{
return $this->pdo->rollBack();
}
public function setAttribute($attr, $value)
{
return $this->pdo->setAttribute($attr, $value);
}
/**
* Profiles a call to a PDO method
*
* @param string $method
* @param string $sql
* @param array $args
* @return mixed The result of the call
*/
protected function profileCall($method, $sql, array $args)
{
$trace = new TracedStatement($sql);
$trace->start();
$ex = null;
try {
$result = call_user_func_array(array($this->pdo, $method), $args);
} catch (PDOException $e) {
$ex = $e;
}
if ($this->pdo->getAttribute(PDO::ATTR_ERRMODE) !== PDO::ERRMODE_EXCEPTION && $result === false) {
$error = $this->pdo->errorInfo();
$ex = new PDOException($error[2], $error[0]);
}
$trace->end($ex);
$this->addExecutedStatement($trace);
if ($this->pdo->getAttribute(PDO::ATTR_ERRMODE) === PDO::ERRMODE_EXCEPTION && $ex !== null) {
throw $ex;
}
return $result;
}
/**
* Adds an executed TracedStatement
*
* @param TracedStatement $stmt
*/
public function addExecutedStatement(TracedStatement $stmt)
{
$this->executedStatements[] = $stmt;
}
/**
* Returns the accumulated execution time of statements
*
* @return int
*/
public function getAccumulatedStatementsDuration()
{
return array_reduce($this->executedStatements, function ($v, $s) { return $v + $s->getDuration(); });
}
/**
* Returns the peak memory usage while performing statements
*
* @return int
*/
public function getMemoryUsage()
{
return array_reduce($this->executedStatements, function ($v, $s) { return $v + $s->getMemoryUsage(); });
}
/**
* Returns the peak memory usage while performing statements
*
* @return int
*/
public function getPeakMemoryUsage()
{
return array_reduce($this->executedStatements, function ($v, $s) { $m = $s->getEndMemory(); return $m > $v ? $m : $v; });
}
/**
* Returns the list of executed statements as TracedStatement objects
*
* @return array
*/
public function getExecutedStatements()
{
return $this->executedStatements;
}
/**
* Returns the list of failed statements
*
* @return array
*/
public function getFailedExecutedStatements()
{
return array_filter($this->executedStatements, function ($s) { return !$s->isSuccess(); });
}
public function __get($name)
{
return $this->pdo->$name;
}
public function __set($name, $value)
{
$this->pdo->$name = $value;
}
public function __call($name, $args)
{
return call_user_func_array(array($this->pdo, $name), $args);
}
}

View File

@@ -0,0 +1,74 @@
<?php
namespace DebugBar\DataCollector\PDO;
use PDO;
use PDOException;
use PDOStatement;
/**
* A traceable PDO statement to use with Traceablepdo
*/
class TraceablePDOStatement extends PDOStatement
{
protected $pdo;
protected $boundParameters = array();
protected function __construct(TraceablePDO $pdo)
{
$this->pdo = $pdo;
}
public function bindColumn($column, &$param, $type = null, $maxlen = null, $driverdata = null)
{
$this->boundParameters[$column] = $param;
$args = array_merge(array($column, &$param), array_slice(func_get_args(), 2));
return call_user_func_array(array("parent", 'bindColumn'), $args);
}
public function bindParam($param, &$var, $data_type = PDO::PARAM_STR, $length = null, $driver_options = null)
{
$this->boundParameters[$param] = $var;
$args = array_merge(array($param, &$var), array_slice(func_get_args(), 2));
return call_user_func_array(array("parent", 'bindParam'), $args);
}
public function bindValue($param, $value, $data_type = PDO::PARAM_STR)
{
$this->boundParameters[$param] = $value;
return call_user_func_array(array("parent", 'bindValue'), func_get_args());
}
public function execute($params = null)
{
$preparedId = spl_object_hash($this);
$boundParameters = $this->boundParameters;
if (is_array($params)) {
$boundParameters = array_merge($boundParameters, $params);
}
$trace = new TracedStatement($this->queryString, $boundParameters, $preparedId);
$trace->start();
$ex = null;
try {
$result = parent::execute($params);
} catch (PDOException $e) {
$ex = $e;
}
if ($this->pdo->getAttribute(PDO::ATTR_ERRMODE) !== PDO::ERRMODE_EXCEPTION && $result === false) {
$error = $this->errorInfo();
$ex = new PDOException($error[2], (int) $error[0]);
}
$trace->end($ex, $this->rowCount());
$this->pdo->addExecutedStatement($trace);
if ($this->pdo->getAttribute(PDO::ATTR_ERRMODE) === PDO::ERRMODE_EXCEPTION && $ex !== null) {
throw $ex;
}
return $result;
}
}

View File

@@ -0,0 +1,240 @@
<?php
namespace DebugBar\DataCollector\PDO;
/**
* Holds information about a statement
*/
class TracedStatement
{
protected $sql;
protected $rowCount;
protected $parameters;
protected $startTime;
protected $endTime;
protected $duration;
protected $startMemory;
protected $endMemory;
protected $memoryDelta;
protected $exception;
/**
* @param string $sql
* @param array $params
* @param string $preparedId
* @param integer $rowCount
* @param integer $startTime
* @param integer $endTime
* @param integer $memoryUsage
* @param \Exception $e
*/
public function __construct($sql, array $params = array(), $preparedId = null)
{
$this->sql = $sql;
$this->parameters = $this->checkParameters($params);
$this->preparedId = $preparedId;
}
public function start($startTime = null, $startMemory = null)
{
$this->startTime = $startTime ?: microtime(true);
$this->startMemory = $startMemory ?: memory_get_usage(true);
}
public function end(\Exception $exception = null, $rowCount = 0, $endTime = null, $endMemory = null)
{
$this->endTime = $endTime ?: microtime(true);
$this->duration = $this->endTime - $this->startTime;
$this->endMemory = $endMemory ?: memory_get_usage(true);
$this->memoryDelta = $this->endMemory - $this->startMemory;
$this->exception = $exception;
$this->rowCount = $rowCount;
}
/**
* Check parameters for illegal (non UTF-8) strings, like Binary data.
*
* @param $params
* @return mixed
*/
public function checkParameters($params)
{
foreach ($params as &$param) {
if (!mb_check_encoding($param, 'UTF-8')) {
$param = '[BINARY DATA]';
}
}
return $params;
}
/**
* Returns the SQL string used for the query
*
* @return string
*/
public function getSql()
{
return $this->sql;
}
/**
* Returns the SQL string with any parameters used embedded
*
* @param string $quotationChar
* @return string
*/
public function getSqlWithParams($quotationChar = '<>')
{
if (($l = strlen($quotationChar)) > 1) {
$quoteLeft = substr($quotationChar, 0, $l / 2);
$quoteRight = substr($quotationChar, $l / 2);
} else {
$quoteLeft = $quoteRight = $quotationChar;
}
$sql = $this->sql;
foreach ($this->parameters as $k => $v) {
$v = "$quoteLeft$v$quoteRight";
if (!is_numeric($k)) {
$sql = str_replace($k, $v, $sql);
} else {
$p = strpos($sql, '?');
$sql = substr($sql, 0, $p) . $v. substr($sql, $p + 1);
}
}
return $sql;
}
/**
* Returns the number of rows affected/returned
*
* @return int
*/
public function getRowCount()
{
return $this->rowCount;
}
/**
* Returns an array of parameters used with the query
*
* @return array
*/
public function getParameters()
{
$params = array();
foreach ($this->parameters as $name => $param) {
$params[$name] = htmlentities($param, ENT_QUOTES, 'UTF-8', false);
}
return $params;
}
/**
* Returns the prepared statement id
*
* @return string
*/
public function getPreparedId()
{
return $this->preparedId;
}
/**
* Checks if this is a prepared statement
*
* @return boolean
*/
public function isPrepared()
{
return $this->preparedId !== null;
}
public function getStartTime()
{
return $this->startTime;
}
public function getEndTime()
{
return $this->endTime;
}
/**
* Returns the duration in seconds of the execution
*
* @return int
*/
public function getDuration()
{
return $this->duration;
}
public function getStartMemory()
{
return $this->startMemory;
}
public function getEndMemory()
{
return $this->endMemory;
}
/**
* Returns the memory usage during the execution
*
* @return int
*/
public function getMemoryUsage()
{
return $this->memoryDelta;
}
/**
* Checks if the statement was successful
*
* @return boolean
*/
public function isSuccess()
{
return $this->exception === null;
}
/**
* Returns the exception triggered
*
* @return \Exception
*/
public function getException()
{
return $this->exception;
}
/**
* Returns the exception's code
*
* @return string
*/
public function getErrorCode()
{
return $this->exception !== null ? $this->exception->getCode() : 0;
}
/**
* Returns the exception's message
*
* @return string
*/
public function getErrorMessage()
{
return $this->exception !== null ? $this->exception->getMessage() : '';
}
}