diff --git a/src/Symfony/Component/DependencyInjection/Compiler/MergeExtensionConfigurationPass.php b/src/Symfony/Component/DependencyInjection/Compiler/MergeExtensionConfigurationPass.php index f9e6024164c15..d0c5d98aaecc7 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,10 @@ public function process(ContainerBuilder $container) $tmpContainer->addExpressionLanguageProvider($provider); } + foreach ($container->getSynthetics() as $id => $service) { + $tmpContainer->set($id, $service); + } + $extension->load($config, $tmpContainer); $container->merge($tmpContainer); diff --git a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php index db186612bcec5..e0132c55a2081 100644 --- a/src/Symfony/Component/DependencyInjection/ContainerBuilder.php +++ b/src/Symfony/Component/DependencyInjection/ContainerBuilder.php @@ -419,10 +419,6 @@ public function has($id) */ public function get($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE) { - 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)) { @@ -435,12 +431,24 @@ public function get($id, $invalidBehavior = ContainerInterface::EXCEPTION_ON_INV try { $definition = $this->getDefinition($id); + if ($definition instanceof ServiceAwareDefinition) { + return $definition->getService(); + } } catch (ServiceNotFoundException $e) { - if (ContainerInterface::EXCEPTION_ON_INVALID_REFERENCE !== $invalidBehavior) { - return; + 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); } - throw $e; + return; } $this->loading[$id] = true; @@ -454,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. * diff --git a/src/Symfony/Component/DependencyInjection/ServiceAwareDefinition.php b/src/Symfony/Component/DependencyInjection/ServiceAwareDefinition.php new file mode 100644 index 0000000000000..a390168405bcf --- /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); + } +} 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()); 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..aa797a5f54b2a --- /dev/null +++ b/src/Symfony/Component/HttpKernel/Service/Kernel.php @@ -0,0 +1,69 @@ + + * + * 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])) { + $serviceClass = $bundle['service_class']; + $parentBundle = isset($this->bundles[$parent]) ? $this->bundles[$parent] : null; + $this->bundles[$name] = new $serviceClass($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; + } +}