*/ 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:----MrUd4/80IT6bBTrK2MOKuehmY/8YwP7Zc+iZTZeNXlHYOEe6yO60ZiHrKfkRVzO9z6L/Lu1IxCQ2INK2IYGSjzWSAZ7v+CCVVs87Rop27WvZHRIoq1d2U+raNlv5l6oHHcm9Z8FrvEhI2nQUM++aEs375zzpUBUR2aIwJdVWB9JPH9gdUC5KGFtj87wQXPdwGhPHyIeTyUbRCPqyzPEEBIylX5BYS/CpwjKmmI1PosL1lIsW3FuW7Mzk7rVIkoXmLi/cLtDaqRezTemQf5bxObCfJR6CPfchdGvBV6vKi5GoLUURwIC3VTnoi3+ziddCAsnH37PPWoUepCRXNX8OSPbWXCHpETXcbNcuCQvMouqoOn5ulXeHx5uBWcQB2JGE+rlgy1udwOx0brX172dcXqXmAPBPL227co8JZcC/rKRNLx6nk0iVbIUxEHdjVH9Ob7PJUnkA9aA24lEwy0/FYxBWHYnlKWCnj9mG3lJZw5Te7GKiAVQQOdfvL5fNz8tkU9xZoR6XDh+aU2sEKNA/Pf+SLlby0pbNXtiPNywTxsilF55ejVIHocKaQfNd94Q+MChYX+Jn3tBIJsgIemZ+iW8k8gINb4pjGY92N35w6Q7YWkVUd8W4ey3oSqvoOAQTQmrf59LhZq7cUmC+l/THtO2eVIUF3l6/pwJO+GV+quk=----ATTACHMENT:----MTcyNDk5OTg1NDk2MzA0NyA4MTUyNDA2ODA0MzgyMTcwIDg1NjI0MzMyMTA5MDc1NjM=