Skip to content

Commit 66c5c12

Browse files
committed
[WebProfilerBundle] Add special "_best" panel
1 parent 2b71c6f commit 66c5c12

File tree

7 files changed

+127
-9
lines changed

7 files changed

+127
-9
lines changed

UPGRADE-4.4.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,7 @@ WebProfilerBundle
344344

345345
* Deprecated the `ExceptionController` class in favor of `ExceptionErrorController`
346346
* Deprecated the `TemplateManager::templateExists()` method
347+
* The `_best` data collector name is reserved for internal usage.
347348

348349
WebServerBundle
349350
---------------

src/Symfony/Bundle/WebProfilerBundle/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ CHANGELOG
1212
* deprecated the `ExceptionController` in favor of `ExceptionPanelController`
1313
* marked all classes of the WebProfilerBundle as internal
1414
* added a section with the stamps of a message after it is dispatched in the Messenger panel
15+
* Added the special `_best` panel that display the most useful panel depending of the collected data.
16+
* The `_best` data collector name is reserved for internal usage.
1517

1618
4.3.0
1719
-----

src/Symfony/Bundle/WebProfilerBundle/Controller/ProfilerController.php

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
use Symfony\Component\HttpFoundation\Request;
1818
use Symfony\Component\HttpFoundation\Response;
1919
use Symfony\Component\HttpFoundation\Session\Flash\AutoExpireFlashBag;
20+
use Symfony\Component\HttpKernel\DataCollector\DumpDataCollector;
21+
use Symfony\Component\HttpKernel\DataCollector\ExceptionDataCollector;
2022
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
2123
use Symfony\Component\HttpKernel\Profiler\Profiler;
2224
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
@@ -78,7 +80,7 @@ public function panelAction(Request $request, $token)
7880
$this->cspHandler->disableCsp();
7981
}
8082

81-
$panel = $request->query->get('panel', 'request');
83+
$panel = $request->query->get('panel', $defaultPanel = 'request');
8284
$page = $request->query->get('page', 'home');
8385

8486
if ('latest' === $token && $latest = current($this->profiler->find(null, null, 1, null, null, null))) {
@@ -89,6 +91,24 @@ public function panelAction(Request $request, $token)
8991
return new Response($this->twig->render('@WebProfiler/Profiler/info.html.twig', ['about' => 'no_token', 'token' => $token, 'request' => $request]), 200, ['Content-Type' => 'text/html']);
9092
}
9193

94+
if ($profile->hasCollector($bestPanel = '_best')) {
95+
@trigger_error(sprintf('The "%s" data collector name is reserved for internal usage since Symfony 4.4. Please rename it.', $bestPanel), E_USER_DEPRECATED);
96+
} elseif ($bestPanel === $panel) {
97+
$panel = $defaultPanel;
98+
99+
foreach ($profile->getCollectors() as $collector) {
100+
if ($collector instanceof ExceptionDataCollector && $collector->hasException()) {
101+
$panel = $collector->getName();
102+
103+
break;
104+
}
105+
106+
if ($collector instanceof DumpDataCollector && $collector->getDumpsCount() > 0) {
107+
$panel = $collector->getName();
108+
}
109+
}
110+
}
111+
92112
if (!$profile->hasCollector($panel)) {
93113
throw new NotFoundHttpException(sprintf('Panel "%s" is not available for token "%s".', $panel, $token));
94114
}

src/Symfony/Bundle/WebProfilerBundle/EventListener/WebDebugToolbarListener.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ public function onKernelResponse(FilterResponseEvent $event)
6969
try {
7070
$response->headers->set(
7171
'X-Debug-Token-Link',
72-
$this->urlGenerator->generate('_profiler', ['token' => $response->headers->get('X-Debug-Token')], UrlGeneratorInterface::ABSOLUTE_URL)
72+
$this->urlGenerator->generate('_profiler', ['token' => $response->headers->get('X-Debug-Token'), 'panel' => '_best'], UrlGeneratorInterface::ABSOLUTE_URL)
7373
);
7474
} catch (\Exception $e) {
7575
$response->headers->set('X-Debug-Error', \get_class($e).': '.preg_replace('/\s+/', ' ', $e->getMessage()));

src/Symfony/Bundle/WebProfilerBundle/Resources/views/Profiler/base_js.html.twig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@
267267
if (request.profilerUrl) {
268268
profilerCell.textContent = '';
269269
var profilerLink = document.createElement('a');
270-
profilerLink.setAttribute('href', request.statusCode < 400 ? request.profilerUrl : request.profilerUrl + '?panel=exception');
270+
profilerLink.setAttribute('href', request.profilerUrl);
271271
profilerLink.textContent = request.profile;
272272
profilerCell.appendChild(profilerLink);
273273
}

src/Symfony/Bundle/WebProfilerBundle/Tests/Controller/ProfilerControllerTest.php

Lines changed: 98 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,16 @@
1515
use Symfony\Bundle\WebProfilerBundle\Controller\ProfilerController;
1616
use Symfony\Bundle\WebProfilerBundle\Csp\ContentSecurityPolicyHandler;
1717
use Symfony\Component\HttpFoundation\Request;
18+
use Symfony\Component\HttpFoundation\Response;
19+
use Symfony\Component\HttpKernel\DataCollector\DumpDataCollector;
20+
use Symfony\Component\HttpKernel\DataCollector\ExceptionDataCollector;
21+
use Symfony\Component\HttpKernel\DataCollector\RequestDataCollector;
1822
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
1923
use Symfony\Component\HttpKernel\Profiler\Profile;
24+
use Symfony\Component\HttpKernel\Profiler\Profiler;
25+
use Twig\Environment;
26+
use Twig\Loader\SourceContextLoaderInterface;
27+
use Twig\Source;
2028

2129
class ProfilerControllerTest extends TestCase
2230
{
@@ -185,17 +193,104 @@ public function provideCspVariants()
185193
];
186194
}
187195

188-
private function createController($profiler, $twig, $withCSP): ProfilerController
196+
/**
197+
* @dataProvider bestPanelProvider
198+
*/
199+
public function testBestPanel(string $expectedPanel, Profile $profile)
200+
{
201+
$profiler = $this->createMock(Profiler::class);
202+
$profiler
203+
->expects($this->atLeastOnce())
204+
->method('loadProfile')
205+
->with($profile->getToken())
206+
->willReturn($profile);
207+
208+
$profiler
209+
->expects($this->atLeastOnce())
210+
->method('has')
211+
->with($this->logicalXor($collectorsNames = array_keys($profile->getCollectors())))
212+
->willReturn(true);
213+
214+
$loader = $this->createMock(SourceContextLoaderInterface::class);
215+
$loader
216+
->expects($this->atLeastOnce())
217+
->method('getSourceContext')
218+
->with($this->logicalXor($expectedTemplate = 'expected_template.html.twig', 'other_template.html.twig'))
219+
->willReturn(new Source('foo', 'bar'));
220+
221+
$twig = $this->createMock(Environment::class);
222+
$twig
223+
->expects($this->atLeastOnce())
224+
->method('getLoader')
225+
->willReturn($loader);
226+
$twig
227+
->expects($this->once())
228+
->method('render')
229+
->with($expectedTemplate);
230+
231+
$request = new Request();
232+
$request->query->set('panel', '_best');
233+
234+
$this
235+
->createController($profiler, $twig, false, array_map(function (string $collectorName) use ($expectedPanel, $expectedTemplate): array {
236+
if ($collectorName === $expectedPanel) {
237+
return [$expectedPanel, $expectedTemplate];
238+
}
239+
240+
return [$collectorName, 'other_template.html.twig'];
241+
}, $collectorsNames))
242+
->panelAction($request, $profile->getToken());
243+
}
244+
245+
public function bestPanelProvider(): \Generator
246+
{
247+
// Test default behavior
248+
$profile = new Profile('xxxxxx');
249+
$profile->addCollector($requestDataCollector = new RequestDataCollector());
250+
yield [$requestDataCollector->getName(), $profile];
251+
252+
// Test exception
253+
$profile = new Profile('xxxxxx');
254+
$profile->addCollector($exceptionDataCollector = new ExceptionDataCollector());
255+
$exceptionDataCollector->collect(new Request(), new Response(), new \DomainException());
256+
yield [$exceptionDataCollector->getName(), $profile];
257+
258+
// Test exception priority
259+
$dumpDataCollector = $this->createMock(DumpDataCollector::class);
260+
$dumpDataCollector
261+
->expects($this->atLeastOnce())
262+
->method('getName')
263+
->willReturn('dump');
264+
$dumpDataCollector
265+
->expects($this->atLeastOnce())
266+
->method('getDumpsCount')
267+
->willReturn(1);
268+
$profile = new Profile('xxxxxx');
269+
$profile->setCollectors([$exceptionDataCollector, $dumpDataCollector]);
270+
yield [$exceptionDataCollector->getName(), $profile];
271+
272+
// Test exception priority when defined afterwards
273+
$profile = new Profile('xxxxxx');
274+
$profile->setCollectors([$dumpDataCollector, $exceptionDataCollector]);
275+
yield [$exceptionDataCollector->getName(), $profile];
276+
277+
// Test dump
278+
$profile = new Profile('xxxxxx');
279+
$profile->addCollector($dumpDataCollector);
280+
yield [$dumpDataCollector->getName(), $profile];
281+
}
282+
283+
private function createController($profiler, $twig, $withCSP, array $templates = []): ProfilerController
189284
{
190285
$urlGenerator = $this->getMockBuilder('Symfony\Component\Routing\Generator\UrlGeneratorInterface')->getMock();
191286

192287
if ($withCSP) {
193288
$nonceGenerator = $this->getMockBuilder('Symfony\Bundle\WebProfilerBundle\Csp\NonceGenerator')->getMock();
194289
$nonceGenerator->method('generate')->willReturn('dummy_nonce');
195290

196-
return new ProfilerController($urlGenerator, $profiler, $twig, [], new ContentSecurityPolicyHandler($nonceGenerator));
291+
return new ProfilerController($urlGenerator, $profiler, $twig, $templates, new ContentSecurityPolicyHandler($nonceGenerator));
197292
}
198293

199-
return new ProfilerController($urlGenerator, $profiler, $twig, []);
294+
return new ProfilerController($urlGenerator, $profiler, $twig, $templates);
200295
}
201296
}

src/Symfony/Bundle/WebProfilerBundle/Tests/EventListener/WebDebugToolbarListenerTest.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ public function testXDebugUrlHeader()
243243
$urlGenerator
244244
->expects($this->once())
245245
->method('generate')
246-
->with('_profiler', ['token' => 'xxxxxxxx'], UrlGeneratorInterface::ABSOLUTE_URL)
246+
->with('_profiler', ['token' => 'xxxxxxxx', 'panel' => '_best'], UrlGeneratorInterface::ABSOLUTE_URL)
247247
->willReturn('http://mydomain.com/_profiler/xxxxxxxx')
248248
;
249249

@@ -264,7 +264,7 @@ public function testThrowingUrlGenerator()
264264
$urlGenerator
265265
->expects($this->once())
266266
->method('generate')
267-
->with('_profiler', ['token' => 'xxxxxxxx'])
267+
->with('_profiler', ['token' => 'xxxxxxxx', 'panel' => '_best'])
268268
->willThrowException(new \Exception('foo'))
269269
;
270270

@@ -285,7 +285,7 @@ public function testThrowingErrorCleanup()
285285
$urlGenerator
286286
->expects($this->once())
287287
->method('generate')
288-
->with('_profiler', ['token' => 'xxxxxxxx'])
288+
->with('_profiler', ['token' => 'xxxxxxxx', 'panel' => '_best'])
289289
->willThrowException(new \Exception("This\nmultiline\r\ntabbed text should\tcome out\r on\n \ta single plain\r\nline"))
290290
;
291291

0 commit comments

Comments
 (0)