1: <?php declare(strict_types = 1);
2:
3: namespace PHPStan\DependencyInjection;
4:
5: use Nette\DI\Extensions\ExtensionsExtension;
6: use Nette\DI\Extensions\PhpExtension;
7: use Phar;
8: use PhpParser\Parser;
9: use PHPStan\BetterReflection\BetterReflection;
10: use PHPStan\BetterReflection\Reflector\Reflector;
11: use PHPStan\BetterReflection\SourceLocator\SourceStubber\PhpStormStubsSourceStubber;
12: use PHPStan\BetterReflection\SourceLocator\Type\SourceLocator;
13: use PHPStan\Broker\Broker;
14: use PHPStan\Command\CommandHelper;
15: use PHPStan\File\FileHelper;
16: use PHPStan\Php\PhpVersion;
17: use PHPStan\Reflection\ReflectionProvider;
18: use PHPStan\Reflection\ReflectionProviderStaticAccessor;
19: use Symfony\Component\Finder\Finder;
20: use function dirname;
21: use function extension_loaded;
22: use function ini_get;
23: use function is_dir;
24: use function sys_get_temp_dir;
25: use function time;
26: use function unlink;
27:
28: /** @api */
29: class ContainerFactory
30: {
31:
32: private FileHelper $fileHelper;
33:
34: private string $rootDirectory;
35:
36: private string $configDirectory;
37:
38: /** @api */
39: public function __construct(private string $currentWorkingDirectory)
40: {
41: $this->fileHelper = new FileHelper($currentWorkingDirectory);
42:
43: $rootDir = __DIR__ . '/../..';
44: $originalRootDir = $this->fileHelper->normalizePath($rootDir);
45: if (extension_loaded('phar')) {
46: $pharPath = Phar::running(false);
47: if ($pharPath !== '') {
48: $rootDir = dirname($pharPath);
49: }
50: }
51: $this->rootDirectory = $this->fileHelper->normalizePath($rootDir);
52: $this->configDirectory = $originalRootDir . '/conf';
53: }
54:
55: /**
56: * @param string[] $additionalConfigFiles
57: * @param string[] $analysedPaths
58: * @param string[] $composerAutoloaderProjectPaths
59: * @param string[] $analysedPathsFromConfig
60: */
61: public function create(
62: string $tempDirectory,
63: array $additionalConfigFiles,
64: array $analysedPaths,
65: array $composerAutoloaderProjectPaths = [],
66: array $analysedPathsFromConfig = [],
67: string $usedLevel = CommandHelper::DEFAULT_LEVEL,
68: ?string $generateBaselineFile = null,
69: ?string $cliAutoloadFile = null,
70: ?string $singleReflectionFile = null,
71: ?string $singleReflectionInsteadOfFile = null,
72: ): Container
73: {
74: $configurator = new Configurator(new LoaderFactory(
75: $this->fileHelper,
76: $this->rootDirectory,
77: $this->currentWorkingDirectory,
78: $generateBaselineFile,
79: ));
80: $configurator->defaultExtensions = [
81: 'php' => PhpExtension::class,
82: 'extensions' => ExtensionsExtension::class,
83: ];
84: $configurator->setDebugMode(true);
85: $configurator->setTempDirectory($tempDirectory);
86: $configurator->addParameters([
87: 'rootDir' => $this->rootDirectory,
88: 'currentWorkingDirectory' => $this->currentWorkingDirectory,
89: 'cliArgumentsVariablesRegistered' => ini_get('register_argc_argv') === '1',
90: 'tmpDir' => $tempDirectory,
91: 'additionalConfigFiles' => $additionalConfigFiles,
92: 'composerAutoloaderProjectPaths' => $composerAutoloaderProjectPaths,
93: 'generateBaselineFile' => $generateBaselineFile,
94: 'usedLevel' => $usedLevel,
95: 'cliAutoloadFile' => $cliAutoloadFile,
96: 'fixerTmpDir' => sys_get_temp_dir() . '/phpstan-fixer',
97: ]);
98: $configurator->addDynamicParameters([
99: 'singleReflectionFile' => $singleReflectionFile,
100: 'singleReflectionInsteadOfFile' => $singleReflectionInsteadOfFile,
101: 'analysedPaths' => $analysedPaths,
102: 'analysedPathsFromConfig' => $analysedPathsFromConfig,
103: ]);
104: $configurator->addConfig($this->configDirectory . '/config.neon');
105: foreach ($additionalConfigFiles as $additionalConfigFile) {
106: $configurator->addConfig($additionalConfigFile);
107: }
108:
109: $container = $configurator->createContainer();
110:
111: /** @var SourceLocator $sourceLocator */
112: $sourceLocator = $container->getService('betterReflectionSourceLocator');
113:
114: /** @var Reflector $reflector */
115: $reflector = $container->getService('betterReflectionReflector');
116:
117: /** @var Parser $phpParser */
118: $phpParser = $container->getService('phpParserDecorator');
119:
120: BetterReflection::populate(
121: $container->getByType(PhpVersion::class)->getVersionId(),
122: $sourceLocator,
123: $reflector,
124: $phpParser,
125: $container->getByType(PhpStormStubsSourceStubber::class),
126: );
127:
128: /** @var Broker $broker */
129: $broker = $container->getByType(Broker::class);
130: Broker::registerInstance($broker);
131: ReflectionProviderStaticAccessor::registerInstance($container->getByType(ReflectionProvider::class));
132: $container->getService('typeSpecifier');
133:
134: BleedingEdgeToggle::setBleedingEdge($container->parameters['featureToggles']['bleedingEdge']);
135:
136: return $container->getByType(Container::class);
137: }
138:
139: public function clearOldContainers(string $tempDirectory): void
140: {
141: $configurator = new Configurator(new LoaderFactory(
142: $this->fileHelper,
143: $this->rootDirectory,
144: $this->currentWorkingDirectory,
145: null,
146: ));
147: $configurator->setDebugMode(true);
148: $configurator->setTempDirectory($tempDirectory);
149:
150: $containerDirectory = $configurator->getContainerCacheDirectory();
151: if (!is_dir($containerDirectory)) {
152: return;
153: }
154:
155: $finder = new Finder();
156: $finder->name('Container_*')->in($containerDirectory);
157: $twoDaysAgo = time() - 24 * 60 * 60 * 2;
158:
159: foreach ($finder as $containerFile) {
160: $path = $containerFile->getRealPath();
161: if ($path === false) {
162: continue;
163: }
164: if ($containerFile->getATime() > $twoDaysAgo) {
165: continue;
166: }
167: if ($containerFile->getCTime() > $twoDaysAgo) {
168: continue;
169: }
170:
171: @unlink($path);
172: }
173: }
174:
175: public function getCurrentWorkingDirectory(): string
176: {
177: return $this->currentWorkingDirectory;
178: }
179:
180: public function getRootDirectory(): string
181: {
182: return $this->rootDirectory;
183: }
184:
185: public function getConfigDirectory(): string
186: {
187: return $this->configDirectory;
188: }
189:
190: }
191: