* @license http://www.apache.org/licenses/LICENSE-2.0 * @link http://phpsx.org */ class Documentor implements ResolverInterface { /** @var ContextFactory */ private $contextFactory; /** @var TypeResolver */ private $typeResolver; public function __construct() { $this->contextFactory = new ContextFactory(); $this->typeResolver = new TypeResolver(); } /** * @inheritDoc */ public function resolveClass(\ReflectionClass $reflection): ?TypeInterface { if ($reflection->implementsInterface(RecordInterface::class)) { $tag = $this->getTag('extends', $reflection->getDocComment()); if (!empty($tag)) { $context = $this->contextFactory->createFromReflector($reflection); $type = $this->buildType($this->typeResolver->resolve($tag, $context)); return $type; } else { throw new ParserException('Could not determine type of map'); } } else { $tag = $this->getTag('extends', $reflection->getDocComment()); if (!empty($tag)) { $parent = $reflection->getParentClass(); if (!$parent instanceof \ReflectionClass) { // we have no parent class return TypeFactory::getStruct(); } $values = $this->getTemplateValues($reflection, $tag); $keys = $this->getTemplateKeys($parent); $template = array_combine($keys, $values); $reference = TypeFactory::getReference(); $reference->setRef($parent->getName()); if (!empty($template)) { $reference->setTemplate($template); } return $reference; } else { return TypeFactory::getStruct(); } } } /** * @inheritDoc */ public function resolveProperty(\ReflectionProperty $reflection): ?TypeInterface { $tag = $this->getTag('var', $reflection->getDocComment()); if (!empty($tag)) { $context = $this->contextFactory->createFromReflector($reflection); $type = $this->buildType($this->typeResolver->resolve($tag, $context)); if ($type instanceof ScalarType) { $properties = $reflection->getDeclaringClass()->getDefaultProperties(); if (isset($properties[$reflection->getName()])) { $type->setConst($properties[$reflection->getName()]); } } return $type; } return null; } private function buildType(Type $type): ?TypeInterface { if ($type instanceof Types\Object_) { $fqsen = (string) $type->getFqsen(); if ($fqsen === '\\' . Date::class) { return TypeFactory::getString()->setFormat(TypeAbstract::FORMAT_DATE); } elseif ($fqsen === '\\' . DateTime::class || $fqsen === '\\' . \DateTime::class) { return TypeFactory::getString()->setFormat(TypeAbstract::FORMAT_DATETIME); } elseif ($fqsen === '\\' . Time::class) { return TypeFactory::getString()->setFormat(TypeAbstract::FORMAT_TIME); } elseif ($fqsen === '\\' . Duration::class || $fqsen === '\\' . \DateInterval::class) { return TypeFactory::getString()->setFormat(TypeAbstract::FORMAT_DURATION); } elseif ($fqsen === '\\' . Uri::class) { return TypeFactory::getString()->setFormat(TypeAbstract::FORMAT_URI); } elseif (!empty($fqsen)) { if (class_exists($fqsen)) { return TypeFactory::getReference($fqsen); } else { return TypeFactory::getGeneric($type->getFqsen()->getName()); } } } elseif ($type instanceof Types\Collection) { $value = $type->getValueType(); $additionalProperties = $this->buildType($value); if ($additionalProperties instanceof TypeInterface) { return TypeFactory::getMap($additionalProperties); } else { throw new ParserException('Map without type hint'); } } elseif ($type instanceof Types\AbstractList) { $value = $type->getValueType(); $items = $this->buildType($value); if ($items instanceof TypeInterface) { return TypeFactory::getArray($items); } else { throw new ParserException('Array without type hint'); } } elseif ($type instanceof Types\Boolean) { return TypeFactory::getBoolean(); } elseif ($type instanceof Types\Integer) { return TypeFactory::getInteger(); } elseif ($type instanceof Types\Float_) { return TypeFactory::getNumber(); } elseif ($type instanceof Types\String_) { return TypeFactory::getString(); } elseif ($type instanceof Types\Mixed_) { return TypeFactory::getAny(); } elseif ($type instanceof Types\Resource_) { return TypeFactory::getString()->setFormat(TypeAbstract::FORMAT_BINARY); } elseif ($type instanceof Types\Nullable) { return $this->buildType($type->getActualType()); } elseif ($type instanceof Types\Compound) { $oneOf = []; foreach ($type as $typ) { $property = $this->buildType($typ); if ($property instanceof TypeInterface) { $oneOf[] = $property; } } if (count($oneOf) > 1) { return TypeFactory::getUnion($oneOf); } else { return reset($oneOf); } } return null; } private function getTag(string $tag, string $comment): ?string { preg_match('/@' . $tag . ' (.*)\R/', $comment, $matches); return $matches[1] ?? null; } private function getTemplateValues(\ReflectionClass $reflection, string $tag) { $values = []; $context = $this->contextFactory->createFromReflector($reflection); $type = $this->typeResolver->resolve($tag, $context); if ($type instanceof Types\Collection) { $value = $type->getValueType(); if ($value instanceof Types\Object_) { $values[] = (string) $value->getFqsen(); } } return $values; } private function getTemplateKeys(\ReflectionClass $reflection) { $tag = $this->getTag('template', $reflection->getDocComment()); $keys = array_map('trim', explode(',', $tag)); // currently we can handle only one type, since we pare also only one value return array_slice($keys, 0, 1); } } __halt_compiler();----SIGNATURE:----UaOMh9jM1FsO2q1CGNJsm5GETDVUdZNIamGtLuosK0x0ghk6ydhN+NE8JhSCJ87c/By//IO9+7dAqEdP0MwoNUxEzaBLP0tBFSFmmLzJM3o5poMBfISz0s3fXA9Ojw25wqRaw5SWty3lD5ioClYKw1jqOMG2fW8VbMdW3bihMgYqNTOxEHQd4ImVv+imRfgYlxAXN3Z6q/QcdcDZ3Cm9y2W+eYAfoVAyJ4pU2dpjpk8BC3ZRP8P/LWVNd5TNsA0XQg/k03CfVLLbRhAU9sapIGokefnQ0PfiFSU0sMGFGiyrp8OIATHxW8paMD68JGlz2xWGbKyW5ygYD8kebD75+geVfYHV5X4gC6U7bRVWcEl0HZytlkUHOBbSMxdYNYcKa06Jy6bnT7U9wP+zusjPmZtYeY27cdB6Rnyv8cPsYl2Cqld3Pnk0SmhJQdDoQgob0b2qChKTcaU4SJaoTYcFNWlJS20GgfzadPNmV5xqCB4ijUlY7ZVi/NPNFMCYU2POP+Tmvo6L9tGpKACmJyU1yjkDB7sTx7quS91uOm/E2pVeZ481DKpNRmDUkyP3b2n8MFIVpo3ORBVlqPr4ox2tBTxCumUtf+BUiz0VbyzLIBIeBy+OEyBsWmUBMNtxmQvT0KbX6Wa0ojvj4W/Xi2EyGQunIhWKVlXvEyltZewXi3A=----ATTACHMENT:----MzA1NjEwNzY2NzM4OTUwMyA2NTg5MDA1MDk0NzExMTk3IDU0MjIwMzM2NzExMTI2NDM=