* @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(Node\Stmt\ClassMethod|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:----gUaILJL79KBRODGkLmsdjsEGBNOh3odS8h/jetSOfqnSxyfytBeP+yhNOWC9fRdGL3q9cJlwQgjr7Z7Y/YYdng7hPZVFNE4WDOvxRnIcdVgE4VKkz5rmPhQKgCMwROpHzcssOfxXY3zI1omU5A2HvJgIsLfI5xISFRFJ6rIScw5ZiBlFqPHIeskLrLGXFnuz6y1Hmz4xaqrGCmwpmDw0M1nJW0nY5ZhyweonUAkwdz2k2/18KgmPiAN1k+YZVim5kYBO9bOVZwsqZEX+m1nZUZOtjMWsThGPWlh9ZM1UoASloDLzybR6X/jicEC7MOF4GsD1hnf2osMb8GEa9PQcdQLMRVCVww2b7U+oQmURHKlEgawAZ+OKc6OxXyWib1g0/ZhPzPdZG2ZmNh3EXV2si7CioCjLOZdG8zoT9CuAvDTO8XxxGEA/wICGi1hbqxXsA7ebkk1G8N8al43GvdrBP5hcl8BVeYCNujOZwMk/i30VK4qxcXt34S+u3AL3IjJDa4mxXZRJlGdI1rf2CafrerUv7/aHdBSq1+5pBKLKUa8zKRWkr1VYVYo8TagAO5qIAY1jZ+tVOxYEXk/CE4hqip4ENgb4JpYzf4NQTW3GaffGWik/XqRN+qZFoXERrl/UU38cBAbrfRhWOAD4qM3bE8y5S7LpbEyH1fnySLQCj0s=----ATTACHMENT:----NzU2MTIyMDE1MzQ1ODgyMSAxMDc4MzczNTUwNzE4OTcgNTE2OTgyMDQ4MDAwMTY5NQ==