4 * This file is part of the Behat Testwork.
5 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
7 * For the full copyright and license information, please view the LICENSE
8 * file that was distributed with this source code.
11 namespace Behat\Testwork\ServiceContainer;
13 use Symfony\Component\DependencyInjection\Alias;
14 use Symfony\Component\DependencyInjection\ContainerBuilder;
15 use Symfony\Component\DependencyInjection\Reference;
18 * Provides additional service finding functionality.
20 * @author Konstantin Kudryashov <ever.zet@gmail.com>
21 * @author Christophe Coevoet <stof@notk.org>
23 final class ServiceProcessor
26 * Finds and sorts (by priority) service references by provided tag.
28 * @param ContainerBuilder $container
33 public function findAndSortTaggedServices(ContainerBuilder $container, $tag)
35 $serviceTags = array();
36 foreach ($container->findTaggedServiceIds($tag) as $id => $tags) {
37 $firstTags = current($tags);
39 $serviceTags[] = array_merge(array('priority' => 0), $firstTags, array('id' => $id));
42 usort($serviceTags, function ($tag1, $tag2) { return $tag2['priority'] - $tag1['priority']; });
43 $serviceReferences = array_map(function ($tag) { return new Reference($tag['id']); }, $serviceTags);
45 return $serviceReferences;
49 * Processes wrappers of a service, found by provided tag.
51 * The wrappers are applied by descending priority.
52 * The first argument of the wrapper service receives the inner service.
54 * @param ContainerBuilder $container
55 * @param string $target The id of the service being decorated
56 * @param string $wrapperTag The tag used by wrappers
58 public function processWrapperServices(ContainerBuilder $container, $target, $wrapperTag)
60 $references = $this->findAndSortTaggedServices($container, $wrapperTag);
62 foreach ($references as $reference) {
63 $id = (string) $reference;
64 $renamedId = $id . '.inner';
66 // This logic is based on Symfony\Component\DependencyInjection\Compiler\DecoratorServicePass
68 // we create a new alias/service for the service we are replacing
69 // to be able to reference it in the new one
70 if ($container->hasAlias($target)) {
71 $alias = $container->getAlias($target);
72 $public = $alias->isPublic();
73 $container->setAlias($renamedId, new Alias((string) $alias, false));
75 $definition = $container->getDefinition($target);
76 $public = $definition->isPublic();
77 $definition->setPublic(false);
78 $container->setDefinition($renamedId, $definition);
81 $container->setAlias($target, new Alias($id, $public));
82 // Replace the reference so that users don't need to bother about the way the inner service is referenced
83 $wrappingService = $container->getDefinition($id);
84 $wrappingService->replaceArgument(0, new Reference($renamedId));