diff --git a/LICENSE b/LICENSE index 00837045..0138f8f0 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2004-2023 Fabien Potencier +Copyright (c) 2004-present Fabien Potencier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Resources/views/Collector/http_client.html.twig b/Resources/views/Collector/http_client.html.twig index ed926bf8..7fda5a93 100644 --- a/Resources/views/Collector/http_client.html.twig +++ b/Resources/views/Collector/http_client.html.twig @@ -91,7 +91,7 @@ Profile {% endif %} - {% if trace.curlCommand is not null %} + {% if trace.curlCommand is defined and trace.curlCommand %} @@ -107,7 +107,7 @@ {% endif %} Response - + {% if trace.http_code >= 500 %} {% set responseStatus = 'error' %} {% elseif trace.http_code >= 400 %} diff --git a/Resources/views/Collector/logger.html.twig b/Resources/views/Collector/logger.html.twig index dc9abc3e..937b4a64 100644 --- a/Resources/views/Collector/logger.html.twig +++ b/Resources/views/Collector/logger.html.twig @@ -131,8 +131,10 @@ - Time - Message + + Time + Message + @@ -185,7 +187,7 @@

Container Compilation Logs ({{ compilerLogTotal }})

-

Log messages generated during the compilation of the service container.

+ Log messages generated during the compilation of the service container.
{% if collector.compilerLogs is empty %} diff --git a/Resources/views/Collector/mailer.html.twig b/Resources/views/Collector/mailer.html.twig index 651c2a16..a42b8b33 100644 --- a/Resources/views/Collector/mailer.html.twig +++ b/Resources/views/Collector/mailer.html.twig @@ -90,8 +90,8 @@ {% for event in collector.events.events(transport) %} {{ loop.index }} - {{ event.message.headers.get('subject').bodyAsString() ?? '(No subject)' }} - {{ (event.message.headers.get('to').bodyAsString() ?? '(empty)')|replace({'To:': ''}) }} + {{ event.message.getSubject() ?? '(No subject)' }} + {{ event.message.getTo()|map(addr => addr.toString())|join(', ')|default('(empty)') }} {% endfor %} @@ -137,12 +137,12 @@

- {{ message.headers.get('subject').bodyAsString() ?? '(No subject)' }} + {{ message.getSubject() ?? '(No subject)' }}

-

From: {{ (message.headers.get('from').bodyAsString() ?? '(empty)')|replace({'From:': ''}) }}

-

To: {{ (message.headers.get('to').bodyAsString() ?? '(empty)')|replace({'To:': ''}) }}

- {% for header in message.headers.all|filter(header => (header.name ?? '') not in ['Subject', 'From', 'To']) %} +

From: {{ message.getFrom()|map(addr => addr.toString())|join(', ')|default('(empty)') }}

+

To: {{ message.getTo()|map(addr => addr.toString())|join(', ')|default('(empty)') }}

+ {% for header in message.headers.all|filter(header => (header.name ?? '')|lower not in ['subject', 'from', 'to']) %}

{{ header.toString }}

{% endfor %}
@@ -182,6 +182,17 @@
{% if message.htmlBody %} {% set htmlBody = message.htmlBody() %} +
+

HTML preview

+
+ +
+
+

HTML content

@@ -194,19 +205,6 @@
- -
-

HTML preview

-
-
-                                                    
-                                                
-
-
{% endif %} {% if message.textBody %} diff --git a/Resources/views/Collector/notifier.html.twig b/Resources/views/Collector/notifier.html.twig index b0c9219b..1f51c328 100644 --- a/Resources/views/Collector/notifier.html.twig +++ b/Resources/views/Collector/notifier.html.twig @@ -17,7 +17,7 @@ {% for transport in events.transports %}
- {{ transport }} + {{ transport ?: 'Empty Transport Name' }} {{ events.messages(transport)|length }}
{% endfor %} @@ -100,7 +100,7 @@
{% for transport in events.transports %} -

{{ transport }}

+

{{ transport ?: 'Empty Transport Name' }}

@@ -114,24 +114,24 @@ Subject

{{ message.getSubject() ?? '(empty)' }}

- {% if message.getNotification is defined %} + {% set notification = message.notification ?? null %} + {% if notification %}
Content -
{{ message.getNotification().getContent() ?? '(empty)' }}
+
{{ notification.getContent() ?? '(empty)' }}
Importance -
{{ message.getNotification().getImportance() }}
+
{{ notification.getImportance() }}
{% endif %}
- {% if message.getNotification is defined %} + {% if notification %}

Notification

- {% set notification = event.message.getNotification() %}
                                                             {{- 'Subject: ' ~ notification.getSubject() }}
diff --git a/Resources/views/Profiler/header.html.twig b/Resources/views/Profiler/header.html.twig index ac62bdcf..e568d7e6 100644 --- a/Resources/views/Profiler/header.html.twig +++ b/Resources/views/Profiler/header.html.twig @@ -4,7 +4,8 @@ diff --git a/Resources/views/Profiler/profiler.css.twig b/Resources/views/Profiler/profiler.css.twig index 7fc32f99..953ee53f 100644 --- a/Resources/views/Profiler/profiler.css.twig +++ b/Resources/views/Profiler/profiler.css.twig @@ -2079,7 +2079,7 @@ tr.log-status-silenced > td:first-child:before { max-width: 888px; } .width-full .sql-explain { - max-width: min-content; + max-width: unset; } .sql-explain table td, .sql-explain table tr { word-break: normal; diff --git a/Resources/views/Profiler/toolbar.html.twig b/Resources/views/Profiler/toolbar.html.twig index d007f451..7a77a9b1 100644 --- a/Resources/views/Profiler/toolbar.html.twig +++ b/Resources/views/Profiler/toolbar.html.twig @@ -1,5 +1,5 @@ -
+
diff --git a/Tests/Controller/ProfilerControllerTest.php b/Tests/Controller/ProfilerControllerTest.php index 86de485e..67355d90 100644 --- a/Tests/Controller/ProfilerControllerTest.php +++ b/Tests/Controller/ProfilerControllerTest.php @@ -11,6 +11,7 @@ namespace Symfony\Bundle\WebProfilerBundle\Tests\Controller; +use PHPUnit\Framework\MockObject\MockObject; use Symfony\Bundle\FrameworkBundle\KernelBrowser; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; use Symfony\Bundle\WebProfilerBundle\Controller\ProfilerController; @@ -135,7 +136,7 @@ public function testToolbarActionWithEmptyToken($token) $this->assertEquals(200, $response->getStatusCode()); } - public function getEmptyTokenCases() + public static function getEmptyTokenCases() { return [ [null], @@ -164,7 +165,7 @@ public function testOpeningDisallowedPaths($path, $isAllowed) } } - public function getOpenFileCases() + public static function getOpenFileCases() { return [ ['README.md', true], @@ -354,7 +355,7 @@ public function testPhpinfoAction() $this->assertStringContainsString('PHP License', $client->getResponse()->getContent()); } - public function provideCspVariants() + public static function provideCspVariants() { return [ [true], @@ -366,6 +367,99 @@ public function provideCspVariants() * @dataProvider defaultPanelProvider */ public function testDefaultPanel(string $expectedPanel, Profile $profile) + { + $this->assertDefaultPanel($expectedPanel, $profile); + } + + public static function defaultPanelProvider(): \Generator + { + // Test default behavior + $profile = new Profile('xxxxxx'); + $profile->addCollector($requestDataCollector = new RequestDataCollector()); + yield [$requestDataCollector->getName(), $profile]; + + // Test exception + $profile = new Profile('xxxxxx'); + $profile->addCollector($exceptionDataCollector = new ExceptionDataCollector()); + $exceptionDataCollector->collect(new Request(), new Response(), new \DomainException()); + yield [$exceptionDataCollector->getName(), $profile]; + } + + private function createController($profiler, $twig, $withCSP, array $templates = []): ProfilerController + { + $urlGenerator = $this->createMock(UrlGeneratorInterface::class); + + if ($withCSP) { + $nonceGenerator = $this->createMock(NonceGenerator::class); + $nonceGenerator->method('generate')->willReturn('dummy_nonce'); + + return new ProfilerController($urlGenerator, $profiler, $twig, $templates, new ContentSecurityPolicyHandler($nonceGenerator)); + } + + return new ProfilerController($urlGenerator, $profiler, $twig, $templates); + } + + public function testDumpPanelExceptionPriority() + { + $exceptionDataCollector = new ExceptionDataCollector(); + $exceptionDataCollector->collect(new Request(), new Response(), new \DomainException()); + + $dumpDataCollector = $this->createDumpDataCollector(); + + $profile = new Profile('xxxxxx'); + $profile->setCollectors([$exceptionDataCollector, $dumpDataCollector]); + + $this->assertDefaultPanel($exceptionDataCollector->getName(), $profile); + } + + public function testDumpPanelWhenDefinedAfterwards() + { + $exceptionDataCollector = new ExceptionDataCollector(); + $exceptionDataCollector->collect(new Request(), new Response(), new \DomainException()); + + $dumpDataCollector = $this->createDumpDataCollector(); + $dumpDataCollector + ->expects($this->atLeastOnce()) + ->method('getDumpsCount') + ->willReturn(1) + ; + + $profile = new Profile('xxxxxx'); + $profile->setCollectors([$dumpDataCollector, $exceptionDataCollector]); + + $this->assertDefaultPanel($exceptionDataCollector->getName(), $profile); + } + + public function testDumpPanel() + { + $dumpDataCollector = $this->createDumpDataCollector(); + $dumpDataCollector + ->expects($this->atLeastOnce()) + ->method('getDumpsCount') + ->willReturn(1) + ; + + $profile = new Profile('xxxxxx'); + $profile->addCollector($dumpDataCollector); + + $this->assertDefaultPanel($dumpDataCollector->getName(), $profile); + } + + /** + * @return MockObject&DumpDataCollector + */ + private function createDumpDataCollector(): MockObject + { + $dumpDataCollector = $this->createMock(DumpDataCollector::class); + $dumpDataCollector + ->expects($this->atLeastOnce()) + ->method('getName') + ->willReturn('dump'); + + return $dumpDataCollector; + } + + private function assertDefaultPanel(string $expectedPanel, Profile $profile) { $profiler = $this->createMock(Profiler::class); $profiler @@ -415,56 +509,4 @@ public function testDefaultPanel(string $expectedPanel, Profile $profile) }, $collectorsNames)) ->panelAction(new Request(), $profile->getToken()); } - - public function defaultPanelProvider(): \Generator - { - // Test default behavior - $profile = new Profile('xxxxxx'); - $profile->addCollector($requestDataCollector = new RequestDataCollector()); - yield [$requestDataCollector->getName(), $profile]; - - // Test exception - $profile = new Profile('xxxxxx'); - $profile->addCollector($exceptionDataCollector = new ExceptionDataCollector()); - $exceptionDataCollector->collect(new Request(), new Response(), new \DomainException()); - yield [$exceptionDataCollector->getName(), $profile]; - - // Test exception priority - $dumpDataCollector = $this->createMock(DumpDataCollector::class); - $dumpDataCollector - ->expects($this->atLeastOnce()) - ->method('getName') - ->willReturn('dump'); - $dumpDataCollector - ->expects($this->atLeastOnce()) - ->method('getDumpsCount') - ->willReturn(1); - $profile = new Profile('xxxxxx'); - $profile->setCollectors([$exceptionDataCollector, $dumpDataCollector]); - yield [$exceptionDataCollector->getName(), $profile]; - - // Test exception priority when defined afterwards - $profile = new Profile('xxxxxx'); - $profile->setCollectors([$dumpDataCollector, $exceptionDataCollector]); - yield [$exceptionDataCollector->getName(), $profile]; - - // Test dump - $profile = new Profile('xxxxxx'); - $profile->addCollector($dumpDataCollector); - yield [$dumpDataCollector->getName(), $profile]; - } - - private function createController($profiler, $twig, $withCSP, array $templates = []): ProfilerController - { - $urlGenerator = $this->createMock(UrlGeneratorInterface::class); - - if ($withCSP) { - $nonceGenerator = $this->createMock(NonceGenerator::class); - $nonceGenerator->method('generate')->willReturn('dummy_nonce'); - - return new ProfilerController($urlGenerator, $profiler, $twig, $templates, new ContentSecurityPolicyHandler($nonceGenerator)); - } - - return new ProfilerController($urlGenerator, $profiler, $twig, $templates); - } } diff --git a/Tests/Csp/ContentSecurityPolicyHandlerTest.php b/Tests/Csp/ContentSecurityPolicyHandlerTest.php index c345b5fb..7d5c0761 100644 --- a/Tests/Csp/ContentSecurityPolicyHandlerTest.php +++ b/Tests/Csp/ContentSecurityPolicyHandlerTest.php @@ -46,7 +46,7 @@ public function testOnKernelResponse($nonce, $expectedNonce, Request $request, R } } - public function provideRequestAndResponses() + public static function provideRequestAndResponses() { $nonce = bin2hex(random_bytes(16)); @@ -73,7 +73,7 @@ public function provideRequestAndResponses() ]; } - public function provideRequestAndResponsesForOnKernelResponse() + public static function provideRequestAndResponsesForOnKernelResponse() { $nonce = bin2hex(random_bytes(16)); diff --git a/Tests/DependencyInjection/ConfigurationTest.php b/Tests/DependencyInjection/ConfigurationTest.php index a0bc7f43..d957cafc 100644 --- a/Tests/DependencyInjection/ConfigurationTest.php +++ b/Tests/DependencyInjection/ConfigurationTest.php @@ -29,7 +29,7 @@ public function testConfigTree(array $options, array $expectedResult) $this->assertEquals($expectedResult, $config); } - public function getDebugModes() + public static function getDebugModes() { return [ [ @@ -71,7 +71,7 @@ public function testConfigTreeUsingInterceptRedirects(bool $interceptRedirects, $this->assertEquals($expectedResult, $config); } - public function getInterceptRedirectsConfiguration() + public static function getInterceptRedirectsConfiguration() { return [ [ diff --git a/Tests/DependencyInjection/WebProfilerExtensionTest.php b/Tests/DependencyInjection/WebProfilerExtensionTest.php index fbfd2ed5..decbd99e 100644 --- a/Tests/DependencyInjection/WebProfilerExtensionTest.php +++ b/Tests/DependencyInjection/WebProfilerExtensionTest.php @@ -22,6 +22,9 @@ use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\HttpKernel\DataCollector\DumpDataCollector; use Symfony\Component\HttpKernel\KernelInterface; +use Symfony\Component\HttpKernel\Profiler\Profiler; +use Symfony\Component\HttpKernel\Profiler\ProfilerStorageInterface; +use Symfony\Component\Routing\RouterInterface; class WebProfilerExtensionTest extends TestCase { @@ -55,11 +58,15 @@ protected function setUp(): void $this->kernel = $this->createMock(KernelInterface::class); + $profiler = $this->createMock(Profiler::class); + $profilerStorage = $this->createMock(ProfilerStorageInterface::class); + $router = $this->createMock(RouterInterface::class); + $this->container = new ContainerBuilder(); $this->container->register('data_collector.dump', DumpDataCollector::class)->setPublic(true); $this->container->register('error_handler.error_renderer.html', HtmlErrorRenderer::class)->setPublic(true); $this->container->register('event_dispatcher', EventDispatcher::class)->setPublic(true); - $this->container->register('router', $this->getMockClass('Symfony\\Component\\Routing\\RouterInterface'))->setPublic(true); + $this->container->register('router', $router::class)->setPublic(true); $this->container->register('twig', 'Twig\Environment')->setPublic(true); $this->container->register('twig_loader', 'Twig\Loader\ArrayLoader')->addArgument([])->setPublic(true); $this->container->register('twig', 'Twig\Environment')->addArgument(new Reference('twig_loader'))->setPublic(true); @@ -71,9 +78,9 @@ protected function setUp(): void $this->container->setParameter('kernel.charset', 'UTF-8'); $this->container->setParameter('debug.file_link_format', null); $this->container->setParameter('profiler.class', ['Symfony\\Component\\HttpKernel\\Profiler\\Profiler']); - $this->container->register('profiler', $this->getMockClass('Symfony\\Component\\HttpKernel\\Profiler\\Profiler')) + $this->container->register('profiler', $profiler::class) ->setPublic(true) - ->addArgument(new Definition($this->getMockClass('Symfony\\Component\\HttpKernel\\Profiler\\ProfilerStorageInterface'))); + ->addArgument(new Definition($profilerStorage::class)); $this->container->setParameter('data_collector.templates', []); $this->container->set('kernel', $this->kernel); $this->container->addCompilerPass(new RegisterListenersPass()); @@ -103,7 +110,7 @@ public function testDefaultConfig($debug) self::assertSaneContainer($this->getCompiledContainer()); } - public function getDebugModes() + public static function getDebugModes() { return [ ['debug' => false], @@ -129,7 +136,7 @@ public function testToolbarConfig(bool $toolbarEnabled, bool $listenerInjected, } } - public function getToolbarConfig() + public static function getToolbarConfig() { return [ [ @@ -170,7 +177,7 @@ public function testToolbarConfigUsingInterceptRedirects( } } - public function getInterceptRedirectsToolbarConfig() + public static function getInterceptRedirectsToolbarConfig() { return [ [ diff --git a/Tests/EventListener/WebDebugToolbarListenerTest.php b/Tests/EventListener/WebDebugToolbarListenerTest.php index 845d1413..3744e47c 100644 --- a/Tests/EventListener/WebDebugToolbarListenerTest.php +++ b/Tests/EventListener/WebDebugToolbarListenerTest.php @@ -39,7 +39,7 @@ public function testInjectToolbar($content, $expected) $this->assertEquals($expected, $response->getContent()); } - public function getInjectToolbarTests() + public static function getInjectToolbarTests() { return [ ['', "\nWDT\n"], @@ -133,6 +133,7 @@ public function testToolbarIsNotInjectedOnContentDispositionAttachment() /** * @depends testToolbarIsInjected + * * @dataProvider provideRedirects */ public function testToolbarIsNotInjectedOnRedirection($statusCode) @@ -147,7 +148,7 @@ public function testToolbarIsNotInjectedOnRedirection($statusCode) $this->assertEquals('', $response->getContent()); } - public function provideRedirects() + public static function provideRedirects() { return [ [301], diff --git a/Tests/Resources/IconTest.php b/Tests/Resources/IconTest.php index 9ce9a4c5..721ebf59 100644 --- a/Tests/Resources/IconTest.php +++ b/Tests/Resources/IconTest.php @@ -32,7 +32,7 @@ public function testIconFileContents($iconFilePath) $this->assertMatchesRegularExpression('~.*~s', file_get_contents($iconFilePath), sprintf('The SVG file of the "%s" icon must include a "viewBox" attribute.', $iconFilePath)); } - public function provideIconFilePaths() + public static function provideIconFilePaths() { return array_map(function ($filePath) { return (array) $filePath; }, glob(__DIR__.'/../../Resources/views/Icon/*.svg')); } diff --git a/Tests/Twig/WebProfilerExtensionTest.php b/Tests/Twig/WebProfilerExtensionTest.php index 6b026bcc..1bb1296b 100644 --- a/Tests/Twig/WebProfilerExtensionTest.php +++ b/Tests/Twig/WebProfilerExtensionTest.php @@ -42,7 +42,7 @@ class_exists(EscaperExtension::class); // Load twig_escape_filter() self::assertSame($dump2HasHeader, str_contains($dump2, $needle)); } - public function provideMessages(): iterable + public static function provideMessages(): iterable { yield ['Some message', ['foo' => 'foo', 'bar' => 'bar'], false, true]; yield ['Some message {@see some text}', ['foo' => 'foo', 'bar' => 'bar'], false, true];