forked from symfony/symfony
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
symfony#39102: migrated all the collector stuff from my PoC.
- Loading branch information
Showing
19 changed files
with
1,033 additions
and
16 deletions.
There are no files selected for viewing
83 changes: 83 additions & 0 deletions
83
src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Compiler/SerializerDebugPass.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Symfony package. | ||
* | ||
* (c) Fabien Potencier <fabien@symfony.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler; | ||
|
||
use ReflectionException; | ||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; | ||
use Symfony\Component\DependencyInjection\ContainerBuilder; | ||
use Symfony\Component\DependencyInjection\Definition; | ||
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; | ||
use Symfony\Component\DependencyInjection\Exception\RuntimeException; | ||
use Symfony\Component\Serializer\Debug\Normalizer\TraceableDenormalizer; | ||
use Symfony\Component\Serializer\Debug\Normalizer\TraceableHybridNormalizer; | ||
use Symfony\Component\Serializer\Debug\Normalizer\TraceableNormalizer; | ||
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; | ||
use Symfony\Component\Serializer\Normalizer\NormalizerInterface; | ||
|
||
class SerializerDebugPass implements CompilerPassInterface | ||
{ | ||
/** | ||
* @throws ReflectionException | ||
*/ | ||
public function process(ContainerBuilder $container): void | ||
{ | ||
foreach ($container->findTaggedServiceIds('serializer.normalizer') as $id => $tags) { | ||
$this->decorateNormalizer($id, $container); | ||
} | ||
} | ||
|
||
/** | ||
* @throws ReflectionException | ||
*/ | ||
private function decorateNormalizer(string $id, ContainerBuilder $container): void | ||
{ | ||
$aliasName = 'debug.'.$id; | ||
|
||
$normalizerDef = $container->getDefinition($id); | ||
$normalizerClass = $normalizerDef->getClass(); | ||
|
||
if (!$normalizerRef = $container->getReflectionClass($normalizerClass)) { | ||
throw new InvalidArgumentException( | ||
sprintf('Class "%s" used for service "%s" cannot be found.', $normalizerClass, $id) | ||
); | ||
} | ||
|
||
$isNormalizer = $normalizerRef->implementsInterface(NormalizerInterface::class); | ||
$isDenormalizer = $normalizerRef->implementsInterface(DenormalizerInterface::class); | ||
|
||
/* | ||
* We must decorate each type of normalizer with a specific decorator, since the serializer behaves | ||
* differently depending of instanceof checks against the used normalizer. | ||
* Therefore we cannot decorate all normalizers equally. | ||
*/ | ||
if ($isNormalizer && $isDenormalizer) { | ||
$decoratorClass = TraceableHybridNormalizer::class; | ||
} elseif ($isNormalizer) { | ||
$decoratorClass = TraceableNormalizer::class; | ||
} elseif ($isDenormalizer) { | ||
$decoratorClass = TraceableDenormalizer::class; | ||
} else { | ||
throw new RuntimeException( | ||
sprintf("Normalizer with id %s neither implements NormalizerInterface nor DenormalizerInterface!", $id) | ||
); | ||
} | ||
|
||
$decoratorDef = (new Definition($decoratorClass)) | ||
->setArguments([$normalizerDef]) | ||
->addTag('debug.normalizer') | ||
->setDecoratedService($id) | ||
->setAutowired(true) | ||
->setAutoconfigured(true); | ||
|
||
$container->setDefinition($aliasName, $decoratorDef); | ||
} | ||
} |
57 changes: 57 additions & 0 deletions
57
...ony/Bundle/FrameworkBundle/Tests/DependencyInjection/Compiler/SerializerDebugPassTest.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Symfony package. | ||
* | ||
* (c) Fabien Potencier <fabien@symfony.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Symfony\Bundle\FrameworkBundle\Tests\DependencyInjection\Compiler; | ||
|
||
use PHPUnit\Framework\TestCase; | ||
use Symfony\Bundle\FrameworkBundle\DependencyInjection\Compiler\SerializerDebugPass; | ||
use Symfony\Component\DependencyInjection\ContainerBuilder; | ||
use Symfony\Component\Serializer\Tests\Normalizer\TestDenormalizer; | ||
use Symfony\Component\Serializer\Tests\Normalizer\TestHybridNormalizer; | ||
use Symfony\Component\Serializer\Tests\Normalizer\TestNormalizer; | ||
|
||
class SerializerDebugPassTest extends TestCase | ||
{ | ||
private const NORMALIZER_TAG = 'serializer.normalizer'; | ||
|
||
public function testProcess(): void | ||
{ | ||
$serializerDebugPass = new SerializerDebugPass(); | ||
$container = new ContainerBuilder(); | ||
|
||
$container->register('Test\normalizer', TestNormalizer::class) | ||
->addTag(self::NORMALIZER_TAG); | ||
|
||
$container->register('Test\denormalizer', TestDenormalizer::class) | ||
->addTag(self::NORMALIZER_TAG); | ||
|
||
$container->register('Test\hybridNormalizer', TestHybridNormalizer::class) | ||
->addTag(self::NORMALIZER_TAG); | ||
|
||
$container->addCompilerPass($serializerDebugPass); | ||
|
||
$serializerDebugPass->process($container); | ||
|
||
$debugDefinitions = [ | ||
'Test\normalizer' => 'debug.Test\normalizer', | ||
'Test\denormalizer' => 'debug.Test\denormalizer', | ||
'Test\hybridNormalizer' => 'debug.Test\hybridNormalizer', | ||
]; | ||
|
||
foreach ($debugDefinitions as $originalName => $decoratorName) { | ||
self::assertTrue($container->hasDefinition($decoratorName)); | ||
$definition = $container->getDefinition($decoratorName); | ||
self::assertTrue($definition->hasTag('debug.normalizer')); | ||
$decoratedService = $definition->getDecoratedService(); | ||
self::assertSame($originalName, $decoratedService[0]); | ||
} | ||
} | ||
} |
184 changes: 184 additions & 0 deletions
184
src/Symfony/Bundle/WebProfilerBundle/Resources/views/Collector/serializer.html.twig
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
{% extends '@WebProfiler/Profiler/layout.html.twig' %} | ||
|
||
{% block toolbar %} | ||
{% set icon %} | ||
{{ include('@WebProfiler/Icon/serializer.svg') }} | ||
{% endset %} | ||
|
||
{% set text %} | ||
<div class="sf-toolbar-info-piece"> | ||
Serializer | ||
</div> | ||
{% endset %} | ||
|
||
{{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { 'link': true }) }} | ||
{% endblock %} | ||
|
||
{% block menu %} | ||
{% set disable = collector.serializations | length == 0 and collector.deserializations | length == 0 %} | ||
<span class="label {{ disable ? 'disabled' }}"> | ||
<span class="icon">{{ include('data_collector/exchange-alt-solid.svg') }}</span> | ||
<strong>Serializer</strong> | ||
</span> | ||
{% endblock %} | ||
|
||
{% block panel %} | ||
<h2>Serializer</h2> | ||
|
||
<div class="metrics"> | ||
<div class="metric"> | ||
<span class="value">{{ collector.serializations | length }}</span> | ||
<span class="label">Serializations</span> | ||
</div> | ||
<div class="metric"> | ||
<span class="value">{{ collector.deserializations | length }}</span> | ||
<span class="label">Deserializations</span> | ||
</div> | ||
<div class="metric"> | ||
<span class="value">{{ collector.normalizations | length }}</span> | ||
<span class="label">Normalizations</span> | ||
</div> | ||
<div class="metric"> | ||
<span class="value">{{ collector.denormalizations | length }}</span> | ||
<span class="label">Denormalizations</span> | ||
</div> | ||
</div> | ||
|
||
<div class="sf-tabs"> | ||
<div class="tab {{ collector.serializations|length == 0 ? 'disabled' }}"> | ||
<h3 class="tab-title">Serializations <span class="badge">{{ collector.serializations | length }}</span></h3> | ||
|
||
<div class="tab-content"> | ||
<h4>Serializations</h4> | ||
<table> | ||
<thead> | ||
<tr> | ||
<th>#</th> | ||
<th>Format</th> | ||
<th>From</th> | ||
<th>To</th> | ||
<th>Context</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
{% for serialization in collector.serializations %} | ||
<tr> | ||
<td class="font-normal text-small text-muted nowrap">{{ loop.index }}</td> | ||
<td class="nowrap">{{ serialization.format }}</td> | ||
<td>{{ profiler_dump(serialization.data, maxDepth=2) }}</td> | ||
<td>{{ serialization.result }}</td> | ||
<td>{{ profiler_dump(serialization.context, maxDepth=2) }}</td> | ||
</tr> | ||
{% endfor %} | ||
</tbody> | ||
</table> | ||
</div> | ||
</div> | ||
<div class="tab {{ collector.deserializations|length == 0 ? 'disabled' }}"> | ||
<h3 class="tab-title">Deserializations <span class="badge">{{ collector.deserializations | length }}</span> | ||
</h3> | ||
|
||
<div class="tab-content"> | ||
<h4>Deserializations</h4> | ||
<table> | ||
<thead> | ||
<tr> | ||
<th>#</th> | ||
<th>Format</th> | ||
<th>Type</th> | ||
<th>From</th> | ||
<th>To</th> | ||
<th>Context</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
{% for deserialization in collector.deserializations %} | ||
<tr> | ||
<td class="font-normal text-small text-muted nowrap">{{ loop.index }}</td> | ||
<td class="nowrap">{{ deserialization.format }}</td> | ||
<td class="nowrap">{{ deserialization.type }}</td> | ||
<td>{{ deserialization.data }}</td> | ||
<td>{{ profiler_dump(deserialization.result, maxDepth=2) }}</td> | ||
<td>{{ profiler_dump(deserialization.context, maxDepth=2) }}</td> | ||
</tr> | ||
{% endfor %} | ||
</tbody> | ||
</table> | ||
</div> | ||
</div> | ||
<div class="tab {{ collector.normalizations|length == 0 ? 'disabled' }}"> | ||
<h3 class="tab-title">Normalizations <span class="badge">{{ collector.normalizations | length }}</span> | ||
</h3> | ||
|
||
<div class="tab-content"> | ||
<h4>Normalizations</h4> | ||
<table> | ||
<thead> | ||
<tr> | ||
<th>#</th> | ||
<th>Format</th> | ||
<th>Normalizer</th> | ||
<th>From</th> | ||
<th>To</th> | ||
<th>Context</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
{% for normalization in collector.normalizations %} | ||
<tr> | ||
<td class="font-normal text-small text-muted nowrap">{{ loop.index }}</td> | ||
<td class="nowrap">{{ normalization.format }}</td> | ||
<td>{{ profiler_dump(normalization.normalizer, maxDepth=2) }}</td> | ||
<td>{{ profiler_dump(normalization.data, maxDepth=2) }}</td> | ||
<td>{{ normalization.result }}</td> | ||
<td>{{ profiler_dump(normalization.context, maxDepth=2) }}</td> | ||
</tr> | ||
{% endfor %} | ||
</tbody> | ||
</table> | ||
</div> | ||
</div> | ||
<div class="tab {{ collector.denormalizations|length == 0 ? 'disabled' }}"> | ||
<h3 class="tab-title">Denormalizations <span class="badge">{{ collector.denormalizations | length }}</span> | ||
</h3> | ||
|
||
<div class="tab-content"> | ||
<h4>Denormalizations</h4> | ||
<table> | ||
<thead> | ||
<tr> | ||
<th>#</th> | ||
<th>Format</th> | ||
<th>Denormalizer</th> | ||
<th>Type</th> | ||
<th>From</th> | ||
<th>To</th> | ||
<th>Context</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
{% for denormalization in collector.denormalizations %} | ||
<tr> | ||
<td class="font-normal text-small text-muted nowrap">{{ loop.index }}</td> | ||
<td class="nowrap">{{ denormalization.format }}</td> | ||
<td class="nowrap">{{ denormalization.denormalizer }}</td> | ||
<td class="nowrap">{{ denormalization.type }}</td> | ||
<td>{{ denormalization.data }}</td> | ||
<td>{{ profiler_dump(denormalization.result, maxDepth=2) }}</td> | ||
<td>{{ profiler_dump(denormalization.context, maxDepth=2) }}</td> | ||
</tr> | ||
{% endfor %} | ||
</tbody> | ||
</table> | ||
</div> | ||
</div> | ||
</div> | ||
|
||
<footer> | ||
<small> | ||
The icon is taken from <a href="https://fontawesome.com/icons/exchange-alt?style=solid">fontawesome</a> and | ||
is under <a href="https://fontawesome.com/license/free">Creative Commons Attribution 4.0 International | ||
license</a>. | ||
</small> | ||
</footer> | ||
{% endblock %} |
4 changes: 4 additions & 0 deletions
4
src/Symfony/Bundle/WebProfilerBundle/Resources/views/Icon/serializer.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Oops, something went wrong.