* @psalm-allow-private-mutation */ private array $parameters; /** @psalm-allow-private-mutation */ private bool $returnsReference; /** @psalm-allow-private-mutation */ private ReflectionNamedType|ReflectionUnionType|ReflectionIntersectionType|null $returnType; /** * @var list * @psalm-allow-private-mutation */ private array $attributes; /** * @var non-empty-string|null * @psalm-allow-private-mutation */ private string|null $docComment; /** * @var positive-int|null * @psalm-allow-private-mutation */ private int|null $startLine; /** * @var positive-int|null * @psalm-allow-private-mutation */ private int|null $endLine; /** * @var positive-int|null * @psalm-allow-private-mutation */ private int|null $startColumn; /** * @var positive-int|null * @psalm-allow-private-mutation */ private int|null $endColumn; /** @psalm-allow-private-mutation */ private bool $couldThrow = false; /** @psalm-allow-private-mutation */ private bool $isClosure = false; /** @psalm-allow-private-mutation */ private bool $isGenerator = false; /** * @return non-empty-string */ abstract public function __toString(): string; /** * @return non-empty-string */ abstract public function getShortName(): string; /** * @psalm-external-mutation-free */ private function fillFromNode(MethodNode|Node\Stmt\Function_|Node\Expr\Closure|Node\Expr\ArrowFunction $node): void { $this->parameters = $this->createParameters($node); $this->returnsReference = $node->returnsByRef(); $this->returnType = $this->createReturnType($node); $this->attributes = ReflectionAttributeHelper::createAttributes($this->reflector, $this, $node->attrGroups); $this->docComment = GetLastDocComment::forNode($node); $this->couldThrow = $this->computeCouldThrow($node); $startLine = null; if ($node->hasAttribute('startLine')) { $startLine = $node->getStartLine(); assert($startLine > 0); } $endLine = null; if ($node->hasAttribute('endLine')) { $endLine = $node->getEndLine(); assert($endLine > 0); } $this->startLine = $startLine; $this->endLine = $endLine; try { $this->startColumn = CalculateReflectionColumn::getStartColumn($this->getLocatedSource()->getSource(), $node); } catch (NoNodePosition) { $this->startColumn = null; } try { $this->endColumn = CalculateReflectionColumn::getEndColumn($this->getLocatedSource()->getSource(), $node); } catch (NoNodePosition) { $this->endColumn = null; } } /** * @return array */ private function createParameters(MethodNode|Node\Stmt\Function_|Node\Expr\Closure|Node\Expr\ArrowFunction $node): array { $parameters = []; /** @var list $nodeParams */ $nodeParams = $node->params; foreach ($nodeParams as $paramIndex => $paramNode) { $parameter = ReflectionParameter::createFromNode( $this->reflector, $paramNode, $this, $paramIndex, $this->isParameterOptional($nodeParams, $paramIndex), ); $parameters[$parameter->getName()] = $parameter; } return $parameters; } /** * Get the "full" name of the function (e.g. for A\B\foo, this will return * "A\B\foo"). * * @return non-empty-string */ public function getName(): string { $namespace = $this->getNamespaceName(); if ($namespace === null) { return $this->getShortName(); } return $namespace . '\\' . $this->getShortName(); } /** * Get the "namespace" name of the function (e.g. for A\B\foo, this will * return "A\B"). */ public function getNamespaceName(): string|null { return $this->namespace; } /** * Decide if this function is part of a namespace. Returns false if the class * is in the global namespace or does not have a specified namespace. */ public function inNamespace(): bool { return $this->namespace !== null; } /** * Get the number of parameters for this class. * * @return positive-int|0 */ public function getNumberOfParameters(): int { return count($this->parameters); } /** * Get the number of required parameters for this method. * * @return positive-int|0 */ public function getNumberOfRequiredParameters(): int { return count(array_filter( $this->parameters, static fn (ReflectionParameter $p): bool => ! $p->isOptional(), )); } /** * Get an array list of the parameters for this method signature, as an * array of ReflectionParameter instances. * * @return list */ public function getParameters(): array { return array_values($this->parameters); } /** * @param list $parameterNodes */ private function isParameterOptional(array $parameterNodes, int $parameterIndex): bool { foreach ($parameterNodes as $otherParameterIndex => $otherParameterNode) { if ($otherParameterIndex < $parameterIndex) { continue; } // When we find next parameter that does not have a default or is not variadic, // it means current parameter cannot be optional EVEN if it has a default value if ($otherParameterNode->default === null && ! $otherParameterNode->variadic) { return false; } } return true; } /** * Get a single parameter by name. Returns null if parameter not found for * the function. * * @param non-empty-string $parameterName */ public function getParameter(string $parameterName): ReflectionParameter|null { return $this->parameters[$parameterName] ?? null; } /** * @return non-empty-string|null */ public function getDocComment(): string|null { return $this->docComment; } /** * @return non-empty-string|null */ public function getFileName(): string|null { return $this->locatedSource->getFileName(); } public function getLocatedSource(): LocatedSource { return $this->locatedSource; } /** * Is this function a closure? */ public function isClosure(): bool { return $this->isClosure; } public function isDeprecated(): bool { return AnnotationHelper::isDeprecated($this->docComment); } public function isInternal(): bool { return $this->locatedSource->isInternal(); } /** * Is this a user-defined function (will always return the opposite of * whatever isInternal returns). */ public function isUserDefined(): bool { return ! $this->isInternal(); } /** * @return non-empty-string|null */ public function getExtensionName(): string|null { return $this->locatedSource->getExtensionName(); } /** * Check if the function has a variadic parameter. */ public function isVariadic(): bool { foreach ($this->parameters as $parameter) { if ($parameter->isVariadic()) { return true; } } return false; } /** * Checks if the function/method contains `throw` expressions. */ public function couldThrow(): bool { return $this->couldThrow; } private function computeCouldThrow(MethodNode|Node\Stmt\Function_|Node\Expr\Closure|Node\Expr\ArrowFunction $node): bool { $statements = $node->getStmts(); if ($statements === null) { return false; } $visitor = new FindingVisitor(static fn (Node $node): bool => $node instanceof NodeThrow); $traverser = new NodeTraverser(); $traverser->addVisitor($visitor); $traverser->traverse($statements); return $visitor->getFoundNodes() !== []; } /** * Recursively search an array of statements (PhpParser nodes) to find if a * yield expression exists anywhere (thus indicating this is a generator). */ private function nodeIsOrContainsYield(Node $node): bool { if ($node instanceof YieldNode) { return true; } if ($node instanceof YieldFromNode) { return true; } /** @psalm-var string $nodeName */ foreach ($node->getSubNodeNames() as $nodeName) { $nodeProperty = $node->$nodeName; if ($nodeProperty instanceof Node && $this->nodeIsOrContainsYield($nodeProperty)) { return true; } if (! is_array($nodeProperty)) { continue; } /** @psalm-var mixed $nodePropertyArrayItem */ foreach ($nodeProperty as $nodePropertyArrayItem) { if ($nodePropertyArrayItem instanceof Node && $this->nodeIsOrContainsYield($nodePropertyArrayItem)) { return true; } } } return false; } /** * Check if this function can be used as a generator (i.e. contains the * "yield" keyword). */ public function isGenerator(): bool { return $this->isGenerator; } /** * Get the line number that this function starts on. * * @return positive-int * * @throws CodeLocationMissing */ public function getStartLine(): int { if ($this->startLine === null) { throw CodeLocationMissing::create(); } return $this->startLine; } /** * Get the line number that this function ends on. * * @return positive-int * * @throws CodeLocationMissing */ public function getEndLine(): int { if ($this->endLine === null) { throw CodeLocationMissing::create(); } return $this->endLine; } /** * @return positive-int * * @throws CodeLocationMissing */ public function getStartColumn(): int { if ($this->startColumn === null) { throw CodeLocationMissing::create(); } return $this->startColumn; } /** * @return positive-int * * @throws CodeLocationMissing */ public function getEndColumn(): int { if ($this->endColumn === null) { throw CodeLocationMissing::create(); } return $this->endColumn; } /** * Is this function declared as a reference. */ public function returnsReference(): bool { return $this->returnsReference; } /** * Get the return type declaration */ public function getReturnType(): ReflectionNamedType|ReflectionUnionType|ReflectionIntersectionType|null { if ($this->hasTentativeReturnType()) { return null; } return $this->returnType; } /** * Do we have a return type declaration */ public function hasReturnType(): bool { if ($this->hasTentativeReturnType()) { return false; } return $this->returnType !== null; } public function hasTentativeReturnType(): bool { if ($this->isUserDefined()) { return false; } return AnnotationHelper::hasTentativeReturnType($this->docComment); } public function getTentativeReturnType(): ReflectionNamedType|ReflectionUnionType|ReflectionIntersectionType|null { if (! $this->hasTentativeReturnType()) { return null; } return $this->returnType; } private function createReturnType(MethodNode|Node\Stmt\Function_|Node\Expr\Closure|Node\Expr\ArrowFunction $node): ReflectionNamedType|ReflectionUnionType|ReflectionIntersectionType|null { $returnType = $node->getReturnType(); if ($returnType === null) { return null; } assert($returnType instanceof Node\Identifier || $returnType instanceof Node\Name || $returnType instanceof Node\NullableType || $returnType instanceof Node\UnionType || $returnType instanceof Node\IntersectionType); return ReflectionType::createFromNode($this->reflector, $this, $returnType); } /** * @return list */ public function getAttributes(): array { return $this->attributes; } /** * @return list */ public function getAttributesByName(string $name): array { return ReflectionAttributeHelper::filterAttributesByName($this->getAttributes(), $name); } /** * @param class-string $className * * @return list */ public function getAttributesByInstance(string $className): array { return ReflectionAttributeHelper::filterAttributesByInstance($this->getAttributes(), $className); } } __halt_compiler();----SIGNATURE:----tDKQ0IGZDpY0gazLVUA9gZVQqllxl0aqV08FB0Bkiz7eHj7kTlBDXZwQutCQ+Xw0dWi9dmIUjZK62LotDR5JqCs7NREMv1e6MJW78+PO+97qhGHI3ZxFeP6JHejjtXUH5NQJQIp5DyBhAuHbOX6GU72do7a5DjsEQTnNuT4zCBuDnyEPPrr0kVlXyGtaMBiXFMgJ+c3ZxvM65jW5n5h/njZZ5ID9CXwlOtBbXNQ/bd2fNiN0XwH7IaE8ICY4RyxK7urnV6AQvlMWp1KgG7M1IV0Tlr9/VaGoC/zbA5moi86YASHgCd7j6DhwLKk4+oucr39S6ArhQWGzXKdf4PV75BMuIH5F1fGxH7zsOpmvWafAIrhCB772G7M4kcTZOwtz+oJTBl0wpmPtPN4hBlCUcv4lfIQqtxytuiJoI5bpdZFPmw51xMJH5KO+VjNjxBeYOuBKahxgmwQOXVUxjKrI9r7b9KbZKGDHUYjNH+nN2AxqzO0IcfSyNxwaV/jU/hAC979m6BB0P+4nec/qwQRLPrriw790t0yW1Hroc/6bHBLXYYDnF1ld/+FgPLJurmvvapwF7358t34QWaju18CSwaPIatQDgJXzOQAgB58KSitw3ha2eVfPElcTq4K23/XAt1/ISwWAJ3DzYDUYOWkziFC/c+PCAXgNX71gyd2MFsM=----ATTACHMENT:----NDAxMzAxNjkwODY4MzQ3NyA4NTQ0MTM4OTExNjc1OTQyIDI2Mjc4Nzg1NjE4NDMxOQ==