Skip to main content
Drupal API
User account menu
  • Log in

Breadcrumb

  1. Drupal Core 11.1.x
  2. PhpDumper.php

function PhpDumper::dump

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 value

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

Overrides DumperInterface::dump

File

vendor/symfony/dependency-injection/Dumper/PhpDumper.php, line 131

Class

PhpDumper
PhpDumper dumps a service container as a PHP class.

Namespace

Symfony\Component\DependencyInjection\Dumper

Code

public function dump(array $options = []) : string|array {
    $this->locatedIds = [];
    $this->targetDirRegex = null;
    $this->inlinedRequires = [];
    $this->exportedVariables = [];
    $this->dynamicParameters = [];
    $options = array_merge([
        'class' => 'ProjectServiceContainer',
        'base_class' => 'Container',
        'namespace' => '',
        'as_files' => false,
        'debug' => true,
        'hot_path_tag' => 'container.hot_path',
        'preload_tags' => [
            'container.preload',
            'container.no_preload',
        ],
        'inline_factories' => null,
        'inline_class_loader' => null,
        'preload_classes' => [],
        'service_locator_tag' => 'container.service_locator',
        'build_time' => time(),
    ], $options);
    $this->addGetService = false;
    $this->namespace = $options['namespace'];
    $this->asFiles = $options['as_files'];
    $this->hotPathTag = $options['hot_path_tag'];
    $this->preloadTags = $options['preload_tags'];
    $this->inlineFactories = false;
    if (isset($options['inline_factories'])) {
        $this->inlineFactories = $this->asFiles && $options['inline_factories'];
    }
    $this->inlineRequires = $options['debug'];
    if (isset($options['inline_class_loader'])) {
        $this->inlineRequires = $options['inline_class_loader'];
    }
    $this->serviceLocatorTag = $options['service_locator_tag'];
    $this->class = $options['class'];
    if (!str_starts_with($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->hasProxyDumper) {
        (new AnalyzeServiceReferencesPass(true, false))->process($this->container);
        (new CheckCircularReferencesPass())->process($this->container);
    }
    $this->analyzeReferences();
    $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 = '#(^|file://|[:;, \\|\\r\\n])' . preg_quote($dir[0], '#') . $regex . '#';
        }
    }
    $proxyClasses = $this->inlineFactories ? $this->generateProxyClasses() : null;
    if ($options['preload_classes']) {
        $this->preload = array_combine($options['preload_classes'], $options['preload_classes']);
    }
    $code = $this->addDefaultParametersMethod();
    $code = $this->startClass($options['class'], $baseClass, $this->inlineFactories && $proxyClasses) . $this->addServices($services) . $this->addDeprecatedAliases() . $code;
    $proxyClasses ??= $this->generateProxyClasses();
    if ($this->addGetService) {
        $code = preg_replace("/\r?\n\r?\n    public function __construct.+?\\{\r?\n/s", "\n    protected \\Closure \$getService;\$0", $code, 1);
    }
    if ($this->asFiles) {
        $fileTemplate = <<<EOF
<?php

use Symfony\\Component\\DependencyInjection\\Argument\\RewindableGenerator;
use Symfony\\Component\\DependencyInjection\\ContainerInterface;
use Symfony\\Component\\DependencyInjection\\Exception\\RuntimeException;

/*{<span class="php-variable">$this</span>-&gt;<span class="php-function-or-constant property member-of-self">docStar</span>}
 * @internal This class has been auto-generated by the Symfony Dependency Injection Component.
 */
class %s extends {<span class="php-variable">$options</span>[<span class="php-string">'class'</span>]}
{%s}

EOF;
        $files = [];
        $preloadedFiles = [];
        $ids = $this->container
            ->getRemovedIds();
        foreach ($this->container
            ->getDefinitions() as $id => $definition) {
            if (!$definition->isPublic() && '.' !== ($id[0] ?? '-')) {
                $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,
                $preload,
            ]) {
                $files[$file] = \sprintf($fileTemplate, substr($file, 0, -4), $c);
                if ($preload) {
                    $preloadedFiles[$file] = $file;
                }
            }
            foreach ($proxyClasses as $file => $c) {
                $files[$file] = "<?php\n" . $c;
                $preloadedFiles[$file] = $file;
            }
        }
        $code .= $this->endClass();
        if ($this->inlineFactories && $proxyClasses) {
            $files['proxy-classes.php'] = "<?php\n\n";
            foreach ($proxyClasses as $c) {
                $files['proxy-classes.php'] .= $c;
            }
        }
        $files[$options['class'] . '.php'] = $code;
        $hash = ucfirst(strtr(ContainerBuilder::hash($files), '._', 'xx'));
        $code = [];
        foreach ($files as $file => $c) {
            $code["Container{$hash}/{$file}"] = substr_replace($c, "<?php\n\nnamespace Container{$hash};\n", 0, 6);
            if (isset($preloadedFiles[$file])) {
                $preloadedFiles[$file] = "Container{$hash}/{$file}";
            }
        }
        $namespaceLine = $this->namespace ? "\nnamespace {$this->namespace};\n" : '';
        $time = $options['build_time'];
        $id = hash('crc32', $hash . $time);
        $this->asFiles = false;
        if ($this->preload && null !== ($autoloadFile = $this->getAutoloadFile())) {
            $autoloadFile = trim($this->export($autoloadFile), '()\\');
            $preloadedFiles = array_reverse($preloadedFiles);
            if ('' !== ($preloadedFiles = implode("';\nrequire __DIR__.'/", $preloadedFiles))) {
                $preloadedFiles = "require __DIR__.'/{$preloadedFiles}';\n";
            }
            $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;

if (in_array(PHP_SAPI, ['cli', 'phpdbg', 'embed'], true)) {
    return;
}

require {<span class="php-variable">$autoloadFile</span>};
(require __DIR__.'/{<span class="php-variable">$options</span>[<span class="php-string">'class'</span>]}.php')->set(\\Container{<span class="php-variable">$hash</span>}\\{<span class="php-variable">$options</span>[<span class="php-string">'class'</span>]}::class, null);
{<span class="php-variable">$preloadedFiles</span>}
\$classes = [];

EOF;
            foreach ($this->preload as $class) {
                if (!$class || str_contains($class, '$') || \in_array($class, [
                    'int',
                    'float',
                    'string',
                    'bool',
                    'resource',
                    'object',
                    'array',
                    'null',
                    'callable',
                    'iterable',
                    'mixed',
                    'void',
                ], true)) {
                    continue;
                }
                if (!(class_exists($class, false) || interface_exists($class, false) || trait_exists($class, false)) || (new \ReflectionClass($class))->isUserDefined()) {
                    $code[$options['class'] . '.preload.php'] .= \sprintf("\$classes[] = '%s';\n", $class);
                }
            }
            $code[$options['class'] . '.preload.php'] .= <<<'EOF'

$preloaded = Preloader::preload($classes);

EOF;
        }
        $code[$options['class'] . '.php'] = <<<EOF
<?php
{<span class="php-variable">$namespaceLine</span>}
// This file has been auto-generated by the Symfony Dependency Injection Component for internal use.

if (\\class_exists(\\Container{<span class="php-variable">$hash</span>}\\{<span class="php-variable">$options</span>[<span class="php-string">'class'</span>]}::class, false)) {
    // no-op
} elseif (!include __DIR__.'/Container{<span class="php-variable">$hash</span>}/{<span class="php-variable">$options</span>[<span class="php-string">'class'</span>]}.php') {
    touch(__DIR__.'/Container{<span class="php-variable">$hash</span>}.legacy');

    return;
}

if (!\\class_exists({<span class="php-variable">$options</span>[<span class="php-string">'class'</span>]}::class, false)) {
    \\class_alias(\\Container{<span class="php-variable">$hash</span>}\\{<span class="php-variable">$options</span>[<span class="php-string">'class'</span>]}::class, {<span class="php-variable">$options</span>[<span class="php-string">'class'</span>]}::class, false);
}

return new \\Container{<span class="php-variable">$hash</span>}\\{<span class="php-variable">$options</span>[<span class="php-string">'class'</span>]}([
    'container.build_hash' => '{<span class="php-variable">$hash</span>}',
    'container.build_id' => '{<span class="php-variable">$id</span>}',
    'container.build_time' => {<span class="php-variable">$time</span>},
    'container.runtime_mode' => \\in_array(\\PHP_SAPI, ['cli', 'phpdbg', 'embed'], true) ? 'web=0' : 'web=1',
], __DIR__.\\DIRECTORY_SEPARATOR.'Container{<span class="php-variable">$hash</span>}');

EOF;
    }
    else {
        $code .= $this->endClass();
        foreach ($proxyClasses as $c) {
            $code .= $c;
        }
    }
    $this->targetDirRegex = null;
    $this->inlinedRequires = [];
    $this->circularReferences = [];
    $this->locatedIds = [];
    $this->exportedVariables = [];
    $this->dynamicParameters = [];
    $this->preload = [];
    $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;
}
RSS feed
Powered by Drupal