*/ private array $classNodes = []; /** @var array> */ private array $functionNodes = []; /** @var array */ private array $constantNodes = []; public function __construct(private BuilderFactory $builderFactory) { } public function enterNode(Node $node): int|null { if ($node instanceof Node\Stmt\Namespace_) { $this->currentNamespace = $node; return null; } if ($node instanceof Node\Stmt\ClassLike) { $classNamespacedName = $node->namespacedName; assert($classNamespacedName instanceof Node\Name); $className = $classNamespacedName->toString(); $this->classNodes[$className] = [$node, $this->currentNamespace]; foreach ($node->getConstants() as $constantsNode) { foreach ($constantsNode->consts as $constNode) { $constClassName = sprintf('%s::%s', $className, $constNode->name->toString()); $this->updateConstantValue($constNode, $constClassName); } } // We need to traverse children to resolve attributes names for methods, properties etc. return null; } if ($node instanceof Node\Stmt\ClassMethod) { return NodeTraverser::DONT_TRAVERSE_CURRENT_AND_CHILDREN; } if ($node instanceof Node\Stmt\Function_) { $functionNamespacedName = $node->namespacedName; assert($functionNamespacedName instanceof Node\Name); $functionName = $functionNamespacedName->toString(); $this->functionNodes[$functionName][] = [$node, $this->currentNamespace]; return NodeTraverser::DONT_TRAVERSE_CHILDREN; } if ($node instanceof Node\Stmt\Const_) { foreach ($node->consts as $constNode) { $constNamespacedName = $constNode->namespacedName; assert($constNamespacedName instanceof Node\Name); $constNodeName = $constNamespacedName->toString(); $this->updateConstantValue($constNode, $constNodeName); $this->constantNodes[$constNodeName] = [$node, $this->currentNamespace]; } return NodeTraverser::DONT_TRAVERSE_CHILDREN; } if ($node instanceof Node\Expr\FuncCall) { $argumentNameNode = $node->args[0]; assert($argumentNameNode instanceof Node\Arg); $nameNode = $argumentNameNode->value; assert($nameNode instanceof Node\Scalar\String_); $constantName = $nameNode->value; // The definition is stubs looks like `define('STDIN', fopen('php://stdin', 'r'))` // We will modify it to `define('STDIN', constant('STDIN')); // The later definition can pass validation in `ConstantNodeChecker` and has support in `CompileNodeToValue` if ( in_array($constantName, ['STDIN', 'STDOUT', 'STDERR'], true) && array_key_exists(1, $node->args) && $node->args[1] instanceof Node\Arg ) { $node->args[1]->value = $this->builderFactory->funcCall('constant', [$constantName]); } // @codeCoverageIgnoreStart // @infection-ignore-all // No invalid definition in PhpStorm stubs try { ConstantNodeChecker::assertValidDefineFunctionCall($node); } catch (InvalidConstantNode) { return null; } // @codeCoverageIgnoreEnd if (in_array($constantName, self::TRUE_FALSE_NULL, true)) { $constantName = strtoupper($constantName); $nameNode->value = $constantName; } $this->updateConstantValue($node, $constantName); $this->constantNodes[$constantName] = [$node, $this->currentNamespace]; if ( array_key_exists(2, $node->args) && $node->args[2] instanceof Node\Arg && $node->args[2]->value instanceof Node\Expr\ConstFetch && $node->args[2]->value->name->toLowerString() === 'true' ) { $this->constantNodes[strtolower($constantName)] = [$node, $this->currentNamespace]; } return NodeTraverser::DONT_TRAVERSE_CHILDREN; } return null; } /** * {@inheritDoc} */ public function leaveNode(Node $node) { if ($node instanceof Node\Stmt\Namespace_) { $this->currentNamespace = null; } return null; } /** @return array */ public function getClassNodes(): array { return $this->classNodes; } /** @return array> */ public function getFunctionNodes(): array { return $this->functionNodes; } /** @return array */ public function getConstantNodes(): array { return $this->constantNodes; } public function clearNodes(): void { $this->classNodes = []; $this->functionNodes = []; $this->constantNodes = []; } /** * Some constants have different values on different systems, some are not actual in stubs. */ private function updateConstantValue(Node\Expr\FuncCall|Node\Const_ $node, string $constantName): void { if (! defined($constantName)) { return; } // @ because access to deprecated constant throws deprecated warning /** @var scalar|resource|list|null $constantValue */ $constantValue = @constant($constantName); $normalizedConstantValue = is_resource($constantValue) ? $this->builderFactory->funcCall('constant', [$constantName]) : $this->builderFactory->val($constantValue); if ($node instanceof Node\Expr\FuncCall) { $argumentValueNode = $node->args[1]; assert($argumentValueNode instanceof Node\Arg); $argumentValueNode->value = $normalizedConstantValue; } else { $node->value = $normalizedConstantValue; } } } __halt_compiler();----SIGNATURE:----aHHhVkVZOb+1vQltSOKUdb5DUeleRH1p3+Lz1Z6uEArTfBU21+sB7Q0cnrI2l9NVHrG7tYvSS4vRfRNdMSfk/RKrHew18z7Y7TvXoIaufKRGilrYhu8etWTHNvYF8+J046M6gzQaEeYKL8782qdZflOIe/Mu94mW1/F93QptM30/IsA0eHCcwtl66/9zR7DRST884ut99W5tTsPIqaEm3U15MVemjp8fAe9gV/MUD3mKRBtFgUb8s2jEAR4FGnJ6RXY6ac6P7ViueFdvP58poYTFCLXxK13wm45ITpQcHhxWWxW8s36V1XtNPMdUjMvo3zJtrCCu9Nbefq8qYGjSGopBXplskScxLQ2sOLqCIt2p9zVrCKyA1Ve3OCPSgjKVmF3a8YhMo/iolqna5ZLuCMxmLRhsDrfE6Jwd6zplUOxbHy2nEaqN2XEF6QiHGUOoZgZ7zE76vCecWQd//xzkV+BmQQCl28y8fpuYh2VWTxa1WBYA47kEnFph7FY/vZbvbx+ESRdvNjuWREJX6zur52sXDFdOegNe31n2lIQjUGM8noc7LdAuQHyklkKNW0SbOppQ6ULHelQGyluqldFd7rpG3BIsLjyxRbuQqzVaTjjK0ugxVTN5OyzBA7oSIzTGZhkWWqkp63D8PlN9RuQJhaafnO2suDhwI9bXcAuOaoU=----ATTACHMENT:----OTQ2MTcwMjcxNDkxMjExNyA1ODYzMjkyNjM4NDkxMzM5IDMwODE5Mjg0ODMzNzAwMTE=