diff options
Diffstat (limited to 'srcs/phpmyadmin/vendor/symfony/dependency-injection/Dumper')
7 files changed, 3192 insertions, 0 deletions
diff --git a/srcs/phpmyadmin/vendor/symfony/dependency-injection/Dumper/Dumper.php b/srcs/phpmyadmin/vendor/symfony/dependency-injection/Dumper/Dumper.php new file mode 100644 index 0000000..e7407b0 --- /dev/null +++ b/srcs/phpmyadmin/vendor/symfony/dependency-injection/Dumper/Dumper.php @@ -0,0 +1,29 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Dumper; + +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * Dumper is the abstract class for all built-in dumpers. + * + * @author Fabien Potencier <fabien@symfony.com> + */ +abstract class Dumper implements DumperInterface +{ + protected $container; + + public function __construct(ContainerBuilder $container) + { + $this->container = $container; + } +} diff --git a/srcs/phpmyadmin/vendor/symfony/dependency-injection/Dumper/DumperInterface.php b/srcs/phpmyadmin/vendor/symfony/dependency-injection/Dumper/DumperInterface.php new file mode 100644 index 0000000..8abc192 --- /dev/null +++ b/srcs/phpmyadmin/vendor/symfony/dependency-injection/Dumper/DumperInterface.php @@ -0,0 +1,27 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Dumper; + +/** + * DumperInterface is the interface implemented by service container dumper classes. + * + * @author Fabien Potencier <fabien@symfony.com> + */ +interface DumperInterface +{ + /** + * Dumps the service container. + * + * @return string|array The representation of the service container + */ + public function dump(array $options = []); +} diff --git a/srcs/phpmyadmin/vendor/symfony/dependency-injection/Dumper/GraphvizDumper.php b/srcs/phpmyadmin/vendor/symfony/dependency-injection/Dumper/GraphvizDumper.php new file mode 100644 index 0000000..c21dd91 --- /dev/null +++ b/srcs/phpmyadmin/vendor/symfony/dependency-injection/Dumper/GraphvizDumper.php @@ -0,0 +1,251 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Dumper; + +use Symfony\Component\DependencyInjection\Argument\ArgumentInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException; +use Symfony\Component\DependencyInjection\Parameter; +use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag; +use Symfony\Component\DependencyInjection\Reference; + +/** + * GraphvizDumper dumps a service container as a graphviz file. + * + * You can convert the generated dot file with the dot utility (http://www.graphviz.org/): + * + * dot -Tpng container.dot > foo.png + * + * @author Fabien Potencier <fabien@symfony.com> + */ +class GraphvizDumper extends Dumper +{ + private $nodes; + private $edges; + private $options = [ + 'graph' => ['ratio' => 'compress'], + 'node' => ['fontsize' => 11, 'fontname' => 'Arial', 'shape' => 'record'], + 'edge' => ['fontsize' => 9, 'fontname' => 'Arial', 'color' => 'grey', 'arrowhead' => 'open', 'arrowsize' => 0.5], + 'node.instance' => ['fillcolor' => '#9999ff', 'style' => 'filled'], + 'node.definition' => ['fillcolor' => '#eeeeee'], + 'node.missing' => ['fillcolor' => '#ff9999', 'style' => 'filled'], + ]; + + /** + * Dumps the service container as a graphviz graph. + * + * Available options: + * + * * graph: The default options for the whole graph + * * node: The default options for nodes + * * edge: The default options for edges + * * node.instance: The default options for services that are defined directly by object instances + * * node.definition: The default options for services that are defined via service definition instances + * * node.missing: The default options for missing services + * + * @return string The dot representation of the service container + */ + public function dump(array $options = []) + { + foreach (['graph', 'node', 'edge', 'node.instance', 'node.definition', 'node.missing'] as $key) { + if (isset($options[$key])) { + $this->options[$key] = array_merge($this->options[$key], $options[$key]); + } + } + + $this->nodes = $this->findNodes(); + + $this->edges = []; + foreach ($this->container->getDefinitions() as $id => $definition) { + $this->edges[$id] = array_merge( + $this->findEdges($id, $definition->getArguments(), true, ''), + $this->findEdges($id, $definition->getProperties(), false, '') + ); + + foreach ($definition->getMethodCalls() as $call) { + $this->edges[$id] = array_merge( + $this->edges[$id], + $this->findEdges($id, $call[1], false, $call[0].'()') + ); + } + } + + return $this->container->resolveEnvPlaceholders($this->startDot().$this->addNodes().$this->addEdges().$this->endDot(), '__ENV_%s__'); + } + + private function addNodes(): string + { + $code = ''; + foreach ($this->nodes as $id => $node) { + $aliases = $this->getAliases($id); + + $code .= sprintf(" node_%s [label=\"%s\\n%s\\n\", shape=%s%s];\n", $this->dotize($id), $id.($aliases ? ' ('.implode(', ', $aliases).')' : ''), $node['class'], $this->options['node']['shape'], $this->addAttributes($node['attributes'])); + } + + return $code; + } + + private function addEdges(): string + { + $code = ''; + foreach ($this->edges as $id => $edges) { + foreach ($edges as $edge) { + $code .= sprintf(" node_%s -> node_%s [label=\"%s\" style=\"%s\"%s];\n", $this->dotize($id), $this->dotize($edge['to']), $edge['name'], $edge['required'] ? 'filled' : 'dashed', $edge['lazy'] ? ' color="#9999ff"' : ''); + } + } + + return $code; + } + + /** + * Finds all edges belonging to a specific service id. + */ + private function findEdges(string $id, array $arguments, bool $required, string $name, bool $lazy = false): array + { + $edges = []; + foreach ($arguments as $argument) { + if ($argument instanceof Parameter) { + $argument = $this->container->hasParameter($argument) ? $this->container->getParameter($argument) : null; + } elseif (\is_string($argument) && preg_match('/^%([^%]+)%$/', $argument, $match)) { + $argument = $this->container->hasParameter($match[1]) ? $this->container->getParameter($match[1]) : null; + } + + if ($argument instanceof Reference) { + $lazyEdge = $lazy; + + if (!$this->container->has((string) $argument)) { + $this->nodes[(string) $argument] = ['name' => $name, 'required' => $required, 'class' => '', 'attributes' => $this->options['node.missing']]; + } elseif ('service_container' !== (string) $argument) { + $lazyEdge = $lazy || $this->container->getDefinition((string) $argument)->isLazy(); + } + + $edges[] = ['name' => $name, 'required' => $required, 'to' => $argument, 'lazy' => $lazyEdge]; + } elseif ($argument instanceof ArgumentInterface) { + $edges = array_merge($edges, $this->findEdges($id, $argument->getValues(), $required, $name, true)); + } elseif ($argument instanceof Definition) { + $edges = array_merge($edges, + $this->findEdges($id, $argument->getArguments(), $required, ''), + $this->findEdges($id, $argument->getProperties(), false, '') + ); + foreach ($argument->getMethodCalls() as $call) { + $edges = array_merge($edges, $this->findEdges($id, $call[1], false, $call[0].'()')); + } + } elseif (\is_array($argument)) { + $edges = array_merge($edges, $this->findEdges($id, $argument, $required, $name, $lazy)); + } + } + + return $edges; + } + + private function findNodes(): array + { + $nodes = []; + + $container = $this->cloneContainer(); + + foreach ($container->getDefinitions() as $id => $definition) { + $class = $definition->getClass(); + + if ('\\' === substr($class, 0, 1)) { + $class = substr($class, 1); + } + + try { + $class = $this->container->getParameterBag()->resolveValue($class); + } catch (ParameterNotFoundException $e) { + } + + $nodes[$id] = ['class' => str_replace('\\', '\\\\', $class), 'attributes' => array_merge($this->options['node.definition'], ['style' => $definition->isShared() ? 'filled' : 'dotted'])]; + $container->setDefinition($id, new Definition('stdClass')); + } + + foreach ($container->getServiceIds() as $id) { + if (\array_key_exists($id, $container->getAliases())) { + continue; + } + + if (!$container->hasDefinition($id)) { + $nodes[$id] = ['class' => str_replace('\\', '\\\\', \get_class($container->get($id))), 'attributes' => $this->options['node.instance']]; + } + } + + return $nodes; + } + + private function cloneContainer(): ContainerBuilder + { + $parameterBag = new ParameterBag($this->container->getParameterBag()->all()); + + $container = new ContainerBuilder($parameterBag); + $container->setDefinitions($this->container->getDefinitions()); + $container->setAliases($this->container->getAliases()); + $container->setResources($this->container->getResources()); + foreach ($this->container->getExtensions() as $extension) { + $container->registerExtension($extension); + } + + return $container; + } + + private function startDot(): string + { + return sprintf("digraph sc {\n %s\n node [%s];\n edge [%s];\n\n", + $this->addOptions($this->options['graph']), + $this->addOptions($this->options['node']), + $this->addOptions($this->options['edge']) + ); + } + + private function endDot(): string + { + return "}\n"; + } + + private function addAttributes(array $attributes): string + { + $code = []; + foreach ($attributes as $k => $v) { + $code[] = sprintf('%s="%s"', $k, $v); + } + + return $code ? ', '.implode(', ', $code) : ''; + } + + private function addOptions(array $options): string + { + $code = []; + foreach ($options as $k => $v) { + $code[] = sprintf('%s="%s"', $k, $v); + } + + return implode(' ', $code); + } + + private function dotize(string $id): string + { + return preg_replace('/\W/i', '_', $id); + } + + private function getAliases(string $id): array + { + $aliases = []; + foreach ($this->container->getAliases() as $alias => $origin) { + if ($id == $origin) { + $aliases[] = $alias; + } + } + + return $aliases; + } +} diff --git a/srcs/phpmyadmin/vendor/symfony/dependency-injection/Dumper/PhpDumper.php b/srcs/phpmyadmin/vendor/symfony/dependency-injection/Dumper/PhpDumper.php new file mode 100644 index 0000000..15d5c09 --- /dev/null +++ b/srcs/phpmyadmin/vendor/symfony/dependency-injection/Dumper/PhpDumper.php @@ -0,0 +1,2062 @@ +<?php + +/* + * This file is part of the Symfony package. + * + * (c) Fabien Potencier <fabien@symfony.com> + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\DependencyInjection\Dumper; + +use Composer\Autoload\ClassLoader; +use Symfony\Component\Debug\DebugClassLoader as LegacyDebugClassLoader; +use Symfony\Component\DependencyInjection\Argument\ArgumentInterface; +use Symfony\Component\DependencyInjection\Argument\IteratorArgument; +use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; +use Symfony\Component\DependencyInjection\Argument\ServiceLocator; +use Symfony\Component\DependencyInjection\Argument\ServiceLocatorArgument; +use Symfony\Component\DependencyInjection\Compiler\AnalyzeServiceReferencesPass; +use Symfony\Component\DependencyInjection\Compiler\CheckCircularReferencesPass; +use Symfony\Component\DependencyInjection\Compiler\ServiceReferenceGraphNode; +use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Exception\EnvParameterException; +use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; +use Symfony\Component\DependencyInjection\Exception\LogicException; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; +use Symfony\Component\DependencyInjection\Exception\ServiceCircularReferenceException; +use Symfony\Component\DependencyInjection\ExpressionLanguage; +use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\DumperInterface as ProxyDumper; +use Symfony\Component\DependencyInjection\LazyProxy\PhpDumper\NullDumper; +use Symfony\Component\DependencyInjection\Loader\FileLoader; +use Symfony\Component\DependencyInjection\Parameter; +use Symfony\Component\DependencyInjection\Reference; +use Symfony\Component\DependencyInjection\ServiceLocator as BaseServiceLocator; +use Symfony\Component\DependencyInjection\TypedReference; +use Symfony\Component\DependencyInjection\Variable; +use Symfony\Component\ErrorHandler\DebugClassLoader; +use Symfony\Component\ExpressionLanguage\Expression; +use Symfony\Component\HttpKernel\Kernel; + +/** + * PhpDumper dumps a service container as a PHP class. + * + * @author Fabien Potencier <fabien@symfony.com> + * @author Johannes M. Schmitt <schmittjoh@gmail.com> + */ +class PhpDumper extends Dumper +{ + /** + * Characters that might appear in the generated variable name as first character. + */ + const FIRST_CHARS = 'abcdefghijklmnopqrstuvwxyz'; + + /** + * Characters that might appear in the generated variable name as any but the first character. + */ + const NON_FIRST_CHARS = 'abcdefghijklmnopqrstuvwxyz0123456789_'; + + private $definitionVariables; + private $referenceVariables; + private $variableCount; + private $inlinedDefinitions; + private $serviceCalls; + private $reservedVariables = ['instance', 'class', 'this']; + private $expressionLanguage; + private $targetDirRegex; + private $targetDirMaxMatches; + private $docStar; + private $serviceIdToMethodNameMap; + private $usedMethodNames; + private $namespace; + private $asFiles; + private $hotPathTag; + private $inlineFactories; + private $inlineRequires; + private $inlinedRequires = []; + private $circularReferences = []; + private $singleUsePrivateIds = []; + private $addThrow = false; + private $addGetService = false; + private $locatedIds = []; + private $serviceLocatorTag; + private $exportedVariables = []; + private $baseClass; + + /** + * @var ProxyDumper + */ + private $proxyDumper; + + /** + * {@inheritdoc} + */ + public function __construct(ContainerBuilder $container) + { + if (!$container->isCompiled()) { + throw new LogicException('Cannot dump an uncompiled container.'); + } + + parent::__construct($container); + } + + /** + * Sets the dumper to be used when dumping proxies in the generated container. + */ + public function setProxyDumper(ProxyDumper $proxyDumper) + { + $this->proxyDumper = $proxyDumper; + } + + /** + * Dumps the service container as a PHP class. + * + * Available options: + * + * * class: The class name + * * base_class: The base class name + * * namespace: The class namespace + * * as_files: To split the container in several files + * + * @return string|array A PHP class representing the service container or an array of PHP files if the "as_files" option is set + * + * @throws EnvParameterException When an env var exists but has not been dumped + */ + public function dump(array $options = []) + { + $this->locatedIds = []; + $this->targetDirRegex = null; + $this->inlinedRequires = []; + $this->exportedVariables = []; + $options = array_merge([ + 'class' => 'ProjectServiceContainer', + 'base_class' => 'Container', + 'namespace' => '', + 'as_files' => false, + 'debug' => true, + 'hot_path_tag' => 'container.hot_path', + 'inline_factories_parameter' => 'container.dumper.inline_factories', + 'inline_class_loader_parameter' => 'container.dumper.inline_class_loader', + 'service_locator_tag' => 'container.service_locator', + 'build_time' => time(), + ], $options); + + $this->addThrow = $this->addGetService = false; + $this->namespace = $options['namespace']; + $this->asFiles = $options['as_files']; + $this->hotPathTag = $options['hot_path_tag']; + $this->inlineFactories = $this->asFiles && $options['inline_factories_parameter'] && $this->container->hasParameter($options['inline_factories_parameter']) && $this->container->getParameter($options['inline_factories_parameter']); + $this->inlineRequires = $options['inline_class_loader_parameter'] && $this->container->hasParameter($options['inline_class_loader_parameter']) && $this->container->getParameter($options['inline_class_loader_parameter']); + $this->serviceLocatorTag = $options['service_locator_tag']; + + if (0 !== strpos($baseClass = $options['base_class'], '\\') && 'Container' !== $baseClass) { + $baseClass = sprintf('%s\%s', $options['namespace'] ? '\\'.$options['namespace'] : '', $baseClass); + $this->baseClass = $baseClass; + } elseif ('Container' === $baseClass) { + $this->baseClass = Container::class; + } else { + $this->baseClass = $baseClass; + } + + $this->initializeMethodNamesMap('Container' === $baseClass ? Container::class : $baseClass); + + if ($this->getProxyDumper() instanceof NullDumper) { + (new AnalyzeServiceReferencesPass(true, false))->process($this->container); + try { + (new CheckCircularReferencesPass())->process($this->container); + } catch (ServiceCircularReferenceException $e) { + $path = $e->getPath(); + end($path); + $path[key($path)] .= '". Try running "composer require symfony/proxy-manager-bridge'; + + throw new ServiceCircularReferenceException($e->getServiceId(), $path); + } + } + + (new AnalyzeServiceReferencesPass(false, !$this->getProxyDumper() instanceof NullDumper))->process($this->container); + $checkedNodes = []; + $this->circularReferences = []; + $this->singleUsePrivateIds = []; + foreach ($this->container->getCompiler()->getServiceReferenceGraph()->getNodes() as $id => $node) { + if (!$node->getValue() instanceof Definition) { + continue; + } + if (!isset($checkedNodes[$id])) { + $this->analyzeCircularReferences($id, $node->getOutEdges(), $checkedNodes); + } + if ($this->isSingleUsePrivateNode($node)) { + $this->singleUsePrivateIds[$id] = $id; + } + } + $this->container->getCompiler()->getServiceReferenceGraph()->clear(); + $checkedNodes = []; + $this->singleUsePrivateIds = array_diff_key($this->singleUsePrivateIds, $this->circularReferences); + + $this->docStar = $options['debug'] ? '*' : ''; + + if (!empty($options['file']) && is_dir($dir = \dirname($options['file']))) { + // Build a regexp where the first root dirs are mandatory, + // but every other sub-dir is optional up to the full path in $dir + // Mandate at least 1 root dir and not more than 5 optional dirs. + + $dir = explode(\DIRECTORY_SEPARATOR, realpath($dir)); + $i = \count($dir); + + if (2 + (int) ('\\' === \DIRECTORY_SEPARATOR) <= $i) { + $regex = ''; + $lastOptionalDir = $i > 8 ? $i - 5 : (2 + (int) ('\\' === \DIRECTORY_SEPARATOR)); + $this->targetDirMaxMatches = $i - $lastOptionalDir; + + while (--$i >= $lastOptionalDir) { + $regex = sprintf('(%s%s)?', preg_quote(\DIRECTORY_SEPARATOR.$dir[$i], '#'), $regex); + } + + do { + $regex = preg_quote(\DIRECTORY_SEPARATOR.$dir[$i], '#').$regex; + } while (0 < --$i); + + $this->targetDirRegex = '#'.preg_quote($dir[0], '#').$regex.'#'; + } + } + + $proxyClasses = $this->inlineFactories ? $this->generateProxyClasses() : null; + + $code = + $this->startClass($options['class'], $baseClass, $preload). + $this->addServices($services). + $this->addDeprecatedAliases(). + $this->addDefaultParametersMethod() + ; + + $proxyClasses = $proxyClasses ?? $this->generateProxyClasses(); + + if ($this->addGetService) { + $code = preg_replace( + "/(\r?\n\r?\n public function __construct.+?\\{\r?\n)/s", + "\n private \$getService;$1 \$this->getService = \\Closure::fromCallable([\$this, 'getService']);\n", + $code, + 1 + ); + } + + if ($this->asFiles) { + $fileStart = <<<EOF +<?php + +use Symfony\Component\DependencyInjection\Argument\RewindableGenerator; +use Symfony\Component\DependencyInjection\Exception\RuntimeException; + +// This file has been auto-generated by the Symfony Dependency Injection Component for internal use. + +EOF; + $files = []; + + $ids = $this->container->getRemovedIds(); + foreach ($this->container->getDefinitions() as $id => $definition) { + if (!$definition->isPublic()) { + $ids[$id] = true; + } + } + if ($ids = array_keys($ids)) { + sort($ids); + $c = "<?php\n\nreturn [\n"; + foreach ($ids as $id) { + $c .= ' '.$this->doExport($id)." => true,\n"; + } + $files['removed-ids.php'] = $c."];\n"; + } + + if (!$this->inlineFactories) { + foreach ($this->generateServiceFiles($services) as $file => $c) { + $files[$file] = $fileStart.$c; + } + foreach ($proxyClasses as $file => $c) { + $files[$file] = "<?php\n".$c; + } + } + + $code .= $this->endClass(); + + if ($this->inlineFactories) { + foreach ($proxyClasses as $c) { + $code .= $c; + } + } + + $files[$options['class'].'.php'] = $code; + $hash = ucfirst(strtr(ContainerBuilder::hash($files), '._', 'xx')); + $code = []; + + foreach ($files as $file => $c) { + $code["Container{$hash}/{$file}"] = $c; + } + array_pop($code); + $code["Container{$hash}/{$options['class']}.php"] = substr_replace($files[$options['class'].'.php'], "<?php\n\nnamespace Container{$hash};\n", 0, 6); + $namespaceLine = $this->namespace ? "\nnamespace {$this->namespace};\n" : ''; + $time = $options['build_time']; + $id = hash('crc32', $hash.$time); + $this->asFiles = false; + + if ($preload && null !== $autoloadFile = $this->getAutoloadFile()) { + $autoloadFile = substr($this->export($autoloadFile), 2, -1); + + $code[$options['class'].'.preload.php'] = <<<EOF +<?php + +// This file has been auto-generated by the Symfony Dependency Injection Component +// You can reference it in the "opcache.preload" php.ini setting on PHP >= 7.4 when preloading is desired + +use Symfony\Component\DependencyInjection\Dumper\Preloader; + +require $autoloadFile; +require __DIR__.'/Container{$hash}/{$options['class']}.php'; + +\$classes = []; + +EOF; + + foreach ($preload as $class) { + $code[$options['class'].'.preload.php'] .= sprintf("\$classes[] = '%s';\n", $class); + } + + $code[$options['class'].'.preload.php'] .= <<<'EOF' + +Preloader::preload($classes); + +EOF; + } + + $code[$options['class'].'.php'] = <<<EOF +<?php +{$namespaceLine} +// This file has been auto-generated by the Symfony Dependency Injection Component for internal use. + +if (\\class_exists(\\Container{$hash}\\{$options['class']}::class, false)) { + // no-op +} elseif (!include __DIR__.'/Container{$hash}/{$options['class']}.php') { + touch(__DIR__.'/Container{$hash}.legacy'); + + return; +} + +if (!\\class_exists({$options['class']}::class, false)) { + \\class_alias(\\Container{$hash}\\{$options['class']}::class, {$options['class']}::class, false); +} + +return new \\Container{$hash}\\{$options['class']}([ + 'container.build_hash' => '$hash', + 'container.build_id' => '$id', + 'container.build_time' => $time, +], __DIR__.\\DIRECTORY_SEPARATOR.'Container{$hash}'); + +EOF; + } else { + $code .= $this->endClass(); + foreach ($proxyClasses as $c) { + $code .= $c; + } + } + + $this->targetDirRegex = null; + $this->inlinedRequires = []; + $this->circularReferences = []; + $this->locatedIds = []; + $this->exportedVariables = []; + + $unusedEnvs = []; + foreach ($this->container->getEnvCounters() as $env => $use) { + if (!$use) { + $unusedEnvs[] = $env; + } + } + if ($unusedEnvs) { + throw new EnvParameterException($unusedEnvs, null, 'Environment variables "%s" are never used. Please, check your container\'s configuration.'); + } + + return $code; + } + + /** + * Retrieves the currently set proxy dumper or instantiates one. + */ + private function getProxyDumper(): ProxyDumper + { + if (!$this->proxyDumper) { + $this->proxyDumper = new NullDumper(); + } + + return $this->proxyDumper; + } + + private function analyzeCircularReferences(string $sourceId, array $edges, array &$checkedNodes, array &$currentPath = [], bool $byConstructor = true) + { + $checkedNodes[$sourceId] = true; + $currentPath[$sourceId] = $byConstructor; + + foreach ($edges as $edge) { + $node = $edge->getDestNode(); + $id = $node->getId(); + + if (!$node->getValue() instanceof Definition || $sourceId === $id || $edge->isLazy() || $edge->isWeak()) { + // no-op + } elseif (isset($currentPath[$id])) { + $this->addCircularReferences($id, $currentPath, $edge->isReferencedByConstructor()); + } elseif (!isset($checkedNodes[$id])) { + $this->analyzeCircularReferences($id, $node->getOutEdges(), $checkedNodes, $currentPath, $edge->isReferencedByConstructor()); + } elseif (isset($this->circularReferences[$id])) { + $this->connectCircularReferences($id, $currentPath, $edge->isReferencedByConstructor()); + } + } + unset($currentPath[$sourceId]); + } + + private function connectCircularReferences(string $sourceId, array &$currentPath, bool $byConstructor, array &$subPath = []) + { + $currentPath[$sourceId] = $subPath[$sourceId] = $byConstructor; + + foreach ($this->circularReferences[$sourceId] as $id => $byConstructor) { + if (isset($currentPath[$id])) { + $this->addCircularReferences($id, $currentPath, $byConstructor); + } elseif (!isset($subPath[$id]) && isset($this->circularReferences[$id])) { + $this->connectCircularReferences($id, $currentPath, $byConstructor, $subPath); + } + } + unset($currentPath[$sourceId], $subPath[$sourceId]); + } + + private function addCircularReferences(string $id, array $currentPath, bool $byConstructor) + { + $currentPath[$id] = $byConstructor; + $circularRefs = []; + + foreach (array_reverse($currentPath) as $parentId => $v) { + $byConstructor = $byConstructor && $v; + $circularRefs[] = $parentId; + + if ($parentId === $id) { + break; + } + } + + $currentId = $id; + foreach ($circularRefs as $parentId) { + if (empty($this->circularReferences[$parentId][$currentId])) { + $this->circularReferences[$parentId][$currentId] = $byConstructor; + } + + $currentId = $parentId; + } + } + + private function collectLineage(string $class, array &$lineage) + { + if (isset($lineage[$class])) { + return; + } + if (!$r = $this->container->getReflectionClass($class, false)) { + return; + } + if (is_a($class, $this->baseClass, true)) { + return; + } + $file = $r->getFileName(); + if (!$file || $this->doExport($file) === $exportedFile = $this->export($file)) { + return; + } + + $lineage[$class] = substr($exportedFile, 1, -1); + + if ($parent = $r->getParentClass()) { + $this->collectLineage($parent->name, $lineage); + } + + foreach ($r->getInterfaces() as $parent) { + $this->collectLineage($parent->name, $lineage); + } + + foreach ($r->getTraits() as $parent) { + $this->collectLineage($parent->name, $lineage); + } + + unset($lineage[$class]); + $lineage[$class] = substr($exportedFile, 1, -1); + } + + private function generateProxyClasses(): array + { + $proxyClasses = []; + $alreadyGenerated = []; + $definitions = $this->container->getDefinitions(); + $strip = '' === $this->docStar && method_exists('Symfony\Component\HttpKernel\Kernel', 'stripComments'); + $proxyDumper = $this->getProxyDumper(); + ksort($definitions); + foreach ($definitions as $definition) { + if (!$proxyDumper->isProxyCandidate($definition)) { + continue; + } + if (isset($alreadyGenerated[$class = $definition->getClass()])) { + continue; + } + $alreadyGenerated[$class] = true; + // register class' reflector for resource tracking + $this->container->getReflectionClass($class); + if ("\n" === $proxyCode = "\n".$proxyDumper->getProxyCode($definition)) { + continue; + } + + if ($this->inlineRequires) { + $lineage = []; + $this->collectLineage($class, $lineage); + + $code = ''; + foreach (array_diff_key(array_flip($lineage), $this->inlinedRequires) as $file => $class) { + if ($this->inlineFactories) { + $this->inlinedRequires[$file] = true; + } + $code .= sprintf("include_once %s;\n", $file); + } + + $proxyCode = $code.$proxyCode; + } + + if ($strip) { + $proxyCode = "<?php\n".$proxyCode; + $proxyCode = substr(Kernel::stripComments($proxyCode), 5); + } + + $proxyClasses[sprintf('%s.php', explode(' ', $this->inlineRequires ? substr($proxyCode, \strlen($code)) : $proxyCode, 3)[1])] = $proxyCode; + } + + return $proxyClasses; + } + + private function addServiceInclude(string $cId, Definition $definition): string + { + $code = ''; + + if ($this->inlineRequires && (!$this->isHotPath($definition) || $this->getProxyDumper()->isProxyCandidate($definition))) { + $lineage = []; + foreach ($this->inlinedDefinitions as $def) { + if (!$def->isDeprecated() && \is_string($class = \is_array($factory = $def->getFactory()) && \is_string($factory[0]) ? $factory[0] : $def->getClass())) { + $this->collectLineage($class, $lineage); + } + } + + foreach ($this->serviceCalls as $id => list($callCount, $behavior)) { + if ('service_container' !== $id && $id !== $cId + && ContainerInterface::IGNORE_ON_UNINITIALIZED_REFERENCE !== $behavior + && $this->container->has($id) + && $this->isTrivialInstance($def = $this->container->findDefinition($id)) + && \is_string($class = \is_array($factory = $def->getFactory()) && \is_string($factory[0]) ? $factory[0] : $def->getClass()) + ) { + $this->collectLineage($class, $lineage); + } + } + + foreach (array_diff_key(array_flip($lineage), $this->inlinedRequires) as $file => $class) { + $code .= sprintf(" include_once %s;\n", $file); + } + } + + foreach ($this->inlinedDefinitions as $def) { + if ($file = $def->getFile()) { + $file = $this->dumpValue($file); + $file = '(' === $file[0] ? substr($file, 1, -1) : $file; + $code .= sprintf(" include_once %s;\n", $file); + } + } + + |
