*/ 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:----GxDjxEoZnI+nJZFBvSfMSJtFVYgXxpi2f3V1L/mRrRKsMtxXHBjP3ma9lCVVROlQ6J9KY9IhvpA1T2NWJT/Q22ULPV4yWDiz6ezI2XUstvy/0p/flydq4N8OFVbDD8u0EmLYRrYAY6zRnMacBNFOSgp/v5IZ1PDEwOfK9eHD4SHj4WzuzljO26VNT1FUXGFdfVlmrDVXut+jUs3Np/Cb7hroXfSWssNe0aoBKHzLFJ7nKBlEnnZ/u2VJgDXkUOX6CjlYsc7ODGLmNwdb7P5eFJ6WgdDWBOPC1XqhBfOMHX+ZzsKkbJWhQ4/JetxBod1TzqWs7cxsqmGY3+z7+6wm7PfKMnXOzagqoZzKwvNr8TMmYw8vsZlcm7iI5YLtRpXorIKDYFyOrv+WajA6Z35KYtGC141qKnbnXUIFQoz2caIn9at5364W97Thj78xJF815xQtOXwNV1yPw09kWFwweJ85cuZUkRDfuqhn526rFNVuLhZrnvvkEF8Nr94aFoCCqtv5RuBEJCw+Bv5dPgZmuiAu/5kiAlObvSVON5Hz/zaNLwTY08NLvGaJ5fBXqAATfNMQ6RnKi+UcjdlpMZrf5U774unm0CLQ7/rx2hEPaoC3Da6C1epUZ+j1zUyyJAgK6fePjTfrmvRwXxjKys+Li7LJobZmpZ8cIo5tRjPoDWY=----ATTACHMENT:----Nzc1MTk4OTgyODEzNzE5MiAyMTAxNzE5OTQ4NDQyNDU2IDkxOTE3NzM4MTY1NTg4MDU=