* @license http://www.apache.org/licenses/LICENSE-2.0 * @link http://phpsx.org */ abstract class CodeGeneratorAbstract implements GeneratorInterface, TypeAwareInterface, FileAwareInterface { /** @var TypeGeneratorInterface */ protected $generator; /** @var string */ protected $namespace; /** @var string */ protected $indent; /** @var array */ protected $mapping; /** @var DefinitionsInterface */ protected $definitions; /** @var Code\Chunks */ private $chunks; /** * @param string|null $namespace * @param array $mapping * @param int $indent */ public function __construct(?string $namespace = null, array $mapping = [], int $indent = 4) { $this->generator = $this->newTypeGenerator($mapping); $this->namespace = $namespace; $this->mapping = $mapping; $this->indent = str_repeat(' ', $indent); } /** * @inheritDoc */ public function generate(SchemaInterface $schema) { $this->chunks = new Code\Chunks($this->namespace); $this->definitions = $schema->getDefinitions(); $types = $this->definitions->getTypes(DefinitionsInterface::SELF_NAMESPACE); foreach ($types as $name => $type) { $this->generateDefinition($name, $type); } $this->generateRoot($schema->getType()); return $this->chunks; } /** * @inheritDoc */ public function getType(TypeInterface $type): string { return $this->generator->getType($type); } /** * @inheritDoc */ public function getDocType(TypeInterface $type): string { return $this->generator->getDocType($type); } /** * @inheritDoc */ public function getFileContent(string $code): string { return $code; } private function generateRoot(TypeInterface $type) { if ($type instanceof StructType) { // for the root schema we need to use the title as class name $this->generateStruct($type->getTitle() ?: 'RootSchema', $type); } } private function generateDefinition(string $name, TypeInterface $type) { if ($type instanceof StructType) { $this->generateStruct($name, $type); } elseif ($type instanceof MapType) { $this->generateMap($name, $type); } elseif ($type instanceof ArrayType) { $this->generateArray($name, $type); } elseif ($type instanceof UnionType) { $this->generateUnion($name, $type); } elseif ($type instanceof IntersectionType) { $this->generateIntersection($name, $type); } elseif ($type instanceof ReferenceType) { $this->generateReference($name, $type); } } private function generateStruct(string $className, StructType $type) { $extends = $type->getExtends(); if (!empty($extends)) { [$ns, $name] = TypeUtil::split($extends); if ($ns === DefinitionsInterface::SELF_NAMESPACE) { $parent = $this->definitions->getType($name); $extends = $this->normalizeClassName($name); if ($parent instanceof StructType) { $this->generateStruct($extends, $parent); } else { throw new GeneratorException('Extends must be of type struct'); } } else { // in case we have an extern namespace we dont need to generate the type $extends = $this->generator->getType((new ReferenceType())->setRef($extends)); } } $className = $this->normalizeClassName($className); $properties = $type->getProperties() ?? []; $generics = []; $required = $type->getRequired() ?: []; $mapping = $type->getAttribute(TypeAbstract::ATTR_MAPPING) ?: []; $props = []; foreach ($properties as $name => $property) { /** @var TypeInterface $property */ if ($property instanceof ReferenceType) { $resolved = $this->definitions->getType($property->getRef()); if (!$this->supportsWrite($name, $resolved)) { // in case the generator produces output for this type we // can also reference the type otherwise we need to define // the type inline $property = $resolved; } } $generic = $this->getGeneric($property); if ($generic instanceof GenericType) { $generics[] = $generic->getGeneric(); } $key = isset($mapping[$name]) ? $mapping[$name] : $name; $key = $this->normalizePropertyName($key); $props[$key] = new Code\Property( $name, $this->generator->getType($property), $this->generator->getDocType($property), in_array($name, $required), $property ); } $code = $this->writeStruct($className, $props, $extends, $generics, $type); if (!empty($code)) { $this->chunks->append($className, $this->wrap($code, $type)); } } private function generateMap(string $className, MapType $type) { $code = $this->writeMap($className, $this->generator->getType($type), $type); if (!empty($code)) { $this->chunks->append($className, $this->wrap($code, $type)); } } private function generateArray(string $className, ArrayType $type) { $code = $this->writeArray($className, $this->generator->getType($type), $type); if (!empty($code)) { $this->chunks->append($className, $this->wrap($code, $type)); } } private function generateUnion(string $className, UnionType $type) { $code = $this->writeUnion($className, $this->generator->getType($type), $type); if (!empty($code)) { $this->chunks->append($className, $this->wrap($code, $type)); } } private function generateIntersection(string $className, IntersectionType $type) { $code = $this->writeIntersection($className, $this->generator->getType($type), $type); if (!empty($code)) { $this->chunks->append($className, $this->wrap($code, $type)); } } private function generateReference(string $className, ReferenceType $type) { $code = $this->writeReference($className, $this->generator->getType($type), $type); if (!empty($code)) { $this->chunks->append($className, $this->wrap($code, $type)); } } private function supportsWrite(string $name, TypeInterface $type) { if ($type instanceof StructType) { return true; } elseif ($type instanceof MapType) { return !!$this->writeMap($name, $this->generator->getType($type), $type); } elseif ($type instanceof ArrayType) { return !!$this->writeArray($name, $this->generator->getType($type), $type); } elseif ($type instanceof UnionType) { return !!$this->writeUnion($name, $this->generator->getType($type), $type); } elseif ($type instanceof IntersectionType) { return !!$this->writeIntersection($name, $this->generator->getType($type), $type); } elseif ($type instanceof ReferenceType) { return !!$this->writeReference($name, $this->generator->getType($type), $type); } return false; } private function getGeneric(TypeInterface $type): ?GenericType { $item = $type; if ($type instanceof MapType) { $item = $type->getAdditionalProperties(); } elseif ($type instanceof ArrayType) { $item = $type->getItems(); } if ($item instanceof GenericType) { return $item; } else { return null; } } private function wrap(string $code, TypeAbstract $type): string { return implode("\n", array_filter(array_map('trim', [ $this->writeHeader($type), $code, $this->writeFooter($type) ]))) . "\n"; } /** * @param string $name * @return string */ protected function normalizePropertyName(string $name): string { return lcfirst(str_replace(' ', '', ucwords(preg_replace('/[^A-Za-z0-9_]/', ' ', $name)))); } /** * @param string $name * @return string */ protected function normalizeClassName(string $name): string { return str_replace(' ', '', ucwords(preg_replace('/[^A-Za-z0-9_]/', ' ', $name))); } /** * @param array $mapping * @return \PSX\Schema\Generator\Type\GeneratorInterface */ abstract protected function newTypeGenerator(array $mapping): TypeGeneratorInterface; /** * @param string $name * @param array $properties * @param string|null $extends * @param array|null $generics * @return string */ abstract protected function writeStruct( string $name, array $properties, ?string $extends, ?array $generics, StructType $origin, ): string; protected function writeMap(string $name, string $type, MapType $origin): string { return ''; } protected function writeArray(string $name, string $type, ArrayType $origin): string { return ''; } protected function writeUnion(string $name, string $type, UnionType $origin): string { return ''; } protected function writeIntersection(string $name, string $type, IntersectionType $origin): string { return ''; } protected function writeReference(string $name, string $type, ReferenceType $origin): string { return ''; } protected function writeHeader(TypeAbstract $origin): string { return ''; } protected function writeFooter(TypeAbstract $origin): string { return ''; } } __halt_compiler();----SIGNATURE:----OS9flbJN7ENNyLTrp8HPIsWaLnWjp0JKkgeQiBSO+uXLGKvJgv/Q3eyMeyqcr0wEA5gkuw9ZrGEtlZ/LX0EnPAW2PLWC/LwhHqxLOHsT7jU7TPT0IVwVhIB+cxWT0IkDdhgcnVr6+A4O1T+RlHdKXzVwbS1WnzPVjPhyLgY5w9esUvc7Qhxf+twBCLuoF9ha5pXdRb+kpOCbhXQE9h88sxrVKfZ2mXJIbUicC6/mwXyizt29enFVKIGxAKGFnk0mmeRGt2B3Dm0TaVVWH6zNO+BnoHjMW9YtNuHus9LSOVgHe5mJIR5p4hQMlK39pvBm8cIiTd2kKz59/D3lrKFqoLaunbv3opVbChkzC92rgU5tugARscRkStReq+OS0z6ByCC3B4eH9VLmgynJUXtul46x8ALIF7reC1Q2XcjeW5OgXRaaeOWVEt/WzaCxIqkheNVAk93AGwKcTL0h/TiyTNfC9J5giG0piquphm2Zr/u502VxvCnjtyChvK9ZDmIaV0fRcbOxWaXP4JywqSKoSAIN8nejYTZht05AiKm4n8amHiVo/9zUn417E7qIA3obN3iSNj+v2FlIQqNhOweTvqo3pPiw6BMnfiybCPzdxrc6VxPFQbNKI3yEerN8mSrLSUHc0/+tXOhc6uq+GfiVxAkRvA+4b0AmkNn6tjnZoz8=----ATTACHMENT:----ODgxNDc5NzEzOTQ1NDc1NSAyMTEzMTU1NTE3MzYyMzAwIDIxODQ5MzEwNzMyMDA4MDY=