*/ 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:----T2k0EQYbojsoS7eZm9xZxb0pONCwYUGsMQOZgZ3gCbjoORvFg+JzazqBDDgNrp08wBQjdtaohAb94nlN+Idjugoj2QZd+XZBJZRqI/7+G59hRxozbkad2ebupetIg7TIRDiU3f5puXLGu5gABCk06zAHoAUZhjv8HnDbRmh+NNv18KDh++tFc2TIqSfRVDfoRpgL8ANBwAUM2q6Aoxo5YdzQKGI7T9zO4dEc8U4EIqpY3oGykubm2SOaXJY1RSo1H5sYA4q5teC400FgTJu7xq2xtgKoUPp8/Y9E5CD0/PWVw4XuGUCJ8iQzONthaE2v6fXboXN1nmDQUq0cAUT/hjeapX+Wb8wxGoW3oQnmUaU5qF1QDxJ2ZxB055vdLByC1QBlAPPUjQH58yzyumP1OyMZOndbt1+TJo+uq9jZhQCZ+zcHiaq22fOWhxq/t+78gDx8Jx8+5fYfBB9e+VcvtQbcCnAb6CJunZOssrNOxZStdq+IW0gKW1qXS91Eypi6bMN6z5BvCidViTsrHTrVqNzFWCuLrX1nkVwkV2nCvXYX0bC5DF20QZglUMfvWBrZy2b7a7vhZWDPqLagoTk9q/7J1BZ15Wk+WQydXdKSlqBjhqqX8k0RiEdpeWWzZsB/SjNcc7qhyX1hfXJgqmB65+RDuk27aNUDIrNGtloZyVY=----ATTACHMENT:----NTg1OTAzMTM4MjY4ODg0OCAzNDg0NDAxMjcxMTYyMTM2IDcwODgxMDg4ODEyMTg4MTI=