true]; private BuilderFactory $builderFactory; private Standard $prettyPrinter; public function __construct() { $this->builderFactory = new BuilderFactory(); $this->prettyPrinter = new Standard(self::BUILDER_OPTIONS); } /** @param class-string|trait-string $className */ public function generateClassStub(string $className): StubData|null { if (! ClassExistenceChecker::exists($className)) { return null; } /** phpcs:disable SlevomatCodingStandard.Namespaces.ReferenceUsedNamesOnly.ReferenceViaFullyQualifiedName */ $isEnum = function_exists('enum_exists') && \enum_exists($className, false); /** phpcs:enable */ $classReflection = $isEnum ? new CoreReflectionEnum($className) : new CoreReflectionClass($className); $classNode = $this->createClass($classReflection); if ($classNode instanceof Class_) { $this->addClassModifiers($classNode, $classReflection); } if ($classNode instanceof Class_ || $classNode instanceof Interface_ || $classNode instanceof Enum_) { $this->addExtendsAndImplements($classNode, $classReflection); } if ($classNode instanceof Class_ || $classNode instanceof Trait_) { $this->addProperties($classNode, $classReflection); } if ($classNode instanceof Class_ || $classNode instanceof Trait_ || $classNode instanceof Enum_) { $this->addTraitUse($classNode, $classReflection); } $this->addDocComment($classNode, $classReflection); if ($classNode instanceof Enum_ && $classReflection instanceof CoreReflectionEnum) { $this->addEnumBackingType($classNode, $classReflection); $this->addEnumCases($classNode, $classReflection); } $this->addClassConstants($classNode, $classReflection); $this->addMethods($classNode, $classReflection); $node = $classNode->getNode(); $stub = $classReflection->inNamespace() ? $this->generateStubInNamespace($node, $classReflection->getNamespaceName()) : $this->generateStub($node); $extensionName = $classReflection->getExtensionName() ?: null; return $this->createStubData($stub, $extensionName); } public function generateFunctionStub(string $functionName): StubData|null { if (! function_exists($functionName)) { return null; } $functionReflection = new CoreReflectionFunction($functionName); $functionNode = $this->builderFactory->function($functionReflection->getShortName()); $this->addDocComment($functionNode, $functionReflection); $this->addParameters($functionNode, $functionReflection); $returnType = $functionReflection->getReturnType(); if ($returnType === null && method_exists($functionReflection, 'getTentativeReturnType')) { $returnType = $functionReflection->getTentativeReturnType(); } if ($returnType !== null) { assert($returnType instanceof CoreReflectionNamedType || $returnType instanceof CoreReflectionUnionType || $returnType instanceof CoreReflectionIntersectionType); $functionNode->setReturnType($this->formatType($returnType)); } $extensionName = $functionReflection->getExtensionName() ?: null; if (! $functionReflection->inNamespace()) { return $this->createStubData($this->generateStub($functionNode->getNode()), $extensionName); } return $this->createStubData($this->generateStubInNamespace($functionNode->getNode(), $functionReflection->getNamespaceName()), $extensionName); } public function generateConstantStub(string $constantName): StubData|null { $constantData = $this->findConstantData($constantName); if ($constantData === null) { return null; } [$constantValue, $extensionName] = $constantData; if (is_resource($constantValue)) { $constantValue = $this->builderFactory->funcCall('constant', [$constantName]); } $constantNode = $this->builderFactory->funcCall('define', [$constantName, $constantValue]); return $this->createStubData($this->generateStub($constantNode), $extensionName); } /** @return array{0: scalar|list|resource|null, 1: non-empty-string|null}|null */ private function findConstantData(string $constantName): array|null { /** @var array|resource|null>> $constants */ $constants = get_defined_constants(true); foreach ($constants as $constantExtensionName => $extensionConstants) { if (array_key_exists($constantName, $extensionConstants)) { return [ $extensionConstants[$constantName], $constantExtensionName !== 'user' ? $constantExtensionName : null, ]; } } return null; } private function createClass(CoreReflectionClass $classReflection): Class_|Interface_|Trait_|Enum_ { if ($classReflection instanceof CoreReflectionEnum) { return $this->builderFactory->enum($classReflection->getShortName()); } if ($classReflection->isTrait()) { return $this->builderFactory->trait($classReflection->getShortName()); } if ($classReflection->isInterface()) { return $this->builderFactory->interface($classReflection->getShortName()); } return $this->builderFactory->class($classReflection->getShortName()); } private function addDocComment( Class_|Interface_|Trait_|Enum_|Method|Property|Function_ $node, CoreReflectionClass|CoreReflectionMethod|CoreReflectionProperty|CoreReflectionFunction $reflection, ): void { $docComment = $reflection->getDocComment() ?: ''; $annotations = []; if ( ($reflection instanceof CoreReflectionMethod || $reflection instanceof CoreReflectionFunction) && $reflection->isInternal() ) { if ($reflection->isDeprecated()) { $annotations[] = '@deprecated'; } if (method_exists($reflection, 'hasTentativeReturnType') && $reflection->hasTentativeReturnType()) { $annotations[] = sprintf('@%s', AnnotationHelper::TENTATIVE_RETURN_TYPE_ANNOTATION); } } if ($docComment === '' && $annotations === []) { return; } if ($docComment === '') { $docComment = sprintf("/**\n* %s\n*/", implode("\n *", $annotations)); } elseif ($annotations !== []) { $docComment = preg_replace('~\s+\*/$~', sprintf("\n* %s\n*/", implode("\n *", $annotations)), $docComment); } $node->setDocComment(new Doc($docComment)); } private function addEnumBackingType(Enum_ $enumNode, CoreReflectionEnum $enumReflection): void { if (! $enumReflection->isBacked()) { return; } $backingType = $enumReflection->getBackingType(); assert($backingType instanceof CoreReflectionNamedType); $enumNode->setScalarType($backingType->getName()); } private function addClassModifiers(Class_ $classNode, CoreReflectionClass $classReflection): void { if (! $classReflection->isInterface() && $classReflection->isAbstract()) { // Interface \Iterator is interface and abstract $classNode->makeAbstract(); } if (! $classReflection->isFinal()) { return; } $classNode->makeFinal(); } private function addExtendsAndImplements(Class_|Interface_|Enum_ $classNode, CoreReflectionClass $classReflection): void { $interfaces = $classReflection->getInterfaceNames(); if ($classNode instanceof Class_ || $classNode instanceof Interface_) { $parentClass = $classReflection->getParentClass(); if ($parentClass !== false) { $classNode->extend(new FullyQualified($parentClass->getName())); $interfaces = array_diff($interfaces, $parentClass->getInterfaceNames()); } } foreach ($classReflection->getInterfaces() as $interface) { $interfaces = array_diff($interfaces, $interface->getInterfaceNames()); } foreach ($interfaces as $interfaceName) { $interfaceNode = new FullyQualified($interfaceName); if ($classNode instanceof Interface_) { $classNode->extend($interfaceNode); } else { $classNode->implement($interfaceNode); } } } private function addTraitUse(Class_|Trait_|Enum_ $classNode, CoreReflectionClass $classReflection): void { /** @var array $traitAliases */ $traitAliases = $classReflection->getTraitAliases(); $traitUseAdaptations = []; foreach ($traitAliases as $methodNameAlias => $methodInfo) { [$traitName, $methodName] = explode('::', $methodInfo); $traitUseAdaptation = $this->builderFactory->traitUseAdaptation(new FullyQualified($traitName), $methodName); $traitUseAdaptation->as($methodNameAlias); $traitUseAdaptations[$traitName] = $traitUseAdaptation; } foreach ($classReflection->getTraitNames() as $traitName) { $traitUse = $this->builderFactory->useTrait(new FullyQualified($traitName)); if (array_key_exists($traitName, $traitUseAdaptations)) { $traitUse->with($traitUseAdaptations[$traitName]); } $classNode->addStmt($traitUse); } } private function addProperties(Class_|Trait_ $classNode, CoreReflectionClass $classReflection): void { foreach ($classReflection->getProperties() as $propertyReflection) { if (! $this->isPropertyDeclaredInClass($propertyReflection, $classReflection)) { continue; } $propertyNode = $this->builderFactory->property($propertyReflection->getName()); $this->addPropertyModifiers($propertyNode, $propertyReflection); $this->addDocComment($propertyNode, $propertyReflection); if ($propertyReflection->hasDefaultValue()) { try { $propertyNode->setDefault($propertyReflection->getDefaultValue()); } catch (LogicException) { // Nothing } } $propertyType = $propertyReflection->getType(); if ($propertyType !== null) { assert($propertyType instanceof CoreReflectionNamedType || $propertyType instanceof CoreReflectionUnionType || $propertyType instanceof CoreReflectionIntersectionType); $propertyNode->setType($this->formatType($propertyType)); } $classNode->addStmt($propertyNode); } } private function isPropertyDeclaredInClass(CoreReflectionProperty $propertyReflection, CoreReflectionClass $classReflection): bool { if ($propertyReflection->getDeclaringClass()->getName() !== $classReflection->getName()) { return false; } foreach ($classReflection->getTraits() as $trait) { if ($trait->hasProperty($propertyReflection->getName())) { return false; } } return true; } private function addPropertyModifiers(Property $propertyNode, CoreReflectionProperty $propertyReflection): void { if (method_exists($propertyReflection, 'isReadOnly') && $propertyReflection->isReadOnly()) { $propertyNode->makeReadonly(); } if ($propertyReflection->isStatic()) { $propertyNode->makeStatic(); } if ($propertyReflection->isPublic()) { $propertyNode->makePublic(); } elseif ($propertyReflection->isProtected()) { $propertyNode->makeProtected(); } else { $propertyNode->makePrivate(); } } private function addEnumCases(Enum_ $enumNode, CoreReflectionEnum $enumReflection): void { foreach ($enumReflection->getCases() as $enumCaseReflection) { $enumCaseNode = $this->builderFactory->enumCase($enumCaseReflection->getName()); if ($enumCaseReflection instanceof CoreReflectionEnumBackedCase) { $enumCaseNode->setValue($enumCaseReflection->getBackingValue()); } $enumNode->addStmt($enumCaseNode); } } private function addClassConstants(Class_|Interface_|Trait_|Enum_ $classNode, CoreReflectionClass $classReflection): void { foreach ($classReflection->getReflectionConstants() as $constantReflection) { if (method_exists($constantReflection, 'isEnumCase') && $constantReflection->isEnumCase()) { continue; } if ($constantReflection->getDeclaringClass()->getName() !== $classReflection->getName()) { continue; } $classConstantNode = $this->builderFactory->classConst($constantReflection->getName(), $constantReflection->getValue()); if ($constantReflection->getDocComment() !== false) { $classConstantNode->setDocComment(new Doc($constantReflection->getDocComment())); } $this->addClassConstantModifiers($classConstantNode, $constantReflection); $classNode->addStmt($classConstantNode); } } private function addClassConstantModifiers(ClassConst $classConstantNode, CoreReflectionClassConstant $classConstantReflection): void { if (method_exists($classConstantReflection, 'isFinal') && $classConstantReflection->isFinal()) { $classConstantNode->makeFinal(); } if ($classConstantReflection->isPrivate()) { $classConstantNode->makePrivate(); } elseif ($classConstantReflection->isProtected()) { $classConstantNode->makeProtected(); } else { $classConstantNode->makePublic(); } } private function addMethods(Class_|Interface_|Trait_|Enum_ $classNode, CoreReflectionClass $classReflection): void { foreach ($classReflection->getMethods() as $methodReflection) { if (! $this->isMethodDeclaredInClass($methodReflection, $classReflection)) { continue; } $methodNode = $this->builderFactory->method($methodReflection->getName()); $this->addMethodFlags($methodNode, $methodReflection); $this->addDocComment($methodNode, $methodReflection); $this->addParameters($methodNode, $methodReflection); $returnType = $methodReflection->getReturnType(); if ($returnType === null && method_exists($methodReflection, 'getTentativeReturnType')) { $returnType = $methodReflection->getTentativeReturnType(); } if ($returnType !== null) { assert($returnType instanceof CoreReflectionNamedType || $returnType instanceof CoreReflectionUnionType || $returnType instanceof CoreReflectionIntersectionType); $methodNode->setReturnType($this->formatType($returnType)); } $classNode->addStmt($methodNode); } } private function isMethodDeclaredInClass(CoreReflectionMethod $methodReflection, CoreReflectionClass $classReflection): bool { if ($methodReflection->getDeclaringClass()->getName() !== $classReflection->getName()) { return false; } /** @var array $traitAliases */ $traitAliases = $classReflection->getTraitAliases(); if (array_key_exists($methodReflection->getName(), $traitAliases)) { return false; } foreach ($classReflection->getTraits() as $trait) { if ($trait->hasMethod($methodReflection->getName())) { return false; } } return true; } private function addMethodFlags(Method $methodNode, CoreReflectionMethod $methodReflection): void { if ($methodReflection->isFinal()) { $methodNode->makeFinal(); } if ($methodReflection->isAbstract() && ! $methodReflection->getDeclaringClass()->isInterface()) { $methodNode->makeAbstract(); } if ($methodReflection->isStatic()) { $methodNode->makeStatic(); } if ($methodReflection->isPublic()) { $methodNode->makePublic(); } if ($methodReflection->isProtected()) { $methodNode->makeProtected(); } if ($methodReflection->isPrivate()) { $methodNode->makePrivate(); } if (! $methodReflection->returnsReference()) { return; } $methodNode->makeReturnByRef(); } private function addParameters(FunctionLike $functionNode, CoreReflectionFunctionAbstract $functionReflectionAbstract): void { foreach ($functionReflectionAbstract->getParameters() as $parameterReflection) { $parameterNode = $this->builderFactory->param($parameterReflection->getName()); $this->addParameterModifiers($parameterReflection, $parameterNode); $this->setParameterDefaultValue($parameterReflection, $parameterNode); $functionNode->addParam($parameterNode); } } private function addParameterModifiers(ReflectionParameter $parameterReflection, Param $parameterNode): void { if ($parameterReflection->isVariadic()) { $parameterNode->makeVariadic(); } if ($parameterReflection->isPassedByReference()) { $parameterNode->makeByRef(); } $parameterType = $parameterReflection->getType(); if ($parameterType === null) { return; } assert($parameterType instanceof CoreReflectionNamedType || $parameterType instanceof CoreReflectionUnionType || $parameterType instanceof CoreReflectionIntersectionType); $parameterNode->setType($this->formatType($parameterType)); } private function setParameterDefaultValue(ReflectionParameter $parameterReflection, Param $parameterNode): void { if (! $parameterReflection->isOptional()) { return; } if ($parameterReflection->isVariadic()) { return; } if (! $parameterReflection->isDefaultValueAvailable()) { return; } $parameterNode->setDefault($parameterReflection->getDefaultValue()); } private function formatType(CoreReflectionType $type): Name|NullableType|UnionType|IntersectionType { if ($type instanceof CoreReflectionIntersectionType) { /** @var list $types */ $types = $this->formatTypes($type->getTypes()); return new IntersectionType($types); } if ($type instanceof CoreReflectionUnionType) { /** @var list $types */ $types = $this->formatTypes($type->getTypes()); return new UnionType($types); } assert($type instanceof CoreReflectionNamedType); $name = $type->getName(); $nameNode = $this->formatNamedType($type); if (! $type->allowsNull() || $name === 'mixed' || $name === 'null') { return $nameNode; } return new NullableType($nameNode); } /** * @param list $types * * @return list */ private function formatTypes(array $types): array { return array_map(function (CoreReflectionType $type): Name|UnionType|IntersectionType { $formatedType = $this->formatType($type); assert($formatedType instanceof Name || $formatedType instanceof UnionType || $formatedType instanceof IntersectionType); return $formatedType; }, $types); } private function formatNamedType(CoreReflectionNamedType $type): Name { $name = $type->getName(); return $type->isBuiltin() || in_array($name, ['self', 'parent', 'static'], true) ? new Name($name) : new FullyQualified($name); } private function generateStubInNamespace(Node $node, string $namespaceName): string { $namespaceBuilder = $this->builderFactory->namespace($namespaceName); $namespaceBuilder->addStmt($node); return $this->generateStub($namespaceBuilder->getNode()); } private function generateStub(Node $node): string { return sprintf( "prettyPrinter->prettyPrint([$node]), ($node instanceof Node\Expr\FuncCall ? ';' : ''), ); } /** @param non-empty-string|null $extensionName */ private function createStubData(string $stub, string|null $extensionName): StubData { return new StubData($stub, $extensionName); } } __halt_compiler();----SIGNATURE:----JvPf080/eUO4dO/pxtuJYWS3zdHSqF2nihnqw38QW6UUpKdCXrJQBjV3KUtkcVDs8EWEb8sEd6gNTkPMrQZ3cSbK/EaOO+DKKtCIbT2CO7+wNI0DXwo1Fer4blJoUdkHkxI+yN2g45g2I7PNUV9FRudUhF6vMbAf9uSezhtnOEq9zFhKkZQGpssYZt4WdqvJzQ3IyLiHeFuxum+SkawLJboCoY3nbeu6D1fF38TYc3AOH7pLIZ0H3NSaM5EglEs3/TTrNkrklrC4jRtu69AxA0huPf4kxkjPotpAo2QC3gYGPxaPiJLDME8ZO4Rahp/3CkJSZ1E1/5LMqFvgqqrR1Xi6F2GC3N3cF1k/vhhPx2EPSkxDy2bJ6QYLoOBVnIHfQPbY+NUDIZoce+YMhj8pUtnXYtOorcTauCTHQkyIvf057mKucv+XSMR8yX2VJHuS+x2ceRtlZx6rEvSeGkDzxV4pRtgKYM92JeBJPumc4F5sNdjqa7DhdXltxhXW7eyMDExe2HUUF3CkHKVUXoCUzwRM6loY9eKTBO4VktoE6MrgPKvijQKWqAGv+CFVR6J7jihZY82shGBJcEGyNJ9sjd/rBtkJkVcDPg/CXmlQNA0zg/RmlmYB6zvMvsTdlx8I12u8vbHS0v3kgOR/+7ONMVU0uoXjh8sKOVJe9ifCWHQ=----ATTACHMENT:----NjI0MzM0OTg2MTU1NDQ1MyAyODEyNjczMTAzOTUwODQyIDM3NTUzMzc2MjA4NjQwNDk=