Skip to content

Commit 4b07d9f

Browse files
authored
Trace file for Gate checks(GateCollector) (#1770)
* Trace file for Gate checks(GateCollector) * fix doc
1 parent d22f42f commit 4b07d9f

File tree

3 files changed

+108
-3
lines changed

3 files changed

+108
-3
lines changed

config/debugbar.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,10 +215,13 @@
215215
'show_name' => env('DEBUGBAR_OPTIONS_AUTH_SHOW_NAME', true), // Also show the users name/email in the debugbar
216216
'show_guards' => env('DEBUGBAR_OPTIONS_AUTH_SHOW_GUARDS', true), // Show the guards that are used
217217
],
218+
'gate' => [
219+
'trace' => false, // Trace the origin of the Gate checks
220+
],
218221
'db' => [
219222
'with_params' => env('DEBUGBAR_OPTIONS_WITH_PARAMS', true), // Render SQL with the parameters substituted
220223
'exclude_paths' => [ // Paths to exclude entirely from the collector
221-
// 'vendor/laravel/framework/src/Illuminate/Session', // Exclude sessions queries
224+
//'vendor/laravel/framework/src/Illuminate/Session', // Exclude sessions queries
222225
],
223226
'backtrace' => env('DEBUGBAR_OPTIONS_DB_BACKTRACE', true), // Use a backtrace to find the origin of the query in your files.
224227
'backtrace_exclude_paths' => [], // Paths to exclude from backtrace. (in addition to defaults)

src/DataCollector/GateCollector.php

Lines changed: 99 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,31 @@
88
use Illuminate\Contracts\Auth\Access\Gate;
99
use Illuminate\Contracts\Auth\Authenticatable;
1010
use Illuminate\Database\Eloquent\Model;
11+
use Illuminate\Routing\Router;
1112
use Symfony\Component\VarDumper\Cloner\VarCloner;
1213
use Illuminate\Support\Str;
1314

1415
/**
15-
* Collector for Laravel's Auth provider
16+
* Collector for Laravel's gate checks
1617
*/
1718
class GateCollector extends MessagesCollector
1819
{
20+
/** @var int */
21+
protected $backtraceLimit = 15;
22+
23+
/** @var array */
24+
protected $reflection = [];
25+
26+
/** @var \Illuminate\Routing\Router */
27+
protected $router;
28+
1929
/**
2030
* @param Gate $gate
2131
*/
22-
public function __construct(Gate $gate)
32+
public function __construct(Gate $gate, Router $router)
2333
{
2434
parent::__construct('gate');
35+
$this->router = $router;
2536
$this->setDataFormatter(new SimpleFormatter());
2637
$gate->after(function ($user, $ability, $result, $arguments = []) {
2738
$this->addCheck($user, $ability, $result, $arguments);
@@ -82,4 +93,90 @@ public function addCheck($user, $ability, $result, $arguments = [])
8293
'arguments' => $this->getDataFormatter()->formatVar($arguments),
8394
], $label, false);
8495
}
96+
97+
/**
98+
* @param array $stacktrace
99+
*
100+
* @return array
101+
*/
102+
protected function getStackTraceItem($stacktrace)
103+
{
104+
foreach ($stacktrace as $i => $trace) {
105+
if (!isset($trace['file'])) {
106+
continue;
107+
}
108+
109+
if (str_ends_with($trace['file'], 'Illuminate/Routing/ControllerDispatcher.php')) {
110+
$trace = $this->findControllerFromDispatcher($trace);
111+
} elseif (str_starts_with($trace['file'], storage_path())) {
112+
$hash = pathinfo($trace['file'], PATHINFO_FILENAME);
113+
114+
if ($file = $this->findViewFromHash($hash)) {
115+
$trace['file'] = $file;
116+
}
117+
}
118+
119+
if ($this->fileIsInExcludedPath($trace['file'])) {
120+
continue;
121+
}
122+
123+
return $trace;
124+
}
125+
126+
return $stacktrace[0];
127+
}
128+
129+
/**
130+
* Find the route action file
131+
*
132+
* @param array $trace
133+
* @return array
134+
*/
135+
protected function findControllerFromDispatcher($trace)
136+
{
137+
/** @var \Closure|string|array $action */
138+
$action = $this->router->current()->getAction('uses');
139+
140+
if (is_string($action)) {
141+
[$controller, $method] = explode('@', $action);
142+
143+
$reflection = new \ReflectionMethod($controller, $method);
144+
$trace['file'] = $reflection->getFileName();
145+
$trace['line'] = $reflection->getStartLine();
146+
} elseif ($action instanceof \Closure) {
147+
$reflection = new \ReflectionFunction($action);
148+
$trace['file'] = $reflection->getFileName();
149+
$trace['line'] = $reflection->getStartLine();
150+
}
151+
152+
return $trace;
153+
}
154+
155+
/**
156+
* Find the template name from the hash.
157+
*
158+
* @param string $hash
159+
* @return null|array
160+
*/
161+
protected function findViewFromHash($hash)
162+
{
163+
$finder = app('view')->getFinder();
164+
165+
if (isset($this->reflection['viewfinderViews'])) {
166+
$property = $this->reflection['viewfinderViews'];
167+
} else {
168+
$reflection = new \ReflectionClass($finder);
169+
$property = $reflection->getProperty('views');
170+
$property->setAccessible(true);
171+
$this->reflection['viewfinderViews'] = $property;
172+
}
173+
174+
$xxh128Exists = in_array('xxh128', hash_algos());
175+
176+
foreach ($property->getValue($finder) as $name => $path) {
177+
if (($xxh128Exists && hash('xxh128', 'v2' . $path) == $hash) || sha1('v2' . $path) == $hash) {
178+
return $path;
179+
}
180+
}
181+
}
85182
}

src/LaravelDebugbar.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -572,6 +572,11 @@ public function __toString(): string
572572
if ($this->shouldCollect('gate', false)) {
573573
try {
574574
$this->addCollector($app->make(GateCollector::class));
575+
576+
if ($config->get('debugbar.options.gate.trace', false)) {
577+
$this['gate']->collectFileTrace(true);
578+
$this['gate']->addBacktraceExcludePaths($config->get('debugbar.options.gate.exclude_paths',[]));
579+
}
575580
} catch (Exception $e) {
576581
$this->addCollectorException('Cannot add GateCollector', $e);
577582
}

0 commit comments

Comments
 (0)