Skip to content

Commit

Permalink
allow to decorate handler registry and event dispatcher
Browse files Browse the repository at this point in the history
  • Loading branch information
goetas committed Aug 7, 2021
1 parent 1a030ec commit 9012ee0
Show file tree
Hide file tree
Showing 16 changed files with 276 additions and 259 deletions.
7 changes: 7 additions & 0 deletions .phpcs.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,13 @@
<exclude-pattern>tests/*</exclude-pattern>
</rule>

<rule ref="Squiz.NamingConventions.ValidVariableName.NotCamelCaps">
<exclude-pattern>DependencyInjection/Compiler/CustomHandlersPass.php</exclude-pattern>
</rule>
<rule ref="PSR1.Methods.CamelCapsMethodName.NotCamelCaps">
<exclude-pattern>DependencyInjection/Compiler/CustomHandlersPass.php</exclude-pattern>
</rule>

<rule ref="PSR2.Classes.PropertyDeclaration.Multiple">
<exclude-pattern>tests/*</exclude-pattern>
</rule>
Expand Down
61 changes: 33 additions & 28 deletions DependencyInjection/Compiler/CustomHandlersPass.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,40 @@
use JMS\Serializer\GraphNavigatorInterface;
use JMS\Serializer\Handler\HandlerRegistry;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;

class CustomHandlersPass implements CompilerPassInterface
/**
* @internal
*/
final class CustomHandlersPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
$handlers = [];
$handlersByDirection = $this->findHandlers($container);
$handlerRegistryDef = $container->findDefinition('jms_serializer.handler_registry');

$handlerServices = [];
foreach ($handlersByDirection as $direction => $handlersByType) {
foreach ($handlersByType as $type => $handlersByFormat) {
foreach ($handlersByFormat as $format => $handlerCallable) {
$id = (string) $handlerCallable[0];

$handlerServices[$id] = $handlerCallable[0];
$handlerCallable[0] = $id;

$handlerRegistryDef->addMethodCall('registerHandler', [$direction, $type, $format, $handlerCallable]);
}
}
}

$container->findDefinition('jms_serializer.handler_registry.service_locator')
->setArgument(0, $handlerServices);
}

private function findHandlers(ContainerBuilder $container): array
{
$handlers = [];
foreach ($container->findTaggedServiceIds('jms_serializer.handler') as $id => $tags) {
foreach ($tags as $attrs) {
if (!isset($attrs['type'], $attrs['format'])) {
Expand All @@ -35,13 +59,8 @@ public function process(ContainerBuilder $container)
foreach ($directions as $direction) {
$method = $attrs['method'] ?? HandlerRegistry::getDefaultMethod($direction, $attrs['type'], $attrs['format']);
$priority = isset($attrs['priority']) ? intval($attrs['priority']) : 0;
$ref = new Reference($id);
if (class_exists(ServiceLocatorTagPass::class) || $container->getDefinition($id)->isPublic()) {
$handlerServices[$id] = $ref;
$handlers[] = [$direction, $attrs['type'], $attrs['format'], $priority, $id, $method];
} else {
$handlers[] = [$direction, $attrs['type'], $attrs['format'], $priority, $ref, $method];
}

$handlers[] = [$direction, $attrs['type'], $attrs['format'], $priority, new Reference($id), $method];
}
}
}
Expand Down Expand Up @@ -69,26 +88,12 @@ public function process(ContainerBuilder $container)
$priority = isset($methodData['priority']) ? intval($methodData['priority']) : 0;
$method = $methodData['method'] ?? HandlerRegistry::getDefaultMethod($direction, $methodData['type'], $methodData['format']);

$ref = new Reference($id);
if (class_exists(ServiceLocatorTagPass::class) || $def->isPublic()) {
$handlerServices[$id] = $ref;
$handlers[] = [$direction, $methodData['type'], $methodData['format'], $priority, $id, $method];
} else {
$handlers[] = [$direction, $methodData['type'], $methodData['format'], $priority, $ref, $method];
}
$handlers[] = [$direction, $methodData['type'], $methodData['format'], $priority, new Reference($id), $method];
}
}
}

$handlers = $this->sortAndFlattenHandlersList($handlers);

$container->findDefinition('jms_serializer.handler_registry')
->addArgument($handlers);

if (class_exists(ServiceLocatorTagPass::class)) {
$serviceLocator = ServiceLocatorTagPass::register($container, $handlerServices);
$container->findDefinition('jms_serializer.handler_registry')->replaceArgument(0, $serviceLocator);
}
return $this->sortAndFlattenHandlersList($handlers);
}

private function sortAndFlattenHandlersList(array $allHandlers)
Expand All @@ -110,11 +115,11 @@ private function sortAndFlattenHandlersList(array $allHandlers)
* Performs stable sorting. Copied from http://php.net/manual/en/function.uasort.php#121283
*
* @param array $array
* @param $value_compare_func
* @param callable $value_compare_func
*
* @return bool
*/
private static function stable_uasort(array &$array, $value_compare_func)
private static function stable_uasort(array &$array, callable $value_compare_func)
{
$index = 0;
foreach ($array as &$item) {
Expand Down
5 changes: 4 additions & 1 deletion DependencyInjection/Compiler/DoctrinePass.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;

class DoctrinePass implements CompilerPassInterface
/**
* @internal
*/
final class DoctrinePass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
use Symfony\Component\DependencyInjection\Reference;

class ExpressionFunctionProviderPass implements CompilerPassInterface
/**
* @internal
*/
final class ExpressionFunctionProviderPass implements CompilerPassInterface
{
/**
* {@inheritdoc}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;

class FormErrorHandlerTranslationDomainPass implements CompilerPassInterface
/**
* @internal
*/
final class FormErrorHandlerTranslationDomainPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,44 @@

use JMS\Serializer\EventDispatcher\EventDispatcher;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Reference;

class RegisterEventListenersAndSubscribersPass implements CompilerPassInterface
/**
* @internal
*/
final class RegisterEventListenersAndSubscribersPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
$listeners = [];
$listeners = $this->findListeners($container);

$dispatcherDef = $container->findDefinition('jms_serializer.event_dispatcher');
$listenerServices = [];

foreach ($listeners as &$events) {
$events = array_merge(...$events);
}

foreach ($listeners as $event => $listenersPerEvent) {
foreach ($listenersPerEvent as $singleListener) {
$id = (string) $singleListener[0][0];

$listenerServices[$id] = $singleListener[0][0];
$singleListener[0][0] = $id;

$dispatcherDef->addMethodCall('addListener', array_merge([$event], $singleListener));
}
}

$container->findDefinition('jms_serializer.event_dispatcher.service_locator')
->setArgument(0, $listenerServices);
}

private function findListeners(ContainerBuilder $container): array
{
$listeners = [];

foreach ($container->findTaggedServiceIds('jms_serializer.event_listener') as $id => $tags) {
foreach ($tags as $attributes) {
if (!isset($attributes['event'])) {
Expand All @@ -29,13 +57,9 @@ public function process(ContainerBuilder $container)
$format = $attributes['format'] ?? null;
$method = $attributes['method'] ?? EventDispatcher::getDefaultMethodName($attributes['event']);
$priority = isset($attributes['priority']) ? (int) $attributes['priority'] : 0;
$interface = $attributes['interface'] ?? null;

if (class_exists(ServiceLocatorTagPass::class) || $container->getDefinition($id)->isPublic()) {
$listenerServices[$id] = new Reference($id);
$listeners[$attributes['event']][$priority][] = [[$id, $method], $class, $format];
} else {
$listeners[$attributes['event']][$priority][] = [[new Reference($id), $method], $class, $format];
}
$listeners[$attributes['event']][$priority][] = [[new Reference($id), $method], $class, $format, $interface];
}
}

Expand All @@ -59,31 +83,14 @@ public function process(ContainerBuilder $container)
$priority = isset($eventData['priority']) ? (int) $eventData['priority'] : 0;
$interface = $eventData['interface'] ?? null;

if (class_exists(ServiceLocatorTagPass::class) || $container->getDefinition($id)->isPublic()) {
$listenerServices[$id] = new Reference($id);
$listeners[$eventData['event']][$priority][] = [[$id, $method], $class, $format, $interface];
} else {
$listeners[$eventData['event']][$priority][] = [[new Reference($id), $method], $class, $format, $interface];
}
$listeners[$eventData['event']][$priority][] = [[new Reference($id), $method], $class, $format, $interface];
}
}

if ($listeners) {
array_walk($listeners, static function (&$value, $key) {
ksort($value);
});
array_walk($listeners, static function (&$value, $key) {
ksort($value);
});

foreach ($listeners as &$events) {
$events = call_user_func_array('array_merge', $events);
}

$container->findDefinition('jms_serializer.event_dispatcher')
->addMethodCall('setListeners', [$listeners]);
}

if (class_exists(ServiceLocatorTagPass::class)) {
$serviceLocator = ServiceLocatorTagPass::register($container, $listenerServices);
$container->getDefinition('jms_serializer.event_dispatcher')->replaceArgument(0, $serviceLocator);
}
return $listeners;
}
}
4 changes: 3 additions & 1 deletion DependencyInjection/Compiler/ServiceMapPass.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,11 @@
*
* In the example above, we convert the visitors into a map service.
*
* @internal
*
* @author Johannes M. Schmitt <[email protected]>
*/
class ServiceMapPass implements CompilerPassInterface, \Serializable
final class ServiceMapPass implements CompilerPassInterface, \Serializable
{
private $tagName;
private $keyAttributeName;
Expand Down
5 changes: 4 additions & 1 deletion DependencyInjection/Compiler/TwigExtensionPass.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;

class TwigExtensionPass implements CompilerPassInterface
/**
* @internal
*/
final class TwigExtensionPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
Expand Down
78 changes: 42 additions & 36 deletions DependencyInjection/JMSSerializerExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -146,44 +146,10 @@ public function loadInternal(array $config, ContainerBuilder $container)
$container->removeDefinition('jms_serializer.cache.cache_warmer');
}

// directories
$directories = [];
if ($config['metadata']['auto_detection']) {
foreach ($bundles as $name => $class) {
$ref = new \ReflectionClass($class);

$dir = dirname($ref->getFileName()) . '/Resources/config/serializer';
if (file_exists($dir)) {
$directories[$ref->getNamespaceName()] = $dir;
}
}
}

foreach ($config['metadata']['directories'] as $directory) {
$directory['path'] = rtrim(str_replace('\\', '/', $directory['path']), '/');

if ('@' === $directory['path'][0]) {
$pathParts = explode('/', $directory['path']);
$bundleName = substr($pathParts[0], 1);

if (!isset($bundles[$bundleName])) {
throw new RuntimeException(sprintf('The bundle "%s" has not been registered with AppKernel. Available bundles: %s', $bundleName, implode(', ', array_keys($bundles))));
}

$ref = new \ReflectionClass($bundles[$bundleName]);
$directory['path'] = dirname($ref->getFileName()) . substr($directory['path'], strlen('@' . $bundleName));
}

$dir = rtrim($directory['path'], '\\/');
if (!file_exists($dir)) {
throw new RuntimeException(sprintf('The metadata directory "%s" does not exist for the namespace "%s"', $dir, $directory['namespace_prefix']));
}

$directories[rtrim($directory['namespace_prefix'], '\\')] = $dir;
}
$directories = $this->detectMetadataDirectories($config['metadata'], $bundles);

$container
->getDefinition('jms_serializer.metadata.file_locator')
->findDefinition('jms_serializer.metadata.file_locator')
->replaceArgument(0, $directories);

$this->setVisitorOptions($config, $container);
Expand Down Expand Up @@ -293,4 +259,44 @@ private function setVisitorOptions(array $config, ContainerBuilder $container):
->addMethodCall('setOptions', [$config['visitors']['xml_deserialization']['options']]);
}
}

private function detectMetadataDirectories($metadata, $bundles): array
{
$directories = [];
if ($metadata['auto_detection']) {
foreach ($bundles as $name => $class) {
$ref = new \ReflectionClass($class);

$dir = dirname($ref->getFileName()) . '/Resources/config/serializer';
if (file_exists($dir)) {
$directories[$ref->getNamespaceName()] = $dir;
}
}
}

foreach ($metadata['directories'] as $directory) {
$directory['path'] = rtrim(str_replace('\\', '/', $directory['path']), '/');

if ('@' === $directory['path'][0]) {
$pathParts = explode('/', $directory['path']);
$bundleName = substr($pathParts[0], 1);

if (!isset($bundles[$bundleName])) {
throw new RuntimeException(sprintf('The bundle "%s" has not been registered with AppKernel. Available bundles: %s', $bundleName, implode(', ', array_keys($bundles))));
}

$ref = new \ReflectionClass($bundles[$bundleName]);
$directory['path'] = dirname($ref->getFileName()) . substr($directory['path'], strlen('@' . $bundleName));
}

$dir = rtrim($directory['path'], '\\/');
if (!file_exists($dir)) {
throw new RuntimeException(sprintf('The metadata directory "%s" does not exist for the namespace "%s"', $dir, $directory['namespace_prefix']));
}

$directories[rtrim($directory['namespace_prefix'], '\\')] = $dir;
}

return $directories;
}
}
Loading

0 comments on commit 9012ee0

Please sign in to comment.