231 lines
7.8 KiB
PHP
231 lines
7.8 KiB
PHP
<?php
|
|
|
|
namespace WebmanTech\Debugbar\DataCollector;
|
|
|
|
use DebugBar\DataCollector\TimeDataCollector as DebugBarTimeDataCollector;
|
|
use Illuminate\Database\Connection;
|
|
use Illuminate\Database\Events\QueryExecuted;
|
|
use Illuminate\Database\Events\TransactionBeginning;
|
|
use Illuminate\Database\Events\TransactionCommitted;
|
|
use Illuminate\Database\Events\TransactionRolledBack;
|
|
use WebmanTech\Debugbar\DebugBar;
|
|
use WebmanTech\Debugbar\Helper\ArrayHelper;
|
|
use WebmanTech\Debugbar\Laravel\DataCollector\QueryCollector;
|
|
use WebmanTech\Debugbar\Laravel\DataFormatter\QueryFormatter;
|
|
|
|
class LaravelQueryCollector extends QueryCollector
|
|
{
|
|
/**
|
|
* @var array
|
|
*/
|
|
protected $config = [
|
|
'with_params' => true, // 将参数绑定上
|
|
'backtrace' => true, // 显示 sql 来源
|
|
'backtrace_exclude_paths' => [], // 排除 sql 来源
|
|
'timeline' => true, // 将 sql 显示到 timeline 上
|
|
'duration_background' => true, // 相对于执行所需的时间,在每个查询上显示阴影背景
|
|
'explain' => [
|
|
'enabled' => false,
|
|
'types' => ['SELECT'], // 废弃的设置,目前只支持 SELECT
|
|
],
|
|
'hints' => false, // 显示常见错误提示
|
|
'show_copy' => true, // 显示复制
|
|
'slow_threshold' => false, // 仅显示慢sql,设置毫秒时间来启用
|
|
];
|
|
|
|
public function __construct(array $config = [], DebugBarTimeDataCollector $timeCollector = null)
|
|
{
|
|
$this->config = ArrayHelper::merge($this->config, $config);
|
|
|
|
if (!$this->config['timeline']) {
|
|
$timeCollector = null;
|
|
}
|
|
|
|
parent::__construct($timeCollector);
|
|
|
|
$this->setConfig();
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
public function getAssets()
|
|
{
|
|
$assets = parent::getAssets();
|
|
$assets['js'] = 'laravel/sqlqueries/widget.js';
|
|
return $assets;
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
public function getName()
|
|
{
|
|
return 'laravelDB';
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
function getWidgets()
|
|
{
|
|
$name = $this->getName();
|
|
return [
|
|
$name => [
|
|
"icon" => "database",
|
|
"widget" => "PhpDebugBar.Widgets.LaravelSQLQueriesWidget",
|
|
"map" => $name,
|
|
"default" => "[]"
|
|
],
|
|
"{$name}:badge" => [
|
|
"map" => "{$name}.nb_statements",
|
|
"default" => 0,
|
|
],
|
|
];
|
|
}
|
|
|
|
protected function setConfig()
|
|
{
|
|
$this->mergeBacktraceExcludePaths([
|
|
'/vendor/webman-tech/debugbar',
|
|
'/vendor/illuminate/support',
|
|
'/vendor/illuminate/database',
|
|
'/vendor/illuminate/events',
|
|
'/vendor/jenssegers/mongodb', // jenssegers/mongodb
|
|
]);
|
|
|
|
$this->setDataFormatter(new QueryFormatter());
|
|
|
|
if ($this->config['with_params']) {
|
|
$this->setRenderSqlWithParams(true);
|
|
}
|
|
|
|
if ($this->config['backtrace']) {
|
|
$middleware = [];
|
|
$this->setFindSource(true, $middleware);
|
|
}
|
|
|
|
if ($this->config['backtrace_exclude_paths']) {
|
|
$excludePaths = $this->config['backtrace_exclude_paths'];
|
|
$this->mergeBacktraceExcludePaths($excludePaths);
|
|
}
|
|
|
|
$this->setDurationBackground($this->config['duration_background']);
|
|
|
|
if ($this->config['explain']['enabled']) {
|
|
$types = $this->config['explain']['types'];
|
|
$this->setExplainSource(true, $types);
|
|
}
|
|
|
|
if ($this->config['hints']) {
|
|
$this->setShowHints(true);
|
|
}
|
|
|
|
if ($this->config['show_copy']) {
|
|
$this->setShowCopyButton(true);
|
|
}
|
|
}
|
|
|
|
public function addQuery($query, $bindings, $time, $connection)
|
|
{
|
|
parent::addQuery($query, $bindings, $time, $connection);
|
|
|
|
// 给 connection 添加 connectionName
|
|
$lastIndex = count($this->queries) - 1;
|
|
$lastQuery = $this->queries[$lastIndex];
|
|
$lastQuery['connection'] = $connection->getName() . '__' . $lastQuery['connection']; // 目前只能用 __ 分隔,其他字符比如冒号、斜杠等会导致js错误
|
|
$this->queries[$lastIndex] = $lastQuery;
|
|
}
|
|
|
|
public function addListener(Connection $db)
|
|
{
|
|
$db->listen(function (QueryExecuted $event) use ($db) {
|
|
$connection = $event->connection;
|
|
if (!$this->isEventConnectionEqual($db, $connection)) {
|
|
return;
|
|
}
|
|
$bindings = $event->bindings;
|
|
$time = $event->time;
|
|
$query = $event->sql;
|
|
|
|
$threshold = $this->config['slow_threshold'];
|
|
if (!$threshold || $time > $threshold) {
|
|
if ($collector = $this->getRequestThisCollector()) {
|
|
$collector->addQuery($query, $bindings, $time, $connection);
|
|
}
|
|
}
|
|
});
|
|
|
|
$db->getEventDispatcher()->listen(TransactionBeginning::class, function (TransactionBeginning $transaction) use ($db) {
|
|
if (!$this->isEventConnectionEqual($db, $transaction->connection)) {
|
|
return;
|
|
}
|
|
if ($collector = $this->getRequestThisCollector()) {
|
|
$collector->collectTransactionEvent('Begin Transaction', $transaction->connection);
|
|
}
|
|
});
|
|
|
|
$db->getEventDispatcher()->listen(TransactionCommitted::class, function (TransactionCommitted $transaction) use ($db) {
|
|
if (!$this->isEventConnectionEqual($db, $transaction->connection)) {
|
|
return;
|
|
}
|
|
if ($collector = $this->getRequestThisCollector()) {
|
|
$collector->collectTransactionEvent('Commit Transaction', $transaction->connection);
|
|
}
|
|
});
|
|
|
|
$db->getEventDispatcher()->listen(TransactionRolledBack::class, function (TransactionRolledBack $transaction) use ($db) {
|
|
if (!$this->isEventConnectionEqual($db, $transaction->connection)) {
|
|
return;
|
|
}
|
|
if ($collector = $this->getRequestThisCollector()) {
|
|
$collector->collectTransactionEvent('Rollback Transaction', $transaction->connection);
|
|
}
|
|
});
|
|
|
|
$db->getEventDispatcher()->listen('connection.*.beganTransaction', function ($event, $params) {
|
|
if ($collector = $this->getRequestThisCollector()) {
|
|
$collector->collectTransactionEvent('Begin Transaction', $params[0]);
|
|
}
|
|
});
|
|
|
|
$db->getEventDispatcher()->listen('connection.*.committed', function ($event, $params) {
|
|
if ($collector = $this->getRequestThisCollector()) {
|
|
$collector->collectTransactionEvent('Commit Transaction', $params[0]);
|
|
}
|
|
});
|
|
|
|
$db->getEventDispatcher()->listen('connection.*.rollingBack', function ($event, $params) {
|
|
if ($collector = $this->getRequestThisCollector()) {
|
|
$collector->collectTransactionEvent('Rollback Transaction', $params[0]);
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 获取 request 下每次新的当前 collector 对象
|
|
* @return $this|null
|
|
*/
|
|
protected function getRequestThisCollector(): ?self
|
|
{
|
|
$debugBar = DebugBar::instance();
|
|
if (!$debugBar->hasCollector($this->getName())) {
|
|
return null;
|
|
}
|
|
/** @var static $collector */
|
|
$collector = $debugBar->getCollector($this->getName());
|
|
return $collector;
|
|
}
|
|
|
|
/**
|
|
* 由于 event 可能是 Container 下的,则事件会重复绑定
|
|
* 因此粗腰判断事件的 connection 是否是当前 DB 的
|
|
* @param Connection $connection
|
|
* @param Connection $eventConnection
|
|
* @return bool
|
|
*/
|
|
protected function isEventConnectionEqual(Connection $connection, Connection $eventConnection): bool
|
|
{
|
|
return $connection->getName() === $eventConnection->getName();
|
|
}
|
|
} |