From d33a871113ce0cd3c2180d0bd05b48b6fe73c37f Mon Sep 17 00:00:00 2001 From: Roland Franssen Date: Fri, 12 Aug 2016 06:40:11 +0000 Subject: [PATCH 1/7] added ServiceAwareDefinition --- .../DependencyInjection/ContainerBuilder.php | 23 +++--- .../ServiceAwareDefinition.php | 72 +++++++++++++++++++ 2 files changed, 87 insertions(+), 8 deletions(-) create mode 100644 src/Symfony/Component/DependencyInjection/ServiceAwareDefinition.php diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index db186612bcec5..47e81470aa8a6 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -419,6 +419,19 @@ public function has($id) */ public function get($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) { + try { + $definition = $this->getDefinition($id); + } catch (ServiceNotFoundException $e) { + if (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE === $invalidBehavior) { + throw $e; + } + $definition = null; + } + + if (null !== $definition && $definition instanceof ServiceAwareDefinition) { + return $definition->getService(); + } + if (!$this->compiled) { @trigger_error(sprintf('Calling %s() before compiling the container is deprecated since version 3.2 and will throw an exception in 4.0.', __METHOD__), E_USER_DEPRECATED); } @@ -433,14 +446,8 @@ public function get($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INV return $this->get($this->aliasDefinitions[$id]); } - try { - $definition = $this->getDefinition($id); - } catch (ServiceNotFoundException $e) { - if (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $invalidBehavior) { - return; - } - - throw $e; + if (null === $definition) { + return; } $this->loading[$id] = true; diff --git a/src/Symfony/Component/DependencyInjection/ServiceAwareDefinition.php b/src/Symfony/Component/DependencyInjection/ServiceAwareDefinition.php new file mode 100644 index 0000000000000..ed56152030ddf --- /dev/null +++ b/src/Symfony/Component/DependencyInjection/ServiceAwareDefinition.php @@ -0,0 +1,72 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection; + +/** + * Definition that is aware of its service. + * + * The definition and service must remain in sync, in a way the created service object from definition is interchangeable with the aware service object. + * + * @author Roland Franssen + */ +class ServiceAwareDefinition extends Definition +{ + private $service; + + /** + * Sets the service this definition is aware of. + * + * @param object $service The service object tight to this definition + * + * @return ServiceAwareDefinition The current instance + */ + public function setService($object) + { + $this->service = $object; + + return $this; + } + + /** + * Gets the aware service. + * + * @return object + * + * @throws \DomainException If the definition is not aware of a service object or the service object is invalid. + */ + public function getService() + { + if (null === $this->service) { + throw new \DomainException('A service aware definition must have a service object.'); + } + $class = $this->getClass(); + if (null !== $class && !$this->service instanceof $class) { + throw new \DomainException('The service object must be an instance of "'.$class.'", "'.get_class($this->service).'" given.'); + } + + return $this->service; + } + + /** + * {@inheritdoc} + * + * @throws \BadMethodCallException When trying to prototype this definition + */ + public function setShared($shared) + { + if ($shared) { + throw new \BadMethodCallException('A service aware definition must always be shared.'); + } + + return parent::setShared($shared); + } +} From b75416a74e56e1ba9651bc68a709f4da07ded111 Mon Sep 17 00:00:00 2001 From: Roland Franssen Date: Fri, 12 Aug 2016 17:51:10 +0000 Subject: [PATCH 2/7] added kernel/bundle service objects --- .../Component/HttpKernel/Service/Bundle.php | 68 +++++++++++++++++++ .../Component/HttpKernel/Service/Kernel.php | 68 +++++++++++++++++++ 2 files changed, 136 insertions(+) create mode 100644 src/Symfony/Component/HttpKernel/Service/Bundle.php create mode 100644 src/Symfony/Component/HttpKernel/Service/Kernel.php diff --git a/src/Symfony/Component/HttpKernel/Service/Bundle.php b/src/Symfony/Component/HttpKernel/Service/Bundle.php new file mode 100644 index 0000000000000..83a5ca6086157 --- /dev/null +++ b/src/Symfony/Component/HttpKernel/Service/Bundle.php @@ -0,0 +1,68 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Service; + +/** + * The bundle service represents a bundle as a service throughout the ecosystem. + * + * @author Roland Franssen + */ +class Bundle +{ + private $name; + private $namespace; + private $className; + private $path; + private $parent; + + /** + * Constructor. + * + * @param string $name + * @param string $namespace + * @param string $className + * @param string $path + * @param Bundle|null $parent + */ + public function __construct($name, $namespace, $className, $path, Bundle $parent = null) + { + $this->name = $name; + $this->className = $className; + $this->path = $path; + $this->parent = $parent; + } + + final public function getName() + { + return $this->name; + } + + final public function getNamespace() + { + return $this->namespace; + } + + final public function getClassName() + { + return $this->className; + } + + final public function getPath() + { + return $this->path; + } + + final public function getParent() + { + return $this->parent; + } +} diff --git a/src/Symfony/Component/HttpKernel/Service/Kernel.php b/src/Symfony/Component/HttpKernel/Service/Kernel.php new file mode 100644 index 0000000000000..fe1f4fcf352f7 --- /dev/null +++ b/src/Symfony/Component/HttpKernel/Service/Kernel.php @@ -0,0 +1,68 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Service; + +/** + * The kernel service represents a kernel as a service throughout the ecosystem. + * + * @author Roland Franssen + */ +class Kernel +{ + private $environment; + private $debug; + private $bundles; + + /** + * Constructor. + * + * @param string $environment + * @param bool $debug + * @param array $bundles + */ + public function __construct($environment, $debug, array $bundles = array()) + { + $this->environment = $environment; + $this->debug = $debug; + $this->bundles = array(); + $numBundles = count($bundles); + $numProcessedBundles = 0; + do { + foreach ($bundles as $name => $bundle) { + $parent = $bundle['parent']; + if (null !== $parent && !isset($this->bundles[$parent])) { + continue; + } + if (!isset($this->bundles[$name])) { + $parentBundle = isset($this->bundles[$parent]) ? $this->bundles[$parent] : null; + $this->bundles[$name] = new $bundle['service_class']($name, $bundle['namespace'], $bundle['class'], $bundle['path'], $parentBundle); + ++$numProcessedBundles; + } + } + } while ($numProcessedBundles < $numBundles); + } + + final public function getEnvironment() + { + return $this->environment; + } + + final public function isDebug() + { + return $this->debug; + } + + final public function getBundles() + { + return $this->bundles; + } +} From a10d5638270a17565bccffc23f4fc70c7ba3ba2e Mon Sep 17 00:00:00 2001 From: Roland Franssen Date: Fri, 12 Aug 2016 17:51:35 +0000 Subject: [PATCH 3/7] the kernel as a service --- src/Symfony/Component/HttpKernel/Kernel.php | 25 +++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 125da81d5ca4d..8ae167389e722 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -23,6 +23,7 @@ use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; use Symfony\Component\DependencyInjection\Loader\DirectoryLoader; use Symfony\Component\DependencyInjection\Loader\ClosureLoader; +use Symfony\Component\DependencyInjection\ServiceAwareDefinition; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Bundle\BundleInterface; @@ -447,6 +448,16 @@ protected function initializeBundles() } } + /** + * Gets the kernel service class. + * + * @return string The service class + */ + protected function getServiceClass() + { + return __NAMESPACE__.'\\Service\\Kernel'; + } + /** * Gets the container class. * @@ -481,7 +492,21 @@ protected function initializeContainer() $cache = new ConfigCache($this->getCacheDir().'/'.$class.'.php', $this->debug); $fresh = true; if (!$cache->isFresh()) { + $serviceClass = $this->getServiceClass(); + $bundles = array(); + foreach ($this->bundles as $name => $bundle) { + $bundles[$name] = array( + 'service_class' => method_exists($bundle, 'getServiceClass') ? $bundle->getServiceClass() : __NAMESPACE__.'\\Service\Bundle', + 'class' => get_class($bundle), + 'namespace' => $bundle->getNamespace(), + 'parent' => $bundle->getParent(), + 'path' => $bundle->getPath(), + ); + } + $serviceDefinition = new ServiceAwareDefinition($serviceClass, array($this->environment, $this->debug, $bundles)); + $serviceDefinition->setService(new $serviceClass($this->environment, $this->debug, $bundles)); $container = $this->buildContainer(); + $container->setDefinition('kernel_as_a_service', $serviceDefinition); $container->compile(); $this->dumpContainer($cache, $container, $class, $this->getContainerBaseClass()); From 6ddc8d53f53106670cd20b10b5db15288a598235 Mon Sep 17 00:00:00 2001 From: Roland Franssen Date: Fri, 12 Aug 2016 19:20:42 +0000 Subject: [PATCH 4/7] fixed tests --- .../DependencyInjection/ContainerBuilder.php | 13 +++---------- src/Symfony/Component/HttpKernel/Service/Kernel.php | 3 ++- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index 47e81470aa8a6..d2b5be60225fe 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -419,15 +419,7 @@ public function has($id) */ public function get($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) { - try { - $definition = $this->getDefinition($id); - } catch (ServiceNotFoundException $e) { - if (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE === $invalidBehavior) { - throw $e; - } - $definition = null; - } - + $definition = $this->hasDefinition($id) ? $this->getDefinition($id) : null; if (null !== $definition && $definition instanceof ServiceAwareDefinition) { return $definition->getService(); } @@ -446,9 +438,10 @@ public function get($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INV return $this->get($this->aliasDefinitions[$id]); } - if (null === $definition) { + if (null === $definition && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $invalidBehavior) { return; } + $definition = $this->getDefinition($id); $this->loading[$id] = true; diff --git a/src/Symfony/Component/HttpKernel/Service/Kernel.php b/src/Symfony/Component/HttpKernel/Service/Kernel.php index fe1f4fcf352f7..aa797a5f54b2a 100644 --- a/src/Symfony/Component/HttpKernel/Service/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Service/Kernel.php @@ -43,8 +43,9 @@ public function __construct($environment, $debug, array $bundles = array()) continue; } if (!isset($this->bundles[$name])) { + $serviceClass = $bundle['service_class']; $parentBundle = isset($this->bundles[$parent]) ? $this->bundles[$parent] : null; - $this->bundles[$name] = new $bundle['service_class']($name, $bundle['namespace'], $bundle['class'], $bundle['path'], $parentBundle); + $this->bundles[$name] = new $serviceClass($name, $bundle['namespace'], $bundle['class'], $bundle['path'], $parentBundle); ++$numProcessedBundles; } } From 4f52e292c3b074f9481ac97ff0ac2286610c3edb Mon Sep 17 00:00:00 2001 From: Roland Franssen Date: Sat, 13 Aug 2016 11:34:11 +0000 Subject: [PATCH 5/7] allow for synthetic services while loading extensions - part 1 --- .../Compiler/MergeExtensionConfigurationPass.php | 9 +++++++++ .../Component/DependencyInjection/ContainerBuilder.php | 1 + 2 files changed, 10 insertions(+) diff --git a/src/Symfony/Component/DependencyInjection/Compiler/MergeExtensionConfigurationPass.php b/src/Symfony/Component/DependencyInjection/Compiler/MergeExtensionConfigurationPass.php index f9e6024164c15..bcb77e3158b01 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/MergeExtensionConfigurationPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/MergeExtensionConfigurationPass.php @@ -13,6 +13,7 @@ use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface; +use Symfony\Component\DependencyInjection\ServiceAwareDefinition; /** * Merges extension configs into the container builder. @@ -52,6 +53,14 @@ public function process(ContainerBuilder $container) $tmpContainer->addExpressionLanguageProvider($provider); } + foreach ($container->getDefinitions() as $id => $definition) { + if ($definition instanceof ServiceAwareDefinition) { + // definitions are not transferred by design + $tmpContainer->set($id, $definition->getService()); + } + // @TODO allow for available synthetic services to be transferred? + } + $extension->load($config, $tmpContainer); $container->merge($tmpContainer); diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index d2b5be60225fe..fef589b3a4d0d 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -423,6 +423,7 @@ public function get($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INV if (null !== $definition && $definition instanceof ServiceAwareDefinition) { return $definition->getService(); } + // @TODO allow for available synthetic services? if (!$this->compiled) { @trigger_error(sprintf('Calling %s() before compiling the container is deprecated since version 3.2 and will throw an exception in 4.0.', __METHOD__), E_USER_DEPRECATED); From 96b0e4b87a496b04a996dcc6644470979b4be4b4 Mon Sep 17 00:00:00 2001 From: Roland Franssen Date: Sat, 13 Aug 2016 11:45:26 +0000 Subject: [PATCH 6/7] oops --- .../Component/DependencyInjection/ServiceAwareDefinition.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Symfony/Component/DependencyInjection/ServiceAwareDefinition.php b/src/Symfony/Component/DependencyInjection/ServiceAwareDefinition.php index ed56152030ddf..a390168405bcf 100644 --- a/src/Symfony/Component/DependencyInjection/ServiceAwareDefinition.php +++ b/src/Symfony/Component/DependencyInjection/ServiceAwareDefinition.php @@ -63,7 +63,7 @@ public function getService() */ public function setShared($shared) { - if ($shared) { + if (!$shared) { throw new \BadMethodCallException('A service aware definition must always be shared.'); } From 9b118aa617be63a10fca1d84367d71ad3b3813a1 Mon Sep 17 00:00:00 2001 From: Roland Franssen Date: Sat, 13 Aug 2016 13:09:36 +0000 Subject: [PATCH 7/7] allow for synthetic services while loading extensions - part 2 --- .../MergeExtensionConfigurationPass.php | 8 +-- .../DependencyInjection/ContainerBuilder.php | 54 ++++++++++++++----- 2 files changed, 44 insertions(+), 18 deletions(-) diff --git a/src/Symfony/Component/DependencyInjection/Compiler/MergeExtensionConfigurationPass.php b/src/Symfony/Component/DependencyInjection/Compiler/MergeExtensionConfigurationPass.php index bcb77e3158b01..d0c5d98aaecc7 100644 --- a/src/Symfony/Component/DependencyInjection/Compiler/MergeExtensionConfigurationPass.php +++ b/src/Symfony/Component/DependencyInjection/Compiler/MergeExtensionConfigurationPass.php @@ -53,12 +53,8 @@ public function process(ContainerBuilder $container) $tmpContainer->addExpressionLanguageProvider($provider); } - foreach ($container->getDefinitions() as $id => $definition) { - if ($definition instanceof ServiceAwareDefinition) { - // definitions are not transferred by design - $tmpContainer->set($id, $definition->getService()); - } - // @TODO allow for available synthetic services to be transferred? + foreach ($container->getSynthetics() as $id => $service) { + $tmpContainer->set($id, $service); } $extension->load($config, $tmpContainer); diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index fef589b3a4d0d..e0132c55a2081 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -419,16 +419,6 @@ public function has($id) */ public function get($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) { - $definition = $this->hasDefinition($id) ? $this->getDefinition($id) : null; - if (null !== $definition && $definition instanceof ServiceAwareDefinition) { - return $definition->getService(); - } - // @TODO allow for available synthetic services? - - if (!$this->compiled) { - @trigger_error(sprintf('Calling %s() before compiling the container is deprecated since version 3.2 and will throw an exception in 4.0.', __METHOD__), E_USER_DEPRECATED); - } - $id = strtolower($id); if ($service = parent::get($id, ContainerInterface::NULL_ON_INVALID_REFERENCE)) { @@ -439,10 +429,27 @@ public function get($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INV return $this->get($this->aliasDefinitions[$id]); } - if (null === $definition && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $invalidBehavior) { + try { + $definition = $this->getDefinition($id); + if ($definition instanceof ServiceAwareDefinition) { + return $definition->getService(); + } + } catch (ServiceNotFoundException $e) { + if ($this->compiled && ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE === $invalidBehavior) { + throw $e; + } + $definition = null; + } + if (!$this->compiled) { + @trigger_error(sprintf('Calling %s() before compiling the container is deprecated for non-synthetic services since version 3.2 and will throw an exception in 4.0.', __METHOD__), E_USER_DEPRECATED); + } + if (null === $definition) { + if (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE === $invalidBehavior) { + throw new ServiceNotFoundException($id); + } + return; } - $definition = $this->getDefinition($id); $this->loading[$id] = true; @@ -455,6 +462,29 @@ public function get($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INV return $service; } + /** + * Get synthetic services. + * + * @return object[] + */ + public function getSynthetics() + { + $synthetics = array(); + foreach ($this->definitions as $id => $definition) { + if ($definition instanceof ServiceAwareDefinition) { + $synthetics[$id] = $definition->getService(); + } + } + foreach (parent::getServiceIds() as $id) { + if ('service_container' === $id) { + continue; + } + $synthetics[$id] = parent::get($id); + } + + return $synthetics; + } + /** * Merges a ContainerBuilder with the current ContainerBuilder configuration. *