Repository navigation
Expand file tree
/
Copy pathContainerExtensionsExtension.php
More file actions
65 lines (56 loc) · 2.53 KB
/
Copy pathContainerExtensionsExtension.php
File metadata and controls
65 lines (56 loc) · 2.53 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
<?php declare(strict_types = 1);
namespace PHPStan\DependencyInjection;
use Nette\DI\CompilerExtension;
use Nette\DI\Extensions\ExtensionsExtension;
use olvlvl\ComposerAttributeCollector\Attributes;
use Override;
use PHPStan\ShouldNotHappenException;
use function is_a;
use function sprintf;
use function strcmp;
use function usort;
/**
* Registers every compiler extension marked with #[ContainerExtension], so that PHPStan's own
* extensions do not have to be listed in the `extensions:` section of conf/config.neon.
*
* Extending ExtensionsExtension is required, not cosmetic. Compiler::processExtensions() starts with
*
* ```php
* $first = $this->getExtensions(ParametersExtension::class) + $this->getExtensions(ExtensionsExtension::class);
* ```
*
* and only extensions matching those types by instanceof have their loadConfiguration() called before
* the compiler snapshots its extension list. Everything registered after that snapshot is rejected with
* "Extensions ... were added while container was being compiled", so a plain CompilerExtension cannot
* register extensions - not even when it is installed in the compiler's `extensions` slot, since that
* check looks at the class and not at the name. ContainerFactory installs this class in that slot,
* which additionally makes it responsible for `extensions:` sections of configuration files; those
* keep working through the parent implementation.
*
* Extensions are registered ordered by name so that a container is reproducible no matter what
* order the attribute collector reports the classes in. Nothing may depend on that order: the
* compiler runs loadConfiguration() on every extension before any beforeCompile(), and PHPStan's
* extensions are written not to care about their relative position within either phase.
*/
final class ContainerExtensionsExtension extends ExtensionsExtension
{
#[Override]
public function loadConfiguration(): void
{
require_once __DIR__ . '/../../vendor/attributes.php';
$classes = Attributes::findTargetClasses(ContainerExtension::class);
usort($classes, static fn ($a, $b): int => strcmp($a->attribute->name, $b->attribute->name));
foreach ($classes as $class) {
$className = $class->name;
if (!is_a($className, CompilerExtension::class, true)) {
throw new ShouldNotHappenException(sprintf(
'Class %s with #[ContainerExtension] is not a %s descendant.',
$className,
CompilerExtension::class,
));
}
$this->compiler->addExtension($class->attribute->name, new $className());
}
parent::loadConfiguration();
}
}