]); $hierarchy[] = $name; } foreach ($hierarchy as $hierarchyBundle) { $this->bundleMap[$hierarchyBundle] = $bundleMap; array_pop($bundleMap); } } } /** * 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 ($collectDeprecations = $this->debug && !\defined('PHPUNIT_COMPOSER_INSTALL')) { $collectedLogs = []; $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 null; } $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] = [ 'type' => $type, 'message' => $message, 'file' => $file, 'line' => $line, 'trace' => $backtrace, 'count' => 1, ]; return null; }); } try { $container = null; $container = $this->buildContainer(); $container->compile(); } finally { if ($collectDeprecations) { 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 = []; $oldContainerDir = \dirname($oldContainer->getFileName()); $legacyContainers[$oldContainerDir.'.legacy'] = true; foreach (glob(\dirname($oldContainerDir).\DIRECTORY_SEPARATOR.'*.legacy', \GLOB_NOSORT) 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 = []; $bundlesMetadata = []; foreach ($this->bundles as $name => $bundle) { $bundles[$name] = \get_class($bundle); $bundlesMetadata[$name] = [ 'parent' => $bundle->getParent(), 'path' => $bundle->getPath(), 'namespace' => $bundle->getNamespace(), ]; } return array_merge( [ '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(), ], $this->getEnvParameters(false) ); } /** * Gets the environment parameters. * * Only the parameters starting with "SYMFONY__" are considered. * * @return array An array of parameters * * @deprecated since version 3.3, to be removed in 4.0 */ protected function getEnvParameters() { if (0 === \func_num_args() || func_get_arg(0)) { @trigger_error(sprintf('The "%s()" method is deprecated as of 3.3 and will be removed in 4.0. Use the %%env()%% syntax to get the value of any environment variable from configuration files instead.', __METHOD__), \E_USER_DEPRECATED); } $parameters = []; foreach ($_SERVER as $key => $value) { if (0 === strpos($key, 'SYMFONY__')) { @trigger_error(sprintf('The support of special environment variables that start with SYMFONY__ (such as "%s") is deprecated as of 3.3 and will be removed in 4.0. Use the %%env()%% syntax instead to get the value of environment variables in configuration files.', $key), \E_USER_DEPRECATED); $parameters[strtolower(str_replace('__', '.', substr($key, 9)))] = $value; } } return $parameters; } /** * Builds the service container. * * @return ContainerBuilder The compiled service container * * @throws \RuntimeException */ protected function buildContainer() { foreach (['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).', $name, $dir)); } } elseif (!is_writable($dir)) { throw new \RuntimeException(sprintf('Unable to write in the "%s" directory (%s).', $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)); $container->addResource(new EnvParametersResource('SYMFONY__')); return $container; } /** * Prepares the ContainerBuilder before it is compiled. */ protected function prepareContainer(ContainerBuilder $container) { $extensions = []; 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([ 'class' => $class, 'base_class' => $baseClass, 'file' => $cache->getPath(), 'as_files' => true, 'debug' => $this->debug, 'inline_class_loader_parameter' => \PHP_VERSION_ID >= 70000 && !$this->loadClassCache && !class_exists(ClassCollectionLoader::class, false) ? 'container.dumper.inline_class_loader' : null, '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()); } $legacyFile = \dirname($dir.key($content)).'.legacy'; if (file_exists($legacyFile)) { @unlink($legacyFile); } $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([ 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(['/\n{2,}/S'], "\n", $token[1]); } elseif (\in_array($token[0], [\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; if (\PHP_VERSION_ID >= 70000) { // 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([$this->environment, $this->debug]); } public function unserialize($data) { if (\PHP_VERSION_ID >= 70000) { list($environment, $debug) = unserialize($data, ['allowed_classes' => false]); } else { list($environment, $debug) = unserialize($data); } $this->__construct($environment, $debug); } }