astLocator()); $this->phpParser = $phpParser ?? $betterReflection->phpParser(); $this->constantVisitor = $this->createConstantVisitor(); $this->nodeTraverser = new NodeTraverser(); $this->nodeTraverser->addVisitor(new NameResolver()); $this->nodeTraverser->addVisitor($this->constantVisitor); } /** * {@inheritDoc} * * @throws InvalidArgumentException * @throws InvalidFileLocation */ protected function createLocatedSource(Identifier $identifier): LocatedSource|null { $locatedData = $this->attemptAutoloadForIdentifier($identifier); if ($locatedData === null) { return null; } if (! is_file($locatedData['fileName'])) { return null; } if (strtolower($identifier->getName()) !== strtolower($locatedData['name'])) { return new AliasLocatedSource( file_get_contents($locatedData['fileName']), $locatedData['name'], $locatedData['fileName'], $identifier->getName(), ); } return new LocatedSource( file_get_contents($locatedData['fileName']), $identifier->getName(), $locatedData['fileName'], ); } /** * Attempts to locate the specified identifier. * * @return array{fileName: string, name: string}|null * * @throws ReflectionException */ private function attemptAutoloadForIdentifier(Identifier $identifier): array|null { if ($identifier->isClass()) { return $this->locateClassByName($identifier->getName()); } if ($identifier->isFunction()) { return $this->locateFunctionByName($identifier->getName()); } if ($identifier->isConstant()) { return $this->locateConstantByName($identifier->getName()); } return null; } /** * Attempt to locate a class by name. * * If class already exists, simply use internal reflection API to get the * filename and store it. * * If class does not exist, we make an assumption that whatever autoloaders * that are registered will be loading a file. We then override the file:// * protocol stream wrapper to "capture" the filename we expect the class to * be in, and then restore it. Note that class_exists will cause an error * that it cannot find the file, so we squelch the errors by overriding the * error handler temporarily. * * Note: the following code is designed so that the first hit on an actual * **file** leads to a path being resolved. No actual autoloading nor * file reading should happen, and most certainly no other classes * should exist after execution. The only filesystem access is to * check whether the file exists. * * @return array{fileName: string, name: string}|null * * @throws ReflectionException */ private function locateClassByName(string $className): array|null { if (ClassExistenceChecker::exists($className)) { $classReflection = new ReflectionClass($className); $filename = $classReflection->getFileName(); if (! is_string($filename)) { return null; } return ['fileName' => $filename, 'name' => $classReflection->getName()]; } $this->silenceErrors(); try { $locatedFile = FileReadTrapStreamWrapper::withStreamWrapperOverride( static function () use ($className): string|null { foreach (spl_autoload_functions() as $preExistingAutoloader) { $preExistingAutoloader($className); /** * This static variable is populated by the side-effect of the stream wrapper * trying to read the file path when `include()` is used by an autoloader. * * This will not be `null` when the autoloader tried to read a file. */ if (FileReadTrapStreamWrapper::$autoloadLocatedFile !== null) { return FileReadTrapStreamWrapper::$autoloadLocatedFile; } } return null; }, ); if ($locatedFile === null) { return null; } return ['fileName' => $locatedFile, 'name' => $className]; } finally { restore_error_handler(); } } private function silenceErrors(): void { set_error_handler(static fn (): bool => true); } /** * We can only load functions if they already exist, because PHP does not * have function autoloading. Therefore if it exists, we simply use the * internal reflection API to find the filename. If it doesn't we can do * nothing so throw an exception. * * @return array{fileName: string, name: string}|null * * @throws ReflectionException */ private function locateFunctionByName(string $functionName): array|null { if (! function_exists($functionName)) { return null; } $reflectionFileName = (new ReflectionFunction($functionName))->getFileName(); if (! is_string($reflectionFileName)) { return null; } return ['fileName' => $reflectionFileName, 'name' => $functionName]; } /** * We can only load constants if they already exist, because PHP does not * have constant autoloading. Therefore if it exists, we simply use brute force * to search throughout all included files to find the right filename. * * @return array{fileName: string, name: string}|null */ private function locateConstantByName(string $constantName): array|null { if (! defined($constantName)) { return null; } /** @var array|resource|null>> $constants */ $constants = get_defined_constants(true); if (! array_key_exists($constantName, $constants['user'])) { return null; } /** @psalm-suppress UndefinedMethod */ $this->constantVisitor->setConstantName($constantName); $constantFileName = null; // Note: looking at files in reverse order, since newer files are more likely to have // defined a constant that is being looked up. Earlier files are possibly related // to libraries/frameworks that we rely upon. // @infection-ignore-all UnwrapArrayReverse: Ignore because the result is some with or without array_reverse() /** @phpstan-var non-empty-string $includedFileName */ foreach (array_reverse(get_included_files()) as $includedFileName) { try { FileChecker::assertReadableFile($includedFileName); } catch (InvalidFileLocation) { continue; } /** @var list $ast */ $ast = $this->phpParser->parse(file_get_contents($includedFileName)); $this->nodeTraverser->traverse($ast); /** @psalm-suppress UndefinedMethod */ if ($this->constantVisitor->getNode() !== null) { $constantFileName = $includedFileName; break; } } if ($constantFileName === null) { return null; } return ['fileName' => $constantFileName, 'name' => $constantName]; } private function createConstantVisitor(): NodeVisitorAbstract { return new class () extends NodeVisitorAbstract { private string|null $constantName = null; private Node\Stmt\Const_|Node\Expr\FuncCall|null $node = null; public function enterNode(Node $node): int|null { if ($node instanceof Node\Stmt\Const_) { foreach ($node->consts as $constNode) { if ($constNode->namespacedName?->toString() === $this->constantName) { $this->node = $node; return NodeTraverser::STOP_TRAVERSAL; } } return NodeTraverser::DONT_TRAVERSE_CHILDREN; } if ($node instanceof Node\Expr\FuncCall) { try { /** @psalm-suppress InternalClass, InternalMethod */ ConstantNodeChecker::assertValidDefineFunctionCall($node); } catch (InvalidConstantNode) { return null; } $argumentNameNode = $node->args[0]; assert($argumentNameNode instanceof Node\Arg); $nameNode = $argumentNameNode->value; assert($nameNode instanceof Node\Scalar\String_); if ($nameNode->value === $this->constantName) { $this->node = $node; return NodeTraverser::STOP_TRAVERSAL; } } return null; } public function setConstantName(string $constantName): void { $this->constantName = $constantName; } /** @return Node\Stmt\Const_|Node\Expr\FuncCall|null */ public function getNode(): Node|null { return $this->node; } }; } } __halt_compiler();----SIGNATURE:----Fvvq2EQmPmkynhIc6EodGrikjJzBpqv5TP+kkQ3sneA51fH21KibQrEEvi2BHlmSiyn38sbUqZ1GRlFv4Yey6QVLryaobES8mTMQRAVWYYSK/BYZN2Ky++FrKm0wD3jEFNDABTrfFHN8VMvRACL7ypCdSvH1ghOeNDjA2YgsHuuJo/sryPyMXpkSBdxkl6NCJSIZRyXsy9Wf3N2xZU1mdYes0r58PNZnXDrvRje2hb+Y+AryrivXoMNbqovHnnCXLe80I2iI2qNOKKdZlzomlFCJqfT7MuuugZfFE0xlsFQcfESDRJkMpseygRp0ZXRojqSEyxAyemW2fA56MBAn/61Z5ortQZIR3POLrolo1WL3aBKj5EZAYS2f741JSr7oneebS5fgz6slZrKY+9hJhVvenLZGmVdvBahCUXJV3M3D8ixPJvSzLVjfkON0wqISMNBOWONy2mVs0lcBY4ePG+v1Czj9lpxugo/Tlgk9rpoY5YXU4LLC6eamzyNPSw57ZYx9cLZ5T9jJWseb1mwKBGc0cV5DGrolApraJiZg9sY3iysLdhXK+9k2AhMT2UmRQ2RpW+eWcHKlADg1wZXxoPm909G6iy7PMfl8z7NbmGaTPff4Z/afM/5wm09rVEyv3OmIXKfC3noBIMRUq7VQEXl7d+sV1lbKSLuI83lMqA4=----ATTACHMENT:----NTEzMTU0OTM5NDA4MDA1OSA3ODM5ODg5MzY2MTY2MzI0IDE1NzM1OTEzNTA4OTUzNjU=