From e138039d3db5cde482b880cfceceb064d248b3f8 Mon Sep 17 00:00:00 2001 From: Ozan Kurt Date: Fri, 22 Mar 2024 17:51:43 +0100 Subject: [PATCH] Ability to add a custom stacktrace parsing method Hello there, I use https://github.com/yajra/laravel-datatables package in almost all of my projects. I currently have a problem of seeing the source of the queries inside my DataTable classes. For example: I have a query builder setup inside my `UsersDataTable`. ```php public function query() { return User::query(); } ``` Since this function only starts to bulid the query it doesn't show up in `debug_backtrace()`. For this I've added a solution which allows me to check the "query sender" object and modify how the `$frame` was filled. I've added simple static method to add my custom callables to `QueryCollector`, in there I can modify and return the `$frame` as I wish in case the `$trace['object']` is matching my first parameter `$objectType`. Here is an example `DataTable::class` query tracer. ```php QueryCollector::addCustomFrameParser(DataTable::class, function (object $frame, array $trace) { // Remove the 'App\' prefix from the namespace $relativeNamespace = str_replace('App\\', '', $trace['object']::class); // Convert namespace to a directory path $directoryPath = str_replace('\\', DIRECTORY_SEPARATOR, $relativeNamespace); $filePath = app_path($directoryPath) . '.php'; // Get the file path inside the project from the class object $frame->file = $filePath; return $frame; }); ``` After I add this to my `AppServiceProvider`, I can simply see the change inside my "Query" tab. Before: IMG HERE After: IMG HERE --- src/DataCollector/QueryCollector.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/DataCollector/QueryCollector.php b/src/DataCollector/QueryCollector.php index 941647ec..59abbd6e 100644 --- a/src/DataCollector/QueryCollector.php +++ b/src/DataCollector/QueryCollector.php @@ -34,6 +34,7 @@ class QueryCollector extends PDOCollector '/vendor/october/rain', '/vendor/barryvdh/laravel-debugbar', ]; + protected static $customFrameParsers = []; /** * @param TimeDataCollector $timeCollector @@ -43,6 +44,14 @@ public function __construct(TimeDataCollector $timeCollector = null) $this->timeCollector = $timeCollector; } + /** + * Add a custom frame parser for a specific object type. + */ + public static function addCustomFrameParser(string $objectType, callable $customFrameParser): void + { + static::$customFrameParsers[$objectType] = $customFrameParser; + } + /** * @param int|null $softLimit After the soft limit, no parameters/backtrace are captured * @param int|null $hardLimit After the hard limit, queries are ignored @@ -334,6 +343,15 @@ protected function parseTrace($index, array $trace) ) { $frame->file = $trace['file']; + foreach (static::$customFrameParsers as $objectType => $customFrameParser) { + if (isset($trace['object']) && is_a($trace['object'], $objectType)) { + $frame->line = '?'; + $frame = $customFrameParser($frame, $trace); + $frame->name = $this->normalizeFilePath($frame->file); + return $frame; + } + } + if (isset($trace['object']) && is_a($trace['object'], 'Twig_Template')) { list($frame->file, $frame->line) = $this->getTwigInfo($trace); } elseif (strpos($frame->file, storage_path()) !== false) {