Spaces:
No application file
No application file
| declare(strict_types=1); | |
| namespace Mautic\CoreBundle\DependencyInjection\Compiler; | |
| use Symfony\Component\DependencyInjection\Alias; | |
| 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\Reference; | |
| use Symfony\Component\ExpressionLanguage\Expression; | |
| final class ServicePass implements CompilerPassInterface | |
| { | |
| public function process(ContainerBuilder $container): void | |
| { | |
| $bundles = array_merge($container->getParameter('mautic.bundles'), $container->getParameter('mautic.plugin.bundles')); | |
| // Store menu renderer options to create unique renderering classes per menu | |
| // since KNP menus doesn't seem to support a Renderer factory | |
| $menus = []; | |
| // Keep track of names used to prevent overwriting any and thus losing functionality | |
| $serviceNames = []; | |
| foreach ($bundles as $bundle) { | |
| if (!empty($bundle['config']['services'])) { | |
| $config = $bundle['config']['services']; | |
| foreach ($config as $type => $services) { | |
| switch ($type) { | |
| case 'events': | |
| $defaultTag = 'kernel.event_subscriber'; | |
| break; | |
| case 'forms': | |
| $defaultTag = 'form.type'; | |
| break; | |
| case 'helpers': | |
| $defaultTag = 'twig.helper'; | |
| break; | |
| case 'menus': | |
| $defaultTag = 'knp_menu.menu'; | |
| break; | |
| case 'models': | |
| $defaultTag = 'mautic.model'; | |
| @trigger_error('Setting "models" in config is deprecated. Convert to using autowiring.', E_USER_DEPRECATED); | |
| break; | |
| case 'permissions': | |
| $defaultTag = 'mautic.permissions'; | |
| break; | |
| case 'integrations': | |
| $defaultTag = 'mautic.integration'; | |
| break; | |
| case 'controllers': | |
| $defaultTag = 'controller.service_arguments'; | |
| break; | |
| default: | |
| $defaultTag = false; | |
| break; | |
| } | |
| foreach ($services as $name => $details) { | |
| if (isset($serviceNames[$name])) { | |
| throw new \InvalidArgumentException("$name is already registered"); | |
| } | |
| $serviceNames[$name] = true; | |
| if (!is_array($details)) { | |
| // Set parameter | |
| $container->setParameter($name, $details); | |
| continue; | |
| } | |
| // Setup default menu details | |
| if ('menus' == $type) { | |
| $details = array_merge( | |
| [ | |
| 'class' => \Knp\Menu\MenuItem::class, | |
| 'factory' => ['@mautic.menu.builder', $details['alias'].'Menu'], | |
| ], | |
| $details | |
| ); | |
| $menus[$details['alias']] = $details['options'] ?? []; | |
| } | |
| // Set service alias | |
| $alias = new Alias($name); | |
| $alias->setPublic(true); | |
| if (isset($details['serviceAlias'])) { | |
| // Fix escaped sprintf placeholders | |
| $details['serviceAlias'] = str_replace('%%', '%', $details['serviceAlias']); | |
| $container->setAlias(sprintf($details['serviceAlias'], $name), $alias); | |
| } elseif (isset($details['serviceAliases'])) { | |
| foreach ($details['serviceAliases'] as $aliasName) { | |
| $aliasName = str_replace('%%', '%', $aliasName); | |
| $container->setAlias(sprintf($aliasName, $name), $alias); | |
| } | |
| } | |
| // Symfony 4 is requiring the classname for some auto-wired services (controllers) | |
| if ($name !== $details['class']) { | |
| $container->setAlias($details['class'], $alias); | |
| } | |
| // Generate definition arguments | |
| $definitionArguments = []; | |
| if (!isset($details['arguments'])) { | |
| $details['arguments'] = []; | |
| } elseif (!is_array($details['arguments'])) { | |
| $details['arguments'] = [$details['arguments']]; | |
| } | |
| foreach ($details['arguments'] as $argument) { | |
| $this->processArgument($argument, $container, $definitionArguments); | |
| } | |
| if ($container->hasDefinition($details['class'])) { | |
| $definition = $container->getDefinition($details['class']); | |
| if ($definitionArguments) { | |
| $definition->setArguments($definitionArguments); | |
| } | |
| } else { | |
| $definition = new Definition($details['class'], $definitionArguments); | |
| $container->setDefinition($name, $definition); | |
| } | |
| if (!$container->hasDefinition($name) && !$container->hasAlias($name) && $name !== $details['class']) { | |
| $container->setAlias($name, new Alias($details['class'], true)); | |
| } | |
| // Generate tag and tag arguments | |
| if (isset($details['tags'])) { | |
| $tagArguments = (!empty($details['tagArguments'])) ? $details['tagArguments'] : []; | |
| foreach ($details['tags'] as $k => $tag) { | |
| if (!isset($tagArguments[$k])) { | |
| $tagArguments[$k] = []; | |
| } | |
| if (!empty($details['alias'])) { | |
| $tagArguments[$k]['alias'] = $details['alias']; | |
| } | |
| $definition->addTag($tag, $tagArguments[$k]); | |
| } | |
| } else { | |
| $tag = (!empty($details['tag'])) ? $details['tag'] : $defaultTag; | |
| $tagArguments = (!empty($details['tagArguments'])) ? $details['tagArguments'] : []; | |
| if (!empty($tag)) { | |
| if (!empty($details['alias'])) { | |
| $tagArguments['alias'] = $details['alias']; | |
| } | |
| $definition->addTag($tag, $tagArguments); | |
| } | |
| if ('events' == $type) { | |
| $definition->addTag('mautic.event_subscriber'); | |
| } | |
| } | |
| // Default to a public service | |
| $public = $details['public'] ?? true; | |
| $definition->setPublic($public); | |
| // Set lazy service | |
| if (!empty($details['lazy'])) { | |
| $definition->setLazy($details['lazy']); | |
| } | |
| // Set synthetic service | |
| if (!empty($details['synthetic'])) { | |
| $definition->setSynthetic($details['synthetic']); | |
| } | |
| // Set abstract service | |
| if (!empty($details['abstract'])) { | |
| $definition->setAbstract($details['abstract']); | |
| } | |
| // Set include file | |
| if (!empty($details['file'])) { | |
| $definition->setFile($details['file']); | |
| } | |
| // Set service configurator | |
| if (!empty($details['configurator'])) { | |
| $definition->setConfigurator($details['configurator']); | |
| } | |
| // Set factory - Preferred API since Symfony 2.6 | |
| if (!empty($details['factory'])) { | |
| $factory = $details['factory']; | |
| /* | |
| * Standardize to an array then convert a service to a Reference if needed | |
| * | |
| * This supports three syntaxes: | |
| * | |
| * 1) @service::method or Class::method | |
| * 2) array('@service', 'method') or array('Class', 'method') | |
| * 3) "Unknown" - Just pass it to the definition | |
| * | |
| * Services must always be prefaced with an @ symbol (similar to "normal" config files) | |
| */ | |
| if (is_string($factory) && str_contains($factory, '::')) { | |
| $factory = explode('::', $factory, 2); | |
| } | |
| // Check if the first item in the factory array is a service and if so fetch its reference | |
| if (is_array($factory) && str_starts_with($factory[0], '@')) { | |
| // Exclude the leading @ character in the service ID | |
| $factory[0] = new Reference(substr($factory[0], 1)); | |
| } | |
| $definition->setFactory($factory); | |
| } | |
| // Set method calls | |
| if (!empty($details['methodCalls'])) { | |
| foreach ($details['methodCalls'] as $method => $methodArguments) { | |
| $methodCallArguments = []; | |
| foreach ($methodArguments as $argument) { | |
| $this->processArgument($argument, $container, $methodCallArguments); | |
| } | |
| $definition->addMethodCall($method, $methodCallArguments); | |
| } | |
| } | |
| // Set deprecated service | |
| if (!empty($details['decoratedService'])) { | |
| // This should be an array and the first parameter cannot be empty | |
| if (!is_array($details['decoratedService'])) { | |
| throw new InvalidArgumentException('The "decoratedService" definition must be an array.'); | |
| } | |
| // The second parameter of setDecoratedService is optional, check if there is a second key in the array | |
| $secondParam = !empty($details['decoratedService'][1]) ? $details['decoratedService'][1] : null; | |
| $definition->setDecoratedService($details['decoratedService'][0], $secondParam); | |
| } | |
| unset($definition); | |
| } | |
| } | |
| } | |
| } | |
| foreach ($menus as $alias => $options) { | |
| $container->setDefinition('mautic.menu_renderer.'.$alias, new Definition( | |
| \Mautic\CoreBundle\Menu\MenuRenderer::class, | |
| [ | |
| new Reference('knp_menu.matcher'), | |
| new Reference('twig'), | |
| $options, | |
| ] | |
| )) | |
| ->addTag('knp_menu.renderer', | |
| [ | |
| 'alias' => $alias, | |
| ] | |
| ); | |
| } | |
| unset($bundles); | |
| } | |
| /** | |
| * @param mixed $argument | |
| * @param mixed[] $definitionArguments | |
| */ | |
| private function processArgument($argument, ContainerBuilder $container, &$definitionArguments): void | |
| { | |
| if ('' === $argument) { | |
| // To be added during compilation | |
| $definitionArguments[] = ''; | |
| } elseif (is_array($argument) || is_object($argument)) { | |
| foreach ($argument as &$v) { | |
| if (str_starts_with($v, '%')) { | |
| $v = str_replace('%%', '%', $v); | |
| $v = $container->getParameter(substr($v, 1, -1)); | |
| } | |
| } | |
| $definitionArguments[] = $argument; | |
| } elseif (str_starts_with($argument, '%')) { | |
| // Parameter | |
| $argument = str_replace('%%', '%', $argument); | |
| $definitionArguments[] = $container->getParameter(substr($argument, 1, -1)); | |
| } elseif (is_bool($argument) || str_contains($argument, '\\')) { | |
| // Parameter or Class | |
| $definitionArguments[] = $argument; | |
| } elseif (str_starts_with($argument, '"')) { | |
| // String | |
| $definitionArguments[] = substr($argument, 1, -1); | |
| } elseif (str_starts_with($argument, '@=')) { | |
| // Expression | |
| $argument = substr($argument, 2); | |
| $definitionArguments[] = new Expression($argument); | |
| } elseif (str_starts_with($argument, '@')) { | |
| // Service | |
| $argument = substr($argument, 1); | |
| $definitionArguments[] = new Reference($argument); | |
| } else { | |
| // Reference | |
| $definitionArguments[] = new Reference($argument); | |
| } | |
| } | |
| } | |