From 35722b1b4e539b94de8346d9e0f2226dc32b82aa Mon Sep 17 00:00:00 2001 From: Alexandre Quercia Date: Tue, 20 Nov 2018 15:54:19 +0100 Subject: [PATCH 1/4] [WIP] Add Kernel component Todo ---- - [ ] Fix BC layer - [ ] Add deprecation --- src/Symfony/Component/HttpKernel/Kernel.php | 752 +---------------- .../HttpKernel/Resources/bootstrap.php | 32 + .../Component/HttpKernel/composer.json | 4 + src/Symfony/Component/Kernel/.gitignore | 5 + src/Symfony/Component/Kernel/.travis.yml | 20 + .../{HttpKernel => Kernel}/Bundle/Bundle.php | 2 +- .../Bundle/BundleInterface.php | 2 +- .../CacheClearer/CacheClearerInterface.php | 2 +- .../CacheClearer/ChainCacheClearer.php | 2 +- .../CacheClearer/Psr6CacheClearer.php | 2 +- .../CacheWarmer/CacheWarmer.php | 2 +- .../CacheWarmer/CacheWarmerAggregate.php | 2 +- .../CacheWarmer/CacheWarmerInterface.php | 2 +- .../CacheWarmer/WarmableInterface.php | 2 +- .../Config/FileLocator.php | 4 +- .../AddAnnotatedClassesToCachePass.php | 4 +- .../ConfigurableExtension.php | 2 +- .../DependencyInjection/Extension.php | 2 +- .../DependencyInjection/LoggerPass.php | 4 +- .../MergeExtensionConfigurationPass.php | 2 +- .../ResettableServicePass.php | 2 +- .../DependencyInjection/ServicesResetter.php | 2 +- .../EventListener/DumpListener.php | 2 +- src/Symfony/Component/Kernel/Kernel.php | 784 ++++++++++++++++++ .../KernelInterface.php | 6 +- src/Symfony/Component/Kernel/LICENSE | 19 + .../{HttpKernel => Kernel}/Log/Logger.php | 2 +- src/Symfony/Component/Kernel/README.md | 15 + .../RebootableInterface.php | 2 +- .../Kernel/Tests/Bundle/BundleTest.php | 69 ++ .../CacheClearer/ChainCacheClearerTest.php | 46 + .../CacheClearer/Psr6CacheClearerTest.php | 48 ++ .../CacheWarmer/CacheWarmerAggregateTest.php | 79 ++ .../Tests/CacheWarmer/CacheWarmerTest.php | 68 ++ .../Kernel/Tests/Config/FileLocatorTest.php | 48 ++ .../AddAnnotatedClassesToCachePassTest.php | 99 +++ .../DependencyInjection/LoggerPassTest.php | 56 ++ .../MergeExtensionConfigurationPassTest.php | 50 ++ .../ResettableServicePassTest.php | 78 ++ .../ServicesResetterTest.php | 42 + .../Tests/EventListener/DumpListenerTest.php | 81 ++ .../Kernel/Tests/Fixtures/123/Kernel123.php | 37 + .../Fixtures/Bundle1Bundle/Resources/foo.txt | 0 .../Tests/Fixtures/Bundle1Bundle/bar.txt | 0 .../Tests/Fixtures/Bundle1Bundle/foo.txt | 0 .../Tests/Fixtures/ClearableService.php | 13 + .../ExtensionNotValidExtension.php | 20 + .../ExtensionNotValidBundle.php | 18 + .../ExtensionPresentExtension.php | 22 + .../ExtensionPresentBundle.php | 18 + .../Tests/Fixtures/KernelForOverrideName.php | 28 + .../Kernel/Tests/Fixtures/KernelForTest.php | 37 + .../Tests/Fixtures/KernelWithoutBundles.php | 33 + .../Tests/Fixtures/ResettableService.php | 13 + .../Fixtures/Resources/BaseBundle/hide.txt | 0 .../Fixtures/Resources/Bundle1Bundle/foo.txt | 0 .../Fixtures/Resources/ChildBundle/foo.txt | 0 .../Fixtures/Resources/FooBundle/foo.txt | 0 .../Component/Kernel/Tests/KernelTest.php | 653 +++++++++++++++ .../Component/Kernel/Tests/Log/LoggerTest.php | 212 +++++ src/Symfony/Component/Kernel/composer.json | 60 ++ src/Symfony/Component/Kernel/phpunit.xml.dist | 34 + 62 files changed, 2871 insertions(+), 774 deletions(-) create mode 100644 src/Symfony/Component/HttpKernel/Resources/bootstrap.php create mode 100644 src/Symfony/Component/Kernel/.gitignore create mode 100644 src/Symfony/Component/Kernel/.travis.yml rename src/Symfony/Component/{HttpKernel => Kernel}/Bundle/Bundle.php (98%) rename src/Symfony/Component/{HttpKernel => Kernel}/Bundle/BundleInterface.php (97%) rename src/Symfony/Component/{HttpKernel => Kernel}/CacheClearer/CacheClearerInterface.php (90%) rename src/Symfony/Component/{HttpKernel => Kernel}/CacheClearer/ChainCacheClearer.php (93%) rename src/Symfony/Component/{HttpKernel => Kernel}/CacheClearer/Psr6CacheClearer.php (95%) rename src/Symfony/Component/{HttpKernel => Kernel}/CacheWarmer/CacheWarmer.php (94%) rename src/Symfony/Component/{HttpKernel => Kernel}/CacheWarmer/CacheWarmerAggregate.php (96%) rename src/Symfony/Component/{HttpKernel => Kernel}/CacheWarmer/CacheWarmerInterface.php (93%) rename src/Symfony/Component/{HttpKernel => Kernel}/CacheWarmer/WarmableInterface.php (91%) rename src/Symfony/Component/{HttpKernel => Kernel}/Config/FileLocator.php (93%) rename src/Symfony/Component/{HttpKernel => Kernel}/DependencyInjection/AddAnnotatedClassesToCachePass.php (97%) rename src/Symfony/Component/{HttpKernel => Kernel}/DependencyInjection/ConfigurableExtension.php (95%) rename src/Symfony/Component/{HttpKernel => Kernel}/DependencyInjection/Extension.php (94%) rename src/Symfony/Component/{HttpKernel => Kernel}/DependencyInjection/LoggerPass.php (89%) rename src/Symfony/Component/{HttpKernel => Kernel}/DependencyInjection/MergeExtensionConfigurationPass.php (94%) rename src/Symfony/Component/{HttpKernel => Kernel}/DependencyInjection/ResettableServicePass.php (96%) rename src/Symfony/Component/{HttpKernel => Kernel}/DependencyInjection/ServicesResetter.php (93%) rename src/Symfony/Component/{HttpKernel => Kernel}/EventListener/DumpListener.php (96%) create mode 100644 src/Symfony/Component/Kernel/Kernel.php rename src/Symfony/Component/{HttpKernel => Kernel}/KernelInterface.php (96%) create mode 100644 src/Symfony/Component/Kernel/LICENSE rename src/Symfony/Component/{HttpKernel => Kernel}/Log/Logger.php (98%) create mode 100644 src/Symfony/Component/Kernel/README.md rename src/Symfony/Component/{HttpKernel => Kernel}/RebootableInterface.php (94%) create mode 100644 src/Symfony/Component/Kernel/Tests/Bundle/BundleTest.php create mode 100644 src/Symfony/Component/Kernel/Tests/CacheClearer/ChainCacheClearerTest.php create mode 100644 src/Symfony/Component/Kernel/Tests/CacheClearer/Psr6CacheClearerTest.php create mode 100644 src/Symfony/Component/Kernel/Tests/CacheWarmer/CacheWarmerAggregateTest.php create mode 100644 src/Symfony/Component/Kernel/Tests/CacheWarmer/CacheWarmerTest.php create mode 100644 src/Symfony/Component/Kernel/Tests/Config/FileLocatorTest.php create mode 100644 src/Symfony/Component/Kernel/Tests/DependencyInjection/AddAnnotatedClassesToCachePassTest.php create mode 100644 src/Symfony/Component/Kernel/Tests/DependencyInjection/LoggerPassTest.php create mode 100644 src/Symfony/Component/Kernel/Tests/DependencyInjection/MergeExtensionConfigurationPassTest.php create mode 100644 src/Symfony/Component/Kernel/Tests/DependencyInjection/ResettableServicePassTest.php create mode 100644 src/Symfony/Component/Kernel/Tests/DependencyInjection/ServicesResetterTest.php create mode 100644 src/Symfony/Component/Kernel/Tests/EventListener/DumpListenerTest.php create mode 100644 src/Symfony/Component/Kernel/Tests/Fixtures/123/Kernel123.php create mode 100644 src/Symfony/Component/Kernel/Tests/Fixtures/Bundle1Bundle/Resources/foo.txt create mode 100644 src/Symfony/Component/Kernel/Tests/Fixtures/Bundle1Bundle/bar.txt create mode 100644 src/Symfony/Component/Kernel/Tests/Fixtures/Bundle1Bundle/foo.txt create mode 100644 src/Symfony/Component/Kernel/Tests/Fixtures/ClearableService.php create mode 100644 src/Symfony/Component/Kernel/Tests/Fixtures/ExtensionNotValidBundle/DependencyInjection/ExtensionNotValidExtension.php create mode 100644 src/Symfony/Component/Kernel/Tests/Fixtures/ExtensionNotValidBundle/ExtensionNotValidBundle.php create mode 100644 src/Symfony/Component/Kernel/Tests/Fixtures/ExtensionPresentBundle/DependencyInjection/ExtensionPresentExtension.php create mode 100644 src/Symfony/Component/Kernel/Tests/Fixtures/ExtensionPresentBundle/ExtensionPresentBundle.php create mode 100644 src/Symfony/Component/Kernel/Tests/Fixtures/KernelForOverrideName.php create mode 100644 src/Symfony/Component/Kernel/Tests/Fixtures/KernelForTest.php create mode 100644 src/Symfony/Component/Kernel/Tests/Fixtures/KernelWithoutBundles.php create mode 100644 src/Symfony/Component/Kernel/Tests/Fixtures/ResettableService.php create mode 100644 src/Symfony/Component/Kernel/Tests/Fixtures/Resources/BaseBundle/hide.txt create mode 100644 src/Symfony/Component/Kernel/Tests/Fixtures/Resources/Bundle1Bundle/foo.txt create mode 100644 src/Symfony/Component/Kernel/Tests/Fixtures/Resources/ChildBundle/foo.txt create mode 100644 src/Symfony/Component/Kernel/Tests/Fixtures/Resources/FooBundle/foo.txt create mode 100644 src/Symfony/Component/Kernel/Tests/KernelTest.php create mode 100644 src/Symfony/Component/Kernel/Tests/Log/LoggerTest.php create mode 100644 src/Symfony/Component/Kernel/composer.json create mode 100644 src/Symfony/Component/Kernel/phpunit.xml.dist diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 23e82059a873..7007a43838fa 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -11,30 +11,9 @@ namespace Symfony\Component\HttpKernel; -use Symfony\Bridge\ProxyManager\LazyProxy\Instantiator\RuntimeInstantiator; -use Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper\ProxyDumper; -use Symfony\Component\Config\ConfigCache; -use Symfony\Component\Config\Loader\DelegatingLoader; -use Symfony\Component\Config\Loader\LoaderResolver; -use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; -use Symfony\Component\DependencyInjection\Compiler\PassConfig; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\ContainerInterface; -use Symfony\Component\DependencyInjection\Dumper\PhpDumper; -use Symfony\Component\DependencyInjection\Loader\ClosureLoader; -use Symfony\Component\DependencyInjection\Loader\DirectoryLoader; -use Symfony\Component\DependencyInjection\Loader\GlobFileLoader; -use Symfony\Component\DependencyInjection\Loader\IniFileLoader; -use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; -use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; -use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; -use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\HttpKernel\Bundle\BundleInterface; -use Symfony\Component\HttpKernel\Config\FileLocator; -use Symfony\Component\HttpKernel\DependencyInjection\AddAnnotatedClassesToCachePass; -use Symfony\Component\HttpKernel\DependencyInjection\MergeExtensionConfigurationPass; +use Symfony\Component\Kernel\Kernel as BaseKernel; /** * The Kernel is the heart of the Symfony system. @@ -43,103 +22,8 @@ * * @author Fabien Potencier */ -abstract class Kernel implements KernelInterface, RebootableInterface, TerminableInterface +abstract class Kernel extends BaseKernel implements TerminableInterface { - /** - * @var BundleInterface[] - */ - protected $bundles = array(); - - protected $container; - protected $rootDir; - protected $environment; - protected $debug; - protected $booted = false; - protected $name; - protected $startTime; - - private $projectDir; - private $warmupDir; - private $requestStackSize = 0; - private $resetServices = false; - - const VERSION = '4.1.6'; - const VERSION_ID = 40106; - const MAJOR_VERSION = 4; - const MINOR_VERSION = 1; - const RELEASE_VERSION = 6; - const EXTRA_VERSION = ''; - - const END_OF_MAINTENANCE = '01/2019'; - const END_OF_LIFE = '07/2019'; - - public function __construct(string $environment, bool $debug) - { - $this->environment = $environment; - $this->debug = $debug; - $this->rootDir = $this->getRootDir(); - $this->name = $this->getName(); - } - - public function __clone() - { - $this->booted = false; - $this->container = null; - $this->requestStackSize = 0; - $this->resetServices = false; - } - - /** - * {@inheritdoc} - */ - public function boot() - { - if (true === $this->booted) { - if (!$this->requestStackSize && $this->resetServices) { - if ($this->container->has('services_resetter')) { - $this->container->get('services_resetter')->reset(); - } - $this->resetServices = false; - if ($this->debug) { - $this->startTime = microtime(true); - } - } - - return; - } - if ($this->debug) { - $this->startTime = microtime(true); - } - if ($this->debug && !isset($_ENV['SHELL_VERBOSITY']) && !isset($_SERVER['SHELL_VERBOSITY'])) { - putenv('SHELL_VERBOSITY=3'); - $_ENV['SHELL_VERBOSITY'] = 3; - $_SERVER['SHELL_VERBOSITY'] = 3; - } - - // init bundles - $this->initializeBundles(); - - // init container - $this->initializeContainer(); - - foreach ($this->getBundles() as $bundle) { - $bundle->setContainer($this->container); - $bundle->boot(); - } - - $this->booted = true; - } - - /** - * {@inheritdoc} - */ - public function reboot($warmupDir) - { - $this->shutdown(); - $this->warmupDir = $warmupDir; - $this->boot(); - } - /** * {@inheritdoc} */ @@ -154,40 +38,18 @@ public function terminate(Request $request, Response $response) } } - /** - * {@inheritdoc} - */ - public function shutdown() - { - if (false === $this->booted) { - return; - } - - $this->booted = false; - - foreach ($this->getBundles() as $bundle) { - $bundle->shutdown(); - $bundle->setContainer(null); - } - - $this->container = null; - $this->requestStackSize = 0; - $this->resetServices = false; - } - /** * {@inheritdoc} */ public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true) { $this->boot(); - ++$this->requestStackSize; - $this->resetServices = true; + $this->enterScope(); try { return $this->getHttpKernel()->handle($request, $type, $catch); } finally { - --$this->requestStackSize; + $this->leaveScope(); } } @@ -200,610 +62,4 @@ protected function getHttpKernel() { return $this->container->get('http_kernel'); } - - /** - * {@inheritdoc} - */ - public function getBundles() - { - return $this->bundles; - } - - /** - * {@inheritdoc} - */ - public function getBundle($name) - { - if (!isset($this->bundles[$name])) { - throw new \InvalidArgumentException(sprintf('Bundle "%s" does not exist or it is not enabled. Maybe you forgot to add it in the registerBundles() method of your %s.php file?', $name, \get_class($this))); - } - - return $this->bundles[$name]; - } - - /** - * {@inheritdoc} - * - * @throws \RuntimeException if a custom resource is hidden by a resource in a derived bundle - */ - public function locateResource($name, $dir = null, $first = true) - { - if ('@' !== $name[0]) { - throw new \InvalidArgumentException(sprintf('A resource name must start with @ ("%s" given).', $name)); - } - - if (false !== strpos($name, '..')) { - throw new \RuntimeException(sprintf('File name "%s" contains invalid characters (..).', $name)); - } - - $bundleName = substr($name, 1); - $path = ''; - if (false !== strpos($bundleName, '/')) { - list($bundleName, $path) = explode('/', $bundleName, 2); - } - - $isResource = 0 === strpos($path, 'Resources') && null !== $dir; - $overridePath = substr($path, 9); - $resourceBundle = null; - $bundle = $this->getBundle($bundleName); - $files = array(); - - if ($isResource && file_exists($file = $dir.'/'.$bundle->getName().$overridePath)) { - if (null !== $resourceBundle) { - throw new \RuntimeException(sprintf('"%s" resource is hidden by a resource from the "%s" derived bundle. Create a "%s" file to override the bundle resource.', $file, $resourceBundle, $dir.'/'.$bundle->getName().$overridePath)); - } - - $files[] = $file; - } - - if (file_exists($file = $bundle->getPath().'/'.$path)) { - if ($first && !$isResource) { - return $file; - } - $files[] = $file; - $resourceBundle = $bundle->getName(); - } - - if (\count($files) > 0) { - return $first && $isResource ? $files[0] : $files; - } - - throw new \InvalidArgumentException(sprintf('Unable to find file "%s".', $name)); - } - - /** - * {@inheritdoc} - */ - public function getName() - { - if (null === $this->name) { - $this->name = preg_replace('/[^a-zA-Z0-9_]+/', '', basename($this->rootDir)); - if (ctype_digit($this->name[0])) { - $this->name = '_'.$this->name; - } - } - - return $this->name; - } - - /** - * {@inheritdoc} - */ - public function getEnvironment() - { - return $this->environment; - } - - /** - * {@inheritdoc} - */ - public function isDebug() - { - return $this->debug; - } - - /** - * {@inheritdoc} - */ - public function getRootDir() - { - if (null === $this->rootDir) { - $r = new \ReflectionObject($this); - $this->rootDir = \dirname($r->getFileName()); - } - - return $this->rootDir; - } - - /** - * Gets the application root dir (path of the project's composer file). - * - * @return string The project root dir - */ - public function getProjectDir() - { - if (null === $this->projectDir) { - $r = new \ReflectionObject($this); - $dir = $rootDir = \dirname($r->getFileName()); - while (!file_exists($dir.'/composer.json')) { - if ($dir === \dirname($dir)) { - return $this->projectDir = $rootDir; - } - $dir = \dirname($dir); - } - $this->projectDir = $dir; - } - - return $this->projectDir; - } - - /** - * {@inheritdoc} - */ - public function getContainer() - { - return $this->container; - } - - /** - * @internal - */ - public function setAnnotatedClassCache(array $annotatedClasses) - { - file_put_contents(($this->warmupDir ?: $this->getCacheDir()).'/annotations.map', sprintf('debug ? $this->startTime : -INF; - } - - /** - * {@inheritdoc} - */ - public function getCacheDir() - { - return $this->rootDir.'/cache/'.$this->environment; - } - - /** - * {@inheritdoc} - */ - public function getLogDir() - { - return $this->rootDir.'/logs'; - } - - /** - * {@inheritdoc} - */ - public function getCharset() - { - return 'UTF-8'; - } - - /** - * Gets the patterns defining the classes to parse and cache for annotations. - */ - public function getAnnotatedClassesToCompile(): array - { - return array(); - } - - /** - * Initializes bundles. - * - * @throws \LogicException if two bundles share a common name - */ - protected function initializeBundles() - { - // init bundles - $this->bundles = array(); - foreach ($this->registerBundles() as $bundle) { - $name = $bundle->getName(); - if (isset($this->bundles[$name])) { - throw new \LogicException(sprintf('Trying to register two bundles with the same name "%s"', $name)); - } - $this->bundles[$name] = $bundle; - } - } - - /** - * The extension point similar to the Bundle::build() method. - * - * Use this method to register compiler passes and manipulate the container during the building process. - */ - protected function build(ContainerBuilder $container) - { - } - - /** - * Gets the container class. - * - * @return string The container class - */ - protected function getContainerClass() - { - return $this->name.ucfirst($this->environment).($this->debug ? 'Debug' : '').'ProjectContainer'; - } - - /** - * Gets the container's base class. - * - * All names except Container must be fully qualified. - * - * @return string - */ - protected function getContainerBaseClass() - { - return 'Container'; - } - - /** - * Initializes the service container. - * - * The cached version of the service container is used when fresh, otherwise the - * container is built. - */ - protected function initializeContainer() - { - $class = $this->getContainerClass(); - $cacheDir = $this->warmupDir ?: $this->getCacheDir(); - $cache = new ConfigCache($cacheDir.'/'.$class.'.php', $this->debug); - $oldContainer = null; - if ($fresh = $cache->isFresh()) { - // Silence E_WARNING to ignore "include" failures - don't use "@" to prevent silencing fatal errors - $errorLevel = error_reporting(\E_ALL ^ \E_WARNING); - $fresh = $oldContainer = false; - try { - if (file_exists($cache->getPath()) && \is_object($this->container = include $cache->getPath())) { - $this->container->set('kernel', $this); - $oldContainer = $this->container; - $fresh = true; - } - } catch (\Throwable $e) { - } catch (\Exception $e) { - } finally { - error_reporting($errorLevel); - } - } - - if ($fresh) { - return; - } - - if ($this->debug) { - $collectedLogs = array(); - $previousHandler = \defined('PHPUNIT_COMPOSER_INSTALL'); - $previousHandler = $previousHandler ?: set_error_handler(function ($type, $message, $file, $line) use (&$collectedLogs, &$previousHandler) { - if (E_USER_DEPRECATED !== $type && E_DEPRECATED !== $type) { - return $previousHandler ? $previousHandler($type, $message, $file, $line) : false; - } - - if (isset($collectedLogs[$message])) { - ++$collectedLogs[$message]['count']; - - return; - } - - $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3); - // Clean the trace by removing first frames added by the error handler itself. - for ($i = 0; isset($backtrace[$i]); ++$i) { - if (isset($backtrace[$i]['file'], $backtrace[$i]['line']) && $backtrace[$i]['line'] === $line && $backtrace[$i]['file'] === $file) { - $backtrace = \array_slice($backtrace, 1 + $i); - break; - } - } - - $collectedLogs[$message] = array( - 'type' => $type, - 'message' => $message, - 'file' => $file, - 'line' => $line, - 'trace' => $backtrace, - 'count' => 1, - ); - }); - } - - try { - $container = null; - $container = $this->buildContainer(); - $container->compile(); - } finally { - if ($this->debug && true !== $previousHandler) { - restore_error_handler(); - - file_put_contents($cacheDir.'/'.$class.'Deprecations.log', serialize(array_values($collectedLogs))); - file_put_contents($cacheDir.'/'.$class.'Compiler.log', null !== $container ? implode("\n", $container->getCompiler()->getLog()) : ''); - } - } - - if (null === $oldContainer && file_exists($cache->getPath())) { - $errorLevel = error_reporting(\E_ALL ^ \E_WARNING); - try { - $oldContainer = include $cache->getPath(); - } catch (\Throwable $e) { - } catch (\Exception $e) { - } finally { - error_reporting($errorLevel); - } - } - $oldContainer = \is_object($oldContainer) ? new \ReflectionClass($oldContainer) : false; - - $this->dumpContainer($cache, $container, $class, $this->getContainerBaseClass()); - $this->container = require $cache->getPath(); - $this->container->set('kernel', $this); - - if ($oldContainer && \get_class($this->container) !== $oldContainer->name) { - // Because concurrent requests might still be using them, - // old container files are not removed immediately, - // but on a next dump of the container. - static $legacyContainers = array(); - $oldContainerDir = \dirname($oldContainer->getFileName()); - $legacyContainers[$oldContainerDir.'.legacy'] = true; - foreach (glob(\dirname($oldContainerDir).\DIRECTORY_SEPARATOR.'*.legacy') as $legacyContainer) { - if (!isset($legacyContainers[$legacyContainer]) && @unlink($legacyContainer)) { - (new Filesystem())->remove(substr($legacyContainer, 0, -7)); - } - } - - touch($oldContainerDir.'.legacy'); - } - - if ($this->container->has('cache_warmer')) { - $this->container->get('cache_warmer')->warmUp($this->container->getParameter('kernel.cache_dir')); - } - } - - /** - * Returns the kernel parameters. - * - * @return array An array of kernel parameters - */ - protected function getKernelParameters() - { - $bundles = array(); - $bundlesMetadata = array(); - - foreach ($this->bundles as $name => $bundle) { - $bundles[$name] = \get_class($bundle); - $bundlesMetadata[$name] = array( - 'path' => $bundle->getPath(), - 'namespace' => $bundle->getNamespace(), - ); - } - - return array( - 'kernel.root_dir' => realpath($this->rootDir) ?: $this->rootDir, - 'kernel.project_dir' => realpath($this->getProjectDir()) ?: $this->getProjectDir(), - 'kernel.environment' => $this->environment, - 'kernel.debug' => $this->debug, - 'kernel.name' => $this->name, - 'kernel.cache_dir' => realpath($cacheDir = $this->warmupDir ?: $this->getCacheDir()) ?: $cacheDir, - 'kernel.logs_dir' => realpath($this->getLogDir()) ?: $this->getLogDir(), - 'kernel.bundles' => $bundles, - 'kernel.bundles_metadata' => $bundlesMetadata, - 'kernel.charset' => $this->getCharset(), - 'kernel.container_class' => $this->getContainerClass(), - ); - } - - /** - * Builds the service container. - * - * @return ContainerBuilder The compiled service container - * - * @throws \RuntimeException - */ - protected function buildContainer() - { - foreach (array('cache' => $this->warmupDir ?: $this->getCacheDir(), 'logs' => $this->getLogDir()) as $name => $dir) { - if (!is_dir($dir)) { - if (false === @mkdir($dir, 0777, true) && !is_dir($dir)) { - throw new \RuntimeException(sprintf("Unable to create the %s directory (%s)\n", $name, $dir)); - } - } elseif (!is_writable($dir)) { - throw new \RuntimeException(sprintf("Unable to write in the %s directory (%s)\n", $name, $dir)); - } - } - - $container = $this->getContainerBuilder(); - $container->addObjectResource($this); - $this->prepareContainer($container); - - if (null !== $cont = $this->registerContainerConfiguration($this->getContainerLoader($container))) { - $container->merge($cont); - } - - $container->addCompilerPass(new AddAnnotatedClassesToCachePass($this)); - - return $container; - } - - /** - * Prepares the ContainerBuilder before it is compiled. - */ - protected function prepareContainer(ContainerBuilder $container) - { - $extensions = array(); - foreach ($this->bundles as $bundle) { - if ($extension = $bundle->getContainerExtension()) { - $container->registerExtension($extension); - } - - if ($this->debug) { - $container->addObjectResource($bundle); - } - } - - foreach ($this->bundles as $bundle) { - $bundle->build($container); - } - - $this->build($container); - - foreach ($container->getExtensions() as $extension) { - $extensions[] = $extension->getAlias(); - } - - // ensure these extensions are implicitly loaded - $container->getCompilerPassConfig()->setMergePass(new MergeExtensionConfigurationPass($extensions)); - } - - /** - * Gets a new ContainerBuilder instance used to build the service container. - * - * @return ContainerBuilder - */ - protected function getContainerBuilder() - { - $container = new ContainerBuilder(); - $container->getParameterBag()->add($this->getKernelParameters()); - - if ($this instanceof CompilerPassInterface) { - $container->addCompilerPass($this, PassConfig::TYPE_BEFORE_OPTIMIZATION, -10000); - } - if (class_exists('ProxyManager\Configuration') && class_exists('Symfony\Bridge\ProxyManager\LazyProxy\Instantiator\RuntimeInstantiator')) { - $container->setProxyInstantiator(new RuntimeInstantiator()); - } - - return $container; - } - - /** - * Dumps the service container to PHP code in the cache. - * - * @param ConfigCache $cache The config cache - * @param ContainerBuilder $container The service container - * @param string $class The name of the class to generate - * @param string $baseClass The name of the container's base class - */ - protected function dumpContainer(ConfigCache $cache, ContainerBuilder $container, $class, $baseClass) - { - // cache the container - $dumper = new PhpDumper($container); - - if (class_exists('ProxyManager\Configuration') && class_exists('Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper\ProxyDumper')) { - $dumper->setProxyDumper(new ProxyDumper()); - } - - $content = $dumper->dump(array( - 'class' => $class, - 'base_class' => $baseClass, - 'file' => $cache->getPath(), - 'as_files' => true, - 'debug' => $this->debug, - 'build_time' => $container->hasParameter('kernel.container_build_time') ? $container->getParameter('kernel.container_build_time') : time(), - )); - - $rootCode = array_pop($content); - $dir = \dirname($cache->getPath()).'/'; - $fs = new Filesystem(); - - foreach ($content as $file => $code) { - $fs->dumpFile($dir.$file, $code); - @chmod($dir.$file, 0666 & ~umask()); - } - @unlink(\dirname($dir.$file).'.legacy'); - - $cache->write($rootCode, $container->getResources()); - } - - /** - * Returns a loader for the container. - * - * @return DelegatingLoader The loader - */ - protected function getContainerLoader(ContainerInterface $container) - { - $locator = new FileLocator($this); - $resolver = new LoaderResolver(array( - new XmlFileLoader($container, $locator), - new YamlFileLoader($container, $locator), - new IniFileLoader($container, $locator), - new PhpFileLoader($container, $locator), - new GlobFileLoader($container, $locator), - new DirectoryLoader($container, $locator), - new ClosureLoader($container), - )); - - return new DelegatingLoader($resolver); - } - - /** - * Removes comments from a PHP source string. - * - * We don't use the PHP php_strip_whitespace() function - * as we want the content to be readable and well-formatted. - * - * @param string $source A PHP string - * - * @return string The PHP string with the comments removed - */ - public static function stripComments($source) - { - if (!\function_exists('token_get_all')) { - return $source; - } - - $rawChunk = ''; - $output = ''; - $tokens = token_get_all($source); - $ignoreSpace = false; - for ($i = 0; isset($tokens[$i]); ++$i) { - $token = $tokens[$i]; - if (!isset($token[1]) || 'b"' === $token) { - $rawChunk .= $token; - } elseif (T_START_HEREDOC === $token[0]) { - $output .= $rawChunk.$token[1]; - do { - $token = $tokens[++$i]; - $output .= isset($token[1]) && 'b"' !== $token ? $token[1] : $token; - } while (T_END_HEREDOC !== $token[0]); - $rawChunk = ''; - } elseif (T_WHITESPACE === $token[0]) { - if ($ignoreSpace) { - $ignoreSpace = false; - - continue; - } - - // replace multiple new lines with a single newline - $rawChunk .= preg_replace(array('/\n{2,}/S'), "\n", $token[1]); - } elseif (\in_array($token[0], array(T_COMMENT, T_DOC_COMMENT))) { - $ignoreSpace = true; - } else { - $rawChunk .= $token[1]; - - // The PHP-open tag already has a new-line - if (T_OPEN_TAG === $token[0]) { - $ignoreSpace = true; - } - } - } - - $output .= $rawChunk; - - // PHP 7 memory manager will not release after token_get_all(), see https://bugs.php.net/70098 - unset($tokens, $rawChunk); - gc_mem_caches(); - - return $output; - } - - public function serialize() - { - return serialize(array($this->environment, $this->debug)); - } - - public function unserialize($data) - { - list($environment, $debug) = unserialize($data, array('allowed_classes' => false)); - - $this->__construct($environment, $debug); - } } diff --git a/src/Symfony/Component/HttpKernel/Resources/bootstrap.php b/src/Symfony/Component/HttpKernel/Resources/bootstrap.php new file mode 100644 index 000000000000..3f20243a97ac --- /dev/null +++ b/src/Symfony/Component/HttpKernel/Resources/bootstrap.php @@ -0,0 +1,32 @@ + diff --git a/src/Symfony/Component/HttpKernel/CacheWarmer/CacheWarmer.php b/src/Symfony/Component/Kernel/CacheWarmer/CacheWarmer.php similarity index 94% rename from src/Symfony/Component/HttpKernel/CacheWarmer/CacheWarmer.php rename to src/Symfony/Component/Kernel/CacheWarmer/CacheWarmer.php index 52dc2ad2c3d8..fd1d52bd79f6 100644 --- a/src/Symfony/Component/HttpKernel/CacheWarmer/CacheWarmer.php +++ b/src/Symfony/Component/Kernel/CacheWarmer/CacheWarmer.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -namespace Symfony\Component\HttpKernel\CacheWarmer; +namespace Symfony\Component\Kernel\CacheWarmer; /** * Abstract cache warmer that knows how to write a file to the cache. diff --git a/src/Symfony/Component/HttpKernel/CacheWarmer/CacheWarmerAggregate.php b/src/Symfony/Component/Kernel/CacheWarmer/CacheWarmerAggregate.php similarity index 96% rename from src/Symfony/Component/HttpKernel/CacheWarmer/CacheWarmerAggregate.php rename to src/Symfony/Component/Kernel/CacheWarmer/CacheWarmerAggregate.php index 8a57732bf384..e3ad07626291 100644 --- a/src/Symfony/Component/HttpKernel/CacheWarmer/CacheWarmerAggregate.php +++ b/src/Symfony/Component/Kernel/CacheWarmer/CacheWarmerAggregate.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -namespace Symfony\Component\HttpKernel\CacheWarmer; +namespace Symfony\Component\Kernel\CacheWarmer; /** * Aggregates several cache warmers into a single one. diff --git a/src/Symfony/Component/HttpKernel/CacheWarmer/CacheWarmerInterface.php b/src/Symfony/Component/Kernel/CacheWarmer/CacheWarmerInterface.php similarity index 93% rename from src/Symfony/Component/HttpKernel/CacheWarmer/CacheWarmerInterface.php rename to src/Symfony/Component/Kernel/CacheWarmer/CacheWarmerInterface.php index 8fece5e95407..aaf7dd957dd9 100644 --- a/src/Symfony/Component/HttpKernel/CacheWarmer/CacheWarmerInterface.php +++ b/src/Symfony/Component/Kernel/CacheWarmer/CacheWarmerInterface.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -namespace Symfony\Component\HttpKernel\CacheWarmer; +namespace Symfony\Component\Kernel\CacheWarmer; /** * Interface for classes able to warm up the cache. diff --git a/src/Symfony/Component/HttpKernel/CacheWarmer/WarmableInterface.php b/src/Symfony/Component/Kernel/CacheWarmer/WarmableInterface.php similarity index 91% rename from src/Symfony/Component/HttpKernel/CacheWarmer/WarmableInterface.php rename to src/Symfony/Component/Kernel/CacheWarmer/WarmableInterface.php index 25d8ee8f61a8..ef8c0308da71 100644 --- a/src/Symfony/Component/HttpKernel/CacheWarmer/WarmableInterface.php +++ b/src/Symfony/Component/Kernel/CacheWarmer/WarmableInterface.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -namespace Symfony\Component\HttpKernel\CacheWarmer; +namespace Symfony\Component\Kernel\CacheWarmer; /** * Interface for classes that support warming their cache. diff --git a/src/Symfony/Component/HttpKernel/Config/FileLocator.php b/src/Symfony/Component/Kernel/Config/FileLocator.php similarity index 93% rename from src/Symfony/Component/HttpKernel/Config/FileLocator.php rename to src/Symfony/Component/Kernel/Config/FileLocator.php index 20d91b2f051a..9eba0503271b 100644 --- a/src/Symfony/Component/HttpKernel/Config/FileLocator.php +++ b/src/Symfony/Component/Kernel/Config/FileLocator.php @@ -9,10 +9,10 @@ * file that was distributed with this source code. */ -namespace Symfony\Component\HttpKernel\Config; +namespace Symfony\Component\Kernel\Config; use Symfony\Component\Config\FileLocator as BaseFileLocator; -use Symfony\Component\HttpKernel\KernelInterface; +use Symfony\Component\Kernel\KernelInterface; /** * FileLocator uses the KernelInterface to locate resources in bundles. diff --git a/src/Symfony/Component/HttpKernel/DependencyInjection/AddAnnotatedClassesToCachePass.php b/src/Symfony/Component/Kernel/DependencyInjection/AddAnnotatedClassesToCachePass.php similarity index 97% rename from src/Symfony/Component/HttpKernel/DependencyInjection/AddAnnotatedClassesToCachePass.php rename to src/Symfony/Component/Kernel/DependencyInjection/AddAnnotatedClassesToCachePass.php index 54f87edd9963..03d2cb4f7465 100644 --- a/src/Symfony/Component/HttpKernel/DependencyInjection/AddAnnotatedClassesToCachePass.php +++ b/src/Symfony/Component/Kernel/DependencyInjection/AddAnnotatedClassesToCachePass.php @@ -9,13 +9,13 @@ * file that was distributed with this source code. */ -namespace Symfony\Component\HttpKernel\DependencyInjection; +namespace Symfony\Component\Kernel\DependencyInjection; use Composer\Autoload\ClassLoader; use Symfony\Component\Debug\DebugClassLoader; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\HttpKernel\Kernel; +use Symfony\Component\Kernel\Kernel; /** * Sets the classes to compile in the cache for the container. diff --git a/src/Symfony/Component/HttpKernel/DependencyInjection/ConfigurableExtension.php b/src/Symfony/Component/Kernel/DependencyInjection/ConfigurableExtension.php similarity index 95% rename from src/Symfony/Component/HttpKernel/DependencyInjection/ConfigurableExtension.php rename to src/Symfony/Component/Kernel/DependencyInjection/ConfigurableExtension.php index 072c35f1c1cc..a1fcdbb6ab2b 100644 --- a/src/Symfony/Component/HttpKernel/DependencyInjection/ConfigurableExtension.php +++ b/src/Symfony/Component/Kernel/DependencyInjection/ConfigurableExtension.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -namespace Symfony\Component\HttpKernel\DependencyInjection; +namespace Symfony\Component\Kernel\DependencyInjection; use Symfony\Component\DependencyInjection\ContainerBuilder; diff --git a/src/Symfony/Component/HttpKernel/DependencyInjection/Extension.php b/src/Symfony/Component/Kernel/DependencyInjection/Extension.php similarity index 94% rename from src/Symfony/Component/HttpKernel/DependencyInjection/Extension.php rename to src/Symfony/Component/Kernel/DependencyInjection/Extension.php index 647875554b00..24912292edbf 100644 --- a/src/Symfony/Component/HttpKernel/DependencyInjection/Extension.php +++ b/src/Symfony/Component/Kernel/DependencyInjection/Extension.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -namespace Symfony\Component\HttpKernel\DependencyInjection; +namespace Symfony\Component\Kernel\DependencyInjection; use Symfony\Component\DependencyInjection\Extension\Extension as BaseExtension; diff --git a/src/Symfony/Component/HttpKernel/DependencyInjection/LoggerPass.php b/src/Symfony/Component/Kernel/DependencyInjection/LoggerPass.php similarity index 89% rename from src/Symfony/Component/HttpKernel/DependencyInjection/LoggerPass.php rename to src/Symfony/Component/Kernel/DependencyInjection/LoggerPass.php index b6df1f6e614c..7659e8c2f102 100644 --- a/src/Symfony/Component/HttpKernel/DependencyInjection/LoggerPass.php +++ b/src/Symfony/Component/Kernel/DependencyInjection/LoggerPass.php @@ -9,12 +9,12 @@ * file that was distributed with this source code. */ -namespace Symfony\Component\HttpKernel\DependencyInjection; +namespace Symfony\Component\Kernel\DependencyInjection; use Psr\Log\LoggerInterface; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\HttpKernel\Log\Logger; +use Symfony\Component\Kernel\Log\Logger; /** * Registers the default logger if necessary. diff --git a/src/Symfony/Component/HttpKernel/DependencyInjection/MergeExtensionConfigurationPass.php b/src/Symfony/Component/Kernel/DependencyInjection/MergeExtensionConfigurationPass.php similarity index 94% rename from src/Symfony/Component/HttpKernel/DependencyInjection/MergeExtensionConfigurationPass.php rename to src/Symfony/Component/Kernel/DependencyInjection/MergeExtensionConfigurationPass.php index 1dbf7f7beeed..90678dfda1e2 100644 --- a/src/Symfony/Component/HttpKernel/DependencyInjection/MergeExtensionConfigurationPass.php +++ b/src/Symfony/Component/Kernel/DependencyInjection/MergeExtensionConfigurationPass.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -namespace Symfony\Component\HttpKernel\DependencyInjection; +namespace Symfony\Component\Kernel\DependencyInjection; use Symfony\Component\DependencyInjection\Compiler\MergeExtensionConfigurationPass as BaseMergeExtensionConfigurationPass; use Symfony\Component\DependencyInjection\ContainerBuilder; diff --git a/src/Symfony/Component/HttpKernel/DependencyInjection/ResettableServicePass.php b/src/Symfony/Component/Kernel/DependencyInjection/ResettableServicePass.php similarity index 96% rename from src/Symfony/Component/HttpKernel/DependencyInjection/ResettableServicePass.php rename to src/Symfony/Component/Kernel/DependencyInjection/ResettableServicePass.php index 564f879ca73e..368b770dfadd 100644 --- a/src/Symfony/Component/HttpKernel/DependencyInjection/ResettableServicePass.php +++ b/src/Symfony/Component/Kernel/DependencyInjection/ResettableServicePass.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -namespace Symfony\Component\HttpKernel\DependencyInjection; +namespace Symfony\Component\Kernel\DependencyInjection; use Symfony\Component\DependencyInjection\Argument\IteratorArgument; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; diff --git a/src/Symfony/Component/HttpKernel/DependencyInjection/ServicesResetter.php b/src/Symfony/Component/Kernel/DependencyInjection/ServicesResetter.php similarity index 93% rename from src/Symfony/Component/HttpKernel/DependencyInjection/ServicesResetter.php rename to src/Symfony/Component/Kernel/DependencyInjection/ServicesResetter.php index b82d2fef3c05..8dc1d8982082 100644 --- a/src/Symfony/Component/HttpKernel/DependencyInjection/ServicesResetter.php +++ b/src/Symfony/Component/Kernel/DependencyInjection/ServicesResetter.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -namespace Symfony\Component\HttpKernel\DependencyInjection; +namespace Symfony\Component\Kernel\DependencyInjection; /** * Resets provided services. diff --git a/src/Symfony/Component/HttpKernel/EventListener/DumpListener.php b/src/Symfony/Component/Kernel/EventListener/DumpListener.php similarity index 96% rename from src/Symfony/Component/HttpKernel/EventListener/DumpListener.php rename to src/Symfony/Component/Kernel/EventListener/DumpListener.php index 3acbe7d46c86..0d71054d9d35 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/DumpListener.php +++ b/src/Symfony/Component/Kernel/EventListener/DumpListener.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -namespace Symfony\Component\HttpKernel\EventListener; +namespace Symfony\Component\Kernel\EventListener; use Symfony\Component\Console\ConsoleEvents; use Symfony\Component\EventDispatcher\EventSubscriberInterface; diff --git a/src/Symfony/Component/Kernel/Kernel.php b/src/Symfony/Component/Kernel/Kernel.php new file mode 100644 index 000000000000..5302aa5f733c --- /dev/null +++ b/src/Symfony/Component/Kernel/Kernel.php @@ -0,0 +1,784 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Kernel; + +use Symfony\Bridge\ProxyManager\LazyProxy\Instantiator\RuntimeInstantiator; +use Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper\ProxyDumper; +use Symfony\Component\Config\ConfigCache; +use Symfony\Component\Config\Loader\DelegatingLoader; +use Symfony\Component\Config\Loader\LoaderResolver; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\Compiler\PassConfig; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Dumper\PhpDumper; +use Symfony\Component\DependencyInjection\Loader\ClosureLoader; +use Symfony\Component\DependencyInjection\Loader\DirectoryLoader; +use Symfony\Component\DependencyInjection\Loader\GlobFileLoader; +use Symfony\Component\DependencyInjection\Loader\IniFileLoader; +use Symfony\Component\DependencyInjection\Loader\PhpFileLoader; +use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; +use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\Kernel\Bundle\BundleInterface; +use Symfony\Component\Kernel\Config\FileLocator; +use Symfony\Component\Kernel\DependencyInjection\AddAnnotatedClassesToCachePass; +use Symfony\Component\Kernel\DependencyInjection\MergeExtensionConfigurationPass; + +/** + * The Kernel is the heart of the Symfony system. + * + * It manages an environment made of bundles. + * + * @author Fabien Potencier + */ +abstract class Kernel implements KernelInterface, RebootableInterface +{ + /** + * @var BundleInterface[] + */ + protected $bundles = array(); + + protected $container; + protected $rootDir; + protected $environment; + protected $debug; + protected $booted = false; + protected $name; + protected $startTime; + + private $projectDir; + private $warmupDir; + private $requestStackSize = 0; + private $resetServices = false; + + const VERSION = '4.1.6'; + const VERSION_ID = 40106; + const MAJOR_VERSION = 4; + const MINOR_VERSION = 1; + const RELEASE_VERSION = 6; + const EXTRA_VERSION = ''; + + const END_OF_MAINTENANCE = '01/2019'; + const END_OF_LIFE = '07/2019'; + + public function __construct(string $environment, bool $debug) + { + $this->environment = $environment; + $this->debug = $debug; + $this->rootDir = $this->getRootDir(); + $this->name = $this->getName(); + } + + public function __clone() + { + $this->booted = false; + $this->container = null; + $this->requestStackSize = 0; + $this->resetServices = false; + } + + /** + * {@inheritdoc} + */ + public function boot() + { + if (true === $this->booted) { + if (!$this->requestStackSize && $this->resetServices) { + if ($this->container->has('services_resetter')) { + $this->container->get('services_resetter')->reset(); + } + $this->resetServices = false; + if ($this->debug) { + $this->startTime = microtime(true); + } + } + + return; + } + if ($this->debug) { + $this->startTime = microtime(true); + } + if ($this->debug && !isset($_ENV['SHELL_VERBOSITY']) && !isset($_SERVER['SHELL_VERBOSITY'])) { + putenv('SHELL_VERBOSITY=3'); + $_ENV['SHELL_VERBOSITY'] = 3; + $_SERVER['SHELL_VERBOSITY'] = 3; + } + + // init bundles + $this->initializeBundles(); + + // init container + $this->initializeContainer(); + + foreach ($this->getBundles() as $bundle) { + $bundle->setContainer($this->container); + $bundle->boot(); + } + + $this->booted = true; + } + + /** + * {@inheritdoc} + */ + public function reboot($warmupDir) + { + $this->shutdown(); + $this->warmupDir = $warmupDir; + $this->boot(); + } + + /** + * {@inheritdoc} + */ + public function shutdown() + { + if (false === $this->booted) { + return; + } + + $this->booted = false; + + foreach ($this->getBundles() as $bundle) { + $bundle->shutdown(); + $bundle->setContainer(null); + } + + $this->container = null; + $this->requestStackSize = 0; + $this->resetServices = false; + } + + /** + * {@inheritdoc} + */ + public function getBundles() + { + return $this->bundles; + } + + /** + * {@inheritdoc} + */ + public function getBundle($name) + { + if (!isset($this->bundles[$name])) { + throw new \InvalidArgumentException(sprintf('Bundle "%s" does not exist or it is not enabled. Maybe you forgot to add it in the registerBundles() method of your %s.php file?', $name, \get_class($this))); + } + + return $this->bundles[$name]; + } + + /** + * {@inheritdoc} + * + * @throws \RuntimeException if a custom resource is hidden by a resource in a derived bundle + */ + public function locateResource($name, $dir = null, $first = true) + { + if ('@' !== $name[0]) { + throw new \InvalidArgumentException(sprintf('A resource name must start with @ ("%s" given).', $name)); + } + + if (false !== strpos($name, '..')) { + throw new \RuntimeException(sprintf('File name "%s" contains invalid characters (..).', $name)); + } + + $bundleName = substr($name, 1); + $path = ''; + if (false !== strpos($bundleName, '/')) { + list($bundleName, $path) = explode('/', $bundleName, 2); + } + + $isResource = 0 === strpos($path, 'Resources') && null !== $dir; + $overridePath = substr($path, 9); + $resourceBundle = null; + $bundle = $this->getBundle($bundleName); + $files = array(); + + if ($isResource && file_exists($file = $dir.'/'.$bundle->getName().$overridePath)) { + if (null !== $resourceBundle) { + throw new \RuntimeException(sprintf('"%s" resource is hidden by a resource from the "%s" derived bundle. Create a "%s" file to override the bundle resource.', $file, $resourceBundle, $dir.'/'.$bundle->getName().$overridePath)); + } + + $files[] = $file; + } + + if (file_exists($file = $bundle->getPath().'/'.$path)) { + if ($first && !$isResource) { + return $file; + } + $files[] = $file; + $resourceBundle = $bundle->getName(); + } + + if (\count($files) > 0) { + return $first && $isResource ? $files[0] : $files; + } + + throw new \InvalidArgumentException(sprintf('Unable to find file "%s".', $name)); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + if (null === $this->name) { + $this->name = preg_replace('/[^a-zA-Z0-9_]+/', '', basename($this->rootDir)); + if (ctype_digit($this->name[0])) { + $this->name = '_'.$this->name; + } + } + + return $this->name; + } + + /** + * {@inheritdoc} + */ + public function getEnvironment() + { + return $this->environment; + } + + /** + * {@inheritdoc} + */ + public function isDebug() + { + return $this->debug; + } + + /** + * {@inheritdoc} + */ + public function getRootDir() + { + if (null === $this->rootDir) { + $r = new \ReflectionObject($this); + $this->rootDir = \dirname($r->getFileName()); + } + + return $this->rootDir; + } + + /** + * Gets the application root dir (path of the project's composer file). + * + * @return string The project root dir + */ + public function getProjectDir() + { + if (null === $this->projectDir) { + $r = new \ReflectionObject($this); + $dir = $rootDir = \dirname($r->getFileName()); + while (!file_exists($dir.'/composer.json')) { + if ($dir === \dirname($dir)) { + return $this->projectDir = $rootDir; + } + $dir = \dirname($dir); + } + $this->projectDir = $dir; + } + + return $this->projectDir; + } + + /** + * {@inheritdoc} + */ + public function getContainer() + { + return $this->container; + } + + /** + * @internal + */ + public function setAnnotatedClassCache(array $annotatedClasses) + { + file_put_contents(($this->warmupDir ?: $this->getCacheDir()).'/annotations.map', sprintf('debug ? $this->startTime : -INF; + } + + /** + * {@inheritdoc} + */ + public function getCacheDir() + { + return $this->rootDir.'/cache/'.$this->environment; + } + + /** + * {@inheritdoc} + */ + public function getLogDir() + { + return $this->rootDir.'/logs'; + } + + /** + * {@inheritdoc} + */ + public function getCharset() + { + return 'UTF-8'; + } + + /** + * Gets the patterns defining the classes to parse and cache for annotations. + */ + public function getAnnotatedClassesToCompile(): array + { + return array(); + } + + /** + * Initializes bundles. + * + * @throws \LogicException if two bundles share a common name + */ + protected function initializeBundles() + { + // init bundles + $this->bundles = array(); + foreach ($this->registerBundles() as $bundle) { + $name = $bundle->getName(); + if (isset($this->bundles[$name])) { + throw new \LogicException(sprintf('Trying to register two bundles with the same name "%s"', $name)); + } + $this->bundles[$name] = $bundle; + } + } + + /** + * The extension point similar to the Bundle::build() method. + * + * Use this method to register compiler passes and manipulate the container during the building process. + */ + protected function build(ContainerBuilder $container) + { + } + + /** + * Gets the container class. + * + * @return string The container class + */ + protected function getContainerClass() + { + return $this->name.ucfirst($this->environment).($this->debug ? 'Debug' : '').'ProjectContainer'; + } + + /** + * Gets the container's base class. + * + * All names except Container must be fully qualified. + * + * @return string + */ + protected function getContainerBaseClass() + { + return 'Container'; + } + + /** + * Initializes the service container. + * + * The cached version of the service container is used when fresh, otherwise the + * container is built. + */ + protected function initializeContainer() + { + $class = $this->getContainerClass(); + $cacheDir = $this->warmupDir ?: $this->getCacheDir(); + $cache = new ConfigCache($cacheDir.'/'.$class.'.php', $this->debug); + $oldContainer = null; + if ($fresh = $cache->isFresh()) { + // Silence E_WARNING to ignore "include" failures - don't use "@" to prevent silencing fatal errors + $errorLevel = error_reporting(\E_ALL ^ \E_WARNING); + $fresh = $oldContainer = false; + try { + if (file_exists($cache->getPath()) && \is_object($this->container = include $cache->getPath())) { + $this->container->set('kernel', $this); + $oldContainer = $this->container; + $fresh = true; + } + } catch (\Throwable $e) { + } catch (\Exception $e) { + } finally { + error_reporting($errorLevel); + } + } + + if ($fresh) { + return; + } + + if ($this->debug) { + $collectedLogs = array(); + $previousHandler = \defined('PHPUNIT_COMPOSER_INSTALL'); + $previousHandler = $previousHandler ?: set_error_handler(function ($type, $message, $file, $line) use (&$collectedLogs, &$previousHandler) { + if (E_USER_DEPRECATED !== $type && E_DEPRECATED !== $type) { + return $previousHandler ? $previousHandler($type, $message, $file, $line) : false; + } + + if (isset($collectedLogs[$message])) { + ++$collectedLogs[$message]['count']; + + return; + } + + $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3); + // Clean the trace by removing first frames added by the error handler itself. + for ($i = 0; isset($backtrace[$i]); ++$i) { + if (isset($backtrace[$i]['file'], $backtrace[$i]['line']) && $backtrace[$i]['line'] === $line && $backtrace[$i]['file'] === $file) { + $backtrace = \array_slice($backtrace, 1 + $i); + break; + } + } + + $collectedLogs[$message] = array( + 'type' => $type, + 'message' => $message, + 'file' => $file, + 'line' => $line, + 'trace' => $backtrace, + 'count' => 1, + ); + }); + } + + try { + $container = null; + $container = $this->buildContainer(); + $container->compile(); + } finally { + if ($this->debug && true !== $previousHandler) { + restore_error_handler(); + + file_put_contents($cacheDir.'/'.$class.'Deprecations.log', serialize(array_values($collectedLogs))); + file_put_contents($cacheDir.'/'.$class.'Compiler.log', null !== $container ? implode("\n", $container->getCompiler()->getLog()) : ''); + } + } + + if (null === $oldContainer && file_exists($cache->getPath())) { + $errorLevel = error_reporting(\E_ALL ^ \E_WARNING); + try { + $oldContainer = include $cache->getPath(); + } catch (\Throwable $e) { + } catch (\Exception $e) { + } finally { + error_reporting($errorLevel); + } + } + $oldContainer = \is_object($oldContainer) ? new \ReflectionClass($oldContainer) : false; + + $this->dumpContainer($cache, $container, $class, $this->getContainerBaseClass()); + $this->container = require $cache->getPath(); + $this->container->set('kernel', $this); + + if ($oldContainer && \get_class($this->container) !== $oldContainer->name) { + // Because concurrent requests might still be using them, + // old container files are not removed immediately, + // but on a next dump of the container. + static $legacyContainers = array(); + $oldContainerDir = \dirname($oldContainer->getFileName()); + $legacyContainers[$oldContainerDir.'.legacy'] = true; + foreach (glob(\dirname($oldContainerDir).\DIRECTORY_SEPARATOR.'*.legacy') as $legacyContainer) { + if (!isset($legacyContainers[$legacyContainer]) && @unlink($legacyContainer)) { + (new Filesystem())->remove(substr($legacyContainer, 0, -7)); + } + } + + touch($oldContainerDir.'.legacy'); + } + + if ($this->container->has('cache_warmer')) { + $this->container->get('cache_warmer')->warmUp($this->container->getParameter('kernel.cache_dir')); + } + } + + /** + * Returns the kernel parameters. + * + * @return array An array of kernel parameters + */ + protected function getKernelParameters() + { + $bundles = array(); + $bundlesMetadata = array(); + + foreach ($this->bundles as $name => $bundle) { + $bundles[$name] = \get_class($bundle); + $bundlesMetadata[$name] = array( + 'path' => $bundle->getPath(), + 'namespace' => $bundle->getNamespace(), + ); + } + + return array( + 'kernel.root_dir' => realpath($this->rootDir) ?: $this->rootDir, + 'kernel.project_dir' => realpath($this->getProjectDir()) ?: $this->getProjectDir(), + 'kernel.environment' => $this->environment, + 'kernel.debug' => $this->debug, + 'kernel.name' => $this->name, + 'kernel.cache_dir' => realpath($cacheDir = $this->warmupDir ?: $this->getCacheDir()) ?: $cacheDir, + 'kernel.logs_dir' => realpath($this->getLogDir()) ?: $this->getLogDir(), + 'kernel.bundles' => $bundles, + 'kernel.bundles_metadata' => $bundlesMetadata, + 'kernel.charset' => $this->getCharset(), + 'kernel.container_class' => $this->getContainerClass(), + ); + } + + /** + * Builds the service container. + * + * @return ContainerBuilder The compiled service container + * + * @throws \RuntimeException + */ + protected function buildContainer() + { + foreach (array('cache' => $this->warmupDir ?: $this->getCacheDir(), 'logs' => $this->getLogDir()) as $name => $dir) { + if (!is_dir($dir)) { + if (false === @mkdir($dir, 0777, true) && !is_dir($dir)) { + throw new \RuntimeException(sprintf("Unable to create the %s directory (%s)\n", $name, $dir)); + } + } elseif (!is_writable($dir)) { + throw new \RuntimeException(sprintf("Unable to write in the %s directory (%s)\n", $name, $dir)); + } + } + + $container = $this->getContainerBuilder(); + $container->addObjectResource($this); + $this->prepareContainer($container); + + if (null !== $cont = $this->registerContainerConfiguration($this->getContainerLoader($container))) { + $container->merge($cont); + } + + $container->addCompilerPass(new AddAnnotatedClassesToCachePass($this)); + + return $container; + } + + /** + * Prepares the ContainerBuilder before it is compiled. + */ + protected function prepareContainer(ContainerBuilder $container) + { + $extensions = array(); + foreach ($this->bundles as $bundle) { + if ($extension = $bundle->getContainerExtension()) { + $container->registerExtension($extension); + } + + if ($this->debug) { + $container->addObjectResource($bundle); + } + } + + foreach ($this->bundles as $bundle) { + $bundle->build($container); + } + + $this->build($container); + + foreach ($container->getExtensions() as $extension) { + $extensions[] = $extension->getAlias(); + } + + // ensure these extensions are implicitly loaded + $container->getCompilerPassConfig()->setMergePass(new MergeExtensionConfigurationPass($extensions)); + } + + /** + * Gets a new ContainerBuilder instance used to build the service container. + * + * @return ContainerBuilder + */ + protected function getContainerBuilder() + { + $container = new ContainerBuilder(); + $container->getParameterBag()->add($this->getKernelParameters()); + + if ($this instanceof CompilerPassInterface) { + $container->addCompilerPass($this, PassConfig::TYPE_BEFORE_OPTIMIZATION, -10000); + } + if (class_exists('ProxyManager\Configuration') && class_exists('Symfony\Bridge\ProxyManager\LazyProxy\Instantiator\RuntimeInstantiator')) { + $container->setProxyInstantiator(new RuntimeInstantiator()); + } + + return $container; + } + + /** + * Dumps the service container to PHP code in the cache. + * + * @param ConfigCache $cache The config cache + * @param ContainerBuilder $container The service container + * @param string $class The name of the class to generate + * @param string $baseClass The name of the container's base class + */ + protected function dumpContainer(ConfigCache $cache, ContainerBuilder $container, $class, $baseClass) + { + // cache the container + $dumper = new PhpDumper($container); + + if (class_exists('ProxyManager\Configuration') && class_exists('Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper\ProxyDumper')) { + $dumper->setProxyDumper(new ProxyDumper()); + } + + $content = $dumper->dump(array( + 'class' => $class, + 'base_class' => $baseClass, + 'file' => $cache->getPath(), + 'as_files' => true, + 'debug' => $this->debug, + 'build_time' => $container->hasParameter('kernel.container_build_time') ? $container->getParameter('kernel.container_build_time') : time(), + )); + + $rootCode = array_pop($content); + $dir = \dirname($cache->getPath()).'/'; + $fs = new Filesystem(); + + foreach ($content as $file => $code) { + $fs->dumpFile($dir.$file, $code); + @chmod($dir.$file, 0666 & ~umask()); + } + @unlink(\dirname($dir.$file).'.legacy'); + + $cache->write($rootCode, $container->getResources()); + } + + /** + * Returns a loader for the container. + * + * @return DelegatingLoader The loader + */ + protected function getContainerLoader(ContainerInterface $container) + { + $locator = new FileLocator($this); + $resolver = new LoaderResolver(array( + new XmlFileLoader($container, $locator), + new YamlFileLoader($container, $locator), + new IniFileLoader($container, $locator), + new PhpFileLoader($container, $locator), + new GlobFileLoader($container, $locator), + new DirectoryLoader($container, $locator), + new ClosureLoader($container), + )); + + return new DelegatingLoader($resolver); + } + + /** + * Enters on a scope. + */ + protected function enterScope() + { + ++$this->requestStackSize; + $this->resetServices = true; + } + + /** + * Leaves the current scope. + */ + protected function leaveScope() + { + --$this->requestStackSize; + } + + /** + * Removes comments from a PHP source string. + * + * We don't use the PHP php_strip_whitespace() function + * as we want the content to be readable and well-formatted. + * + * @param string $source A PHP string + * + * @return string The PHP string with the comments removed + */ + public static function stripComments($source) + { + if (!\function_exists('token_get_all')) { + return $source; + } + + $rawChunk = ''; + $output = ''; + $tokens = token_get_all($source); + $ignoreSpace = false; + for ($i = 0; isset($tokens[$i]); ++$i) { + $token = $tokens[$i]; + if (!isset($token[1]) || 'b"' === $token) { + $rawChunk .= $token; + } elseif (T_START_HEREDOC === $token[0]) { + $output .= $rawChunk.$token[1]; + do { + $token = $tokens[++$i]; + $output .= isset($token[1]) && 'b"' !== $token ? $token[1] : $token; + } while (T_END_HEREDOC !== $token[0]); + $rawChunk = ''; + } elseif (T_WHITESPACE === $token[0]) { + if ($ignoreSpace) { + $ignoreSpace = false; + + continue; + } + + // replace multiple new lines with a single newline + $rawChunk .= preg_replace(array('/\n{2,}/S'), "\n", $token[1]); + } elseif (\in_array($token[0], array(T_COMMENT, T_DOC_COMMENT))) { + $ignoreSpace = true; + } else { + $rawChunk .= $token[1]; + + // The PHP-open tag already has a new-line + if (T_OPEN_TAG === $token[0]) { + $ignoreSpace = true; + } + } + } + + $output .= $rawChunk; + + // PHP 7 memory manager will not release after token_get_all(), see https://bugs.php.net/70098 + unset($tokens, $rawChunk); + gc_mem_caches(); + + return $output; + } + + public function serialize() + { + return serialize(array($this->environment, $this->debug)); + } + + public function unserialize($data) + { + list($environment, $debug) = unserialize($data, array('allowed_classes' => false)); + + $this->__construct($environment, $debug); + } +} diff --git a/src/Symfony/Component/HttpKernel/KernelInterface.php b/src/Symfony/Component/Kernel/KernelInterface.php similarity index 96% rename from src/Symfony/Component/HttpKernel/KernelInterface.php rename to src/Symfony/Component/Kernel/KernelInterface.php index 485f3bf8424b..043e66004282 100644 --- a/src/Symfony/Component/HttpKernel/KernelInterface.php +++ b/src/Symfony/Component/Kernel/KernelInterface.php @@ -9,11 +9,11 @@ * file that was distributed with this source code. */ -namespace Symfony\Component\HttpKernel; +namespace Symfony\Component\Kernel; use Symfony\Component\Config\Loader\LoaderInterface; use Symfony\Component\DependencyInjection\ContainerInterface; -use Symfony\Component\HttpKernel\Bundle\BundleInterface; +use Symfony\Component\Kernel\Bundle\BundleInterface; /** * The Kernel is the heart of the Symfony system. @@ -22,7 +22,7 @@ * * @author Fabien Potencier */ -interface KernelInterface extends HttpKernelInterface, \Serializable +interface KernelInterface extends \Serializable { /** * Returns an array of bundles to register. diff --git a/src/Symfony/Component/Kernel/LICENSE b/src/Symfony/Component/Kernel/LICENSE new file mode 100644 index 000000000000..21d7fb9e2f29 --- /dev/null +++ b/src/Symfony/Component/Kernel/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2018 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/src/Symfony/Component/HttpKernel/Log/Logger.php b/src/Symfony/Component/Kernel/Log/Logger.php similarity index 98% rename from src/Symfony/Component/HttpKernel/Log/Logger.php rename to src/Symfony/Component/Kernel/Log/Logger.php index 8b671d84dfd8..4c66140ffefd 100644 --- a/src/Symfony/Component/HttpKernel/Log/Logger.php +++ b/src/Symfony/Component/Kernel/Log/Logger.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -namespace Symfony\Component\HttpKernel\Log; +namespace Symfony\Component\Kernel\Log; use Psr\Log\AbstractLogger; use Psr\Log\InvalidArgumentException; diff --git a/src/Symfony/Component/Kernel/README.md b/src/Symfony/Component/Kernel/README.md new file mode 100644 index 000000000000..dcbfaca05f45 --- /dev/null +++ b/src/Symfony/Component/Kernel/README.md @@ -0,0 +1,15 @@ +Symfony Kernel Component +======================== + +The Kernel component provides a structured process for create a service container. +It's flexible enough to create a full-stack framework (Symfony), +a micro-framework (Silex) or an advanced CMS system (Drupal). + +Resources +--------- + + * [Documentation](https://symfony.com/doc/current/components/http_kernel/index.html) + * [Contributing](https://symfony.com/doc/current/contributing/index.html) + * [Report issues](https://github.com/symfony/symfony/issues) and + [send Pull Requests](https://github.com/symfony/symfony/pulls) + in the [main Symfony repository](https://github.com/symfony/symfony) diff --git a/src/Symfony/Component/HttpKernel/RebootableInterface.php b/src/Symfony/Component/Kernel/RebootableInterface.php similarity index 94% rename from src/Symfony/Component/HttpKernel/RebootableInterface.php rename to src/Symfony/Component/Kernel/RebootableInterface.php index 58d9ef59e448..5451a18a7bab 100644 --- a/src/Symfony/Component/HttpKernel/RebootableInterface.php +++ b/src/Symfony/Component/Kernel/RebootableInterface.php @@ -9,7 +9,7 @@ * file that was distributed with this source code. */ -namespace Symfony\Component\HttpKernel; +namespace Symfony\Component\Kernel; /** * Allows the Kernel to be rebooted using a temporary cache directory. diff --git a/src/Symfony/Component/Kernel/Tests/Bundle/BundleTest.php b/src/Symfony/Component/Kernel/Tests/Bundle/BundleTest.php new file mode 100644 index 000000000000..966fb94b4e06 --- /dev/null +++ b/src/Symfony/Component/Kernel/Tests/Bundle/BundleTest.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\Kernel\Tests\Bundle; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Kernel\Bundle\Bundle; +use Symfony\Component\Kernel\Tests\Fixtures\ExtensionNotValidBundle\ExtensionNotValidBundle; +use Symfony\Component\Kernel\Tests\Fixtures\ExtensionPresentBundle\ExtensionPresentBundle; + +class BundleTest extends TestCase +{ + public function testGetContainerExtension() + { + $bundle = new ExtensionPresentBundle(); + + $this->assertInstanceOf( + 'Symfony\Component\Kernel\Tests\Fixtures\ExtensionPresentBundle\DependencyInjection\ExtensionPresentExtension', + $bundle->getContainerExtension() + ); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage must implement Symfony\Component\DependencyInjection\Extension\ExtensionInterface + */ + public function testGetContainerExtensionWithInvalidClass() + { + $bundle = new ExtensionNotValidBundle(); + $bundle->getContainerExtension(); + } + + public function testBundleNameIsGuessedFromClass() + { + $bundle = new GuessedNameBundle(); + + $this->assertSame('Symfony\Component\Kernel\Tests\Bundle', $bundle->getNamespace()); + $this->assertSame('GuessedNameBundle', $bundle->getName()); + } + + public function testBundleNameCanBeExplicitlyProvided() + { + $bundle = new NamedBundle(); + + $this->assertSame('ExplicitlyNamedBundle', $bundle->getName()); + $this->assertSame('Symfony\Component\Kernel\Tests\Bundle', $bundle->getNamespace()); + $this->assertSame('ExplicitlyNamedBundle', $bundle->getName()); + } +} + +class NamedBundle extends Bundle +{ + public function __construct() + { + $this->name = 'ExplicitlyNamedBundle'; + } +} + +class GuessedNameBundle extends Bundle +{ +} diff --git a/src/Symfony/Component/Kernel/Tests/CacheClearer/ChainCacheClearerTest.php b/src/Symfony/Component/Kernel/Tests/CacheClearer/ChainCacheClearerTest.php new file mode 100644 index 000000000000..46e0747b5d88 --- /dev/null +++ b/src/Symfony/Component/Kernel/Tests/CacheClearer/ChainCacheClearerTest.php @@ -0,0 +1,46 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Kernel\Tests\CacheClearer; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Kernel\CacheClearer\ChainCacheClearer; + +class ChainCacheClearerTest extends TestCase +{ + protected static $cacheDir; + + public static function setUpBeforeClass() + { + self::$cacheDir = tempnam(sys_get_temp_dir(), 'sf2_cache_clearer_dir'); + } + + public static function tearDownAfterClass() + { + @unlink(self::$cacheDir); + } + + public function testInjectClearersInConstructor() + { + $clearer = $this->getMockClearer(); + $clearer + ->expects($this->once()) + ->method('clear'); + + $chainClearer = new ChainCacheClearer(array($clearer)); + $chainClearer->clear(self::$cacheDir); + } + + protected function getMockClearer() + { + return $this->getMockBuilder('Symfony\Component\Kernel\CacheClearer\CacheClearerInterface')->getMock(); + } +} diff --git a/src/Symfony/Component/Kernel/Tests/CacheClearer/Psr6CacheClearerTest.php b/src/Symfony/Component/Kernel/Tests/CacheClearer/Psr6CacheClearerTest.php new file mode 100644 index 000000000000..55bbf4518277 --- /dev/null +++ b/src/Symfony/Component/Kernel/Tests/CacheClearer/Psr6CacheClearerTest.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Kernel\Tests\CacheClearer; + +use PHPUnit\Framework\TestCase; +use Psr\Cache\CacheItemPoolInterface; +use Symfony\Component\Kernel\CacheClearer\Psr6CacheClearer; + +class Psr6CacheClearerTest extends TestCase +{ + public function testClearPoolsInjectedInConstructor() + { + $pool = $this->getMockBuilder(CacheItemPoolInterface::class)->getMock(); + $pool + ->expects($this->once()) + ->method('clear'); + + (new Psr6CacheClearer(array('pool' => $pool)))->clear(''); + } + + public function testClearPool() + { + $pool = $this->getMockBuilder(CacheItemPoolInterface::class)->getMock(); + $pool + ->expects($this->once()) + ->method('clear'); + + (new Psr6CacheClearer(array('pool' => $pool)))->clearPool('pool'); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage Cache pool not found: unknown + */ + public function testClearPoolThrowsExceptionOnUnreferencedPool() + { + (new Psr6CacheClearer())->clearPool('unknown'); + } +} diff --git a/src/Symfony/Component/Kernel/Tests/CacheWarmer/CacheWarmerAggregateTest.php b/src/Symfony/Component/Kernel/Tests/CacheWarmer/CacheWarmerAggregateTest.php new file mode 100644 index 000000000000..affc5bab2b5b --- /dev/null +++ b/src/Symfony/Component/Kernel/Tests/CacheWarmer/CacheWarmerAggregateTest.php @@ -0,0 +1,79 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Kernel\Tests\CacheWarmer; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Kernel\CacheWarmer\CacheWarmerAggregate; + +class CacheWarmerAggregateTest extends TestCase +{ + protected static $cacheDir; + + public static function setUpBeforeClass() + { + self::$cacheDir = tempnam(sys_get_temp_dir(), 'sf2_cache_warmer_dir'); + } + + public static function tearDownAfterClass() + { + @unlink(self::$cacheDir); + } + + public function testInjectWarmersUsingConstructor() + { + $warmer = $this->getCacheWarmerMock(); + $warmer + ->expects($this->once()) + ->method('warmUp'); + $aggregate = new CacheWarmerAggregate(array($warmer)); + $aggregate->warmUp(self::$cacheDir); + } + + public function testWarmupDoesCallWarmupOnOptionalWarmersWhenEnableOptionalWarmersIsEnabled() + { + $warmer = $this->getCacheWarmerMock(); + $warmer + ->expects($this->never()) + ->method('isOptional'); + $warmer + ->expects($this->once()) + ->method('warmUp'); + + $aggregate = new CacheWarmerAggregate(array($warmer)); + $aggregate->enableOptionalWarmers(); + $aggregate->warmUp(self::$cacheDir); + } + + public function testWarmupDoesNotCallWarmupOnOptionalWarmersWhenEnableOptionalWarmersIsNotEnabled() + { + $warmer = $this->getCacheWarmerMock(); + $warmer + ->expects($this->once()) + ->method('isOptional') + ->will($this->returnValue(true)); + $warmer + ->expects($this->never()) + ->method('warmUp'); + + $aggregate = new CacheWarmerAggregate(array($warmer)); + $aggregate->warmUp(self::$cacheDir); + } + + protected function getCacheWarmerMock() + { + $warmer = $this->getMockBuilder('Symfony\Component\Kernel\CacheWarmer\CacheWarmerInterface') + ->disableOriginalConstructor() + ->getMock(); + + return $warmer; + } +} diff --git a/src/Symfony/Component/Kernel/Tests/CacheWarmer/CacheWarmerTest.php b/src/Symfony/Component/Kernel/Tests/CacheWarmer/CacheWarmerTest.php new file mode 100644 index 000000000000..690462e2ebf6 --- /dev/null +++ b/src/Symfony/Component/Kernel/Tests/CacheWarmer/CacheWarmerTest.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\Kernel\Tests\CacheWarmer; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Kernel\CacheWarmer\CacheWarmer; + +class CacheWarmerTest extends TestCase +{ + protected static $cacheFile; + + public static function setUpBeforeClass() + { + self::$cacheFile = tempnam(sys_get_temp_dir(), 'sf2_cache_warmer_dir'); + } + + public static function tearDownAfterClass() + { + @unlink(self::$cacheFile); + } + + public function testWriteCacheFileCreatesTheFile() + { + $warmer = new TestCacheWarmer(self::$cacheFile); + $warmer->warmUp(\dirname(self::$cacheFile)); + + $this->assertFileExists(self::$cacheFile); + } + + /** + * @expectedException \RuntimeException + */ + public function testWriteNonWritableCacheFileThrowsARuntimeException() + { + $nonWritableFile = '/this/file/is/very/probably/not/writable'; + $warmer = new TestCacheWarmer($nonWritableFile); + $warmer->warmUp(\dirname($nonWritableFile)); + } +} + +class TestCacheWarmer extends CacheWarmer +{ + protected $file; + + public function __construct($file) + { + $this->file = $file; + } + + public function warmUp($cacheDir) + { + $this->writeCacheFile($this->file, 'content'); + } + + public function isOptional() + { + return false; + } +} diff --git a/src/Symfony/Component/Kernel/Tests/Config/FileLocatorTest.php b/src/Symfony/Component/Kernel/Tests/Config/FileLocatorTest.php new file mode 100644 index 000000000000..65315114eaef --- /dev/null +++ b/src/Symfony/Component/Kernel/Tests/Config/FileLocatorTest.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Kernel\Tests\Config; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Kernel\Config\FileLocator; + +class FileLocatorTest extends TestCase +{ + public function testLocate() + { + $kernel = $this->getMockBuilder('Symfony\Component\Kernel\KernelInterface')->getMock(); + $kernel + ->expects($this->atLeastOnce()) + ->method('locateResource') + ->with('@BundleName/some/path', null, true) + ->will($this->returnValue('/bundle-name/some/path')); + $locator = new FileLocator($kernel); + $this->assertEquals('/bundle-name/some/path', $locator->locate('@BundleName/some/path')); + + $kernel + ->expects($this->never()) + ->method('locateResource'); + $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('LogicException'); + $locator->locate('/some/path'); + } + + public function testLocateWithGlobalResourcePath() + { + $kernel = $this->getMockBuilder('Symfony\Component\Kernel\KernelInterface')->getMock(); + $kernel + ->expects($this->atLeastOnce()) + ->method('locateResource') + ->with('@BundleName/some/path', '/global/resource/path', false); + + $locator = new FileLocator($kernel, '/global/resource/path'); + $locator->locate('@BundleName/some/path', null, false); + } +} diff --git a/src/Symfony/Component/Kernel/Tests/DependencyInjection/AddAnnotatedClassesToCachePassTest.php b/src/Symfony/Component/Kernel/Tests/DependencyInjection/AddAnnotatedClassesToCachePassTest.php new file mode 100644 index 000000000000..085fa538c47d --- /dev/null +++ b/src/Symfony/Component/Kernel/Tests/DependencyInjection/AddAnnotatedClassesToCachePassTest.php @@ -0,0 +1,99 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Kernel\Tests\DependencyInjection; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Kernel\DependencyInjection\AddAnnotatedClassesToCachePass; + +class AddAnnotatedClassesToCachePassTest extends TestCase +{ + public function testExpandClasses() + { + $r = new \ReflectionClass(AddAnnotatedClassesToCachePass::class); + $pass = $r->newInstanceWithoutConstructor(); + $r = new \ReflectionMethod(AddAnnotatedClassesToCachePass::class, 'expandClasses'); + $r->setAccessible(true); + $expand = $r->getClosure($pass); + + $this->assertSame('Foo', $expand(array('Foo'), array())[0]); + $this->assertSame('Foo', $expand(array('\\Foo'), array())[0]); + $this->assertSame('Foo', $expand(array('Foo'), array('\\Foo'))[0]); + $this->assertSame('Foo', $expand(array('Foo'), array('Foo'))[0]); + $this->assertSame('Foo', $expand(array('\\Foo'), array('\\Foo\\Bar'))[0]); + $this->assertSame('Foo', $expand(array('Foo'), array('\\Foo\\Bar'))[0]); + $this->assertSame('Foo', $expand(array('\\Foo'), array('\\Foo\\Bar\\Acme'))[0]); + + $this->assertSame('Foo\\Bar', $expand(array('Foo\\'), array('\\Foo\\Bar'))[0]); + $this->assertSame('Foo\\Bar\\Acme', $expand(array('Foo\\'), array('\\Foo\\Bar\\Acme'))[0]); + $this->assertEmpty($expand(array('Foo\\'), array('\\Foo'))); + + $this->assertSame('Acme\\Foo\\Bar', $expand(array('**\\Foo\\'), array('\\Acme\\Foo\\Bar'))[0]); + $this->assertEmpty($expand(array('**\\Foo\\'), array('\\Foo\\Bar'))); + $this->assertEmpty($expand(array('**\\Foo\\'), array('\\Acme\\Foo'))); + $this->assertEmpty($expand(array('**\\Foo\\'), array('\\Foo'))); + + $this->assertSame('Acme\\Foo', $expand(array('**\\Foo'), array('\\Acme\\Foo'))[0]); + $this->assertEmpty($expand(array('**\\Foo'), array('\\Acme\\Foo\\AcmeBundle'))); + $this->assertEmpty($expand(array('**\\Foo'), array('\\Acme\\FooBar\\AcmeBundle'))); + + $this->assertSame('Foo\\Acme\\Bar', $expand(array('Foo\\*\\Bar'), array('\\Foo\\Acme\\Bar'))[0]); + $this->assertEmpty($expand(array('Foo\\*\\Bar'), array('\\Foo\\Acme\\Bundle\\Bar'))); + + $this->assertSame('Foo\\Acme\\Bar', $expand(array('Foo\\**\\Bar'), array('\\Foo\\Acme\\Bar'))[0]); + $this->assertSame('Foo\\Acme\\Bundle\\Bar', $expand(array('Foo\\**\\Bar'), array('\\Foo\\Acme\\Bundle\\Bar'))[0]); + + $this->assertSame('Acme\\Bar', $expand(array('*\\Bar'), array('\\Acme\\Bar'))[0]); + $this->assertEmpty($expand(array('*\\Bar'), array('\\Bar'))); + $this->assertEmpty($expand(array('*\\Bar'), array('\\Foo\\Acme\\Bar'))); + + $this->assertSame('Foo\\Acme\\Bar', $expand(array('**\\Bar'), array('\\Foo\\Acme\\Bar'))[0]); + $this->assertSame('Foo\\Acme\\Bundle\\Bar', $expand(array('**\\Bar'), array('\\Foo\\Acme\\Bundle\\Bar'))[0]); + $this->assertEmpty($expand(array('**\\Bar'), array('\\Bar'))); + + $this->assertSame('Foo\\Bar', $expand(array('Foo\\*'), array('\\Foo\\Bar'))[0]); + $this->assertEmpty($expand(array('Foo\\*'), array('\\Foo\\Acme\\Bar'))); + + $this->assertSame('Foo\\Bar', $expand(array('Foo\\**'), array('\\Foo\\Bar'))[0]); + $this->assertSame('Foo\\Acme\\Bar', $expand(array('Foo\\**'), array('\\Foo\\Acme\\Bar'))[0]); + + $this->assertSame(array('Foo\\Bar'), $expand(array('Foo\\*'), array('Foo\\Bar', 'Foo\\BarTest'))); + $this->assertSame(array('Foo\\Bar', 'Foo\\BarTest'), $expand(array('Foo\\*', 'Foo\\*Test'), array('Foo\\Bar', 'Foo\\BarTest'))); + + $this->assertSame( + 'Acme\\FooBundle\\Controller\\DefaultController', + $expand(array('**Bundle\\Controller\\'), array('\\Acme\\FooBundle\\Controller\\DefaultController'))[0] + ); + + $this->assertSame( + 'FooBundle\\Controller\\DefaultController', + $expand(array('**Bundle\\Controller\\'), array('\\FooBundle\\Controller\\DefaultController'))[0] + ); + + $this->assertSame( + 'Acme\\FooBundle\\Controller\\Bar\\DefaultController', + $expand(array('**Bundle\\Controller\\'), array('\\Acme\\FooBundle\\Controller\\Bar\\DefaultController'))[0] + ); + + $this->assertSame( + 'Bundle\\Controller\\Bar\\DefaultController', + $expand(array('**Bundle\\Controller\\'), array('\\Bundle\\Controller\\Bar\\DefaultController'))[0] + ); + + $this->assertSame( + 'Acme\\Bundle\\Controller\\Bar\\DefaultController', + $expand(array('**Bundle\\Controller\\'), array('\\Acme\\Bundle\\Controller\\Bar\\DefaultController'))[0] + ); + + $this->assertSame('Foo\\Bar', $expand(array('Foo\\Bar'), array())[0]); + $this->assertSame('Foo\\Acme\\Bar', $expand(array('Foo\\**'), array('\\Foo\\Acme\\Bar'))[0]); + } +} diff --git a/src/Symfony/Component/Kernel/Tests/DependencyInjection/LoggerPassTest.php b/src/Symfony/Component/Kernel/Tests/DependencyInjection/LoggerPassTest.php new file mode 100644 index 000000000000..913f78826767 --- /dev/null +++ b/src/Symfony/Component/Kernel/Tests/DependencyInjection/LoggerPassTest.php @@ -0,0 +1,56 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Kernel\Tests\DependencyInjection; + +use PHPUnit\Framework\TestCase; +use Psr\Log\LoggerInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\Kernel\DependencyInjection\LoggerPass; +use Symfony\Component\Kernel\Log\Logger; + +/** + * @author Kévin Dunglas + */ +class LoggerPassTest extends TestCase +{ + public function testAlwaysSetAutowiringAlias() + { + $container = new ContainerBuilder(); + $container->register('logger', 'Foo'); + + (new LoggerPass())->process($container); + + $this->assertFalse($container->getAlias(LoggerInterface::class)->isPublic()); + } + + public function testDoNotOverrideExistingLogger() + { + $container = new ContainerBuilder(); + $container->register('logger', 'Foo'); + + (new LoggerPass())->process($container); + + $this->assertSame('Foo', $container->getDefinition('logger')->getClass()); + } + + public function testRegisterLogger() + { + $container = new ContainerBuilder(); + $container->setParameter('kernel.debug', false); + + (new LoggerPass())->process($container); + + $definition = $container->getDefinition('logger'); + $this->assertSame(Logger::class, $definition->getClass()); + $this->assertFalse($definition->isPublic()); + } +} diff --git a/src/Symfony/Component/Kernel/Tests/DependencyInjection/MergeExtensionConfigurationPassTest.php b/src/Symfony/Component/Kernel/Tests/DependencyInjection/MergeExtensionConfigurationPassTest.php new file mode 100644 index 000000000000..ce8ababf7058 --- /dev/null +++ b/src/Symfony/Component/Kernel/Tests/DependencyInjection/MergeExtensionConfigurationPassTest.php @@ -0,0 +1,50 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Kernel\Tests\DependencyInjection; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\Kernel\DependencyInjection\Extension; +use Symfony\Component\Kernel\DependencyInjection\MergeExtensionConfigurationPass; + +class MergeExtensionConfigurationPassTest extends TestCase +{ + public function testAutoloadMainExtension() + { + $container = new ContainerBuilder(); + $container->registerExtension(new LoadedExtension()); + $container->registerExtension(new NotLoadedExtension()); + $container->loadFromExtension('loaded', array()); + + $configPass = new MergeExtensionConfigurationPass(array('loaded', 'not_loaded')); + $configPass->process($container); + + $this->assertTrue($container->hasDefinition('loaded.foo')); + $this->assertTrue($container->hasDefinition('not_loaded.bar')); + } +} + +class LoadedExtension extends Extension +{ + public function load(array $configs, ContainerBuilder $container) + { + $container->register('loaded.foo'); + } +} + +class NotLoadedExtension extends Extension +{ + public function load(array $configs, ContainerBuilder $container) + { + $container->register('not_loaded.bar'); + } +} diff --git a/src/Symfony/Component/Kernel/Tests/DependencyInjection/ResettableServicePassTest.php b/src/Symfony/Component/Kernel/Tests/DependencyInjection/ResettableServicePassTest.php new file mode 100644 index 000000000000..51e2b4816ea4 --- /dev/null +++ b/src/Symfony/Component/Kernel/Tests/DependencyInjection/ResettableServicePassTest.php @@ -0,0 +1,78 @@ +register('one', ResettableService::class) + ->setPublic(true) + ->addTag('kernel.reset', array('method' => 'reset')); + $container->register('two', ClearableService::class) + ->setPublic(true) + ->addTag('kernel.reset', array('method' => 'clear')); + + $container->register('services_resetter', ServicesResetter::class) + ->setPublic(true) + ->setArguments(array(null, array())); + $container->addCompilerPass(new ResettableServicePass()); + + $container->compile(); + + $definition = $container->getDefinition('services_resetter'); + + $this->assertEquals( + array( + new IteratorArgument(array( + 'one' => new Reference('one', ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE), + 'two' => new Reference('two', ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE), + )), + array( + 'one' => 'reset', + 'two' => 'clear', + ), + ), + $definition->getArguments() + ); + } + + /** + * @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException + * @expectedExceptionMessage Tag kernel.reset requires the "method" attribute to be set. + */ + public function testMissingMethod() + { + $container = new ContainerBuilder(); + $container->register(ResettableService::class) + ->addTag('kernel.reset'); + $container->register('services_resetter', ServicesResetter::class) + ->setArguments(array(null, array())); + $container->addCompilerPass(new ResettableServicePass()); + + $container->compile(); + } + + public function testCompilerPassWithoutResetters() + { + $container = new ContainerBuilder(); + $container->register('services_resetter', ServicesResetter::class) + ->setArguments(array(null, array())); + $container->addCompilerPass(new ResettableServicePass()); + + $container->compile(); + + $this->assertFalse($container->has('services_resetter')); + } +} diff --git a/src/Symfony/Component/Kernel/Tests/DependencyInjection/ServicesResetterTest.php b/src/Symfony/Component/Kernel/Tests/DependencyInjection/ServicesResetterTest.php new file mode 100644 index 000000000000..247ad1204301 --- /dev/null +++ b/src/Symfony/Component/Kernel/Tests/DependencyInjection/ServicesResetterTest.php @@ -0,0 +1,42 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Kernel\Tests\DependencyInjection; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Kernel\DependencyInjection\ServicesResetter; +use Symfony\Component\Kernel\Tests\Fixtures\ClearableService; +use Symfony\Component\Kernel\Tests\Fixtures\ResettableService; + +class ServicesResetterTest extends TestCase +{ + protected function setUp() + { + ResettableService::$counter = 0; + ClearableService::$counter = 0; + } + + public function testResetServices() + { + $resetter = new ServicesResetter(new \ArrayIterator(array( + 'id1' => new ResettableService(), + 'id2' => new ClearableService(), + )), array( + 'id1' => 'reset', + 'id2' => 'clear', + )); + + $resetter->reset(); + + $this->assertEquals(1, ResettableService::$counter); + $this->assertEquals(1, ClearableService::$counter); + } +} diff --git a/src/Symfony/Component/Kernel/Tests/EventListener/DumpListenerTest.php b/src/Symfony/Component/Kernel/Tests/EventListener/DumpListenerTest.php new file mode 100644 index 000000000000..ad09c239638d --- /dev/null +++ b/src/Symfony/Component/Kernel/Tests/EventListener/DumpListenerTest.php @@ -0,0 +1,81 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Kernel\Tests\EventListener; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Console\ConsoleEvents; +use Symfony\Component\Kernel\EventListener\DumpListener; +use Symfony\Component\VarDumper\Cloner\ClonerInterface; +use Symfony\Component\VarDumper\Cloner\Data; +use Symfony\Component\VarDumper\Dumper\DataDumperInterface; +use Symfony\Component\VarDumper\VarDumper; + +/** + * DumpListenerTest. + * + * @author Nicolas Grekas + */ +class DumpListenerTest extends TestCase +{ + public function testSubscribedEvents() + { + $this->assertSame( + array(ConsoleEvents::COMMAND => array('configure', 1024)), + DumpListener::getSubscribedEvents() + ); + } + + public function testConfigure() + { + $prevDumper = VarDumper::setHandler('var_dump'); + VarDumper::setHandler($prevDumper); + + $cloner = new MockCloner(); + $dumper = new MockDumper(); + + ob_start(); + $exception = null; + $listener = new DumpListener($cloner, $dumper); + + try { + $listener->configure(); + + VarDumper::dump('foo'); + VarDumper::dump('bar'); + + $this->assertSame('+foo-+bar-', ob_get_clean()); + } catch (\Exception $exception) { + } + + VarDumper::setHandler($prevDumper); + + if (null !== $exception) { + throw $exception; + } + } +} + +class MockCloner implements ClonerInterface +{ + public function cloneVar($var) + { + return new Data(array(array($var.'-'))); + } +} + +class MockDumper implements DataDumperInterface +{ + public function dump(Data $data) + { + echo '+'.$data->getValue(); + } +} diff --git a/src/Symfony/Component/Kernel/Tests/Fixtures/123/Kernel123.php b/src/Symfony/Component/Kernel/Tests/Fixtures/123/Kernel123.php new file mode 100644 index 000000000000..01c1aa96377b --- /dev/null +++ b/src/Symfony/Component/Kernel/Tests/Fixtures/123/Kernel123.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Kernel\Tests\Fixtures\_123; + +use Symfony\Component\Config\Loader\LoaderInterface; +use Symfony\Component\Kernel\Kernel; + +class Kernel123 extends Kernel +{ + public function registerBundles() + { + return array(); + } + + public function registerContainerConfiguration(LoaderInterface $loader) + { + } + + public function getCacheDir() + { + return sys_get_temp_dir().'/'.Kernel::VERSION.'/kernel123/cache/'.$this->environment; + } + + public function getLogDir() + { + return sys_get_temp_dir().'/'.Kernel::VERSION.'/kernel123/logs'; + } +} diff --git a/src/Symfony/Component/Kernel/Tests/Fixtures/Bundle1Bundle/Resources/foo.txt b/src/Symfony/Component/Kernel/Tests/Fixtures/Bundle1Bundle/Resources/foo.txt new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/src/Symfony/Component/Kernel/Tests/Fixtures/Bundle1Bundle/bar.txt b/src/Symfony/Component/Kernel/Tests/Fixtures/Bundle1Bundle/bar.txt new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/src/Symfony/Component/Kernel/Tests/Fixtures/Bundle1Bundle/foo.txt b/src/Symfony/Component/Kernel/Tests/Fixtures/Bundle1Bundle/foo.txt new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/src/Symfony/Component/Kernel/Tests/Fixtures/ClearableService.php b/src/Symfony/Component/Kernel/Tests/Fixtures/ClearableService.php new file mode 100644 index 000000000000..6e4adfe570a9 --- /dev/null +++ b/src/Symfony/Component/Kernel/Tests/Fixtures/ClearableService.php @@ -0,0 +1,13 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Kernel\Tests\Fixtures\ExtensionNotValidBundle\DependencyInjection; + +class ExtensionNotValidExtension +{ + public function getAlias() + { + return 'extension_not_valid'; + } +} diff --git a/src/Symfony/Component/Kernel/Tests/Fixtures/ExtensionNotValidBundle/ExtensionNotValidBundle.php b/src/Symfony/Component/Kernel/Tests/Fixtures/ExtensionNotValidBundle/ExtensionNotValidBundle.php new file mode 100644 index 000000000000..afa168da1503 --- /dev/null +++ b/src/Symfony/Component/Kernel/Tests/Fixtures/ExtensionNotValidBundle/ExtensionNotValidBundle.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Kernel\Tests\Fixtures\ExtensionNotValidBundle; + +use Symfony\Component\Kernel\Bundle\Bundle; + +class ExtensionNotValidBundle extends Bundle +{ +} diff --git a/src/Symfony/Component/Kernel/Tests/Fixtures/ExtensionPresentBundle/DependencyInjection/ExtensionPresentExtension.php b/src/Symfony/Component/Kernel/Tests/Fixtures/ExtensionPresentBundle/DependencyInjection/ExtensionPresentExtension.php new file mode 100644 index 000000000000..eb30520a66ed --- /dev/null +++ b/src/Symfony/Component/Kernel/Tests/Fixtures/ExtensionPresentBundle/DependencyInjection/ExtensionPresentExtension.php @@ -0,0 +1,22 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Kernel\Tests\Fixtures\ExtensionPresentBundle\DependencyInjection; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Extension\Extension; + +class ExtensionPresentExtension extends Extension +{ + public function load(array $configs, ContainerBuilder $container) + { + } +} diff --git a/src/Symfony/Component/Kernel/Tests/Fixtures/ExtensionPresentBundle/ExtensionPresentBundle.php b/src/Symfony/Component/Kernel/Tests/Fixtures/ExtensionPresentBundle/ExtensionPresentBundle.php new file mode 100644 index 000000000000..ca335ff03758 --- /dev/null +++ b/src/Symfony/Component/Kernel/Tests/Fixtures/ExtensionPresentBundle/ExtensionPresentBundle.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Kernel\Tests\Fixtures\ExtensionPresentBundle; + +use Symfony\Component\Kernel\Bundle\Bundle; + +class ExtensionPresentBundle extends Bundle +{ +} diff --git a/src/Symfony/Component/Kernel/Tests/Fixtures/KernelForOverrideName.php b/src/Symfony/Component/Kernel/Tests/Fixtures/KernelForOverrideName.php new file mode 100644 index 000000000000..aa46432c459e --- /dev/null +++ b/src/Symfony/Component/Kernel/Tests/Fixtures/KernelForOverrideName.php @@ -0,0 +1,28 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Kernel\Tests\Fixtures; + +use Symfony\Component\Config\Loader\LoaderInterface; +use Symfony\Component\Kernel\Kernel; + +class KernelForOverrideName extends Kernel +{ + protected $name = 'overridden'; + + public function registerBundles() + { + } + + public function registerContainerConfiguration(LoaderInterface $loader) + { + } +} diff --git a/src/Symfony/Component/Kernel/Tests/Fixtures/KernelForTest.php b/src/Symfony/Component/Kernel/Tests/Fixtures/KernelForTest.php new file mode 100644 index 000000000000..c526256edcfa --- /dev/null +++ b/src/Symfony/Component/Kernel/Tests/Fixtures/KernelForTest.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Kernel\Tests\Fixtures; + +use Symfony\Component\Config\Loader\LoaderInterface; +use Symfony\Component\Kernel\Kernel; + +class KernelForTest extends Kernel +{ + public function getBundleMap() + { + return $this->bundleMap; + } + + public function registerBundles() + { + return array(); + } + + public function registerContainerConfiguration(LoaderInterface $loader) + { + } + + public function isBooted() + { + return $this->booted; + } +} diff --git a/src/Symfony/Component/Kernel/Tests/Fixtures/KernelWithoutBundles.php b/src/Symfony/Component/Kernel/Tests/Fixtures/KernelWithoutBundles.php new file mode 100644 index 000000000000..91c41ca233bb --- /dev/null +++ b/src/Symfony/Component/Kernel/Tests/Fixtures/KernelWithoutBundles.php @@ -0,0 +1,33 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Kernel\Tests\Fixtures; + +use Symfony\Component\Config\Loader\LoaderInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\Kernel\Kernel; + +class KernelWithoutBundles extends Kernel +{ + public function registerBundles() + { + return array(); + } + + public function registerContainerConfiguration(LoaderInterface $loader) + { + } + + protected function build(ContainerBuilder $container) + { + $container->setParameter('test_executed', true); + } +} diff --git a/src/Symfony/Component/Kernel/Tests/Fixtures/ResettableService.php b/src/Symfony/Component/Kernel/Tests/Fixtures/ResettableService.php new file mode 100644 index 000000000000..b0aa43406f9c --- /dev/null +++ b/src/Symfony/Component/Kernel/Tests/Fixtures/ResettableService.php @@ -0,0 +1,13 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Kernel\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\Config\Loader\LoaderInterface; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\Kernel\Bundle\BundleInterface; +use Symfony\Component\Kernel\DependencyInjection\ResettableServicePass; +use Symfony\Component\Kernel\DependencyInjection\ServicesResetter; +use Symfony\Component\Kernel\Kernel; +use Symfony\Component\Kernel\Tests\Fixtures\KernelForOverrideName; +use Symfony\Component\Kernel\Tests\Fixtures\KernelForTest; +use Symfony\Component\Kernel\Tests\Fixtures\KernelWithoutBundles; +use Symfony\Component\Kernel\Tests\Fixtures\ResettableService; + +class KernelTest extends TestCase +{ + public static function tearDownAfterClass() + { + $fs = new Filesystem(); + $fs->remove(__DIR__.'/Fixtures/cache'); + } + + public function testConstructor() + { + $env = 'test_env'; + $debug = true; + $kernel = new KernelForTest($env, $debug); + + $this->assertEquals($env, $kernel->getEnvironment()); + $this->assertEquals($debug, $kernel->isDebug()); + $this->assertFalse($kernel->isBooted()); + $this->assertLessThanOrEqual(microtime(true), $kernel->getStartTime()); + $this->assertNull($kernel->getContainer()); + } + + public function testClone() + { + $env = 'test_env'; + $debug = true; + $kernel = new KernelForTest($env, $debug); + + $clone = clone $kernel; + + $this->assertEquals($env, $clone->getEnvironment()); + $this->assertEquals($debug, $clone->isDebug()); + $this->assertFalse($clone->isBooted()); + $this->assertLessThanOrEqual(microtime(true), $clone->getStartTime()); + $this->assertNull($clone->getContainer()); + } + + public function testInitializeContainerClearsOldContainers() + { + $fs = new Filesystem(); + $legacyContainerDir = __DIR__.'/Fixtures/cache/custom/ContainerA123456'; + $fs->mkdir($legacyContainerDir); + touch($legacyContainerDir.'.legacy'); + + $kernel = new CustomProjectDirKernel(); + $kernel->boot(); + + $containerDir = __DIR__.'/Fixtures/cache/custom/'.substr(\get_class($kernel->getContainer()), 0, 16); + $this->assertTrue(unlink(__DIR__.'/Fixtures/cache/custom/FixturesCustomDebugProjectContainer.php.meta')); + $this->assertFileExists($containerDir); + $this->assertFileNotExists($containerDir.'.legacy'); + + $kernel = new CustomProjectDirKernel(function ($container) { $container->register('foo', 'stdClass')->setPublic(true); }); + $kernel->boot(); + + $this->assertFileExists($containerDir); + $this->assertFileExists($containerDir.'.legacy'); + + $this->assertFileNotExists($legacyContainerDir); + $this->assertFileNotExists($legacyContainerDir.'.legacy'); + } + + public function testBootInitializesBundlesAndContainer() + { + $kernel = $this->getKernel(array('initializeBundles', 'initializeContainer')); + $kernel->expects($this->once()) + ->method('initializeBundles'); + $kernel->expects($this->once()) + ->method('initializeContainer'); + + $kernel->boot(); + } + + public function testBootSetsTheContainerToTheBundles() + { + $bundle = $this->getMockBuilder('Symfony\Component\Kernel\Bundle\Bundle')->getMock(); + $bundle->expects($this->once()) + ->method('setContainer'); + + $kernel = $this->getKernel(array('initializeBundles', 'initializeContainer', 'getBundles')); + $kernel->expects($this->once()) + ->method('getBundles') + ->will($this->returnValue(array($bundle))); + + $kernel->boot(); + } + + public function testBootSetsTheBootedFlagToTrue() + { + // use test kernel to access isBooted() + $kernel = $this->getKernelForTest(array('initializeBundles', 'initializeContainer')); + $kernel->boot(); + + $this->assertTrue($kernel->isBooted()); + } + + public function testClassCacheIsNotLoadedByDefault() + { + $kernel = $this->getKernel(array('initializeBundles', 'initializeContainer', 'doLoadClassCache')); + $kernel->expects($this->never()) + ->method('doLoadClassCache'); + + $kernel->boot(); + } + + public function testBootKernelSeveralTimesOnlyInitializesBundlesOnce() + { + $kernel = $this->getKernel(array('initializeBundles', 'initializeContainer')); + $kernel->expects($this->once()) + ->method('initializeBundles'); + + $kernel->boot(); + $kernel->boot(); + } + + public function testShutdownCallsShutdownOnAllBundles() + { + $bundle = $this->getMockBuilder('Symfony\Component\Kernel\Bundle\Bundle')->getMock(); + $bundle->expects($this->once()) + ->method('shutdown'); + + $kernel = $this->getKernel(array(), array($bundle)); + + $kernel->boot(); + $kernel->shutdown(); + } + + public function testShutdownGivesNullContainerToAllBundles() + { + $bundle = $this->getMockBuilder('Symfony\Component\Kernel\Bundle\Bundle')->getMock(); + $bundle->expects($this->at(3)) + ->method('setContainer') + ->with(null); + + $kernel = $this->getKernel(array('getBundles')); + $kernel->expects($this->any()) + ->method('getBundles') + ->will($this->returnValue(array($bundle))); + + $kernel->boot(); + $kernel->shutdown(); + } + + public function testStripComments() + { + $source = <<<'EOF' +assertEquals($expected, $output); + } + + public function testGetRootDir() + { + $kernel = new KernelForTest('test', true); + + $this->assertEquals(__DIR__.\DIRECTORY_SEPARATOR.'Fixtures', realpath($kernel->getRootDir())); + } + + public function testGetName() + { + $kernel = new KernelForTest('test', true); + + $this->assertEquals('Fixtures', $kernel->getName()); + } + + public function testOverrideGetName() + { + $kernel = new KernelForOverrideName('test', true); + + $this->assertEquals('overridden', $kernel->getName()); + } + + public function testSerialize() + { + $env = 'test_env'; + $debug = true; + $kernel = new KernelForTest($env, $debug); + + $expected = serialize(array($env, $debug)); + $this->assertEquals($expected, $kernel->serialize()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testLocateResourceThrowsExceptionWhenNameIsNotValid() + { + $this->getKernel()->locateResource('Foo'); + } + + /** + * @expectedException \RuntimeException + */ + public function testLocateResourceThrowsExceptionWhenNameIsUnsafe() + { + $this->getKernel()->locateResource('@FooBundle/../bar'); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testLocateResourceThrowsExceptionWhenBundleDoesNotExist() + { + $this->getKernel()->locateResource('@FooBundle/config/routing.xml'); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testLocateResourceThrowsExceptionWhenResourceDoesNotExist() + { + $kernel = $this->getKernel(array('getBundle')); + $kernel + ->expects($this->once()) + ->method('getBundle') + ->will($this->returnValue($this->getBundle(__DIR__.'/Fixtures/Bundle1Bundle'))) + ; + + $kernel->locateResource('@Bundle1Bundle/config/routing.xml'); + } + + public function testLocateResourceReturnsTheFirstThatMatches() + { + $kernel = $this->getKernel(array('getBundle')); + $kernel + ->expects($this->once()) + ->method('getBundle') + ->will($this->returnValue($this->getBundle(__DIR__.'/Fixtures/Bundle1Bundle'))) + ; + + $this->assertEquals(__DIR__.'/Fixtures/Bundle1Bundle/foo.txt', $kernel->locateResource('@Bundle1Bundle/foo.txt')); + } + + public function testLocateResourceIgnoresDirOnNonResource() + { + $kernel = $this->getKernel(array('getBundle')); + $kernel + ->expects($this->once()) + ->method('getBundle') + ->will($this->returnValue($this->getBundle(__DIR__.'/Fixtures/Bundle1Bundle'))) + ; + + $this->assertEquals( + __DIR__.'/Fixtures/Bundle1Bundle/foo.txt', + $kernel->locateResource('@Bundle1Bundle/foo.txt', __DIR__.'/Fixtures') + ); + } + + public function testLocateResourceReturnsTheDirOneForResources() + { + $kernel = $this->getKernel(array('getBundle')); + $kernel + ->expects($this->once()) + ->method('getBundle') + ->will($this->returnValue($this->getBundle(__DIR__.'/Fixtures/FooBundle', null, null, 'FooBundle'))) + ; + + $this->assertEquals( + __DIR__.'/Fixtures/Resources/FooBundle/foo.txt', + $kernel->locateResource('@FooBundle/Resources/foo.txt', __DIR__.'/Fixtures/Resources') + ); + } + + public function testLocateResourceOnDirectories() + { + $kernel = $this->getKernel(array('getBundle')); + $kernel + ->expects($this->exactly(2)) + ->method('getBundle') + ->will($this->returnValue($this->getBundle(__DIR__.'/Fixtures/FooBundle', null, null, 'FooBundle'))) + ; + + $this->assertEquals( + __DIR__.'/Fixtures/Resources/FooBundle/', + $kernel->locateResource('@FooBundle/Resources/', __DIR__.'/Fixtures/Resources') + ); + $this->assertEquals( + __DIR__.'/Fixtures/Resources/FooBundle', + $kernel->locateResource('@FooBundle/Resources', __DIR__.'/Fixtures/Resources') + ); + + $kernel = $this->getKernel(array('getBundle')); + $kernel + ->expects($this->exactly(2)) + ->method('getBundle') + ->will($this->returnValue($this->getBundle(__DIR__.'/Fixtures/Bundle1Bundle', null, null, 'Bundle1Bundle'))) + ; + + $this->assertEquals( + __DIR__.'/Fixtures/Bundle1Bundle/Resources/', + $kernel->locateResource('@Bundle1Bundle/Resources/') + ); + $this->assertEquals( + __DIR__.'/Fixtures/Bundle1Bundle/Resources', + $kernel->locateResource('@Bundle1Bundle/Resources') + ); + } + + /** + * @expectedException \LogicException + * @expectedExceptionMessage Trying to register two bundles with the same name "DuplicateName" + */ + public function testInitializeBundleThrowsExceptionWhenRegisteringTwoBundlesWithTheSameName() + { + $fooBundle = $this->getBundle(null, null, 'FooBundle', 'DuplicateName'); + $barBundle = $this->getBundle(null, null, 'BarBundle', 'DuplicateName'); + + $kernel = $this->getKernel(array(), array($fooBundle, $barBundle)); + $kernel->boot(); + } + + public function testKernelWithoutBundles() + { + $kernel = new KernelWithoutBundles('test', true); + $kernel->boot(); + + $this->assertTrue($kernel->getContainer()->getParameter('test_executed')); + } + + public function testKernelRootDirNameStartingWithANumber() + { + $dir = __DIR__.'/Fixtures/123'; + require_once $dir.'/Kernel123.php'; + $kernel = new \Symfony\Component\Kernel\Tests\Fixtures\_123\Kernel123('dev', true); + $this->assertEquals('_123', $kernel->getName()); + } + + public function testProjectDirExtension() + { + $kernel = new CustomProjectDirKernel(); + $kernel->boot(); + + $this->assertSame('foo', $kernel->getProjectDir()); + $this->assertSame('foo', $kernel->getContainer()->getParameter('kernel.project_dir')); + } + + public function testKernelReset() + { + (new Filesystem())->remove(__DIR__.'/Fixtures/cache'); + + $kernel = new CustomProjectDirKernel(); + $kernel->boot(); + + $containerClass = \get_class($kernel->getContainer()); + $containerFile = (new \ReflectionClass($kernel->getContainer()))->getFileName(); + unlink(__DIR__.'/Fixtures/cache/custom/FixturesCustomDebugProjectContainer.php.meta'); + + $kernel = new CustomProjectDirKernel(); + $kernel->boot(); + + $this->assertInstanceOf($containerClass, $kernel->getContainer()); + $this->assertFileExists($containerFile); + unlink(__DIR__.'/Fixtures/cache/custom/FixturesCustomDebugProjectContainer.php.meta'); + + $kernel = new CustomProjectDirKernel(function ($container) { $container->register('foo', 'stdClass')->setPublic(true); }); + $kernel->boot(); + + $this->assertNotInstanceOf($containerClass, $kernel->getContainer()); + $this->assertFileExists($containerFile); + $this->assertFileExists(\dirname($containerFile).'.legacy'); + } + + public function testKernelPass() + { + $kernel = new PassKernel(); + $kernel->boot(); + + $this->assertTrue($kernel->getContainer()->getParameter('test.processed')); + } + + public function testServicesResetter() + { + $kernel = new CustomProjectDirKernel(function ($container) { + $container->addCompilerPass(new ResettableServicePass()); + $container->register('one', ResettableService::class) + ->setPublic(true) + ->addTag('kernel.reset', array('method' => 'reset')); + $container->register('services_resetter', ServicesResetter::class)->setPublic(true); + }, 'resetting'); + + ResettableService::$counter = 0; + + $kernel->boot(); + $kernel->executeScope(); + $kernel->getContainer()->get('one'); + + $this->assertEquals(0, ResettableService::$counter); + $this->assertFalse($kernel->getContainer()->initialized('services_resetter')); + + $kernel->boot(); + $kernel->executeScope(); + + $this->assertEquals(1, ResettableService::$counter); + } + + /** + * @group time-sensitive + */ + public function testKernelStartTimeIsResetWhileBootingAlreadyBootedKernel() + { + $kernel = $this->getKernelForTest(array('initializeBundles'), true); + $kernel->boot(); + $preReBoot = $kernel->getStartTime(); + + sleep(3600); //Intentionally large value to detect if ClockMock ever breaks + $kernel->reboot(null); + + $this->assertGreaterThan($preReBoot, $kernel->getStartTime()); + } + + /** + * Returns a mock for the BundleInterface. + * + * @return BundleInterface + */ + protected function getBundle($dir = null, $parent = null, $className = null, $bundleName = null) + { + $bundle = $this + ->getMockBuilder('Symfony\Component\Kernel\Bundle\BundleInterface') + ->setMethods(array('getPath', 'getParent', 'getName')) + ->disableOriginalConstructor() + ; + + if ($className) { + $bundle->setMockClassName($className); + } + + $bundle = $bundle->getMockForAbstractClass(); + + $bundle + ->expects($this->any()) + ->method('getName') + ->will($this->returnValue(null === $bundleName ? \get_class($bundle) : $bundleName)) + ; + + $bundle + ->expects($this->any()) + ->method('getPath') + ->will($this->returnValue($dir)) + ; + + $bundle + ->expects($this->any()) + ->method('getParent') + ->will($this->returnValue($parent)) + ; + + return $bundle; + } + + /** + * Returns a mock for the abstract kernel. + * + * @param array $methods Additional methods to mock (besides the abstract ones) + * @param array $bundles Bundles to register + * + * @return Kernel + */ + protected function getKernel(array $methods = array(), array $bundles = array()) + { + $methods[] = 'registerBundles'; + + $kernel = $this + ->getMockBuilder('Symfony\Component\Kernel\Kernel') + ->setMethods($methods) + ->setConstructorArgs(array('test', false)) + ->getMockForAbstractClass() + ; + $kernel->expects($this->any()) + ->method('registerBundles') + ->will($this->returnValue($bundles)) + ; + $p = new \ReflectionProperty($kernel, 'rootDir'); + $p->setAccessible(true); + $p->setValue($kernel, __DIR__.'/Fixtures'); + + return $kernel; + } + + protected function getKernelForTest(array $methods = array(), $debug = false) + { + $kernel = $this->getMockBuilder('Symfony\Component\Kernel\Tests\Fixtures\KernelForTest') + ->setConstructorArgs(array('test', $debug)) + ->setMethods($methods) + ->getMock(); + $p = new \ReflectionProperty($kernel, 'rootDir'); + $p->setAccessible(true); + $p->setValue($kernel, __DIR__.'/Fixtures'); + + return $kernel; + } +} + +class CustomProjectDirKernel extends Kernel +{ + private $baseDir; + private $buildContainer; + + public function __construct(\Closure $buildContainer = null, $name = 'custom') + { + parent::__construct($name, true); + + $this->baseDir = 'foo'; + $this->buildContainer = $buildContainer; + } + + public function registerBundles() + { + return array(); + } + + public function registerContainerConfiguration(LoaderInterface $loader) + { + } + + public function getProjectDir() + { + return $this->baseDir; + } + + public function getRootDir() + { + return __DIR__.'/Fixtures'; + } + + public function executeScope() + { + $this->enterScope(); + + $this->leaveScope(); + } + + protected function build(ContainerBuilder $container) + { + if ($build = $this->buildContainer) { + $build($container); + } + } +} + +class PassKernel extends CustomProjectDirKernel implements CompilerPassInterface +{ + public function __construct() + { + parent::__construct(); + Kernel::__construct('pass', true); + } + + public function process(ContainerBuilder $container) + { + $container->setParameter('test.processed', true); + } +} diff --git a/src/Symfony/Component/Kernel/Tests/Log/LoggerTest.php b/src/Symfony/Component/Kernel/Tests/Log/LoggerTest.php new file mode 100644 index 000000000000..fc5f35b6eea6 --- /dev/null +++ b/src/Symfony/Component/Kernel/Tests/Log/LoggerTest.php @@ -0,0 +1,212 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Kernel\Tests\Log; + +use PHPUnit\Framework\TestCase; +use Psr\Log\LoggerInterface; +use Psr\Log\LogLevel; +use Symfony\Component\Kernel\Log\Logger; + +/** + * @author Kévin Dunglas + * @author Jordi Boggiano + */ +class LoggerTest extends TestCase +{ + /** + * @var LoggerInterface + */ + private $logger; + + /** + * @var string + */ + private $tmpFile; + + protected function setUp() + { + $this->tmpFile = sys_get_temp_dir().\DIRECTORY_SEPARATOR.'log'; + $this->logger = new Logger(LogLevel::DEBUG, $this->tmpFile); + } + + protected function tearDown() + { + if (!@unlink($this->tmpFile)) { + file_put_contents($this->tmpFile, ''); + } + } + + public static function assertLogsMatch(array $expected, array $given) + { + foreach ($given as $k => $line) { + self::assertThat(1 === preg_match('/[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}[\+-][0-9]{2}:[0-9]{2} '.preg_quote($expected[$k]).'/', $line), self::isTrue(), "\"$line\" do not match expected pattern \"$expected[$k]\""); + } + } + + /** + * Return the log messages in order. + * + * @return string[] + */ + public function getLogs() + { + return file($this->tmpFile, FILE_IGNORE_NEW_LINES); + } + + public function testImplements() + { + $this->assertInstanceOf(LoggerInterface::class, $this->logger); + } + + /** + * @dataProvider provideLevelsAndMessages + */ + public function testLogsAtAllLevels($level, $message) + { + $this->logger->{$level}($message, array('user' => 'Bob')); + $this->logger->log($level, $message, array('user' => 'Bob')); + + $expected = array( + "[$level] message of level $level with context: Bob", + "[$level] message of level $level with context: Bob", + ); + $this->assertLogsMatch($expected, $this->getLogs()); + } + + public function provideLevelsAndMessages() + { + return array( + LogLevel::EMERGENCY => array(LogLevel::EMERGENCY, 'message of level emergency with context: {user}'), + LogLevel::ALERT => array(LogLevel::ALERT, 'message of level alert with context: {user}'), + LogLevel::CRITICAL => array(LogLevel::CRITICAL, 'message of level critical with context: {user}'), + LogLevel::ERROR => array(LogLevel::ERROR, 'message of level error with context: {user}'), + LogLevel::WARNING => array(LogLevel::WARNING, 'message of level warning with context: {user}'), + LogLevel::NOTICE => array(LogLevel::NOTICE, 'message of level notice with context: {user}'), + LogLevel::INFO => array(LogLevel::INFO, 'message of level info with context: {user}'), + LogLevel::DEBUG => array(LogLevel::DEBUG, 'message of level debug with context: {user}'), + ); + } + + public function testLogLevelDisabled() + { + $this->logger = new Logger(LogLevel::INFO, $this->tmpFile); + + $this->logger->debug('test', array('user' => 'Bob')); + $this->logger->log(LogLevel::DEBUG, 'test', array('user' => 'Bob')); + + // Will always be true, but asserts than an exception isn't thrown + $this->assertSame(array(), $this->getLogs()); + } + + /** + * @expectedException \Psr\Log\InvalidArgumentException + */ + public function testThrowsOnInvalidLevel() + { + $this->logger->log('invalid level', 'Foo'); + } + + /** + * @expectedException \Psr\Log\InvalidArgumentException + */ + public function testThrowsOnInvalidMinLevel() + { + new Logger('invalid'); + } + + /** + * @expectedException \Psr\Log\InvalidArgumentException + */ + public function testInvalidOutput() + { + new Logger(LogLevel::DEBUG, '/'); + } + + public function testContextReplacement() + { + $logger = $this->logger; + $logger->info('{Message {nothing} {user} {foo.bar} a}', array('user' => 'Bob', 'foo.bar' => 'Bar')); + + $expected = array('[info] {Message {nothing} Bob Bar a}'); + $this->assertLogsMatch($expected, $this->getLogs()); + } + + public function testObjectCastToString() + { + if (method_exists($this, 'createPartialMock')) { + $dummy = $this->createPartialMock(DummyTest::class, array('__toString')); + } else { + $dummy = $this->getMock(DummyTest::class, array('__toString')); + } + $dummy->expects($this->atLeastOnce()) + ->method('__toString') + ->will($this->returnValue('DUMMY')); + + $this->logger->warning($dummy); + + $expected = array('[warning] DUMMY'); + $this->assertLogsMatch($expected, $this->getLogs()); + } + + public function testContextCanContainAnything() + { + $context = array( + 'bool' => true, + 'null' => null, + 'string' => 'Foo', + 'int' => 0, + 'float' => 0.5, + 'nested' => array('with object' => new DummyTest()), + 'object' => new \DateTime(), + 'resource' => fopen('php://memory', 'r'), + ); + + $this->logger->warning('Crazy context data', $context); + + $expected = array('[warning] Crazy context data'); + $this->assertLogsMatch($expected, $this->getLogs()); + } + + public function testContextExceptionKeyCanBeExceptionOrOtherValues() + { + $logger = $this->logger; + $logger->warning('Random message', array('exception' => 'oops')); + $logger->critical('Uncaught Exception!', array('exception' => new \LogicException('Fail'))); + + $expected = array( + '[warning] Random message', + '[critical] Uncaught Exception!', + ); + $this->assertLogsMatch($expected, $this->getLogs()); + } + + public function testFormatter() + { + $this->logger = new Logger(LogLevel::DEBUG, $this->tmpFile, function ($level, $message, $context) { + return json_encode(array('level' => $level, 'message' => $message, 'context' => $context)).\PHP_EOL; + }); + + $this->logger->error('An error', array('foo' => 'bar')); + $this->logger->warning('A warning', array('baz' => 'bar')); + $this->assertSame(array( + '{"level":"error","message":"An error","context":{"foo":"bar"}}', + '{"level":"warning","message":"A warning","context":{"baz":"bar"}}', + ), $this->getLogs()); + } +} + +class DummyTest +{ + public function __toString() + { + } +} diff --git a/src/Symfony/Component/Kernel/composer.json b/src/Symfony/Component/Kernel/composer.json new file mode 100644 index 000000000000..a72ad44ecc74 --- /dev/null +++ b/src/Symfony/Component/Kernel/composer.json @@ -0,0 +1,60 @@ +{ + "name": "symfony/kernel", + "type": "library", + "description": "Symfony Kernel Component", + "homepage": "https://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "role": "Symfony Project Leader" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors", + "role": "Symfony Developer" + } + ], + "require": { + "php": "^7.1.3", + "symfony/config": "^4.1", + "symfony/dependency-injection": "^4.1", + "symfony/polyfill-ctype": "^1.8" + }, + "require-dev": { + "psr/cache": "^1.0", + "psr/log": "^1.0", + "symfony/config": "self.version", + "symfony/console": "self.version", + "symfony/dependency-injection": "self.version", + "symfony/event-dispatcher": "self.version", + "symfony/filesystem": "self.version", + "symfony/phpunit-bridge": "self.version", + "symfony/var-dumper": "self.version" + }, + "conflict": { + "symfony/config": "<3.4", + "symfony/dependency-injection": "<4.1" + }, + "suggest": { + "symfony/console": "", + "symfony/http-kernel": "", + "symfony/var-dumper": "", + "symfony/yaml": "" + }, + "autoload": { + "psr-4": { "Symfony\\Component\\Kernel\\": "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "config": { + "sort-packages": true + }, + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + } +} diff --git a/src/Symfony/Component/Kernel/phpunit.xml.dist b/src/Symfony/Component/Kernel/phpunit.xml.dist new file mode 100644 index 000000000000..68a952797556 --- /dev/null +++ b/src/Symfony/Component/Kernel/phpunit.xml.dist @@ -0,0 +1,34 @@ + + + + + + + + + + ./Tests/ + + + + + + ./ + + ./Tests + ./vendor + + + + + + + + From b5ec0d69a5148892933b6a2791b045f9fc2e299a Mon Sep 17 00:00:00 2001 From: Alexandre Quercia Date: Tue, 20 Nov 2018 16:49:16 +0100 Subject: [PATCH 2/4] Fix dev dependencies version --- src/Symfony/Component/Kernel/composer.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Symfony/Component/Kernel/composer.json b/src/Symfony/Component/Kernel/composer.json index a72ad44ecc74..4edb3c735b09 100644 --- a/src/Symfony/Component/Kernel/composer.json +++ b/src/Symfony/Component/Kernel/composer.json @@ -25,13 +25,13 @@ "require-dev": { "psr/cache": "^1.0", "psr/log": "^1.0", - "symfony/config": "self.version", - "symfony/console": "self.version", - "symfony/dependency-injection": "self.version", - "symfony/event-dispatcher": "self.version", - "symfony/filesystem": "self.version", - "symfony/phpunit-bridge": "self.version", - "symfony/var-dumper": "self.version" + "symfony/config": "~3.4|~4.0", + "symfony/console": "~3.4|~4.0", + "symfony/dependency-injection": "^4.1", + "symfony/event-dispatcher": "^4.1", + "symfony/filesystem": "^4.1", + "symfony/phpunit-bridge": "^4.1", + "symfony/var-dumper": "^4.1.1" }, "conflict": { "symfony/config": "<3.4", From 24ef48d326dc76e7023f61e89a98b0935b220cd6 Mon Sep 17 00:00:00 2001 From: Alexandre Quercia Date: Tue, 20 Nov 2018 17:12:19 +0100 Subject: [PATCH 3/4] Fix BC layer --- composer.json | 3 +++ src/Symfony/Component/HttpKernel/Kernel.php | 2 +- src/Symfony/Component/HttpKernel/Resources/bootstrap.php | 4 +++- .../HttpKernel/Tests/DependencyInjection/LoggerPassTest.php | 2 +- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index f861cbca3134..b720ca4f9ee1 100644 --- a/composer.json +++ b/composer.json @@ -123,6 +123,9 @@ "classmap": [ "src/Symfony/Component/Intl/Resources/stubs" ], + "files": [ + "src/Symfony/Component/HttpKernel/Resources/bootstrap.php" + ], "exclude-from-classmap": [ "**/Tests/" ] diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index 7007a43838fa..f02236a78b48 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -22,7 +22,7 @@ * * @author Fabien Potencier */ -abstract class Kernel extends BaseKernel implements TerminableInterface +abstract class Kernel extends BaseKernel implements HttpKernelInterface, TerminableInterface { /** * {@inheritdoc} diff --git a/src/Symfony/Component/HttpKernel/Resources/bootstrap.php b/src/Symfony/Component/HttpKernel/Resources/bootstrap.php index 3f20243a97ac..627bcfd2eef4 100644 --- a/src/Symfony/Component/HttpKernel/Resources/bootstrap.php +++ b/src/Symfony/Component/HttpKernel/Resources/bootstrap.php @@ -15,7 +15,9 @@ class_alias(Kernel\CacheClearer\CacheClearerInterface::class, HttpKernel\CacheCl class_alias(Kernel\CacheClearer\ChainCacheClearer::class, HttpKernel\CacheClearer\ChainCacheClearer::class); class_alias(Kernel\CacheClearer\Psr6CacheClearer::class, HttpKernel\CacheClearer\Psr6CacheClearer::class); -class_alias(Kernel\CacheWarmer\WarmableInterface::class, HttpKernel\CacheWarmer\WarmableInterface::class); +class_alias(Kernel\Config\FileLocator::class, HttpKernel\Config\FileLocator::class); + +class_alias(Kernel\CacheWarmer\CacheWarmerInterface::class, HttpKernel\CacheWarmer\CacheWarmerInterface::class); class_alias(Kernel\CacheWarmer\CacheWarmer::class, HttpKernel\CacheWarmer\CacheWarmer::class); class_alias(Kernel\CacheWarmer\CacheWarmerAggregate::class, HttpKernel\CacheWarmer\CacheWarmerAggregate::class); diff --git a/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/LoggerPassTest.php b/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/LoggerPassTest.php index cb504877cdc4..4bd90cedc3d5 100644 --- a/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/LoggerPassTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/DependencyInjection/LoggerPassTest.php @@ -15,7 +15,7 @@ use Psr\Log\LoggerInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpKernel\DependencyInjection\LoggerPass; -use Symfony\Component\HttpKernel\Log\Logger; +use Symfony\Component\Kernel\Log\Logger; /** * @author Kévin Dunglas From a80d18ce6d56be843485cbb7236c4b489703df33 Mon Sep 17 00:00:00 2001 From: Alexandre Quercia Date: Thu, 31 Jan 2019 11:39:25 +0100 Subject: [PATCH 4/4] Add HttpKernel/KernelInterface --- src/Symfony/Component/HttpKernel/Kernel.php | 6 ++--- .../Component/HttpKernel/KernelInterface.php | 25 +++++++++++++++++++ .../HttpKernel/Resources/bootstrap.php | 1 - 3 files changed, 28 insertions(+), 4 deletions(-) create mode 100644 src/Symfony/Component/HttpKernel/KernelInterface.php diff --git a/src/Symfony/Component/HttpKernel/Kernel.php b/src/Symfony/Component/HttpKernel/Kernel.php index f02236a78b48..875a61f1a073 100644 --- a/src/Symfony/Component/HttpKernel/Kernel.php +++ b/src/Symfony/Component/HttpKernel/Kernel.php @@ -16,13 +16,13 @@ use Symfony\Component\Kernel\Kernel as BaseKernel; /** - * The Kernel is the heart of the Symfony system. + * The HTTP Kernel is the heart of the Symfony system to handle HTTP request. * - * It manages an environment made of bundles. + * It manages an environment made of application kernel and bundles. * * @author Fabien Potencier */ -abstract class Kernel extends BaseKernel implements HttpKernelInterface, TerminableInterface +abstract class Kernel extends BaseKernel implements KernelInterface, TerminableInterface { /** * {@inheritdoc} diff --git a/src/Symfony/Component/HttpKernel/KernelInterface.php b/src/Symfony/Component/HttpKernel/KernelInterface.php new file mode 100644 index 000000000000..79b8ed7fc9aa --- /dev/null +++ b/src/Symfony/Component/HttpKernel/KernelInterface.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel; + +use Symfony\Component\Kernel\KernelInterface as BaseKernelInterface; + +/** + * The HTTP Kernel is the heart of the Symfony system to handle HTTP request. + * + * It manages an environment made of application kernel and bundles. + * + * @author Fabien Potencier + */ +interface KernelInterface extends BaseKernelInterface, HttpKernelInterface +{ +} diff --git a/src/Symfony/Component/HttpKernel/Resources/bootstrap.php b/src/Symfony/Component/HttpKernel/Resources/bootstrap.php index 627bcfd2eef4..d6eeca22df5e 100644 --- a/src/Symfony/Component/HttpKernel/Resources/bootstrap.php +++ b/src/Symfony/Component/HttpKernel/Resources/bootstrap.php @@ -5,7 +5,6 @@ // Naïve implementation of the BC layer -class_alias(Kernel\KernelInterface::class, HttpKernel\KernelInterface::class); class_alias(Kernel\RebootableInterface::class, HttpKernel\RebootableInterface::class); class_alias(Kernel\Bundle\BundleInterface::class, HttpKernel\Bundle\BundleInterface::class);