deepeloper/pdo-debugging

PDO 调试和基准测试工具

2.3.0 2022-12-10 14:51 UTC

This package is auto-updated.

Last update: 2024-09-10 21:47:51 UTC


README

Packagist version PHP from Packagist GitHub license GitHub issues Packagist

Donation

兼容性

PHP 8.0.* ‾‾ 3.0.0

PHP ^7.4 ‾‾ 2.3.0

PHP >=7.3 < 7.4 ‾‾ 2.2.0

PHP >=7.2.5 < 7.3 ‾‾ 2.1.0

PHP ^7.0 ‾‾ 2.0.0

PHP ^5.6 ‾‾ 1.2.0

PHP ^5.5 ‾‾ 1.1.0

PHP ^5.4 ‾‾ 1.0.0

安装

运行 composer require deepeloper/pdo-debugging

基准测试

允许按PDO连接收集以下基准指标

  • 全局
    • 查询数量/时间;
    • 预处理数量/时间;
    • 获取数量/时间;
    • 提交数量/时间;
    • 回滚数量/时间;
    • 调用时间总和
      • PDO::__construct();
      • PDO::beginTransaction();
      • PDO::commit();
      • PDO::exec();
      • PDO::prepare();
      • PDO::query();
      • PDO::rollBack();
      • PDOStatement::execute();
      • PDOStatement::fetch*();
  • 语句
    • 查询数量/时间;
    • 获取数量/时间;
    • 调用时间总和
      • PDOStatement::execute();
      • PDOStatement::fetch*().

基准测试用法

use deepeloper\PDO\PDOExcavated;

$pdo = new PDOExcavated(
    $dsn,
    $username,
    $password,
    [
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDOExcavated::ATTR_DEBUG => [],
    ]
);

$pdo->beginTransaction();
$stmt = $pdo->query("SELECT 1");
$stmt->fetch();
$pdo->commit();

$pdo->beginTransaction();
$stmt = $pdo->prepare("SELECT ?");
$stmt->bindValue(1, 100500, PDO::PARAM_INT);
$stmt->execute();
$stmt->fetch();
$pdo->rollBack();

echo "Global benchmarks: ";
print_r($pdo->getBenchmarks());
echo "\nStatement benchmarks: ";print_r($stmt->getBenchmarks());
echo sprintf("\nLast query: %s\n", $stmt->getLastExecutedQuery());

将输出

Global benchmarks: Array
(
    [query] => Array
        (
            [count] => 2
            [time] => ...
        )
    [prepare] => Array
        (
            [count] => 1
            [time] => ...
        )
    [fetch] => Array
        (
            [count] => 2
            [time] => ...
        )
    [commit] => Array
        (
            [count] => 1
            [time] => ...
        )
    [rollBack] => Array
        (
            [count] => 1
            [time] => ...
        )
    [total] => Array
        (
            [time] => ...
        )
)

Statement benchmarks: Array
(
    [query] => Array
        (
            [count] => 1
            [time] => ...
        )
    [fetch] => Array
        (
            [count] => 1
            [time] => ...
        )
    [total] => Array
        (
            [time] => ...
        )
)

Last query: SELECT 100500

调试

允许处理以下方法调用

  • PDO::__construct();
  • PDO::beginTransaction();
  • PDO::commit();
  • PDO::exec();
  • PDO::prepare();
  • PDO::query();
  • PDO::rollBack();
  • PDOStatement::execute().

允许从以下方法记录替换占位符的查询

  • PDO::exec();
  • PDO::query();
  • PDOStatement::execute().

调试用法

use deepeloper\PDO\LoggerInterface;
use deepeloper\PDO\PDOExcavated;

class Logger implements LoggerInterface
{
    /**
     * Logs message.
     *
     * @param string $message
     * @return void
     */
    public function log($message, array $scope)
    {
        echo "$message\n";
    }
}

$dsn = "...";
$username = "...";
$password = "...";

$logger = new Logger();
$pdo = new PDOExcavated(
    $dsn,
    $username,
    $password,
    [
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDOExcavated::ATTR_DEBUG => [
            'logger' => $logger,
            /**
             * Defaults.
             * 
             * @see deepeloper\PDO\ExcavatingTrait::$debuggingOptions
             */
            'format' => [
                'timeStamp' => "Y-m-d H:i:s",
                'count' => "%03d",
                'precision' => "%.05f",
                'call'  => "[ %TIME_STAMP% ] [ %DSN%;user=%USER_NAME% ] [ %EXECUTION_TIME% ] [ CALL  ] [ %SOURCE%(%ARGS%) ]",
                'query' => "[ %TIME_STAMP% ] [ %DSN%;user=%USER_NAME% ] [ %EXECUTION_TIME% ] [ QUERY ] [ #%COUNT% ] [ %SOURCE% ] [ %QUERY% ]",
            ],
            'sources' => [
            ],
        ],
    ]
);

$pdo->exec("UPDATE `test` SET `foo` = 0");

$stmt = $pdo->query("SELECT 1");
$stmt->fetch();

$stmt = $pdo->prepare("SELECT ?");
$stmt->bindValue(1, 100500, PDO::PARAM_INT);
$stmt->execute();

将输出

[ ****-**-** **:**:** ] [ $dsn;user=$username ] [ *.***** ] [ CALL  ] [ PDOExcavated::__construct(["$dsn","$username"]) ]
[ ****-**-** **:**:** ] [ $dsn;user=$username ] [ *.***** ] [ QUERY ] [ #001 ] [ PDOExcavated::exec ] [ UPDATE `test` SET `foo` = 0 ]
[ ****-**-** **:**:** ] [ $dsn;user=$username ] [ *.***** ] [ QUERY ] [ #002 ] [ PDOExcavated::query ] [ SELECT 1 ]
[ ****-**-** **:**:** ] [ $dsn;user=$username ] [ *.***** ] [ CALL  ] [ PDOExcavated::prepare(["SELECT ?"]) ]
[ ****-**-** **:**:** ] [ $dsn;user=$username ] [ *.***** ] [ QUERY ] [ #003 ] [ PDOStatementExcavated::execute ] [ SELECT 100500 ]

如果您只想记录查询,请传递以下'sources'部分

            'sources' => [
                "/^PDOExcavated::(?:exec|query)/",
                "/^PDOStatementExcavated::execute/",
            ],

如果源以"/"开头,它将被处理为正则表达式,否则将被视为字符串。

自定义调试

您可以自定义日志消息范围,并在记录之前处理结果消息。

use deepeloper\PDO\LoggerInterface;
use deepeloper\PDO\PDOExcavated;
use deepeloper\PDO\PDOStatementExcavated;

class class PDOExcavatedExtended extends PDOExcavated
{
    /**
     * Allows to customize log message scope.
     *
     * @param array &$scope
     * @return void
     * @see ExcavatingTrait::after()
     */
    protected function scope(array &$scope)
    {
        $scope['FOO'] = __METHOD__;
    }

    /**
     * Prepares query for logging.
     *
     * @param string &$query
     * @return void
     * @see ExcavatingTrait::after()
     */
    protected function prepareQueryForLogging(&$query)
    {
        // Modify query here.
    }

    /**
     * Method used to replace PDOStatementExcavated by possible child.
     *
     * @param string $template
     * @param PDOStatementExcavated $stmt
     * @return PDOStatementExcavatedExtended
     * @see self::getResultStatement()
     */
    protected function getStatement($template, PDOStatement $stmt)
    {
        return new PDOStatementExcavatedExtended($template, $this, $stmt);
    }
}

class PDOStatementExcavatedExtended extends PDOStatementExcavated
{
    /**
     * Allows to customize log message scope.
     *
     * @param array &$scope
     * @return void
     * @see ExcavatingTrait::after()
     */
    protected function scope(array &$scope)
    {
        $scope['FOO'] = __METHOD__;
    }

    /**
     * Prepares query for logging.
     *
     * @param string &$query
     * @return void
     * @see ExcavatingTrait::after()
     */
    protected function prepareQueryForLogging(&$query)
    {
        // Modify query here.
    }
}

class Logger implements LoggerInterface
{
    /**
     * Logs message.
     *
     * @param string $message
     * @param array $scope
     * @return void
     */
    public function log($message, array $scope)
    {
        echo "$message\n";
    }
}

$dsn = "...";
$username = "...";
$password = "...";

$logger = new Logger();
$pdo = new PDOExcavatedExtended(
    $dsn,
    $username,
    $password,
    [
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDOExcavated::ATTR_DEBUG => [
            'logger' => $logger,
            'format' => [
                'call'  => "%FOO%",
                'query' => "%FOO%",
            ],
        ],
    ]
);

$pdo->exec("UPDATE `test` SET `bar` = ''");
$pdo->query("SELECT 1");
$stmt = $pdo->prepare("SELECT ?, ?");
$stmt->bindValue(1, 100500, PDO::PARAM_INT);
$stmt->bindValue(2, "blah");
$stmt->execute();

将输出

deepeloper\PDO\PDOExcavatedExtended::scope
deepeloper\PDO\PDOExcavatedExtended::scope
deepeloper\PDO\PDOExcavatedExtended::scope
deepeloper\PDO\PDOExcavatedExtended::scope
deepeloper\PDO\PDOStatementExcavatedExtended::scope