parser = $parserFactory->create(ParserFactory::ONLY_PHP7); } public static function getInstance(): PhpParser { if (static::$instance) { return static::$instance; } return static::$instance = new static(); } /** * @return null|Node\Stmt[] */ public function getNodesFromReflectionClass(ReflectionClass $reflectionClass): ?array { $code = file_get_contents($reflectionClass->getFileName()); return $this->parser->parse($code); } public function getNodeFromReflectionType(ReflectionType $reflection): Node\ComplexType|Node\Identifier|Node\Name { if ($reflection instanceof ReflectionUnionType) { $unionType = []; foreach ($reflection->getTypes() as $objType) { $type = $objType->getName(); if (! in_array($type, static::TYPES)) { $unionType[] = new Node\Name('\\' . $type); } else { $unionType[] = new Node\Identifier($type); } } return new Node\UnionType($unionType); } return $this->getTypeWithNullableOrNot($reflection); } public function getNodeFromReflectionParameter(ReflectionParameter $parameter): Node\Param { $result = new Node\Param( new Node\Expr\Variable($parameter->getName()) ); if ($parameter->isDefaultValueAvailable()) { $result->default = $this->getExprFromValue($parameter->getDefaultValue()); } if ($parameter->hasType()) { $result->type = $this->getNodeFromReflectionType($parameter->getType()); } if ($parameter->isPassedByReference()) { $result->byRef = true; } if ($parameter->isVariadic()) { $result->variadic = true; } return $result; } public function getExprFromValue($value): Node\Expr { return match (gettype($value)) { 'array' => value(function ($value) { $isList = ! Arr::isAssoc($value); $result = []; foreach ($value as $i => $item) { $key = null; if (! $isList) { $key = is_int($i) ? new Node\Scalar\LNumber($i) : new Node\Scalar\String_($i); } $result[] = new Node\Expr\ArrayItem($this->getExprFromValue($item), $key); } return new Node\Expr\Array_($result, [ 'kind' => Node\Expr\Array_::KIND_SHORT, ]); }, $value), 'string' => new Node\Scalar\String_($value), 'integer' => new Node\Scalar\LNumber($value), 'double' => new Node\Scalar\DNumber($value), 'NULL' => new Node\Expr\ConstFetch(new Node\Name('null')), 'boolean' => new Node\Expr\ConstFetch(new Node\Name($value ? 'true' : 'false')), 'object' => $this->getExprFromObject($value), default => throw new InvalidArgumentException($value . ' is invalid'), }; } /** * @return Node\Stmt\ClassMethod[] */ public function getAllMethodsFromStmts(array $stmts): array { $methods = []; foreach ($stmts as $namespace) { if (! $namespace instanceof Node\Stmt\Namespace_) { continue; } foreach ($namespace->stmts as $class) { if (! $class instanceof Node\Stmt\Class_ && ! $class instanceof Node\Stmt\Interface_) { continue; } foreach ($class->getMethods() as $method) { $methods[] = $method; } } } return $methods; } private function getExprFromObject(object $value) { $ref = new ReflectionClass($value); if (method_exists($ref, 'isEnum') && $ref->isEnum()) { return new Node\Expr\ClassConstFetch( new Node\Name('\\' . $value::class), $value->name ); } return new Node\Expr\New_( new Node\Name\FullyQualified($value::class) ); } private function getTypeWithNullableOrNot(ReflectionType $reflection): Node\ComplexType|Node\Identifier|Node\Name { if (! $reflection instanceof ReflectionNamedType) { throw new ReflectionException('ReflectionType must be ReflectionNamedType.'); } $name = $reflection->getName(); if ($reflection->allowsNull() && $name !== 'mixed') { return new Node\NullableType($this->getTypeFromString($name)); } if (! in_array($name, static::TYPES)) { return new Node\Name('\\' . $name); } return new Node\Identifier($name); } private function getTypeFromString(string $name) { if (! in_array($name, static::TYPES)) { return '\\' . $name; } return $name; } }