diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..d88fae1 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,4 @@ +/Tests/ export-ignore +/.gitattributes export-ignore +/.gitignore export-ignore +/phpunit.xml export-ignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0a2e709 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +vendor +composer.lock + +Tests/generated/* +!Tests/generated/.gitkeep +Tests/fixtures/*/generated diff --git a/Application.php b/Application.php new file mode 100644 index 0000000..69fd1ee --- /dev/null +++ b/Application.php @@ -0,0 +1,32 @@ +boot(); + } + + protected function boot(): void + { + $configLoader = new ConfigLoader(); + + $this->add(new GenerateCommand($configLoader, new SchemaLoader())); + $this->add(new DumpConfigCommand($configLoader)); + } +} diff --git a/Console/Command/DumpConfigCommand.php b/Console/Command/DumpConfigCommand.php new file mode 100644 index 0000000..78b0d9f --- /dev/null +++ b/Console/Command/DumpConfigCommand.php @@ -0,0 +1,36 @@ +configLoader = $configLoader; + } + + public function configure() + { + $this->setName('dump-config'); + $this->setDescription('Dump Jane configuration for debugging purpose'); + $this->addOption('config-file', 'c', InputOption::VALUE_REQUIRED, 'File to use for Jane configuration', '.jane'); + } + + public function execute(InputInterface $input, OutputInterface $output) + { + VarDumper::dump($this->configLoader->load($input->getOption('config-file'))); + + return 0; + } +} diff --git a/Console/Command/GenerateCommand.php b/Console/Command/GenerateCommand.php new file mode 100644 index 0000000..381e5a2 --- /dev/null +++ b/Console/Command/GenerateCommand.php @@ -0,0 +1,104 @@ +configLoader = $configLoader; + $this->schemaLoader = $schemaLoader; + } + + /** + * {@inheritdoc} + */ + public function configure() + { + $this->setName('generate'); + $this->setDescription('Generate a set of class and normalizers given a specific Json Schema file'); + $this->addOption('config-file', 'c', InputOption::VALUE_REQUIRED, 'File to use for Jane configuration', '.jane'); + } + + /** + * {@inheritdoc} + */ + public function execute(InputInterface $input, OutputInterface $output) + { + $options = $this->configLoader->load($input->getOption('config-file')); + $registries = $this->registries($options); + + foreach ($registries as $registry) { + $jane = Jane::build($options); + $fixerConfigFile = ''; + + if (\array_key_exists('fixer-config-file', $options) && null !== $options['fixer-config-file']) { + $fixerConfigFile = $options['fixer-config-file']; + } + + $printer = new Printer(new Standard(), $fixerConfigFile); + + if (\array_key_exists('use-fixer', $options) && \is_bool($options['use-fixer'])) { + $printer->setUseFixer($options['use-fixer']); + } + if (\array_key_exists('clean-generated', $options) && \is_bool($options['clean-generated'])) { + $printer->setCleanGenerated($options['clean-generated']); + } + + $jane->generate($registry); + $printer->output($registry); + } + + return 0; + } + + protected function registries(array $options): array + { + $registries = []; + if (\array_key_exists($fileKey = $this->configLoader->fileKey(), $options)) { + $localRegistry = $this->newRegistry($options[$fileKey], $options); + $localRegistry->addSchema($this->schemaLoader->resolve($options[$fileKey], $options)); + $localRegistry->addOutputDirectory($options['directory']); + + $registries[] = $localRegistry; + } else { + foreach ($options['mapping'] as $schema => $schemaOptions) { + $mappedSchema = $this->schemaLoader->resolve($schema, $schemaOptions); + $mappedRegistry = $this->newRegistry($schema, $schemaOptions); + + if (!\array_key_exists($hash = $mappedRegistry->getOptionsHash(), $registries)) { + $registries[$hash] = $mappedRegistry; + } + + $registries[$hash]->addSchema($mappedSchema); + $registries[$hash]->addOutputDirectory($schemaOptions['directory']); + } + } + + return $registries; + } + + protected function newRegistry(string $schemaFile, array $options): RegistryInterface + { + return new Registry(); + } +} diff --git a/Console/Loader/ConfigLoader.php b/Console/Loader/ConfigLoader.php new file mode 100644 index 0000000..d8c6637 --- /dev/null +++ b/Console/Loader/ConfigLoader.php @@ -0,0 +1,72 @@ +resolveConfiguration($options); + } + + protected function resolveConfiguration(array $options = []) + { + $optionsResolver = new OptionsResolver(); + $optionsResolver->setDefaults($this->resolveConfigurationDefaults()); + + if (\array_key_exists($this->fileKey(), $options)) { + $optionsResolver->setRequired($this->resolveConfigurationRequired()); + } else { + $optionsResolver->setRequired([ + 'mapping', + ]); + } + + return $optionsResolver->resolve($options); + } + + protected function resolveConfigurationRequired(): array + { + return [ + $this->fileKey(), + 'root-class', + 'namespace', + 'directory', + ]; + } + + protected function resolveConfigurationDefaults(): array + { + return [ + 'reference' => true, + 'strict' => true, + 'date-format' => \DateTime::RFC3339, + 'full-date-format' => 'Y-m-d', + 'date-prefer-interface' => null, + 'date-input-format' => null, + 'use-fixer' => false, + 'fixer-config-file' => null, + 'clean-generated' => true, + 'use-cacheable-supports-method' => null, + 'skip-null-values' => true, + 'skip-required-fields' => false, + ]; + } +} diff --git a/Console/Loader/ConfigLoaderInterface.php b/Console/Loader/ConfigLoaderInterface.php new file mode 100644 index 0000000..60cc446 --- /dev/null +++ b/Console/Loader/ConfigLoaderInterface.php @@ -0,0 +1,10 @@ +setDefined($this->getDefinedOptions()); + $optionsResolver->setRequired($this->getRequiredOptions()); + $options = $optionsResolver->resolve($options); + + return $this->newSchema($schema, $options); + } + + protected function newSchema(string $schema, array $options): SchemaInterface + { + return new Schema($schema, $options['namespace'], $options['directory'], $options['root-class'] ?? ''); + } + + protected function getDefinedOptions(): array + { + return [ + 'json-schema-file', + 'reference', + 'date-format', + 'full-date-format', + 'date-prefer-interface', + 'date-input-format', + 'strict', + 'use-fixer', + 'fixer-config-file', + 'clean-generated', + 'use-cacheable-supports-method', + 'skip-null-values', + 'skip-required-fields', + ]; + } + + protected function getRequiredOptions(): array + { + return [ + 'root-class', + 'namespace', + 'directory', + ]; + } +} diff --git a/Console/Loader/SchemaLoaderInterface.php b/Console/Loader/SchemaLoaderInterface.php new file mode 100644 index 0000000..0d9c918 --- /dev/null +++ b/Console/Loader/SchemaLoaderInterface.php @@ -0,0 +1,10 @@ +generators[] = $generator; + } + + abstract protected function createContext(Registry $registry): Context; + + public function generate(Registry $registry): void + { + $context = $this->createContext($registry); + + foreach ($registry->getSchemas() as $schema) { + $context->setCurrentSchema($schema); + + foreach ($this->generators as $generator) { + $generator->generate($schema, $schema->getRootName(), $context); + } + } + } +} diff --git a/Generator/Context/Context.php b/Generator/Context/Context.php new file mode 100644 index 0000000..0192124 --- /dev/null +++ b/Generator/Context/Context.php @@ -0,0 +1,60 @@ +registry = $registry; + $this->variableScope = new UniqueVariableScope(); + $this->strict = $strict; + } + + public function isStrict(): bool + { + return $this->strict; + } + + public function getRegistry(): Registry + { + return $this->registry; + } + + public function getCurrentSchema(): Schema + { + return $this->currentSchema; + } + + public function setCurrentSchema(Schema $currentSchema): void + { + $this->currentSchema = $currentSchema; + } + + /** + * Refresh the unique variable scope for a context. + */ + public function refreshScope(): void + { + $this->variableScope = new UniqueVariableScope(); + } + + public function getUniqueVariableName(string $prefix = 'var'): string + { + return $this->variableScope->getUniqueName($prefix); + } +} diff --git a/Generator/Context/UniqueVariableScope.php b/Generator/Context/UniqueVariableScope.php new file mode 100644 index 0000000..ce74673 --- /dev/null +++ b/Generator/Context/UniqueVariableScope.php @@ -0,0 +1,31 @@ +registry[$name])) { + $this->registry[$name] = 0; + + return $name; + } + + ++$this->registry[$name]; + + return sprintf('%s_%s', $name, $this->registry[$name]); + } +} diff --git a/Generator/File.php b/Generator/File.php new file mode 100644 index 0000000..e35862a --- /dev/null +++ b/Generator/File.php @@ -0,0 +1,50 @@ +filename = $filename; + $this->node = $node; + $this->type = $type; + } + + public function getFilename(): string + { + return $this->filename; + } + + public function getNode(): Node + { + return $this->node; + } + + public function getType(): string + { + return $this->type; + } +} diff --git a/Generator/GeneratorInterface.php b/Generator/GeneratorInterface.php new file mode 100644 index 0000000..03a2bde --- /dev/null +++ b/Generator/GeneratorInterface.php @@ -0,0 +1,14 @@ +getNaming()->getClassName($name), + [ + 'stmts' => array_merge($properties, $methods), + 'extends' => $hasExtensions ? new Name('\ArrayObject') : null, + ], + $attributes + ); + } +} diff --git a/Generator/Model/GetterSetterGenerator.php b/Generator/Model/GetterSetterGenerator.php new file mode 100644 index 0000000..cc70a87 --- /dev/null +++ b/Generator/Model/GetterSetterGenerator.php @@ -0,0 +1,167 @@ +getType()->getTypeHint($namespace); + + if ($returnType && (!$strict || $property->isNullable())) { + $returnType = '?' . $returnType; + } + + return new Stmt\ClassMethod( + // getProperty + $this->getNaming()->getPrefixedMethodName('get', $property->getPhpName()), + [ + // public function + 'type' => Stmt\Class_::MODIFIER_PUBLIC, + 'stmts' => [ + // return $this->property; + new Stmt\Return_( + new Expr\PropertyFetch(new Expr\Variable('this'), $property->getPhpName()) + ), + ], + 'returnType' => $returnType, + ], [ + 'comments' => [$this->createGetterDoc($property, $namespace, $strict)], + ] + ); + } + + protected function createSetter(Property $property, string $namespace, bool $strict, bool $fluent = true): Stmt\ClassMethod + { + $setType = $property->getType()->getTypeHint($namespace); + + if ($setType && (!$strict || $property->isNullable())) { + $setType = '?' . $setType; + } + + $stmts = [ + // $this->property = $property; + new Stmt\Expression(new Expr\Assign( + new Expr\PropertyFetch( + new Expr\Variable('this'), + $this->getNaming()->getPropertyName($property->getPhpName()) + ), new Expr\Variable($this->getNaming()->getPropertyName($property->getPhpName())) + )), + ]; + + if ($fluent) { + // return $this; + $stmts[] = new Stmt\Return_(new Expr\Variable('this')); + } + + return new Stmt\ClassMethod( + // setProperty + $this->getNaming()->getPrefixedMethodName('set', $property->getPhpName()), + [ + // public function + 'type' => Stmt\Class_::MODIFIER_PUBLIC, + // ($property) + 'params' => [ + new Param(new Expr\Variable($this->getNaming()->getPropertyName($property->getPhpName())), null, $setType), + ], + 'stmts' => $stmts, + 'returnType' => $fluent ? 'self' : null, + ], [ + 'comments' => [$this->createSetterDoc($property, $namespace, $strict, $fluent)], + ] + ); + } + + protected function createGetterDoc(Property $property, string $namespace, bool $strict): Doc + { + $description = sprintf(<<getDescription()); + + if ($property->isDeprecated()) { + $description .= <<getDocType($property, $namespace, $strict)); + + return new Doc($description); + } + + protected function createSetterDoc(Property $property, string $namespace, bool $strict, bool $fluent): Doc + { + $description = sprintf(<<getDescription(), $this->getDocType($property, $namespace, $strict), '$' . $property->getPhpName()); + + if ($property->isDeprecated()) { + $description .= <<getType(); + $returnTypeHint = $returnType->getDocTypeHint($namespace); + if ($strict && !$property->isNullable()) { + return $returnTypeHint; + } + $returnTypes = [$returnType]; + if ($returnType instanceof MultipleType) { + $returnTypes = $returnType->getTypes(); + } + if (!\count(array_intersect([Type::TYPE_MIXED, Type::TYPE_NULL], $returnTypes))) { + $returnTypeHint .= '|' . Type::TYPE_NULL; + } + + return $returnTypeHint; + } +} diff --git a/Generator/Model/PropertyGenerator.php b/Generator/Model/PropertyGenerator.php new file mode 100644 index 0000000..02e0841 --- /dev/null +++ b/Generator/Model/PropertyGenerator.php @@ -0,0 +1,80 @@ +getNaming()->getPropertyName($property->getPhpName()); + $propertyStmt = new Stmt\PropertyProperty($propertyName); + + if (null === $default) { + $default = $property->getDefault(); + } + + if ((null !== $default && is_scalar($default)) || (Type::TYPE_ARRAY === $property->getType()->getTypeHint($namespace) && \is_array($default))) { + $propertyStmt->default = $this->getDefaultAsExpr($default)->expr; + } + + return new Stmt\Property(Stmt\Class_::MODIFIER_PROTECTED, [ + $propertyStmt, + ], [ + 'comments' => [$this->createPropertyDoc($property, $namespace, $strict)], + ]); + } + + protected function createPropertyDoc(Property $property, $namespace, bool $strict): Doc + { + $docTypeHint = $property->getType()->getDocTypeHint($namespace); + if ((!$strict || $property->isNullable()) && strpos($docTypeHint, 'null') === false) { + $docTypeHint .= '|null'; + } + + $description = sprintf(<<getDescription()); + + if ($property->isDeprecated()) { + $description .= <<parser->parse('naming = $naming; + $this->parser = $parser; + } + + /** + * The naming service. + */ + protected function getNaming(): Naming + { + return $this->naming; + } + + /** + * {@inheritdoc} + */ + protected function getParser(): Parser + { + return $this->parser; + } + + /** + * Generate a model given a schema. + */ + public function generate(Schema $schema, string $className, Context $context): void + { + $namespace = $schema->getNamespace() . '\\Model'; + + foreach ($schema->getClasses() as $class) { + $properties = []; + $methods = []; + + /** @var Property $property */ + foreach ($class->getProperties() as $property) { + $properties[] = $this->createProperty($property, $namespace, null, $context->isStrict()); + $methods = array_merge($methods, $this->doCreateClassMethods($class, $property, $namespace, $context->isStrict())); + } + + $model = $this->doCreateModel($class, $properties, $methods); + + $namespaceStmt = new Stmt\Namespace_(new Name($namespace), [$model]); + $schema->addFile(new File($schema->getDirectory() . '/Model/' . $class->getName() . '.php', $namespaceStmt, self::FILE_TYPE_MODEL)); + } + } + + protected function doCreateClassMethods(ClassGuess $classGuess, Property $property, string $namespace, bool $strict): array + { + $methods = []; + $methods[] = $this->createGetter($property, $namespace, $strict); + $methods[] = $this->createSetter($property, $namespace, $strict); + + return $methods; + } + + protected function doCreateModel(ClassGuess $class, array $properties, array $methods): Stmt\Class_ + { + return $this->createModel( + $class->getName(), + $properties, + $methods, + \count($class->getExtensionsType()) > 0, + $class->isDeprecated() + ); + } +} diff --git a/Generator/Naming.php b/Generator/Naming.php new file mode 100644 index 0000000..d6be8a6 --- /dev/null +++ b/Generator/Naming.php @@ -0,0 +1,101 @@ +cleaning($name); + + return $name; + } + + public function getPrefixedMethodName(string $prefix, string $name): string + { + $name = $this->cleaning($name); + + return sprintf('%s%s', $prefix, $this->getInflector()->classify($name)); + } + + public function getClassName(string $name): string + { + $name = $this->cleaning($name, true); + + if (preg_match(self::BAD_CLASS_NAME_REGEX, $name)) { + $name = '_' . $name; + } + + return $name; + } + + public function getAuthName(string $name): string + { + return $this->getClassName(sprintf('%sAuthentication', $name)); + } + + public function getRuntimeNamespace(string $schemaNamespace, array $namespace): string + { + $namespaceSuffix = ''; + if (\count($namespace) > 0) { + $namespaceSuffix = '\\' . implode('\\', $namespace); + } + + return $schemaNamespace . '\\Runtime' . $namespaceSuffix; + } + + public function getRuntimeClassFQCN(string $schemaNamespace, array $namespace, string $class): string + { + return sprintf('%s\\%s', $this->getRuntimeNamespace($schemaNamespace, $namespace), $class); + } + + protected function cleaning(string $name, bool $class = false): string + { + if (preg_match('/\$/', $name)) { + $name = preg_replace_callback('/\$([a-z])/', function ($matches) { + return 'dollar' . ucfirst($matches[1]); + }, $name); + } + + $name = preg_replace_callback('#[/\{\}]+(\w)#', function ($matches) { + return ucfirst($matches[1]); + }, $name); + + // Doctrine Inflector does not seem to handle some characters (like dots, @, :) well. + // So replace invalid char by an underscore to allow Doctrine to uppercase word correctly. + $name = trim(preg_replace('/[^a-z0-9 ]+/iu', '_', $name)); + + if ($class) { + return $this->getInflector()->classify($name); + } + + return $this->getInflector()->camelize($name); + } +} diff --git a/Generator/Normalizer/DenormalizerGenerator.php b/Generator/Normalizer/DenormalizerGenerator.php new file mode 100644 index 0000000..de0a7c6 --- /dev/null +++ b/Generator/Normalizer/DenormalizerGenerator.php @@ -0,0 +1,176 @@ + Stmt\Class_::MODIFIER_PUBLIC, + 'params' => [ + new Param(new Expr\Variable('data')), + new Param(new Expr\Variable('type')), + new Param(new Expr\Variable('format'), new Expr\ConstFetch(new Name('null'))), + ], + 'stmts' => [new Stmt\Return_(new Expr\BinaryOp\Identical(new Expr\Variable('type'), new Scalar\String_($modelFqdn)))], + ]); + } + + protected function createDenormalizeMethod(string $modelFqdn, Context $context, ClassGuess $classGuess): Stmt\ClassMethod + { + $context->refreshScope(); + $objectVariable = new Expr\Variable('object'); + $dataVariable = new Expr\Variable('data'); + $statements = []; + + if ($this->useReference) { + $statements[] = new Stmt\If_( + new Expr\Isset_([new Expr\ArrayDimFetch($dataVariable, new Scalar\String_('$ref'))]), + [ + 'stmts' => [ + new Stmt\Return_(new Expr\New_(new Name('Reference'), [ + new Arg(new Expr\ArrayDimFetch($dataVariable, new Scalar\String_('$ref'))), + new Arg(new Expr\ArrayDimFetch(new Expr\Variable('context'), new Scalar\String_('document-origin'))), + ])), + ], + ] + ); + $statements[] = new Stmt\If_( + new Expr\Isset_([new Expr\ArrayDimFetch($dataVariable, new Scalar\String_('$recursiveRef'))]), + [ + 'stmts' => [ + new Stmt\Return_(new Expr\New_(new Name('Reference'), [ + new Arg(new Expr\ArrayDimFetch($dataVariable, new Scalar\String_('$recursiveRef'))), + new Arg(new Expr\ArrayDimFetch(new Expr\Variable('context'), new Scalar\String_('document-origin'))), + ])), + ], + ] + ); + } + + $statements[] = new Stmt\Expression(new Expr\Assign($objectVariable, new Expr\New_(new Name('\\' . $modelFqdn)))); + + $denormalizeMethodStatements = $this->denormalizeMethodStatements($classGuess, $context); + if (\count($denormalizeMethodStatements) > 0) { + array_unshift($statements, ...$denormalizeMethodStatements); + } + + $unset = \count($classGuess->getExtensionsType()) > 0; + + $statements[] = new Stmt\If_(new Expr\BinaryOp\BooleanOr(new Expr\BinaryOp\Identical(new Expr\ConstFetch(new Name('null')), $dataVariable), new Expr\BinaryOp\Identical(new Expr\ConstFetch(new Name('false')), new Expr\FuncCall(new Name('\is_array'), [new Arg($dataVariable)]))), [ + 'stmts' => [new Stmt\Return_($objectVariable)], + ]); + + foreach ($classGuess->getProperties() as $property) { + $propertyVar = new Expr\ArrayDimFetch($dataVariable, new Scalar\String_($property->getName())); + list($denormalizationStatements, $outputVar) = $property->getType()->createDenormalizationStatement($context, $propertyVar); + + $baseCondition = new Expr\FuncCall(new Name('\array_key_exists'), [ + new Arg(new Scalar\String_($property->getName())), + new Arg($dataVariable), + ]); + $fullCondition = $baseCondition; + + $mutatorStmt = array_merge($denormalizationStatements, [ + new Stmt\Expression(new Expr\MethodCall($objectVariable, $this->getNaming()->getPrefixedMethodName('set', $property->getPhpName()), [$outputVar])), + ], $unset ? [new Stmt\Unset_([$propertyVar])] : []); + + if (!$context->isStrict() || $property->isNullable()) { + $fullCondition = new Expr\BinaryOp\BooleanAnd( + $baseCondition, + new Expr\BinaryOp\NotIdentical( + $propertyVar, + new Expr\ConstFetch(new Name('null')) + ) + ); + } + + $statements[] = new Stmt\If_($fullCondition, [ + 'stmts' => $mutatorStmt, + ]); + + if (!$context->isStrict() || $property->isNullable()) { + $invertCondition = new Expr\BinaryOp\BooleanAnd( + $baseCondition, + new Expr\BinaryOp\Identical( + $propertyVar, + new Expr\ConstFetch(new Name('null')) + ) + ); + + $statements[] = new Stmt\ElseIf_($invertCondition, [ + new Stmt\Expression(new Expr\MethodCall($objectVariable, $this->getNaming()->getPrefixedMethodName('set', $property->getPhpName()), [new Expr\ConstFetch(new Name('null'))])), + ]); + } + } + + $patternCondition = []; + $loopKeyVar = new Expr\Variable($context->getUniqueVariableName('key')); + $loopValueVar = new Expr\Variable($context->getUniqueVariableName('value')); + + foreach ($classGuess->getExtensionsType() as $pattern => $type) { + list($denormalizationStatements, $outputVar) = $type->createDenormalizationStatement($context, $loopValueVar); + + $patternCondition[] = new Stmt\If_( + new Expr\FuncCall(new Name('preg_match'), [ + new Arg(new Expr\ConstFetch(new Name("'/" . str_replace('/', '\/', $pattern) . "/'"))), + new Arg(new Expr\Cast\String_($loopKeyVar)), + ]), + [ + 'stmts' => array_merge($denormalizationStatements, [ + new Stmt\Expression(new Expr\Assign(new Expr\ArrayDimFetch($objectVariable, $loopKeyVar), $outputVar)), + ]), + ] + ); + } + + if (\count($patternCondition) > 0) { + $statements[] = new Stmt\Foreach_($dataVariable, $loopValueVar, [ + 'keyVar' => $loopKeyVar, + 'stmts' => $patternCondition, + ]); + } + $statements[] = new Stmt\Return_($objectVariable); + + return new Stmt\ClassMethod('denormalize', [ + 'type' => Stmt\Class_::MODIFIER_PUBLIC, + 'params' => [ + new Param($dataVariable), + new Param(new Expr\Variable('class')), + new Param(new Expr\Variable('format'), new Expr\ConstFetch(new Name('null'))), + new Param(new Expr\Variable('context'), new Expr\Array_(), 'array'), + ], + 'stmts' => $statements, + ]); + } + + protected function denormalizeMethodStatements(ClassGuess $classGuess, Context $context): array + { + return []; + } +} diff --git a/Generator/Normalizer/JaneObjectNormalizerGenerator.php b/Generator/Normalizer/JaneObjectNormalizerGenerator.php new file mode 100644 index 0000000..29049b1 --- /dev/null +++ b/Generator/Normalizer/JaneObjectNormalizerGenerator.php @@ -0,0 +1,164 @@ + Stmt\Class_::MODIFIER_PUBLIC, + 'params' => [ + new Param(new Expr\Variable('data')), + new Param(new Expr\Variable('type')), + new Param(new Expr\Variable('format'), new Expr\ConstFetch(new Name('null'))), + ], + 'stmts' => [new Stmt\Return_(new Expr\FuncCall(new Name('array_key_exists'), [ + new Arg(new Expr\Variable('type')), + new Arg(new Expr\PropertyFetch(new Expr\Variable('this'), 'normalizers')), + ]))], + ]); + } + + protected function createBaseNormalizerSupportsNormalizationMethod(): Stmt\ClassMethod + { + return new Stmt\ClassMethod('supportsNormalization', [ + 'type' => Stmt\Class_::MODIFIER_PUBLIC, + 'params' => [ + new Param(new Expr\Variable('data')), + new Param(new Expr\Variable('format'), new Expr\ConstFetch(new Name('null'))), + ], + 'stmts' => [new Stmt\Return_( + new Expr\BinaryOp\BooleanAnd( + new Expr\FuncCall(new Name('is_object'), [new Arg(new Expr\Variable('data'))]), + new Expr\FuncCall(new Name('array_key_exists'), [ + new Arg(new Expr\FuncCall(new Name('get_class'), [new Arg(new Expr\Variable('data'))])), + new Arg(new Expr\PropertyFetch(new Expr\Variable('this'), 'normalizers')), + ]) + ))], + ]); + } + + protected function createBaseNormalizerNormalizeMethod(): Stmt\ClassMethod + { + return new Stmt\ClassMethod('normalize', [ + 'type' => Stmt\Class_::MODIFIER_PUBLIC, + 'params' => [ + new Param(new Expr\Variable('object')), + new Param(new Expr\Variable('format'), new Expr\ConstFetch(new Name('null'))), + new Param(new Expr\Variable('context'), new Expr\Array_(), 'array'), + ], + 'stmts' => [ + new Stmt\Expression(new Expr\Assign( + new Expr\Variable('normalizerClass'), + new Expr\ArrayDimFetch( + new Expr\PropertyFetch(new Expr\Variable('this'), 'normalizers'), + new Expr\FuncCall(new Name('get_class'), [new Arg(new Expr\Variable('object'))]) + ) + )), + new Stmt\Expression(new Expr\Assign( + new Expr\Variable('normalizer'), + new Expr\MethodCall(new Expr\Variable('this'), 'getNormalizer', [ + new Arg(new Expr\Variable('normalizerClass')), + ]) + )), + new Stmt\Return_(new Expr\MethodCall(new Expr\Variable('normalizer'), 'normalize', [ + new Arg(new Expr\Variable('object')), + new Arg(new Expr\Variable('format')), + new Arg(new Expr\Variable('context')), + ])), + ], + ]); + } + + protected function createBaseNormalizerDenormalizeMethod(): Stmt\ClassMethod + { + return new Stmt\ClassMethod('denormalize', [ + 'type' => Stmt\Class_::MODIFIER_PUBLIC, + 'params' => [ + new Param(new Expr\Variable('data')), + new Param(new Expr\Variable('class')), + new Param(new Expr\Variable('format'), new Expr\ConstFetch(new Name('null'))), + new Param(new Expr\Variable('context'), new Expr\Array_(), 'array'), + ], + 'stmts' => [ + new Stmt\Expression(new Expr\Assign( + new Expr\Variable('denormalizerClass'), + new Expr\ArrayDimFetch( + new Expr\PropertyFetch(new Expr\Variable('this'), 'normalizers'), + new Expr\Variable('class') + ) + )), + new Stmt\Expression(new Expr\Assign( + new Expr\Variable('denormalizer'), + new Expr\MethodCall(new Expr\Variable('this'), 'getNormalizer', [ + new Arg(new Expr\Variable('denormalizerClass')), + ]) + )), + new Stmt\Return_(new Expr\MethodCall(new Expr\Variable('denormalizer'), 'denormalize', [ + new Arg(new Expr\Variable('data')), + new Arg(new Expr\Variable('class')), + new Arg(new Expr\Variable('format')), + new Arg(new Expr\Variable('context')), + ])), + ], + ]); + } + + protected function createBaseNormalizerGetNormalizer(): Stmt\ClassMethod + { + return new Stmt\ClassMethod('getNormalizer', [ + 'type' => Stmt\Class_::MODIFIER_PRIVATE, + 'params' => [ + new Param(new Expr\Variable('normalizerClass'), null, 'string'), + ], + 'stmts' => [ + new Stmt\Return_(new Expr\BinaryOp\Coalesce( + new Expr\ArrayDimFetch( + new Expr\PropertyFetch(new Expr\Variable('this'), 'normalizersCache'), + new Expr\Variable('normalizerClass') + ), + new Expr\MethodCall(new Expr\Variable('this'), 'initNormalizer', [ + new Arg(new Expr\Variable('normalizerClass')), + ]) + )), + ], + ]); + } + + protected function createBaseNormalizerInitNormalizerMethod(): Stmt\ClassMethod + { + return new Stmt\ClassMethod('initNormalizer', [ + 'type' => Stmt\Class_::MODIFIER_PRIVATE, + 'params' => [ + new Param(new Expr\Variable('normalizerClass'), null, 'string'), + ], + 'stmts' => [ + new Stmt\Expression(new Expr\Assign( + new Expr\Variable('normalizer'), + new Expr\New_(new Expr\Variable('normalizerClass')) + )), + new Stmt\Expression(new Expr\MethodCall(new Expr\Variable('normalizer'), 'setNormalizer', [ + new Arg(new Expr\PropertyFetch(new Expr\Variable('this'), 'normalizer')), + ])), + new Stmt\Expression(new Expr\MethodCall(new Expr\Variable('normalizer'), 'setDenormalizer', [ + new Arg(new Expr\PropertyFetch(new Expr\Variable('this'), 'denormalizer')), + ])), + new Stmt\Expression(new Expr\Assign( + new Expr\ArrayDimFetch( + new Expr\PropertyFetch(new Expr\Variable('this'), 'normalizersCache'), + new Expr\Variable('normalizerClass') + ), + new Expr\Variable('normalizer') + )), + new Stmt\Return_(new Expr\Variable('normalizer')), + ], + ]); + } +} diff --git a/Generator/Normalizer/NormalizerGenerator.php b/Generator/Normalizer/NormalizerGenerator.php new file mode 100644 index 0000000..11a3833 --- /dev/null +++ b/Generator/Normalizer/NormalizerGenerator.php @@ -0,0 +1,182 @@ +getNaming()->getClassName($name), + [ + 'stmts' => array_merge($traits, $methods), + 'implements' => $implements, + ] + ); + } + + /** + * Create method to check if denormalization is supported. + * + * @param string $modelFqdn Fully Qualified name of the model class denormalized + * + * @return Stmt\ClassMethod + */ + protected function createSupportsNormalizationMethod(string $modelFqdn) + { + return new Stmt\ClassMethod('supportsNormalization', [ + 'type' => Stmt\Class_::MODIFIER_PUBLIC, + 'params' => [ + new Param(new Expr\Variable('data')), + new Param(new Expr\Variable('format'), new Expr\ConstFetch(new Name('null'))), + ], + 'stmts' => [new Stmt\Return_(new Expr\Instanceof_(new Expr\Variable('data'), new Name('\\' . $modelFqdn)))], + ]); + } + + /** + * Create the normalization method. + * + * @return Stmt\ClassMethod + */ + protected function createNormalizeMethod(string $modelFqdn, Context $context, ClassGuess $classGuess, bool $skipNullValues = true, bool $skipRequiredFields = false) + { + $context->refreshScope(); + $dataVariable = new Expr\Variable('data'); + $objectVariable = new Expr\Variable('object'); + $statements = []; + + $statements = array_merge($statements, $this->normalizeMethodStatements($dataVariable, $classGuess, $context)); + + /** @var Property $property */ + foreach ($classGuess->getProperties() as $property) { + if (!$property->isReadOnly()) { + $propertyVar = new Expr\MethodCall($objectVariable, $this->getNaming()->getPrefixedMethodName('get', $property->getPhpName())); + + list($normalizationStatements, $outputVar) = $property->getType()->createNormalizationStatement($context, $propertyVar); + + $normalizationStatements[] = new Stmt\Expression(new Expr\Assign(new Expr\ArrayDimFetch($dataVariable, new Scalar\String_($property->getName())), $outputVar)); + + if (!$skipRequiredFields && $property->isRequired()) { + $statements = array_merge($statements, $normalizationStatements); + + continue; + } + + $statements[] = new Stmt\If_( + new Expr\BinaryOp\NotIdentical(new Expr\ConstFetch(new Name('null')), $propertyVar), + [ + 'stmts' => $normalizationStatements, + ] + ); + + if ((!$context->isStrict() || $property->isNullable() || + ($property->getType() instanceof MultipleType && \count(array_intersect([Type::TYPE_NULL], $property->getType()->getTypes())) === 1) || + ($property->getType()->getName() === Type::TYPE_NULL)) && !$skipNullValues) { + $statements[] = new Stmt\Else_( + [new Stmt\Expression(new Expr\Assign(new Expr\ArrayDimFetch($dataVariable, new Scalar\String_($property->getName())), new Expr\ConstFetch(new Name('null'))))] + ); + } + } + } + + $patternCondition = []; + $loopKeyVar = new Expr\Variable($context->getUniqueVariableName('key')); + $loopValueVar = new Expr\Variable($context->getUniqueVariableName('value')); + + foreach ($classGuess->getExtensionsType() as $pattern => $type) { + list($denormalizationStatements, $outputVar) = $type->createNormalizationStatement($context, $loopValueVar); + + $patternCondition[] = new Stmt\If_( + new Expr\FuncCall(new Name('preg_match'), [ + new Arg(new Expr\ConstFetch(new Name("'/" . str_replace('/', '\/', $pattern) . "/'"))), + new Arg(new Expr\Cast\String_($loopKeyVar)), + ]), + [ + 'stmts' => array_merge($denormalizationStatements, [ + new Stmt\Expression(new Expr\Assign(new Expr\ArrayDimFetch($dataVariable, $loopKeyVar), $outputVar)), + ]), + ] + ); + } + + if (\count($patternCondition) > 0) { + $statements[] = new Stmt\Foreach_($objectVariable, $loopValueVar, [ + 'keyVar' => $loopKeyVar, + 'stmts' => $patternCondition, + ]); + } + + $statements[] = new Stmt\Return_($dataVariable); + + return new Stmt\ClassMethod('normalize', [ + 'type' => Stmt\Class_::MODIFIER_PUBLIC, + 'params' => [ + new Param($objectVariable), + new Param(new Expr\Variable('format'), new Expr\ConstFetch(new Name('null'))), + new Param(new Expr\Variable('context'), new Expr\Array_(), 'array'), + ], + 'stmts' => $statements, + ]); + } + + /** + * Create method to say that hasCacheableSupportsMethod is supported. + * + * @return Stmt\ClassMethod + */ + protected function createHasCacheableSupportsMethod() + { + return new Stmt\ClassMethod('hasCacheableSupportsMethod', [ + 'type' => Stmt\Class_::MODIFIER_PUBLIC, + 'returnType' => 'bool', + 'stmts' => [ + new Stmt\Return_(new Expr\ConstFetch(new Name('true'))), + ], + ]); + } + + protected function normalizeMethodStatements(Expr\Variable $dataVariable, ClassGuess $classGuess, Context $context): array + { + return [ + new Stmt\Expression(new Expr\Assign($dataVariable, new Expr\Array_())), + ]; + } +} diff --git a/Generator/NormalizerGenerator.php b/Generator/NormalizerGenerator.php new file mode 100644 index 0000000..285286a --- /dev/null +++ b/Generator/NormalizerGenerator.php @@ -0,0 +1,191 @@ +sf 4.1 + */ + protected $useCacheableSupportsMethod; + + /** + * @var bool Whether to set property to null when object contains null value for it when property is nullable + */ + protected $skipNullValues; + + /** + * @var bool if we handle required fields or not during Normalizer generation + */ + protected $skipRequiedFields; + + /** + * @param bool $useReference Whether to generate the JSON Reference system + * @param bool $useCacheableSupportsMethod Whether to use the CacheableSupportsMethodInterface interface, for >sf 4.1 + * @param bool $skipNullValues Skip null values or not + */ + public function __construct(Naming $naming, Parser $parser, bool $useReference = true, bool $useCacheableSupportsMethod = null, bool $skipNullValues = true, bool $skipRequiedFields = false) + { + $this->naming = $naming; + $this->parser = $parser; + $this->useReference = $useReference; + $this->useCacheableSupportsMethod = $this->canUseCacheableSupportsMethod($useCacheableSupportsMethod); + $this->skipNullValues = $skipNullValues; + $this->skipRequiedFields = $skipRequiedFields; + } + + /** + * The naming service. + */ + protected function getNaming(): Naming + { + return $this->naming; + } + + /** + * {@inheritdoc} + */ + public function generate(Schema $schema, string $className, Context $context): void + { + $normalizers = []; + + foreach ($schema->getClasses() as $class) { + $modelFqdn = $schema->getNamespace() . '\\Model\\' . $class->getName(); + + $methods = []; + $methods[] = $this->createSupportsDenormalizationMethod($modelFqdn); + $methods[] = $this->createSupportsNormalizationMethod($modelFqdn); + $methods[] = $this->createDenormalizeMethod($modelFqdn, $context, $class); + $methods[] = $this->createNormalizeMethod($modelFqdn, $context, $class, $this->skipNullValues, $this->skipRequiedFields); + + if ($this->useCacheableSupportsMethod) { + $methods[] = $this->createHasCacheableSupportsMethod(); + } + + $normalizerClass = $this->createNormalizerClass( + $class->getName() . 'Normalizer', + $methods, + $this->useCacheableSupportsMethod + ); + + $useStmts = [ + new Stmt\Use_([new Stmt\UseUse(new Name('Jane\Component\JsonSchemaRuntime\Reference'))]), + new Stmt\Use_([new Stmt\UseUse(new Name($this->naming->getRuntimeClassFQCN($schema->getNamespace(), ['Normalizer'], 'CheckArray')))]), + new Stmt\Use_([new Stmt\UseUse(new Name('Symfony\Component\Serializer\Exception\InvalidArgumentException'))]), + new Stmt\Use_([new Stmt\UseUse(new Name('Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface'))]), + new Stmt\Use_([new Stmt\UseUse(new Name('Symfony\Component\Serializer\Normalizer\DenormalizerAwareTrait'))]), + new Stmt\Use_([new Stmt\UseUse(new Name('Symfony\Component\Serializer\Normalizer\DenormalizerInterface'))]), + new Stmt\Use_([new Stmt\UseUse(new Name('Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface'))]), + new Stmt\Use_([new Stmt\UseUse(new Name('Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait'))]), + new Stmt\Use_([new Stmt\UseUse(new Name('Symfony\Component\Serializer\Normalizer\NormalizerInterface'))]), + ]; + + if ($this->useCacheableSupportsMethod) { + $useStmts[] = new Stmt\Use_([new Stmt\UseUse(new Name('Symfony\Component\Serializer\Normalizer\CacheableSupportsMethodInterface'))]); + } + + $useStmts[] = $normalizerClass; + + $namespace = new Stmt\Namespace_(new Name($schema->getNamespace() . '\\Normalizer'), $useStmts); + + $normalizers[$modelFqdn] = $schema->getNamespace() . '\\Normalizer\\' . $class->getName() . 'Normalizer'; + + $schema->addFile(new File($schema->getDirectory() . '/Normalizer/' . $normalizerClass->name . '.php', $namespace, self::FILE_TYPE_NORMALIZER)); + } + + $schema->addFile(new File( + $schema->getDirectory() . '/Normalizer/JaneObjectNormalizer.php', + new Stmt\Namespace_(new Name($schema->getNamespace() . '\\Normalizer'), $this->createJaneObjectNormalizerClass($schema, $normalizers)), + self::FILE_TYPE_NORMALIZER + )); + } + + protected function canUseCacheableSupportsMethod(?bool $useCacheableSupportsMethod): bool + { + return + true === $useCacheableSupportsMethod || + (null === $useCacheableSupportsMethod && class_exists('Symfony\Component\Serializer\Normalizer\CacheableSupportsMethodInterface')); + } + + protected function createJaneObjectNormalizerClass(Schema $schema, array $normalizers): array + { + if ($this->useReference) { + $normalizers['\\Jane\\Component\\JsonSchemaRuntime\\Reference'] = '\\' . $this->naming->getRuntimeClassFQCN($schema->getNamespace(), ['Normalizer'], 'ReferenceNormalizer'); + } + + $properties = []; + $propertyName = $this->getNaming()->getPropertyName('normalizers'); + $propertyStmt = new Stmt\PropertyProperty($propertyName); + $propertyStmt->default = $this->parser->parse('expr; + $properties[] = $propertyStmt; + $propertyStmt = new Stmt\PropertyProperty('normalizersCache'); + $propertyStmt->default = new Expr\Array_(); + $properties[] = $propertyStmt; + + $methods = []; + $methods[] = new Stmt\Property(Stmt\Class_::MODIFIER_PROTECTED, $properties); + $methods[] = $this->createBaseNormalizerSupportsDenormalizationMethod(); + $methods[] = $this->createBaseNormalizerSupportsNormalizationMethod(); + $methods[] = $this->createBaseNormalizerNormalizeMethod(); + $methods[] = $this->createBaseNormalizerDenormalizeMethod(); + $methods[] = $this->createBaseNormalizerGetNormalizer(); + $methods[] = $this->createBaseNormalizerInitNormalizerMethod(); + + if ($this->useCacheableSupportsMethod) { + $methods[] = $this->createHasCacheableSupportsMethod(); + } + + $normalizerClass = $this->createNormalizerClass( + 'JaneObjectNormalizer', + $methods, + $this->useCacheableSupportsMethod + ); + + $useStmts = [ + new Stmt\Use_([new Stmt\UseUse(new Name($this->naming->getRuntimeClassFQCN($schema->getNamespace(), ['Normalizer'], 'CheckArray')))]), + new Stmt\Use_([new Stmt\UseUse(new Name('Symfony\Component\Serializer\Normalizer\DenormalizerAwareInterface'))]), + new Stmt\Use_([new Stmt\UseUse(new Name('Symfony\Component\Serializer\Normalizer\DenormalizerAwareTrait'))]), + new Stmt\Use_([new Stmt\UseUse(new Name('Symfony\Component\Serializer\Normalizer\DenormalizerInterface'))]), + new Stmt\Use_([new Stmt\UseUse(new Name('Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface'))]), + new Stmt\Use_([new Stmt\UseUse(new Name('Symfony\Component\Serializer\Normalizer\NormalizerAwareTrait'))]), + new Stmt\Use_([new Stmt\UseUse(new Name('Symfony\Component\Serializer\Normalizer\NormalizerInterface'))]), + ]; + + if ($this->useCacheableSupportsMethod) { + $useStmts[] = new Stmt\Use_([new Stmt\UseUse(new Name('Symfony\Component\Serializer\Normalizer\CacheableSupportsMethodInterface'))]); + } + + return array_merge($useStmts, [$normalizerClass]); + } +} diff --git a/Generator/Runtime/data/Normalizer/CheckArray.php b/Generator/Runtime/data/Normalizer/CheckArray.php new file mode 100644 index 0000000..ba84500 --- /dev/null +++ b/Generator/Runtime/data/Normalizer/CheckArray.php @@ -0,0 +1,9 @@ +getReferenceUri(); + + return $ref; + } + + /** + * {@inheritdoc} + */ + public function supportsNormalization($data, $format = null) + { + return $data instanceof Reference; + } +} diff --git a/Generator/RuntimeGenerator.php b/Generator/RuntimeGenerator.php new file mode 100644 index 0000000..521842b --- /dev/null +++ b/Generator/RuntimeGenerator.php @@ -0,0 +1,79 @@ +naming = $naming; + $this->parser = $parser; + } + + /** + * Generate a set of files given an object and a context. + */ + public function generate(Schema $schema, string $className, Context $context): void + { + foreach ($this->collectFiles() as [$directory, $file]) { + $ast = $this->parser->parse(file_get_contents($file)); + + $fileBasename = basename($file); + $namespace = explode('/', str_replace([$fileBasename, $directory], '', $file)); + array_shift($namespace); + array_pop($namespace); + + $prefixNamespace = ''; + if (\count($namespace) > 0) { + $prefixNamespace = implode('/', $namespace) . '/'; + } + + $stmts = new Namespace_(new Name($this->naming->getRuntimeNamespace($schema->getNamespace(), $namespace)), $ast); + $schema->addFile(new File($schema->getDirectory() . '/Runtime/' . $prefixNamespace . $fileBasename, $stmts, self::FILE_TYPE_RUNTIME)); + } + } + + private function collectFiles(): \Generator + { + foreach ($this->directories() as $directory) { + foreach ($this->files($directory) as $file) { + yield [$directory, $file]; + } + } + } + + private function files(string $directory): \Generator + { + $files = scandir($directory); + foreach ($files as $file) { + $fullPath = sprintf('%s/%s', $directory, $file); + if (\in_array($file, ['.', '..'])) { + continue; + } + + if (is_dir($fullPath)) { + foreach ($this->files($fullPath) as $dirFile) { + yield $dirFile; + } + } else { + yield $fullPath; + } + } + } + + protected function directories(): \Generator + { + yield __DIR__ . '/Runtime/data'; + } +} diff --git a/Guesser/ChainGuesser.php b/Guesser/ChainGuesser.php new file mode 100644 index 0000000..2ee1af9 --- /dev/null +++ b/Guesser/ChainGuesser.php @@ -0,0 +1,83 @@ +setChainGuesser($this); + } + + $this->guessers[] = $guesser; + } + + /** + * {@inheritdoc} + */ + public function guessClass($object, string $name, string $reference, Registry $registry): void + { + foreach ($this->guessers as $guesser) { + if (!($guesser instanceof ClassGuesserInterface)) { + continue; + } + + if ($guesser->supportObject($object)) { + $guesser->guessClass($object, $name, $reference, $registry); + } + } + } + + /** + * {@inheritdoc} + */ + public function guessType($object, string $name, string $reference, Registry $registry): Type + { + $type = null; + + foreach ($this->guessers as $guesser) { + if (!($guesser instanceof TypeGuesserInterface)) { + continue; + } + + if ($guesser->supportObject($object)) { + return $guesser->guessType($object, $name, $reference, $registry); + } + } + + if (null === $type) { + return new Type($object, 'mixed'); + } + + return $type; + } + + /** + * {@inheritdoc} + */ + public function guessProperties($object, string $name, string $reference, Registry $registry): array + { + $properties = []; + + foreach ($this->guessers as $guesser) { + if (!($guesser instanceof PropertiesGuesserInterface)) { + continue; + } + + if ($guesser->supportObject($object)) { + $properties = array_merge($properties, $guesser->guessProperties($object, $name, $reference, $registry)); + } + } + + return $properties; + } +} diff --git a/Guesser/ChainGuesserAwareInterface.php b/Guesser/ChainGuesserAwareInterface.php new file mode 100644 index 0000000..8ae4355 --- /dev/null +++ b/Guesser/ChainGuesserAwareInterface.php @@ -0,0 +1,13 @@ +chainGuesser = $chainGuesser; + } +} diff --git a/Guesser/ChainGuesserFactory.php b/Guesser/ChainGuesserFactory.php new file mode 100644 index 0000000..b6f3f60 --- /dev/null +++ b/Guesser/ChainGuesserFactory.php @@ -0,0 +1,16 @@ +addGuesser(new ReferenceGuesser($serializer)); + + return $chainGuesser; + } +} diff --git a/Guesser/ClassGuesserInterface.php b/Guesser/ClassGuesserInterface.php new file mode 100644 index 0000000..1378fa4 --- /dev/null +++ b/Guesser/ClassGuesserInterface.php @@ -0,0 +1,18 @@ +itemType = $itemType; + } + + public function getItemType(): Type + { + return $this->itemType; + } + + /** + * (@inheritDoc}. + */ + public function getDocTypeHint(string $namespace) + { + if ($this->itemType instanceof MultipleType) { + $typesString = []; + + foreach ($this->itemType->getTypes() as $type) { + $typesString[] = $type->getDocTypeHint($namespace) . '[]'; + } + + return implode('|', $typesString); + } + + return $this->itemType->getDocTypeHint($namespace) . '[]'; + } + + /** + * (@inheritDoc}. + */ + public function createDenormalizationStatement(Context $context, Expr $input, bool $normalizerFromObject = true): array + { + $valuesVar = new Expr\Variable($context->getUniqueVariableName('values')); + $statements = [ + // $values = []; + new Stmt\Expression(new Expr\Assign($valuesVar, $this->createArrayValueStatement())), + ]; + + $loopValueVar = new Expr\Variable($context->getUniqueVariableName('value')); + $loopKeyVar = $this->createLoopKeyStatement($context); + + list($subStatements, $outputExpr) = $this->itemType->createDenormalizationStatement($context, $loopValueVar, $normalizerFromObject); + + $loopStatements = array_merge($subStatements, [ + new Stmt\Expression(new Expr\Assign($this->createLoopOutputAssignement($valuesVar, $loopKeyVar), $outputExpr)), + ]); + + $statements[] = new Stmt\Foreach_($input, $loopValueVar, [ + 'keyVar' => $loopKeyVar, + 'stmts' => $loopStatements, + ]); + + return [$statements, $valuesVar]; + } + + public function createConditionStatement(Expr $input): Expr + { + return new Expr\BinaryOp\BooleanAnd( + parent::createConditionStatement($input), + new Expr\MethodCall(new Expr\Variable('this'), 'isOnlyNumericKeys', [ + new Arg($input), + ]) + ); + } + + /** + * (@inheritDoc}. + */ + public function createNormalizationStatement(Context $context, Expr $input, bool $normalizerFromObject = true): array + { + $valuesVar = new Expr\Variable($context->getUniqueVariableName('values')); + $statements = [ + // $values = []; + new Stmt\Expression(new Expr\Assign($valuesVar, $this->createNormalizationArrayValueStatement())), + ]; + + $loopValueVar = new Expr\Variable($context->getUniqueVariableName('value')); + $loopKeyVar = $this->createLoopKeyStatement($context); + + list($subStatements, $outputExpr) = $this->itemType->createNormalizationStatement($context, $loopValueVar, $normalizerFromObject); + + $loopStatements = array_merge($subStatements, [ + new Stmt\Expression(new Expr\Assign($this->createNormalizationLoopOutputAssignement($valuesVar, $loopKeyVar), $outputExpr)), + ]); + + $statements[] = new Stmt\Foreach_($input, $loopValueVar, [ + 'keyVar' => $loopKeyVar, + 'stmts' => $loopStatements, + ]); + + return [$statements, $valuesVar]; + } + + public function getTypeHint(string $namespace) + { + return 'array'; + } + + protected function createArrayValueStatement(): Expr + { + return new Expr\Array_(); + } + + protected function createNormalizationArrayValueStatement(): Expr + { + return new Expr\Array_(); + } + + protected function createLoopKeyStatement(Context $context): ?Expr + { + return null; + } + + protected function createLoopOutputAssignement(Expr $valuesVar, $loopKeyVar): Expr + { + return new Expr\ArrayDimFetch($valuesVar); + } + + protected function createNormalizationLoopOutputAssignement(Expr $valuesVar, $loopKeyVar): Expr + { + return new Expr\ArrayDimFetch($valuesVar); + } +} diff --git a/Guesser/Guess/ClassGuess.php b/Guesser/Guess/ClassGuess.php new file mode 100644 index 0000000..494ca5f --- /dev/null +++ b/Guesser/Guess/ClassGuess.php @@ -0,0 +1,106 @@ +name = $name; + $this->object = $object; + $this->reference = $reference; + $this->extensionsObject = $extensionsObject; + $this->deprecated = $deprecated; + } + + public function getObject(): object + { + return $this->object; + } + + public function getName(): string + { + return $this->name; + } + + public function getReference(): string + { + return $this->reference; + } + + /** + * @return Property[] + */ + public function getProperties(): array + { + return $this->properties; + } + + public function setProperties(array $properties): void + { + $this->properties = $properties; + } + + /** + * @return Type[] + */ + public function getExtensionsType(): array + { + return $this->extensionsType; + } + + /** + * @param Type[] $extensionsType + */ + public function setExtensionsType(array $extensionsType): void + { + $this->extensionsType = $extensionsType; + } + + public function getExtensionsObject(): array + { + return $this->extensionsObject; + } + + public function getConstraints(): array + { + return $this->constraints; + } + + public function setConstraints($constraints): void + { + $this->constraints = $constraints; + } + + public function isDeprecated(): bool + { + return $this->deprecated; + } +} diff --git a/Guesser/Guess/DateTimeType.php b/Guesser/Guess/DateTimeType.php new file mode 100644 index 0000000..5f16b8e --- /dev/null +++ b/Guesser/Guess/DateTimeType.php @@ -0,0 +1,117 @@ +outputFormat = $outputFormat; + $this->inputFormat = $inputFormat ?? $outputFormat; + $this->preferInterface = $preferInterface ?? false; + } + + /** + * (@inheritDoc}. + */ + protected function createDenormalizationValueStatement(Context $context, Expr $input, bool $normalizerFromObject = true): Expr + { + return $this->generateParseExpression($input); + } + + /** + * (@inheritDoc}. + */ + protected function createNormalizationValueStatement(Context $context, Expr $input, bool $normalizerFromObject = true): Expr + { + // $object->format($format); + return new Expr\MethodCall($input, 'format', [ + new Arg(new Scalar\String_($this->outputFormat)), + ]); + } + + /** + * (@inheritDoc}. + */ + public function createConditionStatement(Expr $input): Expr + { + return new Expr\BinaryOp\LogicalAnd(new Expr\FuncCall( + new Name('is_string'), [ + new Arg($input), + ]), + new Expr\BinaryOp\NotIdentical( + new Expr\ConstFetch(new Name('false')), + $this->generateParseExpression($input) + ) + ); + } + + public function getTypeHint(string $namespace) + { + return $this->preferInterface ? '\DateTimeInterface' : '\DateTime'; + } + + public function __toString(): string + { + return '\DateTime'; + } + + protected function generateParseExpression(Expr $input): Expr + { + if (empty($this->inputFormat)) { + // new \DateTime($data) + $new = new Expr\New_(new Name('\DateTime'), [new Arg($input)]); + // (new \DateTime($data))->getTimezone()->getName() + $timezoneName = new Expr\MethodCall( + new Expr\MethodCall($new, new Name('getTimezone')), + new Name('getName') + ); + // new \DateTimeZone('GMT') + $gmtTimezone = new Expr\New_(new Name('\DateTimeZone'), [new Scalar\String_('GMT')]); + // (new \DateTime($data))->getTimezone()->getName() === 'Z' ? (new \DateTime($data))->setTimezone(new \DateTimeZone('GMT')) : \DateTime($data) + return new Expr\Ternary( + new Expr\BinaryOp\Equal($timezoneName, new Scalar\String_('Z')), + new Expr\MethodCall($new, new Name('setTimezone'), [new Arg($gmtTimezone)]), + $new + ); + } + + // \DateTime::createFromFormat($format, $data) + return new Expr\StaticCall(new Name('\DateTime'), 'createFromFormat', [ + new Arg(new Scalar\String_($this->inputFormat)), + new Arg($input), + ]); + } +} diff --git a/Guesser/Guess/DateType.php b/Guesser/Guess/DateType.php new file mode 100644 index 0000000..31eabac --- /dev/null +++ b/Guesser/Guess/DateType.php @@ -0,0 +1,111 @@ +format = $format; + $this->preferInterface = $preferInterface ?? false; + } + + /** + * (@inheritDoc}. + */ + protected function createDenormalizationValueStatement(Context $context, Expr $input, bool $normalizerFromObject = true): Expr + { + // \DateTime::createFromFormat($format, $data)->setTime(0, 0, 0) + return new Expr\MethodCall( + new Expr\StaticCall( + new Name('\DateTime'), + 'createFromFormat', + [ + new Arg(new Scalar\String_($this->format)), + new Arg($input), + ] + ), + 'setTime', + [ + new Arg(new Scalar\LNumber(0)), + new Arg(new Scalar\LNumber(0)), + new Arg(new Scalar\LNumber(0)), + ]); + } + + /** + * (@inheritDoc}. + */ + protected function createNormalizationValueStatement(Context $context, Expr $input, bool $normalizerFromObject = true): Expr + { + // $object->format($format); + return new Expr\MethodCall($input, 'format', [ + new Arg(new Scalar\String_($this->format)), + ]); + } + + /** + * (@inheritDoc}. + */ + public function createConditionStatement(Expr $input): Expr + { + return new Expr\BinaryOp\LogicalAnd(new Expr\FuncCall( + new Name('is_string'), [ + new Arg($input), + ]), + new Expr\BinaryOp\NotIdentical( + new Expr\ConstFetch(new Name('false')), + new Expr\MethodCall( + new Expr\StaticCall( + new Name('\DateTime'), + 'createFromFormat', + [ + new Arg(new Scalar\String_($this->format)), + new Arg($input), + ] + ), + 'setTime', + [ + new Arg(new Scalar\LNumber(0)), + new Arg(new Scalar\LNumber(0)), + new Arg(new Scalar\LNumber(0)), + ]) + ) + ); + } + + public function getTypeHint(string $namespace) + { + return $this->preferInterface ? '\DateTimeInterface' : '\DateTime'; + } + + public function __toString(): string + { + return '\DateTime'; + } +} diff --git a/Guesser/Guess/MapType.php b/Guesser/Guess/MapType.php new file mode 100644 index 0000000..dc9624d --- /dev/null +++ b/Guesser/Guess/MapType.php @@ -0,0 +1,68 @@ +itemType = $itemType; + } + + /** + * (@inheritDoc}. + */ + public function getTypeHint(string $namespace) + { + return new Name('iterable'); + } + + /** + * {@inheritdoc} + */ + protected function createArrayValueStatement(): Expr + { + return new Expr\New_(new Name('\ArrayObject'), [ + new Expr\Array_(), + new Expr\ClassConstFetch(new Name('\ArrayObject'), 'ARRAY_AS_PROPS'), + ]); + } + + /** + * {@inheritdoc} + */ + protected function createNormalizationArrayValueStatement(): Expr + { + return new Expr\Array_(); + } + + /** + * {@inheritdoc} + */ + protected function createLoopKeyStatement(Context $context): Expr + { + return new Expr\Variable($context->getUniqueVariableName('key')); + } + + /** + * {@inheritdoc} + */ + protected function createLoopOutputAssignement(Expr $valuesVar, $loopKeyVar): Expr + { + return new Expr\ArrayDimFetch($valuesVar, $loopKeyVar); + } + + /** + * {@inheritdoc} + */ + protected function createNormalizationLoopOutputAssignement(Expr $valuesVar, $loopKeyVar): Expr + { + return new Expr\ArrayDimFetch($valuesVar, $loopKeyVar); + } +} diff --git a/Guesser/Guess/MultipleType.php b/Guesser/Guess/MultipleType.php new file mode 100644 index 0000000..688f7da --- /dev/null +++ b/Guesser/Guess/MultipleType.php @@ -0,0 +1,176 @@ +types = $types; + } + + /** + * Add a type. + */ + public function addType(Type $type): self + { + if ($type instanceof self) { + foreach ($type->getTypes() as $subType) { + $this->types[] = $subType; + } + + return $this; + } + + $this->types[] = $type; + + return $this; + } + + /** + * Return a list of types. + * + * @return Type[] + */ + public function getTypes(): array + { + return $this->types; + } + + /** + * We have to place mixed normalization path at the last. + * + * @return Type[] + */ + protected function getTypesSorted(): array + { + $types = $this->getTypes(); + usort($types, function ($first, $second) { + /* @var Type $first */ + /* @var Type $second */ + if (($second instanceof ObjectType && 'Reference' === $second->getClassName()) || 'mixed' === $first->getName()) { + return 1; + } + + return 0; + }); + + return $types; + } + + /** + * {@inheritdoc} + */ + public function getDocTypeHint(string $namespace) + { + $stringTypes = array_map(function ($type) use ($namespace) { + return $type->getDocTypeHint($namespace); + }, $this->types); + + return implode('|', $stringTypes); + } + + /** + * {@inheritdoc} + */ + public function getTypeHint(string $namespace) + { + if (1 === \count($this->types)) { + $type = current($this->types); + + return $type->getTypeHint($namespace); + } + + // We have exactly two types: one null and an object + if (2 === \count($this->types)) { + list($type1, $type2) = $this->types; + + if ($this->isOptionalType($type1)) { + return $type2->getTypeHint($namespace); + } + + if ($this->isOptionalType($type2)) { + return $type1->getTypeHint($namespace); + } + } + + return null; + } + + private function isOptionalType(Type $nullType): bool + { + return 'null' === $nullType->getName(); + } + + /** + * {@inheritdoc} + */ + public function createDenormalizationStatement(Context $context, Expr $input, bool $normalizerFromObject = true): array + { + $output = new Expr\Variable($context->getUniqueVariableName('value')); + $statements = [ + new Stmt\Expression(new Expr\Assign($output, $input)), + ]; + + /** @var Stmt\If_|null $ifStmt */ + $ifStmt = null; + foreach ($this->getTypesSorted() as $type) { + list($typeStatements, $typeOutput) = $type->createDenormalizationStatement($context, $input, $normalizerFromObject); + + $condition = $type->createConditionStatement($input); + $statement = array_merge($typeStatements, [new Stmt\Expression(new Expr\Assign($output, $typeOutput))]); + + if ($ifStmt === null) { + $ifStmt = new Stmt\If_($condition, ['stmts' => $statement]); + } else { + $ifStmt->elseifs[] = new Stmt\ElseIf_($condition, $statement); + } + } + + if (null !== $ifStmt) { + $statements[] = $ifStmt; + } + + return [$statements, $output]; + } + + /** + * {@inheritdoc} + */ + public function createNormalizationStatement(Context $context, Expr $input, bool $normalizerFromObject = true): array + { + $output = new Expr\Variable($context->getUniqueVariableName('value')); + $statements = [ + new Stmt\Expression(new Expr\Assign($output, $input)), + ]; + + /** @var Stmt\If_|null $ifStmt */ + $ifStmt = null; + foreach ($this->getTypesSorted() as $type) { + list($typeStatements, $typeOutput) = $type->createNormalizationStatement($context, $input, $normalizerFromObject); + + $condition = $type->createNormalizationConditionStatement($input); + $statement = array_merge($typeStatements, [new Stmt\Expression(new Expr\Assign($output, $typeOutput))]); + + if ($ifStmt === null) { + $ifStmt = new Stmt\If_($condition, ['stmts' => $statement]); + } else { + $ifStmt->elseifs[] = new Stmt\ElseIf_($condition, $statement); + } + } + + if (null !== $ifStmt) { + $statements[] = $ifStmt; + } + + return [$statements, $output]; + } +} diff --git a/Guesser/Guess/ObjectType.php b/Guesser/Guess/ObjectType.php new file mode 100644 index 0000000..cf3ec70 --- /dev/null +++ b/Guesser/Guess/ObjectType.php @@ -0,0 +1,142 @@ +namespace = $namespace; + $this->className = $className; + $this->discriminants = $discriminants; + } + + /** + * (@inheritDoc}. + */ + protected function createDenormalizationValueStatement(Context $context, Expr $input, bool $normalizerFromObject = true): Expr + { + $denormalizerVar = new Expr\PropertyFetch(new Expr\Variable('this'), 'denormalizer'); + if (!$normalizerFromObject) { + $denormalizerVar = new Expr\Variable('denormalizer'); + } + + return new Expr\MethodCall($denormalizerVar, 'denormalize', [ + new Arg($input), + new Arg(new Scalar\String_($this->getFqdn(false))), + new Arg(new Scalar\String_('json')), + new Arg(new Expr\Variable('context')), + ]); + } + + /** + * (@inheritDoc}. + */ + protected function createNormalizationValueStatement(Context $context, Expr $input, bool $normalizerFromObject = true): Expr + { + $normalizerVar = new Expr\PropertyFetch(new Expr\Variable('this'), 'normalizer'); + if (!$normalizerFromObject) { + $normalizerVar = new Expr\Variable('normalizer'); + } + + return new Expr\MethodCall($normalizerVar, 'normalize', [ + new Arg($input), + new Arg(new Scalar\String_('json')), + new Arg(new Expr\Variable('context')), + ]); + } + + /** + * (@inheritDoc}. + */ + public function createConditionStatement(Expr $input): Expr + { + $conditionStatement = parent::createConditionStatement($input); + + foreach ($this->discriminants as $key => $values) { + $issetCondition = new Expr\FuncCall( + new Name('isset'), + [ + new Arg(new Expr\ArrayDimFetch($input, new Scalar\String_($key))), + ] + ); + + $logicalOr = null; + + if (null !== $values) { + foreach ($values as $value) { + if (null === $logicalOr) { + $logicalOr = new Expr\BinaryOp\Equal( + new Expr\ArrayDimFetch($input, new Scalar\String_($key)), + new Scalar\String_($value) + ); + } else { + $logicalOr = new Expr\BinaryOp\LogicalOr( + $logicalOr, + new Expr\BinaryOp\Equal( + new Expr\ArrayDimFetch($input, new Scalar\String_($key)), + new Scalar\String_($value) + ) + ); + } + } + } + + if (null !== $logicalOr) { + $conditionStatement = new Expr\BinaryOp\LogicalAnd($conditionStatement, new Expr\BinaryOp\LogicalAnd($issetCondition, $logicalOr)); + } else { + $conditionStatement = new Expr\BinaryOp\LogicalAnd($conditionStatement, $issetCondition); + } + } + + return $conditionStatement; + } + + /** + * (@inheritDoc}. + */ + public function getTypeHint(string $currentNamespace) + { + if ('\\' . $currentNamespace . '\\' . $this->className === $this->getFqdn()) { + return $this->className; + } + + return $this->getFqdn(); + } + + /** + * (@inheritDoc}. + */ + public function getDocTypeHint(string $namespace) + { + return $this->getTypeHint($namespace); + } + + public function getClassName(): string + { + return $this->className; + } + + private function getFqdn(bool $withRoot = true): string + { + if ($withRoot) { + return '\\' . $this->namespace . '\\Model\\' . $this->className; + } + + return $this->namespace . '\\Model\\' . $this->className; + } +} diff --git a/Guesser/Guess/PatternMultipleType.php b/Guesser/Guess/PatternMultipleType.php new file mode 100644 index 0000000..bb284b5 --- /dev/null +++ b/Guesser/Guess/PatternMultipleType.php @@ -0,0 +1,158 @@ +types = $types; + } + + /** + * Add a type. + */ + public function addType(string $pattern, Type $type): self + { + $this->types[$pattern] = $type; + + return $this; + } + + /** + * {@inheritdoc} + */ + public function getDocTypeHint(string $namespace) + { + $stringTypes = array_map(function ($type) use ($namespace) { + return $type->getDocTypeHint($namespace) . '[]'; + }, $this->types); + + return implode('|', $stringTypes); + } + + /** + * (@inheritDoc}. + */ + public function createDenormalizationStatement(Context $context, Expr $input, bool $normalizerFromObject = true): array + { + $valuesVar = new Expr\Variable($context->getUniqueVariableName('values')); + $statements = [ + // $values = []; + new Stmt\Expression(new Expr\Assign($valuesVar, $this->createArrayValueStatement())), + ]; + + $loopValueVar = new Expr\Variable($context->getUniqueVariableName('value')); + $loopKeyVar = $this->createLoopKeyStatement($context); + $loopStatements = []; + + foreach ($this->types as $pattern => $type) { + list($typeStatements, $typeOutput) = $type->createDenormalizationStatement($context, $loopValueVar, $normalizerFromObject); + $loopStatements = array_merge($loopStatements, [ + new Stmt\If_( + new Expr\BinaryOp\BooleanAnd( + new Expr\FuncCall(new Name('preg_match'), [ + new Arg(new Expr\ConstFetch(new Name("'/" . str_replace('/', '\/', $pattern) . "/'"))), + new Arg(new Expr\Cast\String_($loopKeyVar)), + ]), + $type->createConditionStatement($loopValueVar) + ), + [ + 'stmts' => array_merge($typeStatements, [ + new Stmt\Expression(new Expr\Assign(new Expr\ArrayDimFetch($valuesVar, $loopKeyVar), $typeOutput)), + new Stmt\Continue_(), + ]), + ] + ), + ]); + } + + $statements[] = new Stmt\Foreach_($input, $loopValueVar, [ + 'keyVar' => $loopKeyVar, + 'stmts' => $loopStatements, + ]); + + return [$statements, $valuesVar]; + } + + /** + * (@inheritDoc}. + */ + public function createNormalizationStatement(Context $context, Expr $input, bool $normalizerFromObject = true): array + { + $valuesVar = new Expr\Variable($context->getUniqueVariableName('values')); + $statements = [ + // $values = []; + new Stmt\Expression(new Expr\Assign($valuesVar, $this->createNormalizationArrayValueStatement())), + ]; + + $loopValueVar = new Expr\Variable($context->getUniqueVariableName('value')); + $loopKeyVar = $this->createLoopKeyStatement($context); + $loopStatements = []; + + foreach ($this->types as $pattern => $type) { + list($typeStatements, $typeOutput) = $type->createNormalizationStatement($context, $loopValueVar, $normalizerFromObject); + $loopStatements = array_merge($loopStatements, [ + new Stmt\If_( + new Expr\BinaryOp\BooleanAnd( + new Expr\FuncCall(new Name('preg_match'), [ + new Arg(new Expr\ConstFetch(new Name("'/" . str_replace('/', '\/', $pattern) . "/'"))), + new Arg(new Expr\Cast\String_($loopKeyVar)), + ]), + $type->createNormalizationConditionStatement($loopValueVar) + ), + [ + 'stmts' => array_merge($typeStatements, [ + new Stmt\Expression(new Expr\Assign(new Expr\ArrayDimFetch($valuesVar, $loopKeyVar), $typeOutput)), + new Stmt\Continue_(), + ]), + ] + ), + ]); + } + + $statements[] = new Stmt\Foreach_($input, $loopValueVar, [ + 'keyVar' => $loopKeyVar, + 'stmts' => $loopStatements, + ]); + + return [$statements, $valuesVar]; + } + + /** + * {@inheritdoc} + */ + protected function createArrayValueStatement(): Expr + { + return new Expr\New_(new Name('\ArrayObject'), [ + new Expr\Array_(), + new Expr\ClassConstFetch(new Name('\ArrayObject'), 'ARRAY_AS_PROPS'), + ]); + } + + /** + * {@inheritdoc} + */ + protected function createNormalizationArrayValueStatement(): Expr + { + return new Expr\Array_(); + } + + /** + * {@inheritdoc} + */ + protected function createLoopKeyStatement(Context $context): Expr + { + return new Expr\Variable($context->getUniqueVariableName('key')); + } +} diff --git a/Guesser/Guess/Property.php b/Guesser/Guess/Property.php new file mode 100644 index 0000000..d9afc40 --- /dev/null +++ b/Guesser/Guess/Property.php @@ -0,0 +1,140 @@ +name = $name; + $this->object = $object; + $this->reference = $reference; + $this->nullable = $nullable; + $this->required = $required; + $this->type = $type; + $this->description = $description; + $this->default = $default; + $this->readOnly = $readOnly ?? false; + } + + public function setPhpName(string $name): void + { + $this->phpName = $name; + } + + public function getPhpName(): string + { + return $this->phpName; + } + + public function getObject(): object + { + return $this->object; + } + + public function getName(): string + { + return $this->name; + } + + public function getReference(): string + { + return $this->reference; + } + + public function isNullable(): bool + { + return $this->nullable; + } + + public function isRequired(): bool + { + return $this->required; + } + + public function getType(): Type + { + return $this->type; + } + + public function setType(Type $type): void + { + $this->type = $type; + } + + public function getDescription(): string + { + return (string) $this->description; + } + + public function getDefault() + { + return $this->default; + } + + public function isReadOnly(): bool + { + return $this->readOnly; + } + + public function setDeprecated(bool $deprecated): void + { + $this->deprecated = $deprecated; + } + + public function isDeprecated(): bool + { + return $this->deprecated; + } +} diff --git a/Guesser/Guess/Type.php b/Guesser/Guess/Type.php new file mode 100644 index 0000000..a091540 --- /dev/null +++ b/Guesser/Guess/Type.php @@ -0,0 +1,134 @@ + 'bool', + self::TYPE_INTEGER => 'int', + self::TYPE_FLOAT => 'float', + self::TYPE_STRING => 'string', + self::TYPE_NULL => null, + self::TYPE_MIXED => null, + self::TYPE_ARRAY => 'array', + self::TYPE_OBJECT => null, + ]; + + protected $conditionMapping = [ + self::TYPE_BOOLEAN => 'is_bool', + self::TYPE_INTEGER => 'is_int', + self::TYPE_FLOAT => 'is_float', + self::TYPE_STRING => 'is_string', + self::TYPE_NULL => 'is_null', + self::TYPE_MIXED => 'isset', + self::TYPE_ARRAY => 'is_array', + self::TYPE_OBJECT => 'is_array', + ]; + + protected $normalizationConditionMapping = [ + self::TYPE_BOOLEAN => 'is_bool', + self::TYPE_INTEGER => 'is_int', + self::TYPE_FLOAT => 'is_float', + self::TYPE_STRING => 'is_string', + self::TYPE_NULL => 'is_null', + self::TYPE_MIXED => '!is_null', + self::TYPE_ARRAY => 'is_array', + self::TYPE_OBJECT => 'is_object', + ]; + + protected $name; + + protected $object; + + public function __construct(?object $object, string $name) + { + $this->name = $name; + $this->object = $object; + } + + public function getName() + { + return $this->name; + } + + public function getObject() + { + return $this->object; + } + + public function __toString(): string + { + return $this->name; + } + + public function createDenormalizationStatement(Context $context, Expr $input, bool $normalizerFromObject = true): array + { + return [[], $this->createDenormalizationValueStatement($context, $input, $normalizerFromObject)]; + } + + public function createNormalizationStatement(Context $context, Expr $input, bool $normalizerFromObject = true): array + { + return [[], $this->createNormalizationValueStatement($context, $input, $normalizerFromObject)]; + } + + protected function createDenormalizationValueStatement(Context $context, Expr $input, bool $normalizerFromObject = true): Expr + { + return $input; + } + + protected function createNormalizationValueStatement(Context $context, Expr $input, bool $normalizerFromObject = true): Expr + { + return $input; + } + + public function createConditionStatement(Expr $input): Expr + { + return new Expr\FuncCall( + new Name($this->conditionMapping[$this->name]), + [ + new Arg($input), + ] + ); + } + + public function createNormalizationConditionStatement(Expr $input): Expr + { + return new Expr\FuncCall( + new Name($this->normalizationConditionMapping[$this->name]), + [ + new Arg($input), + ] + ); + } + + /** + * @return string|Name|null + */ + public function getTypeHint(string $namespace) + { + return $this->phpMapping[$this->name]; + } + + /** + * @return string|Name|null + */ + public function getDocTypeHint(string $namespace) + { + return (string) $this; + } +} diff --git a/Guesser/GuesserInterface.php b/Guesser/GuesserInterface.php new file mode 100644 index 0000000..a403607 --- /dev/null +++ b/Guesser/GuesserInterface.php @@ -0,0 +1,13 @@ +resolve(function ($data) use ($result, $class) { + return $this->serializer->denormalize($data, $class, 'json', [ + 'document-origin' => (string) $result->getMergedUri()->withFragment(''), + ]); + }); + } + + return $result; + } +} diff --git a/Guesser/JsonSchema/AdditionalItemsGuesser.php b/Guesser/JsonSchema/AdditionalItemsGuesser.php new file mode 100644 index 0000000..6287f57 --- /dev/null +++ b/Guesser/JsonSchema/AdditionalItemsGuesser.php @@ -0,0 +1,31 @@ +chainGuesser->guessClass($object->getAdditionalItems(), $name . 'AdditionalItems', $reference . '/additionalItems', $registry); + } + + /** + * {@inheritdoc} + */ + public function supportObject($object): bool + { + return ($object instanceof JsonSchema) && ($object->getAdditionalItems() instanceof JsonSchema); + } +} diff --git a/Guesser/JsonSchema/AdditionalPropertiesGuesser.php b/Guesser/JsonSchema/AdditionalPropertiesGuesser.php new file mode 100644 index 0000000..4b107e2 --- /dev/null +++ b/Guesser/JsonSchema/AdditionalPropertiesGuesser.php @@ -0,0 +1,67 @@ +getAdditionalProperties(), $this->getSchemaClass())) { + $this->chainGuesser->guessClass($object->getAdditionalProperties(), $name . 'Item', $reference . '/additionalProperties', $registry); + } + } + + /** + * {@inheritdoc} + */ + public function supportObject($object): bool + { + $class = $this->getSchemaClass(); + + if (!($object instanceof $class)) { + return false; + } + + if ('object' !== $object->getType()) { + return false; + } + + if (true !== $object->getAdditionalProperties() && !\is_object($object->getAdditionalProperties())) { + return false; + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function guessType($object, string $name, string $reference, Registry $registry): Type + { + if (true === $object->getAdditionalProperties()) { + return new MapType($object, new Type($object, 'mixed')); + } + + return new MapType($object, $this->chainGuesser->guessType($object->getAdditionalProperties(), $name . 'Item', $reference . '/additionalProperties', $registry)); + } + + protected function getSchemaClass(): string + { + return JsonSchema::class; + } +} diff --git a/Guesser/JsonSchema/AllOfGuesser.php b/Guesser/JsonSchema/AllOfGuesser.php new file mode 100644 index 0000000..a950c85 --- /dev/null +++ b/Guesser/JsonSchema/AllOfGuesser.php @@ -0,0 +1,178 @@ +serializer = $serializer; + $this->naming = $naming; + } + + /** + * {@inheritdoc} + */ + public function guessClass($object, string $name, string $reference, Registry $registry): void + { + $hasSubObject = false; + + foreach ($object->getAllOf() as $allOf) { + if ($allOf instanceof Reference) { + $allOf = $this->resolve($allOf, $this->getSchemaClass()); + } + if ('object' === $allOf->getType()) { + $hasSubObject = true; + break; + } + } + + if ($hasSubObject) { + if (!$registry->hasClass($reference)) { + $extensions = []; + + if ($object->getAdditionalProperties()) { + $extensionObject = null; + + if (\is_object($object->getAdditionalProperties())) { + $extensionObject = $object->getAdditionalProperties(); + } + + $extensions['.*'] = [ + 'object' => $extensionObject, + 'reference' => $reference . '/additionalProperties', + ]; + } elseif (method_exists($object, 'getPatternProperties') && $object->getPatternProperties() !== null) { + foreach ($object->getPatternProperties() as $pattern => $patternProperty) { + $extensions[$pattern] = [ + 'object' => $patternProperty, + 'reference' => $reference . '/patternProperties/' . $pattern, + ]; + } + } + + $registry->getSchema($reference)->addClass($reference, $this->createClassGuess($object, $reference, $name, $extensions)); + } + + foreach ($object->getAllOf() as $allOfIndex => $allOf) { + if (is_a($allOf, $this->getSchemaClass())) { + if ($allOf->getProperties()) { + foreach ($allOf->getProperties() as $key => $property) { + $this->chainGuesser->guessClass($property, $name . $key, $reference . '/allOf/' . $allOfIndex . '/properties/' . $key, $registry); + } + } + } + } + } + } + + /** + * {@inheritdoc} + */ + public function guessType($object, string $name, string $reference, Registry $registry): Type + { + $type = null; + $allOfType = null; + + // Mainly a merged class + if ($registry->hasClass($reference)) { + return new ObjectType($object, $registry->getClass($reference)->getName(), $registry->getSchema($reference)->getNamespace()); + } + + foreach ($object->getAllOf() as $allOfIndex => $allOf) { + $allOfSchema = $allOf; + $allOfReference = $reference . '/allOf/' . $allOfIndex; + + if ($allOfSchema instanceof Reference) { + $allOfReference = (string) $allOf->getMergedUri(); + + if ((string) $allOf->getMergedUri() === (string) $allOf->getMergedUri()->withFragment('')) { + $allOfReference .= '#'; + } + + $allOfSchema = $this->resolve($allOfSchema, $this->getSchemaClass()); + } + + if (null !== $allOfSchema->getType()) { + if (null !== $type && $allOfType !== $allOfSchema->getType()) { + throw new \RuntimeException('an allOf instruction with 2 or more types is strictly impossible, check your schema'); + } + + $allOfType = $allOfSchema->getType(); + $type = $this->chainGuesser->guessType($allOf, $name, $allOfReference, $registry); + } + } + + if (null === $type) { + return new Type($object, 'mixed'); + } + + return $type; + } + + /** + * {@inheritdoc} + */ + public function supportObject($object): bool + { + $class = $this->getSchemaClass(); + + return ($object instanceof $class) && \is_array($object->getAllOf()) && \count($object->getAllOf()) > 0; + } + + /** + * {@inheritdoc} + */ + public function guessProperties($object, string $name, string $reference, Registry $registry): array + { + $properties = []; + foreach ($object->getAllOf() as $allOfIndex => $allOfSchema) { + $allOfReference = $reference . '/allOf/' . $allOfIndex; + + if ($allOfSchema instanceof Reference) { + $allOfReference = (string) $allOfSchema->getMergedUri(); + + if ((string) $allOfSchema->getMergedUri() === (string) $allOfSchema->getMergedUri()->withFragment('')) { + $allOfReference .= '#'; + } + + $allOfSchema = $this->resolve($allOfSchema, $this->getSchemaClass()); + } + + $properties = array_merge($properties, $this->chainGuesser->guessProperties($allOfSchema, $name, $allOfReference, $registry)); + } + + return $properties; + } + + protected function getSchemaClass(): string + { + return JsonSchema::class; + } + + protected function createClassGuess($object, string $reference, string $name, array $extensions): ClassGuess + { + return new ClassGuess($object, $reference, $this->naming->getClassName($name), $extensions); + } +} diff --git a/Guesser/JsonSchema/AnyOfGuesser.php b/Guesser/JsonSchema/AnyOfGuesser.php new file mode 100644 index 0000000..5d94167 --- /dev/null +++ b/Guesser/JsonSchema/AnyOfGuesser.php @@ -0,0 +1,54 @@ +getAnyOf() as $anyOfKey => $anyOfObject) { + $this->chainGuesser->guessClass($anyOfObject, $name . 'AnyOf', $reference . '/anyOf/' . $anyOfKey, $registry); + } + } + + /** + * {@inheritdoc} + */ + public function guessType($object, string $name, string $reference, Registry $registry): Type + { + if (1 == \count($object->getAnyOf())) { + return $this->chainGuesser->guessType($object->getAnyOf()[0], $name, $reference . '/anyOf/0', $registry); + } + + $type = new MultipleType($object); + + foreach ($object->getAnyOf() as $anyOfKey => $anyOfObject) { + $type->addType($this->chainGuesser->guessType($anyOfObject, $name, $reference . '/anyOf/' . $anyOfKey, $registry)); + } + + return $type; + } + + /** + * {@inheritdoc} + */ + public function supportObject($object): bool + { + return ($object instanceof JsonSchema) && \is_array($object->getAnyOf()) && \count($object->getAnyOf()) > 0; + } +} diff --git a/Guesser/JsonSchema/ArrayGuesser.php b/Guesser/JsonSchema/ArrayGuesser.php new file mode 100644 index 0000000..8bce4fb --- /dev/null +++ b/Guesser/JsonSchema/ArrayGuesser.php @@ -0,0 +1,67 @@ +getItems(), $this->getSchemaClass())) { + $this->chainGuesser->guessClass($object->getItems(), $name . 'Item', $reference . '/items', $registry); + } + } + + /** + * {@inheritdoc} + */ + public function supportObject($object): bool + { + return ($object instanceof JsonSchema) && 'array' === $object->getType(); + } + + /** + * {@inheritdoc} + */ + public function guessType($object, string $name, string $reference, Registry $registry): Type + { + $items = $object->getItems(); + + if (null === $items || (\is_array($items) && 0 === \count($items))) { + return new ArrayType($object, new Type($object, 'mixed')); + } + + if (!\is_array($items)) { + return new ArrayType($object, $this->chainGuesser->guessType($items, $name . 'Item', $reference . '/items', $registry)); + } + + $type = new MultipleType($object); + + foreach ($items as $key => $item) { + $type->addType(new ArrayType($object, $this->chainGuesser->guessType($item, $name . 'Item', $reference . '/items/' . $key, $registry))); + } + + return $type; + } + + protected function getSchemaClass(): string + { + return Schema::class; + } +} diff --git a/Guesser/JsonSchema/DateGuesser.php b/Guesser/JsonSchema/DateGuesser.php new file mode 100644 index 0000000..d1eb020 --- /dev/null +++ b/Guesser/JsonSchema/DateGuesser.php @@ -0,0 +1,52 @@ +dateFormat = $dateFormat; + $this->preferInterface = $preferInterface; + } + + /** + * {@inheritdoc} + */ + public function supportObject($object): bool + { + $class = $this->getSchemaClass(); + + return ($object instanceof $class) && 'string' === $object->getType() && 'date' === $object->getFormat(); + } + + /** + * {@inheritdoc} + */ + public function guessType($object, string $name, string $reference, Registry $registry): Type + { + return new DateType($object, $this->dateFormat, $this->preferInterface); + } + + protected function getSchemaClass(): string + { + return JsonSchema::class; + } +} diff --git a/Guesser/JsonSchema/DateTimeGuesser.php b/Guesser/JsonSchema/DateTimeGuesser.php new file mode 100644 index 0000000..4481717 --- /dev/null +++ b/Guesser/JsonSchema/DateTimeGuesser.php @@ -0,0 +1,54 @@ +outputDateFormat = $outputDateFormat; + $this->inputDateFormat = $inputDateFormat; + $this->preferInterface = $preferInterface; + } + + /** + * {@inheritdoc} + */ + public function supportObject($object): bool + { + $class = $this->getSchemaClass(); + + return ($object instanceof $class) && 'string' === $object->getType() && 'date-time' === $object->getFormat(); + } + + /** + * {@inheritdoc} + */ + public function guessType($object, string $name, string $reference, Registry $registry): Type + { + return new DateTimeType($object, $this->outputDateFormat, $this->inputDateFormat, $this->preferInterface); + } + + protected function getSchemaClass(): string + { + return JsonSchema::class; + } +} diff --git a/Guesser/JsonSchema/DefinitionGuesser.php b/Guesser/JsonSchema/DefinitionGuesser.php new file mode 100644 index 0000000..7c278b0 --- /dev/null +++ b/Guesser/JsonSchema/DefinitionGuesser.php @@ -0,0 +1,42 @@ +getDefinitions() as $key => $definition) { + $this->chainGuesser->guessClass($definition, $key, $reference . '/definitions/' . $key, $registry); + } + } + + /** + * {@inheritdoc} + */ + public function supportObject($object): bool + { + return ($object instanceof JsonSchema) && null !== $object->getDefinitions() && \count($object->getDefinitions()) > 0; + } + + protected function getSchemaClass(): string + { + return JsonSchema::class; + } +} diff --git a/Guesser/JsonSchema/ItemsGuesser.php b/Guesser/JsonSchema/ItemsGuesser.php new file mode 100644 index 0000000..dce20b5 --- /dev/null +++ b/Guesser/JsonSchema/ItemsGuesser.php @@ -0,0 +1,50 @@ +getItems() instanceof JsonSchema) { + $this->chainGuesser->guessClass($object->getItems(), $name . 'Item', $reference . '/items', $registry); + } else { + foreach ($object->getItems() as $key => $item) { + $this->chainGuesser->guessClass($item, $name . 'Item' . $key, $reference . '/items/' . $key, $registry); + } + } + } + + /** + * {@inheritdoc} + */ + public function supportObject($object): bool + { + $class = $this->getSchemaClass(); + + return + $object instanceof $class && + ( + $object->getItems() instanceof $class || + (\is_array($object->getItems()) && \count($object->getItems()) > 0) + ) + ; + } + + protected function getSchemaClass(): string + { + return JsonSchema::class; + } +} diff --git a/Guesser/JsonSchema/JsonSchemaGuesserFactory.php b/Guesser/JsonSchema/JsonSchemaGuesserFactory.php new file mode 100644 index 0000000..9ecdea0 --- /dev/null +++ b/Guesser/JsonSchema/JsonSchemaGuesserFactory.php @@ -0,0 +1,41 @@ +addGuesser(new DateGuesser($dateFormat, $datePreferInterface)); + $chainGuesser->addGuesser(new DateTimeGuesser($outputDateTimeFormat, $inputDateTimeFormat, $datePreferInterface)); + $chainGuesser->addGuesser(new SimpleTypeGuesser()); + $chainGuesser->addGuesser(new ArrayGuesser()); + $chainGuesser->addGuesser(new MultipleGuesser()); + $chainGuesser->addGuesser(new ObjectGuesser($naming, $serializer)); + $chainGuesser->addGuesser(new DefinitionGuesser()); + $chainGuesser->addGuesser(new ItemsGuesser()); + $chainGuesser->addGuesser(new AnyOfGuesser()); + $chainGuesser->addGuesser(new AllOfGuesser($serializer, $naming)); + $chainGuesser->addGuesser(new OneOfGuesser()); + $chainGuesser->addGuesser(new ObjectOneOfGuesser($merger, $serializer)); + $chainGuesser->addGuesser(new PatternPropertiesGuesser()); + $chainGuesser->addGuesser(new AdditionalItemsGuesser()); + $chainGuesser->addGuesser(new AdditionalPropertiesGuesser()); + + return $chainGuesser; + } +} diff --git a/Guesser/JsonSchema/MultipleGuesser.php b/Guesser/JsonSchema/MultipleGuesser.php new file mode 100644 index 0000000..63bb716 --- /dev/null +++ b/Guesser/JsonSchema/MultipleGuesser.php @@ -0,0 +1,54 @@ +getSchemaClass(); + + return ($object instanceof $class) && \is_array($object->getType()); + } + + protected function getSchemaClass(): string + { + return JsonSchema::class; + } + + /** + * {@inheritdoc} + */ + public function guessType($object, string $name, string $reference, Registry $registry): Type + { + $typeGuess = new MultipleType($object); + $fakeSchema = clone $object; + + foreach ($object->getType() as $type) { + if (\in_array($type, $this->bannedTypes)) { + continue; + } + + $fakeSchema->setType($type); + $typeGuess->addType($this->chainGuesser->guessType($fakeSchema, $name, $reference, $registry)); + } + + return $typeGuess; + } +} diff --git a/Guesser/JsonSchema/ObjectGuesser.php b/Guesser/JsonSchema/ObjectGuesser.php new file mode 100644 index 0000000..1c9524b --- /dev/null +++ b/Guesser/JsonSchema/ObjectGuesser.php @@ -0,0 +1,168 @@ +naming = $naming; + $this->serializer = $serializer; + } + + /** + * {@inheritdoc} + */ + public function supportObject($object): bool + { + return ($object instanceof JsonSchema) && (\is_array($object->getType()) ? \in_array('object', $object->getType()) : 'object' === $object->getType()) && null !== $object->getProperties(); + } + + /** + * {@inheritdoc} + * + * @param JsonSchema $object + */ + public function guessClass($object, string $name, string $reference, Registry $registry): void + { + if (!$registry->hasClass($reference)) { + $extensions = []; + + if ($object->getAdditionalProperties()) { + $extensionObject = null; + + if (\is_object($object->getAdditionalProperties())) { + $extensionObject = $object->getAdditionalProperties(); + } + + $extensions['.*'] = [ + 'object' => $extensionObject, + 'reference' => $reference . '/additionalProperties', + ]; + } elseif (method_exists($object, 'getPatternProperties') && $object->getPatternProperties() !== null) { + foreach ($object->getPatternProperties() as $pattern => $patternProperty) { + $extensions[$pattern] = [ + 'object' => $patternProperty, + 'reference' => $reference . '/patternProperties/' . $pattern, + ]; + } + } + + $registry->getSchema($reference)->addClass($reference, $this->createClassGuess($object, $reference, $name, $extensions)); + } + + foreach ($object->getProperties() as $key => $property) { + $this->chainGuesser->guessClass($property, $name . ucfirst($key), $reference . '/properties/' . $key, $registry); + } + } + + /** + * {@inheritdoc} + */ + public function guessProperties($object, string $name, string $reference, Registry $registry): array + { + /** @var JsonSchema $object */ + $properties = []; + + foreach ($object->getProperties() as $key => $property) { + $propertyObj = $property; + + if ($propertyObj instanceof Reference) { + $propertyObj = $this->resolve($propertyObj, $this->getSchemaClass()); + } + + $nullable = $this->isPropertyNullable($propertyObj); + + $required = false; + if (\is_array($object->getRequired())) { + $required = \in_array($key, $object->getRequired()); + } + + $properties[$key] = new Property($property, $key, $reference . '/properties/' . $key, $nullable, $required, null, $propertyObj->getDescription(), $propertyObj->getDefault(), $propertyObj->getReadOnly()); + if (method_exists($propertyObj, 'getDeprecated')) { + $properties[$key]->setDeprecated($propertyObj->getDeprecated()); + } + } + + return $properties; + } + + protected function isPropertyNullable($property): bool + { + return 'null' == $property->getType() || (\is_array($property->getType()) && \in_array('null', $property->getType())); + } + + /** + * {@inheritdoc} + */ + public function guessType($object, string $name, string $reference, Registry $registry): Type + { + $discriminants = []; + $required = $object->getRequired() ?: []; + + foreach ($object->getProperties() as $key => $property) { + if (!\in_array($key, $required)) { + continue; + } + + if ($property instanceof Reference) { + $property = $this->resolve($property, $this->getSchemaClass()); + } + + if (null !== $property->getEnum()) { + $isSimple = true; + foreach ($property->getEnum() as $value) { + if (\is_array($value) || \is_object($value)) { + $isSimple = false; + } + } + if ($isSimple) { + $discriminants[$key] = $property->getEnum(); + } + } else { + $discriminants[$key] = null; + } + } + + if ($registry->hasClass($reference)) { + return new ObjectType($object, $registry->getClass($reference)->getName(), $registry->getSchema($reference)->getNamespace(), $discriminants); + } + + return new Type($object, 'object'); + } + + protected function getSchemaClass(): string + { + return JsonSchema::class; + } + + protected function createClassGuess($object, string $reference, string $name, array $extensions): ClassGuess + { + return new ClassGuess($object, $reference, $this->naming->getClassName($name), $extensions, $object->getDeprecated()); + } +} diff --git a/Guesser/JsonSchema/ObjectOneOfGuesser.php b/Guesser/JsonSchema/ObjectOneOfGuesser.php new file mode 100644 index 0000000..ed0cadc --- /dev/null +++ b/Guesser/JsonSchema/ObjectOneOfGuesser.php @@ -0,0 +1,84 @@ +jsonSchemaMerger = $jsonSchemaMerger; + $this->serializer = $serializer; + } + + /** + * {@inheritdoc} + */ + public function guessClass($object, string $name, string $reference, Registry $registry): void + { + foreach ($object->getOneOf() as $key => $oneOf) { + $oneOfName = $name . 'Sub'; + $oneOfResolved = $oneOf; + + if ($oneOf instanceof Reference) { + $fragmentParts = explode('/', $oneOf->getMergedUri()->getFragment()); + $oneOfName = array_pop($fragmentParts); + $oneOfResolved = $this->resolve($oneOf, JsonSchema::class); + } + + $merged = $this->jsonSchemaMerger->merge($object, $oneOfResolved); + $this->chainGuesser->guessClass($merged, $oneOfName, $reference . '/oneOf/' . $key, $registry); + } + } + + /** + * {@inheritdoc} + */ + public function guessType($object, string $name, string $reference, Registry $registry): Type + { + $type = new MultipleType($object); + + foreach ($object->getOneOf() as $key => $oneOf) { + $oneOfName = $name . 'Sub'; + $oneOfResolved = $oneOf; + + if ($oneOf instanceof Reference) { + $fragmentParts = explode('/', $oneOf->getMergedUri()->getFragment()); + $oneOfName = array_pop($fragmentParts); + $oneOfResolved = $this->resolve($oneOf, JsonSchema::class); + } + + $merged = $this->jsonSchemaMerger->merge($object, $oneOfResolved); + $type->addType($this->chainGuesser->guessType($merged, $oneOfName, $reference . '/oneOf/' . $key, $registry)); + } + + return $type; + } + + /** + * {@inheritdoc} + */ + public function supportObject($object): bool + { + return ($object instanceof JsonSchema) && 'object' === $object->getType() && \is_array($object->getOneOf()) && \count($object->getOneOf()) > 0; + } +} diff --git a/Guesser/JsonSchema/OneOfGuesser.php b/Guesser/JsonSchema/OneOfGuesser.php new file mode 100644 index 0000000..a057c90 --- /dev/null +++ b/Guesser/JsonSchema/OneOfGuesser.php @@ -0,0 +1,39 @@ +getType() && \is_array($object->getOneOf()) && \count($object->getOneOf()) > 0; + } + + /** + * {@inheritdoc} + */ + public function guessType($object, string $name, string $reference, Registry $registry): Type + { + $type = new MultipleType($object); + + foreach ($object->getOneOf() as $oneOfKey => $oneOf) { + $type->addType($this->chainGuesser->guessType($oneOf, $name, $reference . '/oneOf/' . $oneOfKey, $registry)); + } + + return $type; + } +} diff --git a/Guesser/JsonSchema/PatternPropertiesGuesser.php b/Guesser/JsonSchema/PatternPropertiesGuesser.php new file mode 100644 index 0000000..a23d277 --- /dev/null +++ b/Guesser/JsonSchema/PatternPropertiesGuesser.php @@ -0,0 +1,55 @@ +getType()) { + return false; + } + + if (null !== $object->getProperties()) { + return false; + } + + if (!($object->getPatternProperties() instanceof \ArrayObject) || 0 == \count($object->getPatternProperties())) { + return false; + } + + return true; + } + + /** + * {@inheritdoc} + */ + public function guessType($object, string $name, string $reference, Registry $registry): Type + { + $type = new PatternMultipleType($object); + + foreach ($object->getPatternProperties() as $pattern => $patternProperty) { + $type->addType($pattern, $this->chainGuesser->guessType($patternProperty, $name, $reference . '/patternProperties/' . $pattern, $registry)); + } + + return $type; + } +} diff --git a/Guesser/JsonSchema/SimpleTypeGuesser.php b/Guesser/JsonSchema/SimpleTypeGuesser.php new file mode 100644 index 0000000..f21c02c --- /dev/null +++ b/Guesser/JsonSchema/SimpleTypeGuesser.php @@ -0,0 +1,66 @@ + 'bool', + 'integer' => 'int', + 'number' => 'float', + 'string' => 'string', + 'null' => 'null', + ]; + + protected $excludeFormat = [ + 'string' => [ + 'date-time', + ], + ]; + + /** + * {@inheritdoc} + */ + public function supportObject($object): bool + { + $class = $this->getSchemaClass(); + + return ($object instanceof $class) + && + \in_array($object->getType(), $this->typesSupported) + && + ( + !\in_array($object->getType(), $this->excludeFormat) + || + !\in_array($object->getFormat(), $this->excludeFormat[$object->getType()]) + ) + ; + } + + protected function getSchemaClass(): string + { + return JsonSchema::class; + } + + /** + * {@inheritdoc} + */ + public function guessType($object, string $name, string $reference, Registry $registry): Type + { + return new Type($object, $this->phpTypesMapping[$object->getType()]); + } +} diff --git a/Guesser/PropertiesGuesserInterface.php b/Guesser/PropertiesGuesserInterface.php new file mode 100644 index 0000000..13ce41a --- /dev/null +++ b/Guesser/PropertiesGuesserInterface.php @@ -0,0 +1,18 @@ +serializer = $serializer; + } + + /** + * {@inheritdoc} + */ + public function supportObject($object): bool + { + return $object instanceof Reference; + } + + /** + * {@inheritdoc} + * + * @param Reference $object + */ + public function guessClass($object, string $name, string $reference, Registry $registry): void + { + if ($object->isInCurrentDocument()) { + return; + } + + $mergedReference = (string) $object->getMergedUri(); + + if (null === $registry->getSchema($mergedReference)) { + $schema = $registry->getSchema((string) $object->getOriginUri()); + $schema->addReference((string) $object->getMergedUri()->withFragment('')); + } + + $this->chainGuesser->guessClass( + $this->resolve($object, $this->getSchemaClass()), + $name, + (string) $object->getMergedUri()->withFragment('') === (string) $object->getMergedUri() ? (string) $object->getMergedUri()->withFragment('') . '#' : (string) $object->getMergedUri(), + $registry + ); + } + + /** + * {@inheritdoc} + * + * @param Reference $object + */ + public function guessType($object, string $name, string $reference, Registry $registry): Type + { + $resolved = $this->resolve($object, $this->getSchemaClass()); + $classKey = (string) $object->getMergedUri(); + + if ((string) $object->getMergedUri() === (string) $object->getMergedUri()->withFragment('')) { + $classKey .= '#'; + } + + if ($registry->hasClass($classKey)) { + $name = $registry->getClass($classKey)->getName(); + } + + return $this->chainGuesser->guessType($resolved, $name, $classKey, $registry); + } + + protected function getSchemaClass(): string + { + return JsonSchema::class; + } +} diff --git a/Guesser/TypeGuesserInterface.php b/Guesser/TypeGuesserInterface.php new file mode 100644 index 0000000..c6e4c30 --- /dev/null +++ b/Guesser/TypeGuesserInterface.php @@ -0,0 +1,16 @@ +serializer = $serializer; + $this->chainGuesser = $chainGuesser; + $this->strict = $strict; + $this->naming = $naming; + } + + public function createContext(Registry $registry): Context + { + // List of schemas can evolve, but we don't want to generate new schema dynamically added, so we "clone" the array + // to have a fixed list of schemas + $schemas = array_values($registry->getSchemas()); + + /** @var Schema $schema */ + foreach ($schemas as $schema) { + $jsonSchema = $this->serializer->deserialize(file_get_contents($schema->getOrigin()), 'Jane\Component\JsonSchema\JsonSchema\Model\JsonSchema', 'json', [ + 'document-origin' => $schema->getOrigin(), + ]); + + $this->chainGuesser->guessClass($jsonSchema, $schema->getRootName(), $schema->getOrigin() . '#', $registry); + } + + foreach ($registry->getSchemas() as $schema) { + foreach ($schema->getClasses() as $class) { + $properties = $this->chainGuesser->guessProperties($class->getObject(), $schema->getRootName(), $class->getReference(), $registry); + $names = []; + + foreach ($properties as $property) { + $property->setPhpName($this->naming->getPropertyName($property->getName())); + + $i = 2; + $newName = $property->getPhpName(); + + while (\in_array(strtolower($newName), $names, true)) { + $newName = $property->getPhpName() . $i; + ++$i; + } + + if ($newName !== $property->getPhpName()) { + $property->setPhpName($newName); + } + + $names[] = strtolower($property->getPhpName()); + + $property->setType($this->chainGuesser->guessType($property->getObject(), $property->getName(), $property->getReference(), $registry)); + } + + $class->setProperties($properties); + $schema->addClassRelations($class); + + $extensionsTypes = []; + + foreach ($class->getExtensionsObject() as $pattern => $extensionData) { + $extensionsTypes[$pattern] = $this->chainGuesser->guessType($extensionData['object'], $class->getName(), $extensionData['reference'], $registry); + } + + $class->setExtensionsType($extensionsTypes); + } + } + + return new Context($registry, $this->strict); + } + + public static function build(array $options = []): self + { + $serializer = self::buildSerializer(); + $chainGuesser = JsonSchemaGuesserFactory::create($serializer, $options); + $naming = new Naming(); + $parser = (new ParserFactory())->create(ParserFactory::PREFER_PHP7); + + $self = new self($serializer, $chainGuesser, $naming, $options['strict']); + $self->addGenerator(new ModelGenerator($naming, $parser)); + $self->addGenerator(new NormalizerGenerator($naming, $parser, $options['reference'], $options['use-cacheable-supports-method'] ?? false, $options['skip-null-values'] ?? true, $options['skip-required-fields'] ?? false)); + $self->addGenerator(new RuntimeGenerator($naming, $parser)); + + return $self; + } + + public static function buildSerializer(): SerializerInterface + { + $encoders = [new JsonEncoder(new JsonEncode([JsonEncode::OPTIONS => JSON_UNESCAPED_SLASHES]), new JsonDecode([JsonDecode::ASSOCIATIVE => true]))]; + + return new Serializer([new JaneObjectNormalizer()], $encoders); + } +} diff --git a/JsonSchema/.editorconfig b/JsonSchema/.editorconfig new file mode 100644 index 0000000..d92284c --- /dev/null +++ b/JsonSchema/.editorconfig @@ -0,0 +1,2 @@ +[*] +insert_final_newline = false diff --git a/JsonSchema/Model/JsonSchema.php b/JsonSchema/Model/JsonSchema.php new file mode 100644 index 0000000..50494b4 --- /dev/null +++ b/JsonSchema/Model/JsonSchema.php @@ -0,0 +1,1600 @@ +definitions; + } + /** + * + * + * @param JsonSchema[]|bool[]|null $definitions + * + * @return self + */ + public function setDefinitions(?iterable $definitions) : self + { + $this->definitions = $definitions; + return $this; + } + /** + * + * + * @return JsonSchema[]|bool[]|string[][]|null + */ + public function getDependencies() : ?iterable + { + return $this->dependencies; + } + /** + * + * + * @param JsonSchema[]|bool[]|string[][]|null $dependencies + * + * @return self + */ + public function setDependencies(?iterable $dependencies) : self + { + $this->dependencies = $dependencies; + return $this; + } + /** + * + * + * @return JsonSchema|bool|null + */ + public function getAdditionalItems() + { + return $this->additionalItems; + } + /** + * + * + * @param JsonSchema|bool|null $additionalItems + * + * @return self + */ + public function setAdditionalItems($additionalItems) : self + { + $this->additionalItems = $additionalItems; + return $this; + } + /** + * + * + * @return JsonSchema|bool|null + */ + public function getUnevaluatedItems() + { + return $this->unevaluatedItems; + } + /** + * + * + * @param JsonSchema|bool|null $unevaluatedItems + * + * @return self + */ + public function setUnevaluatedItems($unevaluatedItems) : self + { + $this->unevaluatedItems = $unevaluatedItems; + return $this; + } + /** + * + * + * @return JsonSchema|bool|JsonSchema[]|bool[]|null + */ + public function getItems() + { + return $this->items; + } + /** + * + * + * @param JsonSchema|bool|JsonSchema[]|bool[]|null $items + * + * @return self + */ + public function setItems($items) : self + { + $this->items = $items; + return $this; + } + /** + * + * + * @return JsonSchema|bool|null + */ + public function getContains() + { + return $this->contains; + } + /** + * + * + * @param JsonSchema|bool|null $contains + * + * @return self + */ + public function setContains($contains) : self + { + $this->contains = $contains; + return $this; + } + /** + * + * + * @return JsonSchema|bool|null + */ + public function getAdditionalProperties() + { + return $this->additionalProperties; + } + /** + * + * + * @param JsonSchema|bool|null $additionalProperties + * + * @return self + */ + public function setAdditionalProperties($additionalProperties) : self + { + $this->additionalProperties = $additionalProperties; + return $this; + } + /** + * + * + * @return JsonSchema[]|bool[]|null + */ + public function getUnevaluatedProperties() : ?iterable + { + return $this->unevaluatedProperties; + } + /** + * + * + * @param JsonSchema[]|bool[]|null $unevaluatedProperties + * + * @return self + */ + public function setUnevaluatedProperties(?iterable $unevaluatedProperties) : self + { + $this->unevaluatedProperties = $unevaluatedProperties; + return $this; + } + /** + * + * + * @return JsonSchema[]|bool[]|null + */ + public function getProperties() : ?iterable + { + return $this->properties; + } + /** + * + * + * @param JsonSchema[]|bool[]|null $properties + * + * @return self + */ + public function setProperties(?iterable $properties) : self + { + $this->properties = $properties; + return $this; + } + /** + * + * + * @return JsonSchema[]|bool[]|null + */ + public function getPatternProperties() : ?iterable + { + return $this->patternProperties; + } + /** + * + * + * @param JsonSchema[]|bool[]|null $patternProperties + * + * @return self + */ + public function setPatternProperties(?iterable $patternProperties) : self + { + $this->patternProperties = $patternProperties; + return $this; + } + /** + * + * + * @return JsonSchema[]|bool[]|null + */ + public function getDependentSchemas() : ?iterable + { + return $this->dependentSchemas; + } + /** + * + * + * @param JsonSchema[]|bool[]|null $dependentSchemas + * + * @return self + */ + public function setDependentSchemas(?iterable $dependentSchemas) : self + { + $this->dependentSchemas = $dependentSchemas; + return $this; + } + /** + * + * + * @return JsonSchema|bool|null + */ + public function getPropertyNames() + { + return $this->propertyNames; + } + /** + * + * + * @param JsonSchema|bool|null $propertyNames + * + * @return self + */ + public function setPropertyNames($propertyNames) : self + { + $this->propertyNames = $propertyNames; + return $this; + } + /** + * + * + * @return JsonSchema|bool|null + */ + public function getIf() + { + return $this->if; + } + /** + * + * + * @param JsonSchema|bool|null $if + * + * @return self + */ + public function setIf($if) : self + { + $this->if = $if; + return $this; + } + /** + * + * + * @return JsonSchema|bool|null + */ + public function getThen() + { + return $this->then; + } + /** + * + * + * @param JsonSchema|bool|null $then + * + * @return self + */ + public function setThen($then) : self + { + $this->then = $then; + return $this; + } + /** + * + * + * @return JsonSchema|bool|null + */ + public function getElse() + { + return $this->else; + } + /** + * + * + * @param JsonSchema|bool|null $else + * + * @return self + */ + public function setElse($else) : self + { + $this->else = $else; + return $this; + } + /** + * + * + * @return JsonSchema[]|bool[]|null + */ + public function getAllOf() : ?array + { + return $this->allOf; + } + /** + * + * + * @param JsonSchema[]|bool[]|null $allOf + * + * @return self + */ + public function setAllOf(?array $allOf) : self + { + $this->allOf = $allOf; + return $this; + } + /** + * + * + * @return JsonSchema[]|bool[]|null + */ + public function getAnyOf() : ?array + { + return $this->anyOf; + } + /** + * + * + * @param JsonSchema[]|bool[]|null $anyOf + * + * @return self + */ + public function setAnyOf(?array $anyOf) : self + { + $this->anyOf = $anyOf; + return $this; + } + /** + * + * + * @return JsonSchema[]|bool[]|null + */ + public function getOneOf() : ?array + { + return $this->oneOf; + } + /** + * + * + * @param JsonSchema[]|bool[]|null $oneOf + * + * @return self + */ + public function setOneOf(?array $oneOf) : self + { + $this->oneOf = $oneOf; + return $this; + } + /** + * + * + * @return JsonSchema|bool|null + */ + public function getNot() + { + return $this->not; + } + /** + * + * + * @param JsonSchema|bool|null $not + * + * @return self + */ + public function setNot($not) : self + { + $this->not = $not; + return $this; + } + /** + * + * + * @return string|null + */ + public function getContentMediaType() : ?string + { + return $this->contentMediaType; + } + /** + * + * + * @param string|null $contentMediaType + * + * @return self + */ + public function setContentMediaType(?string $contentMediaType) : self + { + $this->contentMediaType = $contentMediaType; + return $this; + } + /** + * + * + * @return string|null + */ + public function getContentEncoding() : ?string + { + return $this->contentEncoding; + } + /** + * + * + * @param string|null $contentEncoding + * + * @return self + */ + public function setContentEncoding(?string $contentEncoding) : self + { + $this->contentEncoding = $contentEncoding; + return $this; + } + /** + * + * + * @return JsonSchema|bool|null + */ + public function getContentSchema() + { + return $this->contentSchema; + } + /** + * + * + * @param JsonSchema|bool|null $contentSchema + * + * @return self + */ + public function setContentSchema($contentSchema) : self + { + $this->contentSchema = $contentSchema; + return $this; + } + /** + * + * + * @return string|null + */ + public function getDollarId() : ?string + { + return $this->dollarId; + } + /** + * + * + * @param string|null $dollarId + * + * @return self + */ + public function setDollarId(?string $dollarId) : self + { + $this->dollarId = $dollarId; + return $this; + } + /** + * + * + * @return string|null + */ + public function getDollarSchema() : ?string + { + return $this->dollarSchema; + } + /** + * + * + * @param string|null $dollarSchema + * + * @return self + */ + public function setDollarSchema(?string $dollarSchema) : self + { + $this->dollarSchema = $dollarSchema; + return $this; + } + /** + * + * + * @return string|null + */ + public function getDollarAnchor() : ?string + { + return $this->dollarAnchor; + } + /** + * + * + * @param string|null $dollarAnchor + * + * @return self + */ + public function setDollarAnchor(?string $dollarAnchor) : self + { + $this->dollarAnchor = $dollarAnchor; + return $this; + } + /** + * + * + * @return string|null + */ + public function getDollarRef() : ?string + { + return $this->dollarRef; + } + /** + * + * + * @param string|null $dollarRef + * + * @return self + */ + public function setDollarRef(?string $dollarRef) : self + { + $this->dollarRef = $dollarRef; + return $this; + } + /** + * + * + * @return string|null + */ + public function getDollarRecursiveRef() : ?string + { + return $this->dollarRecursiveRef; + } + /** + * + * + * @param string|null $dollarRecursiveRef + * + * @return self + */ + public function setDollarRecursiveRef(?string $dollarRecursiveRef) : self + { + $this->dollarRecursiveRef = $dollarRecursiveRef; + return $this; + } + /** + * + * + * @return bool|null + */ + public function getDollarRecursiveAnchor() : ?bool + { + return $this->dollarRecursiveAnchor; + } + /** + * + * + * @param bool|null $dollarRecursiveAnchor + * + * @return self + */ + public function setDollarRecursiveAnchor(?bool $dollarRecursiveAnchor) : self + { + $this->dollarRecursiveAnchor = $dollarRecursiveAnchor; + return $this; + } + /** + * + * + * @return bool[]|null + */ + public function getDollarVocabulary() : ?iterable + { + return $this->dollarVocabulary; + } + /** + * + * + * @param bool[]|null $dollarVocabulary + * + * @return self + */ + public function setDollarVocabulary(?iterable $dollarVocabulary) : self + { + $this->dollarVocabulary = $dollarVocabulary; + return $this; + } + /** + * + * + * @return string|null + */ + public function getDollarComment() : ?string + { + return $this->dollarComment; + } + /** + * + * + * @param string|null $dollarComment + * + * @return self + */ + public function setDollarComment(?string $dollarComment) : self + { + $this->dollarComment = $dollarComment; + return $this; + } + /** + * + * + * @return JsonSchema[]|bool[]|null + */ + public function getDollarDefs() : ?iterable + { + return $this->dollarDefs; + } + /** + * + * + * @param JsonSchema[]|bool[]|null $dollarDefs + * + * @return self + */ + public function setDollarDefs(?iterable $dollarDefs) : self + { + $this->dollarDefs = $dollarDefs; + return $this; + } + /** + * + * + * @return string|null + */ + public function getFormat() : ?string + { + return $this->format; + } + /** + * + * + * @param string|null $format + * + * @return self + */ + public function setFormat(?string $format) : self + { + $this->format = $format; + return $this; + } + /** + * + * + * @return string|null + */ + public function getTitle() : ?string + { + return $this->title; + } + /** + * + * + * @param string|null $title + * + * @return self + */ + public function setTitle(?string $title) : self + { + $this->title = $title; + return $this; + } + /** + * + * + * @return string|null + */ + public function getDescription() : ?string + { + return $this->description; + } + /** + * + * + * @param string|null $description + * + * @return self + */ + public function setDescription(?string $description) : self + { + $this->description = $description; + return $this; + } + /** + * + * + * @return mixed + */ + public function getDefault() + { + return $this->default; + } + /** + * + * + * @param mixed $default + * + * @return self + */ + public function setDefault($default) : self + { + $this->default = $default; + return $this; + } + /** + * + * + * @return bool|null + */ + public function getDeprecated() : ?bool + { + return $this->deprecated; + } + /** + * + * + * @param bool|null $deprecated + * + * @return self + */ + public function setDeprecated(?bool $deprecated) : self + { + $this->deprecated = $deprecated; + return $this; + } + /** + * + * + * @return bool|null + */ + public function getReadOnly() : ?bool + { + return $this->readOnly; + } + /** + * + * + * @param bool|null $readOnly + * + * @return self + */ + public function setReadOnly(?bool $readOnly) : self + { + $this->readOnly = $readOnly; + return $this; + } + /** + * + * + * @return bool|null + */ + public function getWriteOnly() : ?bool + { + return $this->writeOnly; + } + /** + * + * + * @param bool|null $writeOnly + * + * @return self + */ + public function setWriteOnly(?bool $writeOnly) : self + { + $this->writeOnly = $writeOnly; + return $this; + } + /** + * + * + * @return mixed[]|null + */ + public function getExamples() : ?array + { + return $this->examples; + } + /** + * + * + * @param mixed[]|null $examples + * + * @return self + */ + public function setExamples(?array $examples) : self + { + $this->examples = $examples; + return $this; + } + /** + * + * + * @return float|null + */ + public function getMultipleOf() : ?float + { + return $this->multipleOf; + } + /** + * + * + * @param float|null $multipleOf + * + * @return self + */ + public function setMultipleOf(?float $multipleOf) : self + { + $this->multipleOf = $multipleOf; + return $this; + } + /** + * + * + * @return float|null + */ + public function getMaximum() : ?float + { + return $this->maximum; + } + /** + * + * + * @param float|null $maximum + * + * @return self + */ + public function setMaximum(?float $maximum) : self + { + $this->maximum = $maximum; + return $this; + } + /** + * + * + * @return float|null + */ + public function getExclusiveMaximum() : ?float + { + return $this->exclusiveMaximum; + } + /** + * + * + * @param float|null $exclusiveMaximum + * + * @return self + */ + public function setExclusiveMaximum(?float $exclusiveMaximum) : self + { + $this->exclusiveMaximum = $exclusiveMaximum; + return $this; + } + /** + * + * + * @return float|null + */ + public function getMinimum() : ?float + { + return $this->minimum; + } + /** + * + * + * @param float|null $minimum + * + * @return self + */ + public function setMinimum(?float $minimum) : self + { + $this->minimum = $minimum; + return $this; + } + /** + * + * + * @return float|null + */ + public function getExclusiveMinimum() : ?float + { + return $this->exclusiveMinimum; + } + /** + * + * + * @param float|null $exclusiveMinimum + * + * @return self + */ + public function setExclusiveMinimum(?float $exclusiveMinimum) : self + { + $this->exclusiveMinimum = $exclusiveMinimum; + return $this; + } + /** + * + * + * @return int|null + */ + public function getMaxLength() : ?int + { + return $this->maxLength; + } + /** + * + * + * @param int|null $maxLength + * + * @return self + */ + public function setMaxLength(?int $maxLength) : self + { + $this->maxLength = $maxLength; + return $this; + } + /** + * + * + * @return int|null + */ + public function getMinLength() : ?int + { + return $this->minLength; + } + /** + * + * + * @param int|null $minLength + * + * @return self + */ + public function setMinLength(?int $minLength) : self + { + $this->minLength = $minLength; + return $this; + } + /** + * + * + * @return string|null + */ + public function getPattern() : ?string + { + return $this->pattern; + } + /** + * + * + * @param string|null $pattern + * + * @return self + */ + public function setPattern(?string $pattern) : self + { + $this->pattern = $pattern; + return $this; + } + /** + * + * + * @return int|null + */ + public function getMaxItems() : ?int + { + return $this->maxItems; + } + /** + * + * + * @param int|null $maxItems + * + * @return self + */ + public function setMaxItems(?int $maxItems) : self + { + $this->maxItems = $maxItems; + return $this; + } + /** + * + * + * @return int|null + */ + public function getMinItems() : ?int + { + return $this->minItems; + } + /** + * + * + * @param int|null $minItems + * + * @return self + */ + public function setMinItems(?int $minItems) : self + { + $this->minItems = $minItems; + return $this; + } + /** + * + * + * @return bool|null + */ + public function getUniqueItems() : ?bool + { + return $this->uniqueItems; + } + /** + * + * + * @param bool|null $uniqueItems + * + * @return self + */ + public function setUniqueItems(?bool $uniqueItems) : self + { + $this->uniqueItems = $uniqueItems; + return $this; + } + /** + * + * + * @return int|null + */ + public function getMaxContains() : ?int + { + return $this->maxContains; + } + /** + * + * + * @param int|null $maxContains + * + * @return self + */ + public function setMaxContains(?int $maxContains) : self + { + $this->maxContains = $maxContains; + return $this; + } + /** + * + * + * @return int|null + */ + public function getMinContains() : ?int + { + return $this->minContains; + } + /** + * + * + * @param int|null $minContains + * + * @return self + */ + public function setMinContains(?int $minContains) : self + { + $this->minContains = $minContains; + return $this; + } + /** + * + * + * @return int|null + */ + public function getMaxProperties() : ?int + { + return $this->maxProperties; + } + /** + * + * + * @param int|null $maxProperties + * + * @return self + */ + public function setMaxProperties(?int $maxProperties) : self + { + $this->maxProperties = $maxProperties; + return $this; + } + /** + * + * + * @return int|null + */ + public function getMinProperties() : ?int + { + return $this->minProperties; + } + /** + * + * + * @param int|null $minProperties + * + * @return self + */ + public function setMinProperties(?int $minProperties) : self + { + $this->minProperties = $minProperties; + return $this; + } + /** + * + * + * @return string[]|null + */ + public function getRequired() : ?array + { + return $this->required; + } + /** + * + * + * @param string[]|null $required + * + * @return self + */ + public function setRequired(?array $required) : self + { + $this->required = $required; + return $this; + } + /** + * + * + * @return string[][]|null + */ + public function getDependentRequired() : ?iterable + { + return $this->dependentRequired; + } + /** + * + * + * @param string[][]|null $dependentRequired + * + * @return self + */ + public function setDependentRequired(?iterable $dependentRequired) : self + { + $this->dependentRequired = $dependentRequired; + return $this; + } + /** + * + * + * @return string|null + */ + public function getConst() : ?string + { + return $this->const; + } + /** + * + * + * @param string|null $const + * + * @return self + */ + public function setConst(?string $const) : self + { + $this->const = $const; + return $this; + } + /** + * + * + * @return string[]|null + */ + public function getEnum() : ?array + { + return $this->enum; + } + /** + * + * + * @param string[]|null $enum + * + * @return self + */ + public function setEnum(?array $enum) : self + { + $this->enum = $enum; + return $this; + } + /** + * + * + * @return mixed|mixed[] + */ + public function getType() + { + return $this->type; + } + /** + * + * + * @param mixed|mixed[] $type + * + * @return self + */ + public function setType($type) : self + { + $this->type = $type; + return $this; + } +} \ No newline at end of file diff --git a/JsonSchema/Normalizer/JaneObjectNormalizer.php b/JsonSchema/Normalizer/JaneObjectNormalizer.php new file mode 100644 index 0000000..535c941 --- /dev/null +++ b/JsonSchema/Normalizer/JaneObjectNormalizer.php @@ -0,0 +1,50 @@ + 'Jane\\Component\\JsonSchema\\JsonSchema\\Normalizer\\JsonSchemaNormalizer', '\\Jane\\Component\\JsonSchemaRuntime\\Reference' => '\\Jane\\Component\\JsonSchema\\JsonSchema\\Runtime\\Normalizer\\ReferenceNormalizer'), $normalizersCache = array(); + public function supportsDenormalization($data, $type, $format = null) + { + return array_key_exists($type, $this->normalizers); + } + public function supportsNormalization($data, $format = null) + { + return is_object($data) && array_key_exists(get_class($data), $this->normalizers); + } + public function normalize($object, $format = null, array $context = array()) + { + $normalizerClass = $this->normalizers[get_class($object)]; + $normalizer = $this->getNormalizer($normalizerClass); + return $normalizer->normalize($object, $format, $context); + } + public function denormalize($data, $class, $format = null, array $context = array()) + { + $denormalizerClass = $this->normalizers[$class]; + $denormalizer = $this->getNormalizer($denormalizerClass); + return $denormalizer->denormalize($data, $class, $format, $context); + } + private function getNormalizer(string $normalizerClass) + { + return $this->normalizersCache[$normalizerClass] ?? $this->initNormalizer($normalizerClass); + } + private function initNormalizer(string $normalizerClass) + { + $normalizer = new $normalizerClass(); + $normalizer->setNormalizer($this->normalizer); + $normalizer->setDenormalizer($this->denormalizer); + $this->normalizersCache[$normalizerClass] = $normalizer; + return $normalizer; + } +} \ No newline at end of file diff --git a/JsonSchema/Normalizer/JsonSchemaNormalizer.php b/JsonSchema/Normalizer/JsonSchemaNormalizer.php new file mode 100644 index 0000000..1355146 --- /dev/null +++ b/JsonSchema/Normalizer/JsonSchemaNormalizer.php @@ -0,0 +1,1190 @@ + $value) { + $value_1 = $value; + if (is_array($value)) { + $value_1 = $this->denormalizer->denormalize($value, 'Jane\\Component\\JsonSchema\\JsonSchema\\Model\\JsonSchema', 'json', $context); + } elseif (is_bool($value)) { + $value_1 = $value; + } + $values[$key] = $value_1; + } + $object->setDefinitions($values); + } + elseif (\array_key_exists('definitions', $data) && $data['definitions'] === null) { + $object->setDefinitions(null); + } + if (\array_key_exists('dependencies', $data) && $data['dependencies'] !== null) { + $values_1 = new \ArrayObject(array(), \ArrayObject::ARRAY_AS_PROPS); + foreach ($data['dependencies'] as $key_1 => $value_2) { + $value_3 = $value_2; + if (is_array($value_2)) { + $value_3 = $this->denormalizer->denormalize($value_2, 'Jane\\Component\\JsonSchema\\JsonSchema\\Model\\JsonSchema', 'json', $context); + } elseif (is_bool($value_2)) { + $value_3 = $value_2; + } elseif (is_array($value_2) && $this->isOnlyNumericKeys($value_2)) { + $values_2 = array(); + foreach ($value_2 as $value_4) { + $values_2[] = $value_4; + } + $value_3 = $values_2; + } + $values_1[$key_1] = $value_3; + } + $object->setDependencies($values_1); + } + elseif (\array_key_exists('dependencies', $data) && $data['dependencies'] === null) { + $object->setDependencies(null); + } + if (\array_key_exists('additionalItems', $data) && $data['additionalItems'] !== null) { + $value_5 = $data['additionalItems']; + if (is_array($data['additionalItems'])) { + $value_5 = $this->denormalizer->denormalize($data['additionalItems'], 'Jane\\Component\\JsonSchema\\JsonSchema\\Model\\JsonSchema', 'json', $context); + } elseif (is_bool($data['additionalItems'])) { + $value_5 = $data['additionalItems']; + } + $object->setAdditionalItems($value_5); + } + elseif (\array_key_exists('additionalItems', $data) && $data['additionalItems'] === null) { + $object->setAdditionalItems(null); + } + if (\array_key_exists('unevaluatedItems', $data) && $data['unevaluatedItems'] !== null) { + $value_6 = $data['unevaluatedItems']; + if (is_array($data['unevaluatedItems'])) { + $value_6 = $this->denormalizer->denormalize($data['unevaluatedItems'], 'Jane\\Component\\JsonSchema\\JsonSchema\\Model\\JsonSchema', 'json', $context); + } elseif (is_bool($data['unevaluatedItems'])) { + $value_6 = $data['unevaluatedItems']; + } + $object->setUnevaluatedItems($value_6); + } + elseif (\array_key_exists('unevaluatedItems', $data) && $data['unevaluatedItems'] === null) { + $object->setUnevaluatedItems(null); + } + if (\array_key_exists('items', $data) && $data['items'] !== null) { + $value_7 = $data['items']; + if (is_array($data['items'])) { + $value_7 = $this->denormalizer->denormalize($data['items'], 'Jane\\Component\\JsonSchema\\JsonSchema\\Model\\JsonSchema', 'json', $context); + } elseif (is_bool($data['items'])) { + $value_7 = $data['items']; + } elseif (is_array($data['items']) && $this->isOnlyNumericKeys($data['items'])) { + $values_3 = array(); + foreach ($data['items'] as $value_8) { + $value_9 = $value_8; + if (is_array($value_8)) { + $value_9 = $this->denormalizer->denormalize($value_8, 'Jane\\Component\\JsonSchema\\JsonSchema\\Model\\JsonSchema', 'json', $context); + } elseif (is_bool($value_8)) { + $value_9 = $value_8; + } + $values_3[] = $value_9; + } + $value_7 = $values_3; + } + $object->setItems($value_7); + } + elseif (\array_key_exists('items', $data) && $data['items'] === null) { + $object->setItems(null); + } + if (\array_key_exists('contains', $data) && $data['contains'] !== null) { + $value_10 = $data['contains']; + if (is_array($data['contains'])) { + $value_10 = $this->denormalizer->denormalize($data['contains'], 'Jane\\Component\\JsonSchema\\JsonSchema\\Model\\JsonSchema', 'json', $context); + } elseif (is_bool($data['contains'])) { + $value_10 = $data['contains']; + } + $object->setContains($value_10); + } + elseif (\array_key_exists('contains', $data) && $data['contains'] === null) { + $object->setContains(null); + } + if (\array_key_exists('additionalProperties', $data) && $data['additionalProperties'] !== null) { + $value_11 = $data['additionalProperties']; + if (is_array($data['additionalProperties'])) { + $value_11 = $this->denormalizer->denormalize($data['additionalProperties'], 'Jane\\Component\\JsonSchema\\JsonSchema\\Model\\JsonSchema', 'json', $context); + } elseif (is_bool($data['additionalProperties'])) { + $value_11 = $data['additionalProperties']; + } + $object->setAdditionalProperties($value_11); + } + elseif (\array_key_exists('additionalProperties', $data) && $data['additionalProperties'] === null) { + $object->setAdditionalProperties(null); + } + if (\array_key_exists('unevaluatedProperties', $data) && $data['unevaluatedProperties'] !== null) { + $values_4 = new \ArrayObject(array(), \ArrayObject::ARRAY_AS_PROPS); + foreach ($data['unevaluatedProperties'] as $key_2 => $value_12) { + $value_13 = $value_12; + if (is_array($value_12)) { + $value_13 = $this->denormalizer->denormalize($value_12, 'Jane\\Component\\JsonSchema\\JsonSchema\\Model\\JsonSchema', 'json', $context); + } elseif (is_bool($value_12)) { + $value_13 = $value_12; + } + $values_4[$key_2] = $value_13; + } + $object->setUnevaluatedProperties($values_4); + } + elseif (\array_key_exists('unevaluatedProperties', $data) && $data['unevaluatedProperties'] === null) { + $object->setUnevaluatedProperties(null); + } + if (\array_key_exists('properties', $data) && $data['properties'] !== null) { + $values_5 = new \ArrayObject(array(), \ArrayObject::ARRAY_AS_PROPS); + foreach ($data['properties'] as $key_3 => $value_14) { + $value_15 = $value_14; + if (is_array($value_14)) { + $value_15 = $this->denormalizer->denormalize($value_14, 'Jane\\Component\\JsonSchema\\JsonSchema\\Model\\JsonSchema', 'json', $context); + } elseif (is_bool($value_14)) { + $value_15 = $value_14; + } + $values_5[$key_3] = $value_15; + } + $object->setProperties($values_5); + } + elseif (\array_key_exists('properties', $data) && $data['properties'] === null) { + $object->setProperties(null); + } + if (\array_key_exists('patternProperties', $data) && $data['patternProperties'] !== null) { + $values_6 = new \ArrayObject(array(), \ArrayObject::ARRAY_AS_PROPS); + foreach ($data['patternProperties'] as $key_4 => $value_16) { + $value_17 = $value_16; + if (is_array($value_16)) { + $value_17 = $this->denormalizer->denormalize($value_16, 'Jane\\Component\\JsonSchema\\JsonSchema\\Model\\JsonSchema', 'json', $context); + } elseif (is_bool($value_16)) { + $value_17 = $value_16; + } + $values_6[$key_4] = $value_17; + } + $object->setPatternProperties($values_6); + } + elseif (\array_key_exists('patternProperties', $data) && $data['patternProperties'] === null) { + $object->setPatternProperties(null); + } + if (\array_key_exists('dependentSchemas', $data) && $data['dependentSchemas'] !== null) { + $values_7 = new \ArrayObject(array(), \ArrayObject::ARRAY_AS_PROPS); + foreach ($data['dependentSchemas'] as $key_5 => $value_18) { + $value_19 = $value_18; + if (is_array($value_18)) { + $value_19 = $this->denormalizer->denormalize($value_18, 'Jane\\Component\\JsonSchema\\JsonSchema\\Model\\JsonSchema', 'json', $context); + } elseif (is_bool($value_18)) { + $value_19 = $value_18; + } + $values_7[$key_5] = $value_19; + } + $object->setDependentSchemas($values_7); + } + elseif (\array_key_exists('dependentSchemas', $data) && $data['dependentSchemas'] === null) { + $object->setDependentSchemas(null); + } + if (\array_key_exists('propertyNames', $data) && $data['propertyNames'] !== null) { + $value_20 = $data['propertyNames']; + if (is_array($data['propertyNames'])) { + $value_20 = $this->denormalizer->denormalize($data['propertyNames'], 'Jane\\Component\\JsonSchema\\JsonSchema\\Model\\JsonSchema', 'json', $context); + } elseif (is_bool($data['propertyNames'])) { + $value_20 = $data['propertyNames']; + } + $object->setPropertyNames($value_20); + } + elseif (\array_key_exists('propertyNames', $data) && $data['propertyNames'] === null) { + $object->setPropertyNames(null); + } + if (\array_key_exists('if', $data) && $data['if'] !== null) { + $value_21 = $data['if']; + if (is_array($data['if'])) { + $value_21 = $this->denormalizer->denormalize($data['if'], 'Jane\\Component\\JsonSchema\\JsonSchema\\Model\\JsonSchema', 'json', $context); + } elseif (is_bool($data['if'])) { + $value_21 = $data['if']; + } + $object->setIf($value_21); + } + elseif (\array_key_exists('if', $data) && $data['if'] === null) { + $object->setIf(null); + } + if (\array_key_exists('then', $data) && $data['then'] !== null) { + $value_22 = $data['then']; + if (is_array($data['then'])) { + $value_22 = $this->denormalizer->denormalize($data['then'], 'Jane\\Component\\JsonSchema\\JsonSchema\\Model\\JsonSchema', 'json', $context); + } elseif (is_bool($data['then'])) { + $value_22 = $data['then']; + } + $object->setThen($value_22); + } + elseif (\array_key_exists('then', $data) && $data['then'] === null) { + $object->setThen(null); + } + if (\array_key_exists('else', $data) && $data['else'] !== null) { + $value_23 = $data['else']; + if (is_array($data['else'])) { + $value_23 = $this->denormalizer->denormalize($data['else'], 'Jane\\Component\\JsonSchema\\JsonSchema\\Model\\JsonSchema', 'json', $context); + } elseif (is_bool($data['else'])) { + $value_23 = $data['else']; + } + $object->setElse($value_23); + } + elseif (\array_key_exists('else', $data) && $data['else'] === null) { + $object->setElse(null); + } + if (\array_key_exists('allOf', $data) && $data['allOf'] !== null) { + $values_8 = array(); + foreach ($data['allOf'] as $value_24) { + $value_25 = $value_24; + if (is_array($value_24)) { + $value_25 = $this->denormalizer->denormalize($value_24, 'Jane\\Component\\JsonSchema\\JsonSchema\\Model\\JsonSchema', 'json', $context); + } elseif (is_bool($value_24)) { + $value_25 = $value_24; + } + $values_8[] = $value_25; + } + $object->setAllOf($values_8); + } + elseif (\array_key_exists('allOf', $data) && $data['allOf'] === null) { + $object->setAllOf(null); + } + if (\array_key_exists('anyOf', $data) && $data['anyOf'] !== null) { + $values_9 = array(); + foreach ($data['anyOf'] as $value_26) { + $value_27 = $value_26; + if (is_array($value_26)) { + $value_27 = $this->denormalizer->denormalize($value_26, 'Jane\\Component\\JsonSchema\\JsonSchema\\Model\\JsonSchema', 'json', $context); + } elseif (is_bool($value_26)) { + $value_27 = $value_26; + } + $values_9[] = $value_27; + } + $object->setAnyOf($values_9); + } + elseif (\array_key_exists('anyOf', $data) && $data['anyOf'] === null) { + $object->setAnyOf(null); + } + if (\array_key_exists('oneOf', $data) && $data['oneOf'] !== null) { + $values_10 = array(); + foreach ($data['oneOf'] as $value_28) { + $value_29 = $value_28; + if (is_array($value_28)) { + $value_29 = $this->denormalizer->denormalize($value_28, 'Jane\\Component\\JsonSchema\\JsonSchema\\Model\\JsonSchema', 'json', $context); + } elseif (is_bool($value_28)) { + $value_29 = $value_28; + } + $values_10[] = $value_29; + } + $object->setOneOf($values_10); + } + elseif (\array_key_exists('oneOf', $data) && $data['oneOf'] === null) { + $object->setOneOf(null); + } + if (\array_key_exists('not', $data) && $data['not'] !== null) { + $value_30 = $data['not']; + if (is_array($data['not'])) { + $value_30 = $this->denormalizer->denormalize($data['not'], 'Jane\\Component\\JsonSchema\\JsonSchema\\Model\\JsonSchema', 'json', $context); + } elseif (is_bool($data['not'])) { + $value_30 = $data['not']; + } + $object->setNot($value_30); + } + elseif (\array_key_exists('not', $data) && $data['not'] === null) { + $object->setNot(null); + } + if (\array_key_exists('contentMediaType', $data) && $data['contentMediaType'] !== null) { + $object->setContentMediaType($data['contentMediaType']); + } + elseif (\array_key_exists('contentMediaType', $data) && $data['contentMediaType'] === null) { + $object->setContentMediaType(null); + } + if (\array_key_exists('contentEncoding', $data) && $data['contentEncoding'] !== null) { + $object->setContentEncoding($data['contentEncoding']); + } + elseif (\array_key_exists('contentEncoding', $data) && $data['contentEncoding'] === null) { + $object->setContentEncoding(null); + } + if (\array_key_exists('contentSchema', $data) && $data['contentSchema'] !== null) { + $value_31 = $data['contentSchema']; + if (is_array($data['contentSchema'])) { + $value_31 = $this->denormalizer->denormalize($data['contentSchema'], 'Jane\\Component\\JsonSchema\\JsonSchema\\Model\\JsonSchema', 'json', $context); + } elseif (is_bool($data['contentSchema'])) { + $value_31 = $data['contentSchema']; + } + $object->setContentSchema($value_31); + } + elseif (\array_key_exists('contentSchema', $data) && $data['contentSchema'] === null) { + $object->setContentSchema(null); + } + if (\array_key_exists('$id', $data) && $data['$id'] !== null) { + $object->setDollarId($data['$id']); + } + elseif (\array_key_exists('$id', $data) && $data['$id'] === null) { + $object->setDollarId(null); + } + if (\array_key_exists('$schema', $data) && $data['$schema'] !== null) { + $object->setDollarSchema($data['$schema']); + } + elseif (\array_key_exists('$schema', $data) && $data['$schema'] === null) { + $object->setDollarSchema(null); + } + if (\array_key_exists('$anchor', $data) && $data['$anchor'] !== null) { + $object->setDollarAnchor($data['$anchor']); + } + elseif (\array_key_exists('$anchor', $data) && $data['$anchor'] === null) { + $object->setDollarAnchor(null); + } + if (\array_key_exists('$ref', $data) && $data['$ref'] !== null) { + $object->setDollarRef($data['$ref']); + } + elseif (\array_key_exists('$ref', $data) && $data['$ref'] === null) { + $object->setDollarRef(null); + } + if (\array_key_exists('$recursiveRef', $data) && $data['$recursiveRef'] !== null) { + $object->setDollarRecursiveRef($data['$recursiveRef']); + } + elseif (\array_key_exists('$recursiveRef', $data) && $data['$recursiveRef'] === null) { + $object->setDollarRecursiveRef(null); + } + if (\array_key_exists('$recursiveAnchor', $data) && $data['$recursiveAnchor'] !== null) { + $object->setDollarRecursiveAnchor($data['$recursiveAnchor']); + } + elseif (\array_key_exists('$recursiveAnchor', $data) && $data['$recursiveAnchor'] === null) { + $object->setDollarRecursiveAnchor(null); + } + if (\array_key_exists('$vocabulary', $data) && $data['$vocabulary'] !== null) { + $values_11 = new \ArrayObject(array(), \ArrayObject::ARRAY_AS_PROPS); + foreach ($data['$vocabulary'] as $key_6 => $value_32) { + $values_11[$key_6] = $value_32; + } + $object->setDollarVocabulary($values_11); + } + elseif (\array_key_exists('$vocabulary', $data) && $data['$vocabulary'] === null) { + $object->setDollarVocabulary(null); + } + if (\array_key_exists('$comment', $data) && $data['$comment'] !== null) { + $object->setDollarComment($data['$comment']); + } + elseif (\array_key_exists('$comment', $data) && $data['$comment'] === null) { + $object->setDollarComment(null); + } + if (\array_key_exists('$defs', $data) && $data['$defs'] !== null) { + $values_12 = new \ArrayObject(array(), \ArrayObject::ARRAY_AS_PROPS); + foreach ($data['$defs'] as $key_7 => $value_33) { + $value_34 = $value_33; + if (is_array($value_33)) { + $value_34 = $this->denormalizer->denormalize($value_33, 'Jane\\Component\\JsonSchema\\JsonSchema\\Model\\JsonSchema', 'json', $context); + } elseif (is_bool($value_33)) { + $value_34 = $value_33; + } + $values_12[$key_7] = $value_34; + } + $object->setDollarDefs($values_12); + } + elseif (\array_key_exists('$defs', $data) && $data['$defs'] === null) { + $object->setDollarDefs(null); + } + if (\array_key_exists('format', $data) && $data['format'] !== null) { + $object->setFormat($data['format']); + } + elseif (\array_key_exists('format', $data) && $data['format'] === null) { + $object->setFormat(null); + } + if (\array_key_exists('title', $data) && $data['title'] !== null) { + $object->setTitle($data['title']); + } + elseif (\array_key_exists('title', $data) && $data['title'] === null) { + $object->setTitle(null); + } + if (\array_key_exists('description', $data) && $data['description'] !== null) { + $object->setDescription($data['description']); + } + elseif (\array_key_exists('description', $data) && $data['description'] === null) { + $object->setDescription(null); + } + if (\array_key_exists('default', $data) && $data['default'] !== null) { + $object->setDefault($data['default']); + } + elseif (\array_key_exists('default', $data) && $data['default'] === null) { + $object->setDefault(null); + } + if (\array_key_exists('deprecated', $data) && $data['deprecated'] !== null) { + $object->setDeprecated($data['deprecated']); + } + elseif (\array_key_exists('deprecated', $data) && $data['deprecated'] === null) { + $object->setDeprecated(null); + } + if (\array_key_exists('readOnly', $data) && $data['readOnly'] !== null) { + $object->setReadOnly($data['readOnly']); + } + elseif (\array_key_exists('readOnly', $data) && $data['readOnly'] === null) { + $object->setReadOnly(null); + } + if (\array_key_exists('writeOnly', $data) && $data['writeOnly'] !== null) { + $object->setWriteOnly($data['writeOnly']); + } + elseif (\array_key_exists('writeOnly', $data) && $data['writeOnly'] === null) { + $object->setWriteOnly(null); + } + if (\array_key_exists('examples', $data) && $data['examples'] !== null) { + $values_13 = array(); + foreach ($data['examples'] as $value_35) { + $values_13[] = $value_35; + } + $object->setExamples($values_13); + } + elseif (\array_key_exists('examples', $data) && $data['examples'] === null) { + $object->setExamples(null); + } + if (\array_key_exists('multipleOf', $data) && $data['multipleOf'] !== null) { + $object->setMultipleOf($data['multipleOf']); + } + elseif (\array_key_exists('multipleOf', $data) && $data['multipleOf'] === null) { + $object->setMultipleOf(null); + } + if (\array_key_exists('maximum', $data) && $data['maximum'] !== null) { + $object->setMaximum($data['maximum']); + } + elseif (\array_key_exists('maximum', $data) && $data['maximum'] === null) { + $object->setMaximum(null); + } + if (\array_key_exists('exclusiveMaximum', $data) && $data['exclusiveMaximum'] !== null) { + $object->setExclusiveMaximum($data['exclusiveMaximum']); + } + elseif (\array_key_exists('exclusiveMaximum', $data) && $data['exclusiveMaximum'] === null) { + $object->setExclusiveMaximum(null); + } + if (\array_key_exists('minimum', $data) && $data['minimum'] !== null) { + $object->setMinimum($data['minimum']); + } + elseif (\array_key_exists('minimum', $data) && $data['minimum'] === null) { + $object->setMinimum(null); + } + if (\array_key_exists('exclusiveMinimum', $data) && $data['exclusiveMinimum'] !== null) { + $object->setExclusiveMinimum($data['exclusiveMinimum']); + } + elseif (\array_key_exists('exclusiveMinimum', $data) && $data['exclusiveMinimum'] === null) { + $object->setExclusiveMinimum(null); + } + if (\array_key_exists('maxLength', $data) && $data['maxLength'] !== null) { + $object->setMaxLength($data['maxLength']); + } + elseif (\array_key_exists('maxLength', $data) && $data['maxLength'] === null) { + $object->setMaxLength(null); + } + if (\array_key_exists('minLength', $data) && $data['minLength'] !== null) { + $object->setMinLength($data['minLength']); + } + elseif (\array_key_exists('minLength', $data) && $data['minLength'] === null) { + $object->setMinLength(null); + } + if (\array_key_exists('pattern', $data) && $data['pattern'] !== null) { + $object->setPattern($data['pattern']); + } + elseif (\array_key_exists('pattern', $data) && $data['pattern'] === null) { + $object->setPattern(null); + } + if (\array_key_exists('maxItems', $data) && $data['maxItems'] !== null) { + $object->setMaxItems($data['maxItems']); + } + elseif (\array_key_exists('maxItems', $data) && $data['maxItems'] === null) { + $object->setMaxItems(null); + } + if (\array_key_exists('minItems', $data) && $data['minItems'] !== null) { + $object->setMinItems($data['minItems']); + } + elseif (\array_key_exists('minItems', $data) && $data['minItems'] === null) { + $object->setMinItems(null); + } + if (\array_key_exists('uniqueItems', $data) && $data['uniqueItems'] !== null) { + $object->setUniqueItems($data['uniqueItems']); + } + elseif (\array_key_exists('uniqueItems', $data) && $data['uniqueItems'] === null) { + $object->setUniqueItems(null); + } + if (\array_key_exists('maxContains', $data) && $data['maxContains'] !== null) { + $object->setMaxContains($data['maxContains']); + } + elseif (\array_key_exists('maxContains', $data) && $data['maxContains'] === null) { + $object->setMaxContains(null); + } + if (\array_key_exists('minContains', $data) && $data['minContains'] !== null) { + $object->setMinContains($data['minContains']); + } + elseif (\array_key_exists('minContains', $data) && $data['minContains'] === null) { + $object->setMinContains(null); + } + if (\array_key_exists('maxProperties', $data) && $data['maxProperties'] !== null) { + $object->setMaxProperties($data['maxProperties']); + } + elseif (\array_key_exists('maxProperties', $data) && $data['maxProperties'] === null) { + $object->setMaxProperties(null); + } + if (\array_key_exists('minProperties', $data) && $data['minProperties'] !== null) { + $object->setMinProperties($data['minProperties']); + } + elseif (\array_key_exists('minProperties', $data) && $data['minProperties'] === null) { + $object->setMinProperties(null); + } + if (\array_key_exists('required', $data) && $data['required'] !== null) { + $values_14 = array(); + foreach ($data['required'] as $value_36) { + $values_14[] = $value_36; + } + $object->setRequired($values_14); + } + elseif (\array_key_exists('required', $data) && $data['required'] === null) { + $object->setRequired(null); + } + if (\array_key_exists('dependentRequired', $data) && $data['dependentRequired'] !== null) { + $values_15 = new \ArrayObject(array(), \ArrayObject::ARRAY_AS_PROPS); + foreach ($data['dependentRequired'] as $key_8 => $value_37) { + $values_16 = array(); + foreach ($value_37 as $value_38) { + $values_16[] = $value_38; + } + $values_15[$key_8] = $values_16; + } + $object->setDependentRequired($values_15); + } + elseif (\array_key_exists('dependentRequired', $data) && $data['dependentRequired'] === null) { + $object->setDependentRequired(null); + } + if (\array_key_exists('const', $data) && $data['const'] !== null) { + $object->setConst($data['const']); + } + elseif (\array_key_exists('const', $data) && $data['const'] === null) { + $object->setConst(null); + } + if (\array_key_exists('enum', $data) && $data['enum'] !== null) { + $values_17 = array(); + foreach ($data['enum'] as $value_39) { + $values_17[] = $value_39; + } + $object->setEnum($values_17); + } + elseif (\array_key_exists('enum', $data) && $data['enum'] === null) { + $object->setEnum(null); + } + if (\array_key_exists('type', $data) && $data['type'] !== null) { + $value_40 = $data['type']; + if (is_array($data['type']) && $this->isOnlyNumericKeys($data['type'])) { + $values_18 = array(); + foreach ($data['type'] as $value_41) { + $values_18[] = $value_41; + } + $value_40 = $values_18; + } elseif (isset($data['type'])) { + $value_40 = $data['type']; + } + $object->setType($value_40); + } + elseif (\array_key_exists('type', $data) && $data['type'] === null) { + $object->setType(null); + } + return $object; + } + public function normalize($object, $format = null, array $context = array()) + { + $data = array(); + if (null !== $object->getDefinitions()) { + $values = array(); + foreach ($object->getDefinitions() as $key => $value) { + $value_1 = $value; + if (is_object($value)) { + $value_1 = $this->normalizer->normalize($value, 'json', $context); + } elseif (is_bool($value)) { + $value_1 = $value; + } + $values[$key] = $value_1; + } + $data['definitions'] = $values; + } + else { + $data['definitions'] = null; + } + if (null !== $object->getDependencies()) { + $values_1 = array(); + foreach ($object->getDependencies() as $key_1 => $value_2) { + $value_3 = $value_2; + if (is_object($value_2)) { + $value_3 = $this->normalizer->normalize($value_2, 'json', $context); + } elseif (is_bool($value_2)) { + $value_3 = $value_2; + } elseif (is_array($value_2)) { + $values_2 = array(); + foreach ($value_2 as $value_4) { + $values_2[] = $value_4; + } + $value_3 = $values_2; + } + $values_1[$key_1] = $value_3; + } + $data['dependencies'] = $values_1; + } + else { + $data['dependencies'] = null; + } + if (null !== $object->getAdditionalItems()) { + $value_5 = $object->getAdditionalItems(); + if (is_object($object->getAdditionalItems())) { + $value_5 = $this->normalizer->normalize($object->getAdditionalItems(), 'json', $context); + } elseif (is_bool($object->getAdditionalItems())) { + $value_5 = $object->getAdditionalItems(); + } + $data['additionalItems'] = $value_5; + } + else { + $data['additionalItems'] = null; + } + if (null !== $object->getUnevaluatedItems()) { + $value_6 = $object->getUnevaluatedItems(); + if (is_object($object->getUnevaluatedItems())) { + $value_6 = $this->normalizer->normalize($object->getUnevaluatedItems(), 'json', $context); + } elseif (is_bool($object->getUnevaluatedItems())) { + $value_6 = $object->getUnevaluatedItems(); + } + $data['unevaluatedItems'] = $value_6; + } + else { + $data['unevaluatedItems'] = null; + } + if (null !== $object->getItems()) { + $value_7 = $object->getItems(); + if (is_object($object->getItems())) { + $value_7 = $this->normalizer->normalize($object->getItems(), 'json', $context); + } elseif (is_bool($object->getItems())) { + $value_7 = $object->getItems(); + } elseif (is_array($object->getItems())) { + $values_3 = array(); + foreach ($object->getItems() as $value_8) { + $value_9 = $value_8; + if (is_object($value_8)) { + $value_9 = $this->normalizer->normalize($value_8, 'json', $context); + } elseif (is_bool($value_8)) { + $value_9 = $value_8; + } + $values_3[] = $value_9; + } + $value_7 = $values_3; + } + $data['items'] = $value_7; + } + else { + $data['items'] = null; + } + if (null !== $object->getContains()) { + $value_10 = $object->getContains(); + if (is_object($object->getContains())) { + $value_10 = $this->normalizer->normalize($object->getContains(), 'json', $context); + } elseif (is_bool($object->getContains())) { + $value_10 = $object->getContains(); + } + $data['contains'] = $value_10; + } + else { + $data['contains'] = null; + } + if (null !== $object->getAdditionalProperties()) { + $value_11 = $object->getAdditionalProperties(); + if (is_object($object->getAdditionalProperties())) { + $value_11 = $this->normalizer->normalize($object->getAdditionalProperties(), 'json', $context); + } elseif (is_bool($object->getAdditionalProperties())) { + $value_11 = $object->getAdditionalProperties(); + } + $data['additionalProperties'] = $value_11; + } + else { + $data['additionalProperties'] = null; + } + if (null !== $object->getUnevaluatedProperties()) { + $values_4 = array(); + foreach ($object->getUnevaluatedProperties() as $key_2 => $value_12) { + $value_13 = $value_12; + if (is_object($value_12)) { + $value_13 = $this->normalizer->normalize($value_12, 'json', $context); + } elseif (is_bool($value_12)) { + $value_13 = $value_12; + } + $values_4[$key_2] = $value_13; + } + $data['unevaluatedProperties'] = $values_4; + } + else { + $data['unevaluatedProperties'] = null; + } + if (null !== $object->getProperties()) { + $values_5 = array(); + foreach ($object->getProperties() as $key_3 => $value_14) { + $value_15 = $value_14; + if (is_object($value_14)) { + $value_15 = $this->normalizer->normalize($value_14, 'json', $context); + } elseif (is_bool($value_14)) { + $value_15 = $value_14; + } + $values_5[$key_3] = $value_15; + } + $data['properties'] = $values_5; + } + else { + $data['properties'] = null; + } + if (null !== $object->getPatternProperties()) { + $values_6 = array(); + foreach ($object->getPatternProperties() as $key_4 => $value_16) { + $value_17 = $value_16; + if (is_object($value_16)) { + $value_17 = $this->normalizer->normalize($value_16, 'json', $context); + } elseif (is_bool($value_16)) { + $value_17 = $value_16; + } + $values_6[$key_4] = $value_17; + } + $data['patternProperties'] = $values_6; + } + else { + $data['patternProperties'] = null; + } + if (null !== $object->getDependentSchemas()) { + $values_7 = array(); + foreach ($object->getDependentSchemas() as $key_5 => $value_18) { + $value_19 = $value_18; + if (is_object($value_18)) { + $value_19 = $this->normalizer->normalize($value_18, 'json', $context); + } elseif (is_bool($value_18)) { + $value_19 = $value_18; + } + $values_7[$key_5] = $value_19; + } + $data['dependentSchemas'] = $values_7; + } + else { + $data['dependentSchemas'] = null; + } + if (null !== $object->getPropertyNames()) { + $value_20 = $object->getPropertyNames(); + if (is_object($object->getPropertyNames())) { + $value_20 = $this->normalizer->normalize($object->getPropertyNames(), 'json', $context); + } elseif (is_bool($object->getPropertyNames())) { + $value_20 = $object->getPropertyNames(); + } + $data['propertyNames'] = $value_20; + } + else { + $data['propertyNames'] = null; + } + if (null !== $object->getIf()) { + $value_21 = $object->getIf(); + if (is_object($object->getIf())) { + $value_21 = $this->normalizer->normalize($object->getIf(), 'json', $context); + } elseif (is_bool($object->getIf())) { + $value_21 = $object->getIf(); + } + $data['if'] = $value_21; + } + else { + $data['if'] = null; + } + if (null !== $object->getThen()) { + $value_22 = $object->getThen(); + if (is_object($object->getThen())) { + $value_22 = $this->normalizer->normalize($object->getThen(), 'json', $context); + } elseif (is_bool($object->getThen())) { + $value_22 = $object->getThen(); + } + $data['then'] = $value_22; + } + else { + $data['then'] = null; + } + if (null !== $object->getElse()) { + $value_23 = $object->getElse(); + if (is_object($object->getElse())) { + $value_23 = $this->normalizer->normalize($object->getElse(), 'json', $context); + } elseif (is_bool($object->getElse())) { + $value_23 = $object->getElse(); + } + $data['else'] = $value_23; + } + else { + $data['else'] = null; + } + if (null !== $object->getAllOf()) { + $values_8 = array(); + foreach ($object->getAllOf() as $value_24) { + $value_25 = $value_24; + if (is_object($value_24)) { + $value_25 = $this->normalizer->normalize($value_24, 'json', $context); + } elseif (is_bool($value_24)) { + $value_25 = $value_24; + } + $values_8[] = $value_25; + } + $data['allOf'] = $values_8; + } + else { + $data['allOf'] = null; + } + if (null !== $object->getAnyOf()) { + $values_9 = array(); + foreach ($object->getAnyOf() as $value_26) { + $value_27 = $value_26; + if (is_object($value_26)) { + $value_27 = $this->normalizer->normalize($value_26, 'json', $context); + } elseif (is_bool($value_26)) { + $value_27 = $value_26; + } + $values_9[] = $value_27; + } + $data['anyOf'] = $values_9; + } + else { + $data['anyOf'] = null; + } + if (null !== $object->getOneOf()) { + $values_10 = array(); + foreach ($object->getOneOf() as $value_28) { + $value_29 = $value_28; + if (is_object($value_28)) { + $value_29 = $this->normalizer->normalize($value_28, 'json', $context); + } elseif (is_bool($value_28)) { + $value_29 = $value_28; + } + $values_10[] = $value_29; + } + $data['oneOf'] = $values_10; + } + else { + $data['oneOf'] = null; + } + if (null !== $object->getNot()) { + $value_30 = $object->getNot(); + if (is_object($object->getNot())) { + $value_30 = $this->normalizer->normalize($object->getNot(), 'json', $context); + } elseif (is_bool($object->getNot())) { + $value_30 = $object->getNot(); + } + $data['not'] = $value_30; + } + else { + $data['not'] = null; + } + if (null !== $object->getContentMediaType()) { + $data['contentMediaType'] = $object->getContentMediaType(); + } + else { + $data['contentMediaType'] = null; + } + if (null !== $object->getContentEncoding()) { + $data['contentEncoding'] = $object->getContentEncoding(); + } + else { + $data['contentEncoding'] = null; + } + if (null !== $object->getContentSchema()) { + $value_31 = $object->getContentSchema(); + if (is_object($object->getContentSchema())) { + $value_31 = $this->normalizer->normalize($object->getContentSchema(), 'json', $context); + } elseif (is_bool($object->getContentSchema())) { + $value_31 = $object->getContentSchema(); + } + $data['contentSchema'] = $value_31; + } + else { + $data['contentSchema'] = null; + } + if (null !== $object->getDollarId()) { + $data['$id'] = $object->getDollarId(); + } + else { + $data['$id'] = null; + } + if (null !== $object->getDollarSchema()) { + $data['$schema'] = $object->getDollarSchema(); + } + else { + $data['$schema'] = null; + } + if (null !== $object->getDollarAnchor()) { + $data['$anchor'] = $object->getDollarAnchor(); + } + else { + $data['$anchor'] = null; + } + if (null !== $object->getDollarRef()) { + $data['$ref'] = $object->getDollarRef(); + } + else { + $data['$ref'] = null; + } + if (null !== $object->getDollarRecursiveRef()) { + $data['$recursiveRef'] = $object->getDollarRecursiveRef(); + } + else { + $data['$recursiveRef'] = null; + } + if (null !== $object->getDollarRecursiveAnchor()) { + $data['$recursiveAnchor'] = $object->getDollarRecursiveAnchor(); + } + else { + $data['$recursiveAnchor'] = null; + } + if (null !== $object->getDollarVocabulary()) { + $values_11 = array(); + foreach ($object->getDollarVocabulary() as $key_6 => $value_32) { + $values_11[$key_6] = $value_32; + } + $data['$vocabulary'] = $values_11; + } + else { + $data['$vocabulary'] = null; + } + if (null !== $object->getDollarComment()) { + $data['$comment'] = $object->getDollarComment(); + } + else { + $data['$comment'] = null; + } + if (null !== $object->getDollarDefs()) { + $values_12 = array(); + foreach ($object->getDollarDefs() as $key_7 => $value_33) { + $value_34 = $value_33; + if (is_object($value_33)) { + $value_34 = $this->normalizer->normalize($value_33, 'json', $context); + } elseif (is_bool($value_33)) { + $value_34 = $value_33; + } + $values_12[$key_7] = $value_34; + } + $data['$defs'] = $values_12; + } + else { + $data['$defs'] = null; + } + if (null !== $object->getFormat()) { + $data['format'] = $object->getFormat(); + } + else { + $data['format'] = null; + } + if (null !== $object->getTitle()) { + $data['title'] = $object->getTitle(); + } + else { + $data['title'] = null; + } + if (null !== $object->getDescription()) { + $data['description'] = $object->getDescription(); + } + else { + $data['description'] = null; + } + if (null !== $object->getDefault()) { + $data['default'] = $object->getDefault(); + } + else { + $data['default'] = null; + } + if (null !== $object->getDeprecated()) { + $data['deprecated'] = $object->getDeprecated(); + } + else { + $data['deprecated'] = null; + } + if (null !== $object->getReadOnly()) { + $data['readOnly'] = $object->getReadOnly(); + } + else { + $data['readOnly'] = null; + } + if (null !== $object->getWriteOnly()) { + $data['writeOnly'] = $object->getWriteOnly(); + } + else { + $data['writeOnly'] = null; + } + if (null !== $object->getExamples()) { + $values_13 = array(); + foreach ($object->getExamples() as $value_35) { + $values_13[] = $value_35; + } + $data['examples'] = $values_13; + } + else { + $data['examples'] = null; + } + if (null !== $object->getMultipleOf()) { + $data['multipleOf'] = $object->getMultipleOf(); + } + else { + $data['multipleOf'] = null; + } + if (null !== $object->getMaximum()) { + $data['maximum'] = $object->getMaximum(); + } + else { + $data['maximum'] = null; + } + if (null !== $object->getExclusiveMaximum()) { + $data['exclusiveMaximum'] = $object->getExclusiveMaximum(); + } + else { + $data['exclusiveMaximum'] = null; + } + if (null !== $object->getMinimum()) { + $data['minimum'] = $object->getMinimum(); + } + else { + $data['minimum'] = null; + } + if (null !== $object->getExclusiveMinimum()) { + $data['exclusiveMinimum'] = $object->getExclusiveMinimum(); + } + else { + $data['exclusiveMinimum'] = null; + } + if (null !== $object->getMaxLength()) { + $data['maxLength'] = $object->getMaxLength(); + } + else { + $data['maxLength'] = null; + } + if (null !== $object->getMinLength()) { + $data['minLength'] = $object->getMinLength(); + } + else { + $data['minLength'] = null; + } + if (null !== $object->getPattern()) { + $data['pattern'] = $object->getPattern(); + } + else { + $data['pattern'] = null; + } + if (null !== $object->getMaxItems()) { + $data['maxItems'] = $object->getMaxItems(); + } + else { + $data['maxItems'] = null; + } + if (null !== $object->getMinItems()) { + $data['minItems'] = $object->getMinItems(); + } + else { + $data['minItems'] = null; + } + if (null !== $object->getUniqueItems()) { + $data['uniqueItems'] = $object->getUniqueItems(); + } + else { + $data['uniqueItems'] = null; + } + if (null !== $object->getMaxContains()) { + $data['maxContains'] = $object->getMaxContains(); + } + else { + $data['maxContains'] = null; + } + if (null !== $object->getMinContains()) { + $data['minContains'] = $object->getMinContains(); + } + else { + $data['minContains'] = null; + } + if (null !== $object->getMaxProperties()) { + $data['maxProperties'] = $object->getMaxProperties(); + } + else { + $data['maxProperties'] = null; + } + if (null !== $object->getMinProperties()) { + $data['minProperties'] = $object->getMinProperties(); + } + else { + $data['minProperties'] = null; + } + if (null !== $object->getRequired()) { + $values_14 = array(); + foreach ($object->getRequired() as $value_36) { + $values_14[] = $value_36; + } + $data['required'] = $values_14; + } + else { + $data['required'] = null; + } + if (null !== $object->getDependentRequired()) { + $values_15 = array(); + foreach ($object->getDependentRequired() as $key_8 => $value_37) { + $values_16 = array(); + foreach ($value_37 as $value_38) { + $values_16[] = $value_38; + } + $values_15[$key_8] = $values_16; + } + $data['dependentRequired'] = $values_15; + } + else { + $data['dependentRequired'] = null; + } + if (null !== $object->getConst()) { + $data['const'] = $object->getConst(); + } + else { + $data['const'] = null; + } + if (null !== $object->getEnum()) { + $values_17 = array(); + foreach ($object->getEnum() as $value_39) { + $values_17[] = $value_39; + } + $data['enum'] = $values_17; + } + else { + $data['enum'] = null; + } + if (null !== $object->getType()) { + $value_40 = $object->getType(); + if (is_array($object->getType())) { + $values_18 = array(); + foreach ($object->getType() as $value_41) { + $values_18[] = $value_41; + } + $value_40 = $values_18; + } elseif (!is_null($object->getType())) { + $value_40 = $object->getType(); + } + $data['type'] = $value_40; + } + else { + $data['type'] = null; + } + return $data; + } +} \ No newline at end of file diff --git a/JsonSchema/Runtime/Normalizer/CheckArray.php b/JsonSchema/Runtime/Normalizer/CheckArray.php new file mode 100644 index 0000000..10e341b --- /dev/null +++ b/JsonSchema/Runtime/Normalizer/CheckArray.php @@ -0,0 +1,13 @@ +getReferenceUri(); + return $ref; + } + /** + * {@inheritdoc} + */ + public function supportsNormalization($data, $format = null) + { + return $data instanceof Reference; + } +} \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..0f8e62b --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (C) 2016-2017 Joel Wurtz + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/Printer.php b/Printer.php new file mode 100644 index 0000000..73e37ce --- /dev/null +++ b/Printer.php @@ -0,0 +1,111 @@ +prettyPrinter = $prettyPrinter; + $this->fixerConfig = $fixerConfig; + } + + public function setUseFixer(bool $useFixer): void + { + $this->useFixer = $useFixer; + } + + public function setCleanGenerated(bool $cleanGenerated): void + { + $this->cleanGenerated = $cleanGenerated; + } + + public function output(Registry $registry): void + { + if ($this->cleanGenerated) { + $fs = new Filesystem(); + foreach ($registry->getOutputDirectories() as $directory) { + $fs->remove($directory); + $fs->mkdir($directory); + } + } + + foreach ($registry->getSchemas() as $schema) { + foreach ($schema->getFiles() as $file) { + if (!file_exists(\dirname($file->getFilename()))) { + mkdir(\dirname($file->getFilename()), 0755, true); + } + + file_put_contents($file->getFilename(), $this->prettyPrinter->prettyPrintFile([$file->getNode()])); + } + } + + if ($this->useFixer) { + foreach ($registry->getOutputDirectories() as $directory) { + $this->fix($directory); + } + } + } + + protected function getDefaultRules(): string + { + $rules = [ + '@Symfony' => true, + 'self_accessor' => true, + 'array_syntax' => ['syntax' => 'short'], + 'concat_space' => ['spacing' => 'one'], + 'declare_strict_types' => true, + 'header_comment' => [ + 'header' => <<=')) { + $rules['yoda_style'] = null; + } + + return json_encode($rules); + } + + protected function fix(string $path): void + { + if (!class_exists(FixCommand::class)) { + return; + } + + $command = new FixCommand(new ToolInfo()); + $config = [ + 'path' => [$path], + '--allow-risky' => true, + ]; + + if (!empty($this->fixerConfig)) { + $config['--config'] = $this->fixerConfig; + } else { + $config['--rules'] = $this->getDefaultRules(); + } + + $command->run(new ArrayInput($config, $command->getDefinition()), new NullOutput()); + } +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..b93858b --- /dev/null +++ b/README.md @@ -0,0 +1,25 @@ +# Jane JsonSchema + +Jane JsonSchema is a library to generate, in PHP, a model and a serializer from a [JSON Schema](http://json-schema.org/). + +## License + +View the [LICENSE](LICENSE) file attach to this project. + +## Resources + + * [Documentation](http://jane.readthedocs.io/en/latest/) + * [Contributing](https://github.com/janephp/janephp/blob/master/CONTRIBUTING.md) + * [Report Issues](https://github.com/janephp/janephp/issues) and [send Pull Requests](https://github.com/janephp/janephp/pulls) + in the [main Jane Repository](https://github.com/janephp/janephp) + +## Sponsor + +[![JoliCode](https://jolicode.com/images/logo.svg)](https://jolicode.com) + +Open Source time sponsored by JoliCode + +## Credits + +* [All contributors](https://github.com/jolicode/jane/graphs/contributors) + diff --git a/Registry/Registry.php b/Registry/Registry.php new file mode 100644 index 0000000..51323c9 --- /dev/null +++ b/Registry/Registry.php @@ -0,0 +1,76 @@ +outputDirectories[] = $outputDirectory; + } + + /** + * @return string[] + */ + public function getOutputDirectories(): array + { + return $this->outputDirectories; + } + + public function addSchema(SchemaInterface $schema): void + { + $this->schemas[] = $schema; + } + + public function getSchema(string $reference): ?Schema + { + $uri = Http::createFromString($reference); + $schemaUri = (string) $uri->withFragment(''); + + foreach ($this->schemas as $schema) { + if ($schema->hasReference($schemaUri)) { + return $schema; + } + } + + return null; + } + + /** + * @return Schema[] + */ + public function getSchemas(): array + { + return $this->schemas; + } + + public function hasClass(string $classReference): bool + { + return null !== $this->getClass($classReference); + } + + public function getClass(string $classReference): ?ClassGuess + { + $schema = $this->getSchema($classReference); + + if (null === $schema) { + return null; + } + + return $schema->getClass($classReference); + } + + public function getOptionsHash(): string + { + return md5(json_encode([])); + } +} diff --git a/Registry/RegistryInterface.php b/Registry/RegistryInterface.php new file mode 100644 index 0000000..2ae4484 --- /dev/null +++ b/Registry/RegistryInterface.php @@ -0,0 +1,8 @@ +origin = $this->fixPath($origin); + $this->namespace = $namespace; + $this->directory = $directory; + $this->rootName = $rootName; + $this->references = [$this->origin]; + } + + public function getOrigin(): string + { + return $this->origin; + } + + public function getNamespace(): string + { + return $this->namespace; + } + + public function getDirectory(): string + { + return $this->directory; + } + + public function getRootName(): string + { + return $this->rootName; + } + + public function addClass(string $reference, ClassGuess $class): void + { + $this->classes[urldecode($reference)] = $class; + } + + public function removeClass(string $reference): void + { + unset($this->classes[urldecode($reference)]); + } + + public function getClass($reference): ?ClassGuess + { + $reference = urldecode($reference); + + if (\array_key_exists($reference, $this->classes)) { + return $this->classes[$reference]; + } + + if (\array_key_exists($reference . '#', $this->classes)) { + return $this->classes[$reference . '#']; + } + + return null; + } + + /** + * @return ClassGuess[] + */ + public function getClasses(): array + { + return $this->classes; + } + + public function addFile(File $file): void + { + $this->files[] = $file; + } + + public function getFiles(): array + { + return $this->files; + } + + public function addReference(string $reference): void + { + $this->references[] = $reference; + } + + public function hasReference(string $reference): bool + { + return \in_array($reference, $this->references, true); + } + + /** + * @return mixed + */ + public function getParsed() + { + return $this->parsed; + } + + /** + * @param mixed $parsed + */ + public function setParsed($parsed): void + { + $this->parsed = $parsed; + } + + public function addRelation(string $model, string $needs): void + { + if ($needs === $model) { + return; + } + + if (!\array_key_exists($model, $this->relations)) { + $this->relations[$model] = []; + } + + $this->relations[$model][] = $needs; + } + + public function relationExists($model): bool + { + return \array_key_exists($model, $this->relations); + } + + public function addClassRelations(ClassGuess $classGuess): void + { + $baseModel = $classGuess->getName(); + if ($this->relationExists($baseModel)) { + return; + } + + foreach ($classGuess->getProperties() as $property) { + // second condition is here to avoid mapping PHP classes such as \DateTime + /** @var ObjectType $objectType */ + if (($objectType = $property->getType()) instanceof ObjectType && + '\\' !== substr($objectType->getClassName(), 0, 1)) { + $this->addRelation($baseModel, $objectType->getClassName()); + } + + if (($arrayType = $property->getType()) instanceof ArrayType && + ($itemType = $arrayType->getItemType()) instanceof ObjectType && + '\\' !== substr($itemType->getClassName(), 0, 1)) { + $this->addRelation($baseModel, $itemType->getClassName()); + } + } + } + + private function fixPath(string $path): string + { + $path = preg_replace('#([^:]){1}/{2,}#', '$1/', $path); + + if ('/' === $path) { + return '/'; + } + + $pathParts = []; + foreach (explode('/', rtrim($path, '/')) as $part) { + if ('.' === $part) { + continue; + } + + if ('..' === $part && \count($pathParts) > 0) { + array_pop($pathParts); + continue; + } + + $pathParts[] = $part; + } + + return implode('/', $pathParts); + } +} diff --git a/Registry/SchemaInterface.php b/Registry/SchemaInterface.php new file mode 100644 index 0000000..2f317c1 --- /dev/null +++ b/Registry/SchemaInterface.php @@ -0,0 +1,7 @@ +getUniqueName('name'); + $this->assertEquals('name', $name); + + $name = $uniqueVariableScope->getUniqueName('name'); + $this->assertEquals('name_1', $name); + + $name = $uniqueVariableScope->getUniqueName('name'); + $this->assertEquals('name_2', $name); + } +} diff --git a/Tests/JaneBaseTest.php b/Tests/JaneBaseTest.php new file mode 100644 index 0000000..5cfd582 --- /dev/null +++ b/Tests/JaneBaseTest.php @@ -0,0 +1,71 @@ + $testDirectory->getRealPath() . \DIRECTORY_SEPARATOR . '.jane', + ], $command->getDefinition()); + + $command->execute($inputArray, new NullOutput()); + + // 2. Compare + $expectedFinder = new Finder(); + $expectedFinder->in($testDirectory->getRealPath() . \DIRECTORY_SEPARATOR . 'expected'); + $generatedFinder = new Finder(); + $generatedFinder->in($testDirectory->getRealPath() . \DIRECTORY_SEPARATOR . 'generated'); + $generatedData = []; + + $this->assertEquals(\count($expectedFinder), \count($generatedFinder), sprintf('No same number of files for %s', $testDirectory->getRelativePathname())); + + foreach ($generatedFinder as $generatedFile) { + $generatedData[$generatedFile->getRelativePathname()] = $generatedFile->getRealPath(); + } + + foreach ($expectedFinder as $expectedFile) { + $this->assertArrayHasKey( + $expectedFile->getRelativePathname(), + $generatedData, + sprintf('File %s does not exist for %s', $expectedFile->getRelativePathname(), $testDirectory->getRelativePathname()) + ); + + if ($expectedFile->isFile()) { + $this->assertEquals( + file_get_contents($expectedFile->getRealPath()), + file_get_contents($generatedData[$expectedFile->getRelativePathname()]), + sprintf('File %s does not have the same content for %s', $expectedFile->getRelativePathname(), $testDirectory->getRelativePathname()) + ); + } + } + } + + public function schemaProvider(): array + { + $finder = new Finder(); + $finder->directories()->in(__DIR__ . '/fixtures'); + $finder->depth('< 1'); + $data = []; + foreach ($finder as $directory) { + $data[] = [$directory]; + } + + return $data; + } +} diff --git a/Tests/LibraryTest.php b/Tests/LibraryTest.php new file mode 100644 index 0000000..33c4040 --- /dev/null +++ b/Tests/LibraryTest.php @@ -0,0 +1,63 @@ +jane = Jane::build([ + 'reference' => true, + 'strict' => false, + 'skip-null-values' => false, + ]); + $this->printer = new Printer(new Standard(), ''); + $this->printer->setCleanGenerated(false); + } + + /** + * Unique test with ~70% coverage, library generated from json schema must be the same as the library used. + */ + public function testLibrary(): void + { + $registry = new Registry(); + $registry->addSchema(new Schema(__DIR__ . '/data/json-schema.json', 'Jane\Component\JsonSchema\JsonSchema', __DIR__ . '/generated', 'JsonSchema')); + $registry->addOutputDirectory(__DIR__ . '/generated'); + + $this->jane->generate($registry); + $this->printer->output($registry); + + $this->assertFileExists(__DIR__ . '/generated/Model/JsonSchema.php'); + $this->assertFileExists(__DIR__ . '/generated/Normalizer/JsonSchemaNormalizer.php'); + $this->assertFileExists(__DIR__ . '/generated/Normalizer/JaneObjectNormalizer.php'); + + $this->assertEquals( + file_get_contents(__DIR__ . '/../JsonSchema/Model/JsonSchema.php'), + file_get_contents(__DIR__ . '/generated/Model/JsonSchema.php') + ); + + $this->assertEquals( + file_get_contents(__DIR__ . '/../JsonSchema/Normalizer/JsonSchemaNormalizer.php'), + file_get_contents(__DIR__ . '/generated/Normalizer/JsonSchemaNormalizer.php') + ); + + $this->assertEquals( + file_get_contents(__DIR__ . '/../JsonSchema/Normalizer/JaneObjectNormalizer.php'), + file_get_contents(__DIR__ . '/generated/Normalizer/JaneObjectNormalizer.php') + ); + } +} diff --git a/Tests/data/json-schema.json b/Tests/data/json-schema.json new file mode 100644 index 0000000..22e79d8 --- /dev/null +++ b/Tests/data/json-schema.json @@ -0,0 +1,234 @@ +{ + "$schema": "https://json-schema.org/draft/2019-09/schema", + "$id": "https://json-schema.org/draft/2019-09/schema", + "$recursiveAnchor": true, + + "title": "Core and Validation specifications meta-schema", + "type": ["object", "boolean"], + "properties": { + "definitions": { + "$comment": "While no longer an official keyword as it is replaced by $defs, this keyword is retained in the meta-schema to prevent incompatible extensions as it remains in common use.", + "type": "object", + "additionalProperties": { "$ref": "#" }, + "default": {} + }, + "dependencies": { + "$comment": "\"dependencies\" is no longer a keyword, but schema authors should avoid redefining it to facilitate a smooth transition to \"dependentSchemas\" and \"dependentRequired\"", + "type": "object", + "additionalProperties": { + "anyOf": [ + { "$ref": "#" }, + { "$ref": "#/$defs/stringArray" } + ] + } + }, + "additionalItems": { "$ref": "#" }, + "unevaluatedItems": { "$ref": "#" }, + "items": { + "anyOf": [ + { "$ref": "#" }, + { "$ref": "#/$defs/schemaArray" } + ] + }, + "contains": { "$ref": "#" }, + "additionalProperties": { "$ref": "#" }, + "unevaluatedProperties": { + "type": "object", + "additionalProperties": { + "$ref": "#" + } + }, + "properties": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "default": {} + }, + "patternProperties": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "propertyNames": { "format": "regex" }, + "default": {} + }, + "dependentSchemas": { + "type": "object", + "additionalProperties": { + "$ref": "#" + } + }, + "propertyNames": { "$ref": "#" }, + "if": { "$ref": "#" }, + "then": { "$ref": "#" }, + "else": { "$ref": "#" }, + "allOf": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#" } + }, + "anyOf": { "$ref": "#/$defs/schemaArray" }, + "oneOf": { "$ref": "#/$defs/schemaArray" }, + "not": { "$ref": "#" }, + "contentMediaType": { "type": "string" }, + "contentEncoding": { "type": "string" }, + "contentSchema": { "$ref": "#" }, + + "$id": { + "type": "string", + "format": "uri-reference", + "$comment": "Non-empty fragments not allowed.", + "pattern": "^[^#]*#?$" + }, + "$schema": { + "type": "string", + "format": "uri" + }, + "$anchor": { + "type": "string", + "pattern": "^[A-Za-z][-A-Za-z0-9.:_]*$" + }, + "$ref": { + "type": "string", + "format": "uri-reference" + }, + "$recursiveRef": { + "type": "string", + "format": "uri-reference" + }, + "$recursiveAnchor": { + "type": "boolean", + "default": false + }, + "$vocabulary": { + "type": "object", + "propertyNames": { + "type": "string", + "format": "uri" + }, + "additionalProperties": { + "type": "boolean" + } + }, + "$comment": { + "type": "string" + }, + "$defs": { + "type": "object", + "additionalProperties": { "$ref": "#" }, + "default": {} + }, + "format": { "type": "string" }, + + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "default": {}, + "deprecated": { + "type": "boolean", + "default": false + }, + "readOnly": { + "type": "boolean", + "default": false + }, + "writeOnly": { + "type": "boolean", + "default": false + }, + "examples": { + "type": "array", + "items": { "type": "#" } + }, + "multipleOf": { + "type": "number", + "exclusiveMinimum": 0 + }, + "maximum": { + "type": "number" + }, + "exclusiveMaximum": { + "type": "number" + }, + "minimum": { + "type": "number" + }, + "exclusiveMinimum": { + "type": "number" + }, + "maxLength": { "$ref": "#/$defs/nonNegativeInteger" }, + "minLength": { "$ref": "#/$defs/nonNegativeIntegerDefault0" }, + "pattern": { + "type": "string", + "format": "regex" + }, + "maxItems": { "$ref": "#/$defs/nonNegativeInteger" }, + "minItems": { "$ref": "#/$defs/nonNegativeIntegerDefault0" }, + "uniqueItems": { + "type": "boolean", + "default": false + }, + "maxContains": { "$ref": "#/$defs/nonNegativeInteger" }, + "minContains": { + "$ref": "#/$defs/nonNegativeInteger", + "default": 1 + }, + "maxProperties": { "$ref": "#/$defs/nonNegativeInteger" }, + "minProperties": { "$ref": "#/$defs/nonNegativeIntegerDefault0" }, + "required": { "$ref": "#/$defs/stringArray" }, + "dependentRequired": { + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/stringArray" + } + }, + "const": { "type": "string" }, + "enum": { + "type": "array", + "items": { "type": "string" } + }, + "type": { + "anyOf": [ + { "$ref": "#/$defs/simpleTypes" }, + { + "type": "array", + "items": { "$ref": "#/$defs/simpleTypes" }, + "minItems": 1, + "uniqueItems": true + } + ] + } + }, + "$defs": { + "schemaArray": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#" } + }, + "nonNegativeInteger": { + "type": "integer", + "minimum": 0 + }, + "nonNegativeIntegerDefault0": { + "$ref": "#/$defs/nonNegativeInteger", + "default": 0 + }, + "simpleTypes": { + "enum": [ + "array", + "boolean", + "integer", + "null", + "number", + "object", + "string" + ] + }, + "stringArray": { + "type": "array", + "items": { "type": "string" }, + "uniqueItems": true, + "default": [] + } + } +} diff --git a/Tests/fixtures/additional-properties/.jane b/Tests/fixtures/additional-properties/.jane new file mode 100644 index 0000000..bcd1207 --- /dev/null +++ b/Tests/fixtures/additional-properties/.jane @@ -0,0 +1,8 @@ + __DIR__ . '/schema.json', + 'root-class' => 'Test', + 'namespace' => 'Jane\Component\JsonSchema\Tests\Expected', + 'directory' => __DIR__ . '/generated', +]; diff --git a/Tests/fixtures/additional-properties/expected/Model/AdditionalProperties.php b/Tests/fixtures/additional-properties/expected/Model/AdditionalProperties.php new file mode 100644 index 0000000..169cb37 --- /dev/null +++ b/Tests/fixtures/additional-properties/expected/Model/AdditionalProperties.php @@ -0,0 +1,34 @@ +foo; + } + /** + * + * + * @param string $foo + * + * @return self + */ + public function setFoo(string $foo) : self + { + $this->foo = $foo; + return $this; + } +} \ No newline at end of file diff --git a/Tests/fixtures/additional-properties/expected/Model/PatternProperties.php b/Tests/fixtures/additional-properties/expected/Model/PatternProperties.php new file mode 100644 index 0000000..b5e1f9e --- /dev/null +++ b/Tests/fixtures/additional-properties/expected/Model/PatternProperties.php @@ -0,0 +1,34 @@ +foo; + } + /** + * + * + * @param string $foo + * + * @return self + */ + public function setFoo(string $foo) : self + { + $this->foo = $foo; + return $this; + } +} \ No newline at end of file diff --git a/Tests/fixtures/additional-properties/expected/Normalizer/AdditionalPropertiesNormalizer.php b/Tests/fixtures/additional-properties/expected/Normalizer/AdditionalPropertiesNormalizer.php new file mode 100644 index 0000000..1a55b30 --- /dev/null +++ b/Tests/fixtures/additional-properties/expected/Normalizer/AdditionalPropertiesNormalizer.php @@ -0,0 +1,63 @@ +setFoo($data['foo']); + unset($data['foo']); + } + foreach ($data as $key => $value) { + if (preg_match('/.*/', (string) $key)) { + $object[$key] = $value; + } + } + return $object; + } + public function normalize($object, $format = null, array $context = array()) + { + $data = array(); + if (null !== $object->getFoo()) { + $data['foo'] = $object->getFoo(); + } + foreach ($object as $key => $value) { + if (preg_match('/.*/', (string) $key)) { + $data[$key] = $value; + } + } + return $data; + } +} \ No newline at end of file diff --git a/Tests/fixtures/additional-properties/expected/Normalizer/JaneObjectNormalizer.php b/Tests/fixtures/additional-properties/expected/Normalizer/JaneObjectNormalizer.php new file mode 100644 index 0000000..71b076c --- /dev/null +++ b/Tests/fixtures/additional-properties/expected/Normalizer/JaneObjectNormalizer.php @@ -0,0 +1,50 @@ + 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Normalizer\\AdditionalPropertiesNormalizer', 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Model\\PatternProperties' => 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Normalizer\\PatternPropertiesNormalizer', '\\Jane\\Component\\JsonSchemaRuntime\\Reference' => '\\Jane\\Component\\JsonSchema\\Tests\\Expected\\Runtime\\Normalizer\\ReferenceNormalizer'), $normalizersCache = array(); + public function supportsDenormalization($data, $type, $format = null) + { + return array_key_exists($type, $this->normalizers); + } + public function supportsNormalization($data, $format = null) + { + return is_object($data) && array_key_exists(get_class($data), $this->normalizers); + } + public function normalize($object, $format = null, array $context = array()) + { + $normalizerClass = $this->normalizers[get_class($object)]; + $normalizer = $this->getNormalizer($normalizerClass); + return $normalizer->normalize($object, $format, $context); + } + public function denormalize($data, $class, $format = null, array $context = array()) + { + $denormalizerClass = $this->normalizers[$class]; + $denormalizer = $this->getNormalizer($denormalizerClass); + return $denormalizer->denormalize($data, $class, $format, $context); + } + private function getNormalizer(string $normalizerClass) + { + return $this->normalizersCache[$normalizerClass] ?? $this->initNormalizer($normalizerClass); + } + private function initNormalizer(string $normalizerClass) + { + $normalizer = new $normalizerClass(); + $normalizer->setNormalizer($this->normalizer); + $normalizer->setDenormalizer($this->denormalizer); + $this->normalizersCache[$normalizerClass] = $normalizer; + return $normalizer; + } +} \ No newline at end of file diff --git a/Tests/fixtures/additional-properties/expected/Normalizer/PatternPropertiesNormalizer.php b/Tests/fixtures/additional-properties/expected/Normalizer/PatternPropertiesNormalizer.php new file mode 100644 index 0000000..377edfb --- /dev/null +++ b/Tests/fixtures/additional-properties/expected/Normalizer/PatternPropertiesNormalizer.php @@ -0,0 +1,69 @@ +setFoo($data['foo']); + unset($data['foo']); + } + foreach ($data as $key => $value) { + if (preg_match('/x-.*/', (string) $key)) { + $object[$key] = $value; + } + if (preg_match('/xxxx-.*/', (string) $key)) { + $object[$key] = $this->denormalizer->denormalize($value, 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Model\\AdditionalProperties', 'json', $context); + } + } + return $object; + } + public function normalize($object, $format = null, array $context = array()) + { + $data = array(); + if (null !== $object->getFoo()) { + $data['foo'] = $object->getFoo(); + } + foreach ($object as $key => $value) { + if (preg_match('/x-.*/', (string) $key)) { + $data[$key] = $value; + } + if (preg_match('/xxxx-.*/', (string) $key)) { + $data[$key] = $this->normalizer->normalize($value, 'json', $context); + } + } + return $data; + } +} \ No newline at end of file diff --git a/Tests/fixtures/additional-properties/expected/Runtime/Normalizer/CheckArray.php b/Tests/fixtures/additional-properties/expected/Runtime/Normalizer/CheckArray.php new file mode 100644 index 0000000..3f60a9f --- /dev/null +++ b/Tests/fixtures/additional-properties/expected/Runtime/Normalizer/CheckArray.php @@ -0,0 +1,13 @@ +getReferenceUri(); + return $ref; + } + /** + * {@inheritdoc} + */ + public function supportsNormalization($data, $format = null) + { + return $data instanceof Reference; + } +} \ No newline at end of file diff --git a/Tests/fixtures/additional-properties/schema.json b/Tests/fixtures/additional-properties/schema.json new file mode 100644 index 0000000..743c629 --- /dev/null +++ b/Tests/fixtures/additional-properties/schema.json @@ -0,0 +1,28 @@ +{ + "id": "http://json-schema.org/draft-04/schema#", + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Schema for object with null values", + "definitions": { + "AdditionalProperties": { + "type": "object", + "properties": { + "foo": { "type" : "string" } + }, + "additionalProperties": true + }, + "PatternProperties": { + "type": "object", + "properties": { + "foo": { "type" : "string" } + }, + "patternProperties": { + "x-.*": { + "type": "string" + }, + "xxxx-.*": { + "$ref": "#/definitions/AdditionalProperties" + } + } + } + } +} diff --git a/Tests/fixtures/all-of/.jane b/Tests/fixtures/all-of/.jane new file mode 100644 index 0000000..9907db2 --- /dev/null +++ b/Tests/fixtures/all-of/.jane @@ -0,0 +1,9 @@ + __DIR__ . '/schema.json', + 'root-class' => 'Test', + 'namespace' => 'Jane\Component\JsonSchema\Tests\Expected', + 'directory' => __DIR__ . '/generated', + 'reference' => false, +]; diff --git a/Tests/fixtures/all-of/expected/Model/Bar.php b/Tests/fixtures/all-of/expected/Model/Bar.php new file mode 100644 index 0000000..402c2df --- /dev/null +++ b/Tests/fixtures/all-of/expected/Model/Bar.php @@ -0,0 +1,61 @@ +foo; + } + /** + * + * + * @param string $foo + * + * @return self + */ + public function setFoo(string $foo) : self + { + $this->foo = $foo; + return $this; + } + /** + * + * + * @return string + */ + public function getBar() : string + { + return $this->bar; + } + /** + * + * + * @param string $bar + * + * @return self + */ + public function setBar(string $bar) : self + { + $this->bar = $bar; + return $this; + } +} \ No newline at end of file diff --git a/Tests/fixtures/all-of/expected/Model/Baz.php b/Tests/fixtures/all-of/expected/Model/Baz.php new file mode 100644 index 0000000..f59daa2 --- /dev/null +++ b/Tests/fixtures/all-of/expected/Model/Baz.php @@ -0,0 +1,88 @@ +foo; + } + /** + * + * + * @param string $foo + * + * @return self + */ + public function setFoo(string $foo) : self + { + $this->foo = $foo; + return $this; + } + /** + * + * + * @return Bar + */ + public function getBar() : Bar + { + return $this->bar; + } + /** + * + * + * @param Bar $bar + * + * @return self + */ + public function setBar(Bar $bar) : self + { + $this->bar = $bar; + return $this; + } + /** + * + * + * @return BazBaz + */ + public function getBaz() : BazBaz + { + return $this->baz; + } + /** + * + * + * @param BazBaz $baz + * + * @return self + */ + public function setBaz(BazBaz $baz) : self + { + $this->baz = $baz; + return $this; + } +} \ No newline at end of file diff --git a/Tests/fixtures/all-of/expected/Model/BazBaz.php b/Tests/fixtures/all-of/expected/Model/BazBaz.php new file mode 100644 index 0000000..fe0a4ed --- /dev/null +++ b/Tests/fixtures/all-of/expected/Model/BazBaz.php @@ -0,0 +1,34 @@ +baz; + } + /** + * + * + * @param string $baz + * + * @return self + */ + public function setBaz(string $baz) : self + { + $this->baz = $baz; + return $this; + } +} \ No newline at end of file diff --git a/Tests/fixtures/all-of/expected/Model/Childtype.php b/Tests/fixtures/all-of/expected/Model/Childtype.php new file mode 100644 index 0000000..bc259a4 --- /dev/null +++ b/Tests/fixtures/all-of/expected/Model/Childtype.php @@ -0,0 +1,61 @@ +childProperty; + } + /** + * + * + * @param string $childProperty + * + * @return self + */ + public function setChildProperty(string $childProperty) : self + { + $this->childProperty = $childProperty; + return $this; + } + /** + * + * + * @return string + */ + public function getInheritedProperty() : string + { + return $this->inheritedProperty; + } + /** + * + * + * @param string $inheritedProperty + * + * @return self + */ + public function setInheritedProperty(string $inheritedProperty) : self + { + $this->inheritedProperty = $inheritedProperty; + return $this; + } +} \ No newline at end of file diff --git a/Tests/fixtures/all-of/expected/Model/Foo.php b/Tests/fixtures/all-of/expected/Model/Foo.php new file mode 100644 index 0000000..f8de199 --- /dev/null +++ b/Tests/fixtures/all-of/expected/Model/Foo.php @@ -0,0 +1,34 @@ +foo; + } + /** + * + * + * @param string $foo + * + * @return self + */ + public function setFoo(string $foo) : self + { + $this->foo = $foo; + return $this; + } +} \ No newline at end of file diff --git a/Tests/fixtures/all-of/expected/Model/Otherchildtype.php b/Tests/fixtures/all-of/expected/Model/Otherchildtype.php new file mode 100644 index 0000000..8b95a9a --- /dev/null +++ b/Tests/fixtures/all-of/expected/Model/Otherchildtype.php @@ -0,0 +1,61 @@ +inheritedProperty; + } + /** + * + * + * @param string $inheritedProperty + * + * @return self + */ + public function setInheritedProperty(string $inheritedProperty) : self + { + $this->inheritedProperty = $inheritedProperty; + return $this; + } + /** + * + * + * @return string + */ + public function getChildProperty() : string + { + return $this->childProperty; + } + /** + * + * + * @param string $childProperty + * + * @return self + */ + public function setChildProperty(string $childProperty) : self + { + $this->childProperty = $childProperty; + return $this; + } +} \ No newline at end of file diff --git a/Tests/fixtures/all-of/expected/Model/Parenttype.php b/Tests/fixtures/all-of/expected/Model/Parenttype.php new file mode 100644 index 0000000..7cfc85b --- /dev/null +++ b/Tests/fixtures/all-of/expected/Model/Parenttype.php @@ -0,0 +1,34 @@ +inheritedProperty; + } + /** + * + * + * @param string $inheritedProperty + * + * @return self + */ + public function setInheritedProperty(string $inheritedProperty) : self + { + $this->inheritedProperty = $inheritedProperty; + return $this; + } +} \ No newline at end of file diff --git a/Tests/fixtures/all-of/expected/Model/Test.php b/Tests/fixtures/all-of/expected/Model/Test.php new file mode 100644 index 0000000..bc15780 --- /dev/null +++ b/Tests/fixtures/all-of/expected/Model/Test.php @@ -0,0 +1,61 @@ +child; + } + /** + * + * + * @param Childtype $child + * + * @return self + */ + public function setChild(Childtype $child) : self + { + $this->child = $child; + return $this; + } + /** + * + * + * @return Parenttype + */ + public function getParent() : Parenttype + { + return $this->parent; + } + /** + * + * + * @param Parenttype $parent + * + * @return self + */ + public function setParent(Parenttype $parent) : self + { + $this->parent = $parent; + return $this; + } +} \ No newline at end of file diff --git a/Tests/fixtures/all-of/expected/Normalizer/BarNormalizer.php b/Tests/fixtures/all-of/expected/Normalizer/BarNormalizer.php new file mode 100644 index 0000000..4248985 --- /dev/null +++ b/Tests/fixtures/all-of/expected/Normalizer/BarNormalizer.php @@ -0,0 +1,52 @@ +setFoo($data['foo']); + } + if (\array_key_exists('bar', $data)) { + $object->setBar($data['bar']); + } + return $object; + } + public function normalize($object, $format = null, array $context = array()) + { + $data = array(); + if (null !== $object->getFoo()) { + $data['foo'] = $object->getFoo(); + } + if (null !== $object->getBar()) { + $data['bar'] = $object->getBar(); + } + return $data; + } +} \ No newline at end of file diff --git a/Tests/fixtures/all-of/expected/Normalizer/BazBazNormalizer.php b/Tests/fixtures/all-of/expected/Normalizer/BazBazNormalizer.php new file mode 100644 index 0000000..511c75a --- /dev/null +++ b/Tests/fixtures/all-of/expected/Normalizer/BazBazNormalizer.php @@ -0,0 +1,46 @@ +setBaz($data['baz']); + } + return $object; + } + public function normalize($object, $format = null, array $context = array()) + { + $data = array(); + if (null !== $object->getBaz()) { + $data['baz'] = $object->getBaz(); + } + return $data; + } +} \ No newline at end of file diff --git a/Tests/fixtures/all-of/expected/Normalizer/BazNormalizer.php b/Tests/fixtures/all-of/expected/Normalizer/BazNormalizer.php new file mode 100644 index 0000000..955c65f --- /dev/null +++ b/Tests/fixtures/all-of/expected/Normalizer/BazNormalizer.php @@ -0,0 +1,58 @@ +setFoo($data['foo']); + } + if (\array_key_exists('Bar', $data)) { + $object->setBar($this->denormalizer->denormalize($data['Bar'], 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Model\\Bar', 'json', $context)); + } + if (\array_key_exists('Baz', $data)) { + $object->setBaz($this->denormalizer->denormalize($data['Baz'], 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Model\\BazBaz', 'json', $context)); + } + return $object; + } + public function normalize($object, $format = null, array $context = array()) + { + $data = array(); + if (null !== $object->getFoo()) { + $data['foo'] = $object->getFoo(); + } + if (null !== $object->getBar()) { + $data['Bar'] = $this->normalizer->normalize($object->getBar(), 'json', $context); + } + if (null !== $object->getBaz()) { + $data['Baz'] = $this->normalizer->normalize($object->getBaz(), 'json', $context); + } + return $data; + } +} \ No newline at end of file diff --git a/Tests/fixtures/all-of/expected/Normalizer/ChildtypeNormalizer.php b/Tests/fixtures/all-of/expected/Normalizer/ChildtypeNormalizer.php new file mode 100644 index 0000000..3530730 --- /dev/null +++ b/Tests/fixtures/all-of/expected/Normalizer/ChildtypeNormalizer.php @@ -0,0 +1,52 @@ +setChildProperty($data['childProperty']); + } + if (\array_key_exists('inheritedProperty', $data)) { + $object->setInheritedProperty($data['inheritedProperty']); + } + return $object; + } + public function normalize($object, $format = null, array $context = array()) + { + $data = array(); + if (null !== $object->getChildProperty()) { + $data['childProperty'] = $object->getChildProperty(); + } + if (null !== $object->getInheritedProperty()) { + $data['inheritedProperty'] = $object->getInheritedProperty(); + } + return $data; + } +} \ No newline at end of file diff --git a/Tests/fixtures/all-of/expected/Normalizer/FooNormalizer.php b/Tests/fixtures/all-of/expected/Normalizer/FooNormalizer.php new file mode 100644 index 0000000..82d357f --- /dev/null +++ b/Tests/fixtures/all-of/expected/Normalizer/FooNormalizer.php @@ -0,0 +1,46 @@ +setFoo($data['foo']); + } + return $object; + } + public function normalize($object, $format = null, array $context = array()) + { + $data = array(); + if (null !== $object->getFoo()) { + $data['foo'] = $object->getFoo(); + } + return $data; + } +} \ No newline at end of file diff --git a/Tests/fixtures/all-of/expected/Normalizer/JaneObjectNormalizer.php b/Tests/fixtures/all-of/expected/Normalizer/JaneObjectNormalizer.php new file mode 100644 index 0000000..4d068e8 --- /dev/null +++ b/Tests/fixtures/all-of/expected/Normalizer/JaneObjectNormalizer.php @@ -0,0 +1,50 @@ + 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Normalizer\\TestNormalizer', 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Model\\Otherchildtype' => 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Normalizer\\OtherchildtypeNormalizer', 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Model\\Childtype' => 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Normalizer\\ChildtypeNormalizer', 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Model\\Parenttype' => 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Normalizer\\ParenttypeNormalizer', 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Model\\Foo' => 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Normalizer\\FooNormalizer', 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Model\\Bar' => 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Normalizer\\BarNormalizer', 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Model\\Baz' => 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Normalizer\\BazNormalizer', 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Model\\BazBaz' => 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Normalizer\\BazBazNormalizer'), $normalizersCache = array(); + public function supportsDenormalization($data, $type, $format = null) + { + return array_key_exists($type, $this->normalizers); + } + public function supportsNormalization($data, $format = null) + { + return is_object($data) && array_key_exists(get_class($data), $this->normalizers); + } + public function normalize($object, $format = null, array $context = array()) + { + $normalizerClass = $this->normalizers[get_class($object)]; + $normalizer = $this->getNormalizer($normalizerClass); + return $normalizer->normalize($object, $format, $context); + } + public function denormalize($data, $class, $format = null, array $context = array()) + { + $denormalizerClass = $this->normalizers[$class]; + $denormalizer = $this->getNormalizer($denormalizerClass); + return $denormalizer->denormalize($data, $class, $format, $context); + } + private function getNormalizer(string $normalizerClass) + { + return $this->normalizersCache[$normalizerClass] ?? $this->initNormalizer($normalizerClass); + } + private function initNormalizer(string $normalizerClass) + { + $normalizer = new $normalizerClass(); + $normalizer->setNormalizer($this->normalizer); + $normalizer->setDenormalizer($this->denormalizer); + $this->normalizersCache[$normalizerClass] = $normalizer; + return $normalizer; + } +} \ No newline at end of file diff --git a/Tests/fixtures/all-of/expected/Normalizer/OtherchildtypeNormalizer.php b/Tests/fixtures/all-of/expected/Normalizer/OtherchildtypeNormalizer.php new file mode 100644 index 0000000..250b143 --- /dev/null +++ b/Tests/fixtures/all-of/expected/Normalizer/OtherchildtypeNormalizer.php @@ -0,0 +1,52 @@ +setInheritedProperty($data['inheritedProperty']); + } + if (\array_key_exists('childProperty', $data)) { + $object->setChildProperty($data['childProperty']); + } + return $object; + } + public function normalize($object, $format = null, array $context = array()) + { + $data = array(); + if (null !== $object->getInheritedProperty()) { + $data['inheritedProperty'] = $object->getInheritedProperty(); + } + if (null !== $object->getChildProperty()) { + $data['childProperty'] = $object->getChildProperty(); + } + return $data; + } +} \ No newline at end of file diff --git a/Tests/fixtures/all-of/expected/Normalizer/ParenttypeNormalizer.php b/Tests/fixtures/all-of/expected/Normalizer/ParenttypeNormalizer.php new file mode 100644 index 0000000..d0f6cd6 --- /dev/null +++ b/Tests/fixtures/all-of/expected/Normalizer/ParenttypeNormalizer.php @@ -0,0 +1,46 @@ +setInheritedProperty($data['inheritedProperty']); + } + return $object; + } + public function normalize($object, $format = null, array $context = array()) + { + $data = array(); + if (null !== $object->getInheritedProperty()) { + $data['inheritedProperty'] = $object->getInheritedProperty(); + } + return $data; + } +} \ No newline at end of file diff --git a/Tests/fixtures/all-of/expected/Normalizer/TestNormalizer.php b/Tests/fixtures/all-of/expected/Normalizer/TestNormalizer.php new file mode 100644 index 0000000..57036a9 --- /dev/null +++ b/Tests/fixtures/all-of/expected/Normalizer/TestNormalizer.php @@ -0,0 +1,52 @@ +setChild($this->denormalizer->denormalize($data['child'], 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Model\\Childtype', 'json', $context)); + } + if (\array_key_exists('parent', $data)) { + $object->setParent($this->denormalizer->denormalize($data['parent'], 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Model\\Parenttype', 'json', $context)); + } + return $object; + } + public function normalize($object, $format = null, array $context = array()) + { + $data = array(); + if (null !== $object->getChild()) { + $data['child'] = $this->normalizer->normalize($object->getChild(), 'json', $context); + } + if (null !== $object->getParent()) { + $data['parent'] = $this->normalizer->normalize($object->getParent(), 'json', $context); + } + return $data; + } +} \ No newline at end of file diff --git a/Tests/fixtures/all-of/expected/Runtime/Normalizer/CheckArray.php b/Tests/fixtures/all-of/expected/Runtime/Normalizer/CheckArray.php new file mode 100644 index 0000000..3f60a9f --- /dev/null +++ b/Tests/fixtures/all-of/expected/Runtime/Normalizer/CheckArray.php @@ -0,0 +1,13 @@ +getReferenceUri(); + return $ref; + } + /** + * {@inheritdoc} + */ + public function supportsNormalization($data, $format = null) + { + return $data instanceof Reference; + } +} \ No newline at end of file diff --git a/Tests/fixtures/all-of/schema.json b/Tests/fixtures/all-of/schema.json new file mode 100644 index 0000000..78849cb --- /dev/null +++ b/Tests/fixtures/all-of/schema.json @@ -0,0 +1,98 @@ +{ + "id": "http://json-schema.org/draft-04/schema#", + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Testing allOf", + "type": "object", + "definitions": { + "otherchildtype": { + "allOf": [ + { + "$ref": "#/definitions/parenttype" + }, + { + "type": "object", + "properties": { + "childProperty": { + "type": "string" + } + } + } + ] + }, + "childtype": { + "type": "object", + "allOf": [ + { + "$ref": "#/definitions/parenttype" + } + ], + "properties": { + "childProperty": { + "type": "string" + } + } + }, + "parenttype": { + "type": "object", + "properties": { + "inheritedProperty": { + "type": "string" + } + } + }, + "Foo": { + "type": "object", + "properties": { + "foo": { + "type": "string" + } + } + }, + "Bar": { + "allOf": [ + { + "$ref": "#/definitions/Foo" + }, + { + "type": "object", + "properties": { + "bar": { + "type": "string" + } + } + } + ] + }, + "Baz": { + "allOf": [ + { + "$ref": "#/definitions/Foo" + }, + { + "type": "object", + "properties": { + "Bar": { + "$ref": "#/definitions/Bar" + }, + "Baz": { + "type": "object", + "properties": { + "baz": { + "type": "string" + } + } + } + } + } + ] + } + }, + "properties": { + "child": { + "$ref": "#/definitions/childtype" + }, + "parent": { + "$ref": "#/definitions/parenttype" + } + } +} diff --git a/Tests/fixtures/array-object-nullable/.jane b/Tests/fixtures/array-object-nullable/.jane new file mode 100644 index 0000000..1bc1d29 --- /dev/null +++ b/Tests/fixtures/array-object-nullable/.jane @@ -0,0 +1,9 @@ + __DIR__ . '/schema.json', + 'root-class' => 'Test', + 'namespace' => 'Jane\Component\JsonSchema\Tests\Expected', + 'directory' => __DIR__ . '/generated', + 'strict' => true, +]; diff --git a/Tests/fixtures/array-object-nullable/expected/Model/Attributes.php b/Tests/fixtures/array-object-nullable/expected/Model/Attributes.php new file mode 100644 index 0000000..8d17042 --- /dev/null +++ b/Tests/fixtures/array-object-nullable/expected/Model/Attributes.php @@ -0,0 +1,34 @@ +foo; + } + /** + * + * + * @param string $foo + * + * @return self + */ + public function setFoo(string $foo) : self + { + $this->foo = $foo; + return $this; + } +} \ No newline at end of file diff --git a/Tests/fixtures/array-object-nullable/expected/Model/Document.php b/Tests/fixtures/array-object-nullable/expected/Model/Document.php new file mode 100644 index 0000000..75af311 --- /dev/null +++ b/Tests/fixtures/array-object-nullable/expected/Model/Document.php @@ -0,0 +1,34 @@ +attributes; + } + /** + * + * + * @param Attributes[]|null $attributes + * + * @return self + */ + public function setAttributes(?array $attributes) : self + { + $this->attributes = $attributes; + return $this; + } +} \ No newline at end of file diff --git a/Tests/fixtures/array-object-nullable/expected/Normalizer/AttributesNormalizer.php b/Tests/fixtures/array-object-nullable/expected/Normalizer/AttributesNormalizer.php new file mode 100644 index 0000000..75565a4 --- /dev/null +++ b/Tests/fixtures/array-object-nullable/expected/Normalizer/AttributesNormalizer.php @@ -0,0 +1,52 @@ +setFoo($data['foo']); + } + return $object; + } + public function normalize($object, $format = null, array $context = array()) + { + $data = array(); + if (null !== $object->getFoo()) { + $data['foo'] = $object->getFoo(); + } + return $data; + } +} \ No newline at end of file diff --git a/Tests/fixtures/array-object-nullable/expected/Normalizer/DocumentNormalizer.php b/Tests/fixtures/array-object-nullable/expected/Normalizer/DocumentNormalizer.php new file mode 100644 index 0000000..91ac291 --- /dev/null +++ b/Tests/fixtures/array-object-nullable/expected/Normalizer/DocumentNormalizer.php @@ -0,0 +1,75 @@ +isOnlyNumericKeys($data['attributes'])) { + $values = array(); + foreach ($data['attributes'] as $value_1) { + $values[] = $this->denormalizer->denormalize($value_1, 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Model\\Attributes', 'json', $context); + } + $value = $values; + } elseif (is_null($data['attributes'])) { + $value = $data['attributes']; + } + $object->setAttributes($value); + } + elseif (\array_key_exists('attributes', $data) && $data['attributes'] === null) { + $object->setAttributes(null); + } + return $object; + } + public function normalize($object, $format = null, array $context = array()) + { + $data = array(); + if (null !== $object->getAttributes()) { + $value = $object->getAttributes(); + if (is_array($object->getAttributes())) { + $values = array(); + foreach ($object->getAttributes() as $value_1) { + $values[] = $this->normalizer->normalize($value_1, 'json', $context); + } + $value = $values; + } elseif (is_null($object->getAttributes())) { + $value = $object->getAttributes(); + } + $data['attributes'] = $value; + } + return $data; + } +} \ No newline at end of file diff --git a/Tests/fixtures/array-object-nullable/expected/Normalizer/JaneObjectNormalizer.php b/Tests/fixtures/array-object-nullable/expected/Normalizer/JaneObjectNormalizer.php new file mode 100644 index 0000000..4352694 --- /dev/null +++ b/Tests/fixtures/array-object-nullable/expected/Normalizer/JaneObjectNormalizer.php @@ -0,0 +1,50 @@ + 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Normalizer\\DocumentNormalizer', 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Model\\Attributes' => 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Normalizer\\AttributesNormalizer', '\\Jane\\Component\\JsonSchemaRuntime\\Reference' => '\\Jane\\Component\\JsonSchema\\Tests\\Expected\\Runtime\\Normalizer\\ReferenceNormalizer'), $normalizersCache = array(); + public function supportsDenormalization($data, $type, $format = null) + { + return array_key_exists($type, $this->normalizers); + } + public function supportsNormalization($data, $format = null) + { + return is_object($data) && array_key_exists(get_class($data), $this->normalizers); + } + public function normalize($object, $format = null, array $context = array()) + { + $normalizerClass = $this->normalizers[get_class($object)]; + $normalizer = $this->getNormalizer($normalizerClass); + return $normalizer->normalize($object, $format, $context); + } + public function denormalize($data, $class, $format = null, array $context = array()) + { + $denormalizerClass = $this->normalizers[$class]; + $denormalizer = $this->getNormalizer($denormalizerClass); + return $denormalizer->denormalize($data, $class, $format, $context); + } + private function getNormalizer(string $normalizerClass) + { + return $this->normalizersCache[$normalizerClass] ?? $this->initNormalizer($normalizerClass); + } + private function initNormalizer(string $normalizerClass) + { + $normalizer = new $normalizerClass(); + $normalizer->setNormalizer($this->normalizer); + $normalizer->setDenormalizer($this->denormalizer); + $this->normalizersCache[$normalizerClass] = $normalizer; + return $normalizer; + } +} \ No newline at end of file diff --git a/Tests/fixtures/array-object-nullable/expected/Runtime/Normalizer/CheckArray.php b/Tests/fixtures/array-object-nullable/expected/Runtime/Normalizer/CheckArray.php new file mode 100644 index 0000000..3f60a9f --- /dev/null +++ b/Tests/fixtures/array-object-nullable/expected/Runtime/Normalizer/CheckArray.php @@ -0,0 +1,13 @@ +getReferenceUri(); + return $ref; + } + /** + * {@inheritdoc} + */ + public function supportsNormalization($data, $format = null) + { + return $data instanceof Reference; + } +} \ No newline at end of file diff --git a/Tests/fixtures/array-object-nullable/schema.json b/Tests/fixtures/array-object-nullable/schema.json new file mode 100644 index 0000000..5c86c6f --- /dev/null +++ b/Tests/fixtures/array-object-nullable/schema.json @@ -0,0 +1,24 @@ +{ + "$schema": "http://json-schema.org/2019-09/schema#", + "definitions": { + "Document": { + "type": "object", + "properties": { + "attributes": { + "type": ["array", "null"], + "items": { + "$ref": "#/definitions/Attributes" + } + } + } + }, + "Attributes": { + "type": "object", + "properties": { + "foo": { + "type": "string" + } + } + } + } +} diff --git a/Tests/fixtures/date-format/.jane b/Tests/fixtures/date-format/.jane new file mode 100644 index 0000000..53e0f65 --- /dev/null +++ b/Tests/fixtures/date-format/.jane @@ -0,0 +1,9 @@ + __DIR__ . '/schema.json', + 'root-class' => 'Test', + 'namespace' => 'Jane\Component\JsonSchema\Tests\Expected', + 'directory' => __DIR__ . '/generated', + 'full-date-format' => 'd.m.Y' +]; diff --git a/Tests/fixtures/date-format/expected/Model/Test.php b/Tests/fixtures/date-format/expected/Model/Test.php new file mode 100644 index 0000000..c424d74 --- /dev/null +++ b/Tests/fixtures/date-format/expected/Model/Test.php @@ -0,0 +1,88 @@ +date; + } + /** + * + * + * @param \DateTime $date + * + * @return self + */ + public function setDate(\DateTime $date) : self + { + $this->date = $date; + return $this; + } + /** + * + * + * @return \DateTime|null + */ + public function getDateOrNull() : ?\DateTime + { + return $this->dateOrNull; + } + /** + * + * + * @param \DateTime|null $dateOrNull + * + * @return self + */ + public function setDateOrNull(?\DateTime $dateOrNull) : self + { + $this->dateOrNull = $dateOrNull; + return $this; + } + /** + * + * + * @return \DateTime|null|int + */ + public function getDateOrNullOrInt() + { + return $this->dateOrNullOrInt; + } + /** + * + * + * @param \DateTime|null|int $dateOrNullOrInt + * + * @return self + */ + public function setDateOrNullOrInt($dateOrNullOrInt) : self + { + $this->dateOrNullOrInt = $dateOrNullOrInt; + return $this; + } +} \ No newline at end of file diff --git a/Tests/fixtures/date-format/expected/Normalizer/JaneObjectNormalizer.php b/Tests/fixtures/date-format/expected/Normalizer/JaneObjectNormalizer.php new file mode 100644 index 0000000..9b98fa6 --- /dev/null +++ b/Tests/fixtures/date-format/expected/Normalizer/JaneObjectNormalizer.php @@ -0,0 +1,50 @@ + 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Normalizer\\TestNormalizer', '\\Jane\\Component\\JsonSchemaRuntime\\Reference' => '\\Jane\\Component\\JsonSchema\\Tests\\Expected\\Runtime\\Normalizer\\ReferenceNormalizer'), $normalizersCache = array(); + public function supportsDenormalization($data, $type, $format = null) + { + return array_key_exists($type, $this->normalizers); + } + public function supportsNormalization($data, $format = null) + { + return is_object($data) && array_key_exists(get_class($data), $this->normalizers); + } + public function normalize($object, $format = null, array $context = array()) + { + $normalizerClass = $this->normalizers[get_class($object)]; + $normalizer = $this->getNormalizer($normalizerClass); + return $normalizer->normalize($object, $format, $context); + } + public function denormalize($data, $class, $format = null, array $context = array()) + { + $denormalizerClass = $this->normalizers[$class]; + $denormalizer = $this->getNormalizer($denormalizerClass); + return $denormalizer->denormalize($data, $class, $format, $context); + } + private function getNormalizer(string $normalizerClass) + { + return $this->normalizersCache[$normalizerClass] ?? $this->initNormalizer($normalizerClass); + } + private function initNormalizer(string $normalizerClass) + { + $normalizer = new $normalizerClass(); + $normalizer->setNormalizer($this->normalizer); + $normalizer->setDenormalizer($this->denormalizer); + $this->normalizersCache[$normalizerClass] = $normalizer; + return $normalizer; + } +} \ No newline at end of file diff --git a/Tests/fixtures/date-format/expected/Normalizer/TestNormalizer.php b/Tests/fixtures/date-format/expected/Normalizer/TestNormalizer.php new file mode 100644 index 0000000..17dd5b8 --- /dev/null +++ b/Tests/fixtures/date-format/expected/Normalizer/TestNormalizer.php @@ -0,0 +1,98 @@ +setDate(\DateTime::createFromFormat('d.m.Y', $data['date'])->setTime(0, 0, 0)); + } + if (\array_key_exists('dateOrNull', $data) && $data['dateOrNull'] !== null) { + $value = $data['dateOrNull']; + if (is_string($data['dateOrNull']) and false !== \DateTime::createFromFormat('d.m.Y', $data['dateOrNull'])->setTime(0, 0, 0)) { + $value = \DateTime::createFromFormat('d.m.Y', $data['dateOrNull'])->setTime(0, 0, 0); + } elseif (is_null($data['dateOrNull'])) { + $value = $data['dateOrNull']; + } + $object->setDateOrNull($value); + } + elseif (\array_key_exists('dateOrNull', $data) && $data['dateOrNull'] === null) { + $object->setDateOrNull(null); + } + if (\array_key_exists('dateOrNullOrInt', $data) && $data['dateOrNullOrInt'] !== null) { + $value_1 = $data['dateOrNullOrInt']; + if (is_string($data['dateOrNullOrInt']) and false !== \DateTime::createFromFormat('d.m.Y', $data['dateOrNullOrInt'])->setTime(0, 0, 0)) { + $value_1 = \DateTime::createFromFormat('d.m.Y', $data['dateOrNullOrInt'])->setTime(0, 0, 0); + } elseif (is_null($data['dateOrNullOrInt'])) { + $value_1 = $data['dateOrNullOrInt']; + } elseif (is_int($data['dateOrNullOrInt'])) { + $value_1 = $data['dateOrNullOrInt']; + } + $object->setDateOrNullOrInt($value_1); + } + elseif (\array_key_exists('dateOrNullOrInt', $data) && $data['dateOrNullOrInt'] === null) { + $object->setDateOrNullOrInt(null); + } + return $object; + } + public function normalize($object, $format = null, array $context = array()) + { + $data = array(); + if (null !== $object->getDate()) { + $data['date'] = $object->getDate()->format('d.m.Y'); + } + if (null !== $object->getDateOrNull()) { + $value = $object->getDateOrNull(); + if (is_object($object->getDateOrNull())) { + $value = $object->getDateOrNull()->format('d.m.Y'); + } elseif (is_null($object->getDateOrNull())) { + $value = $object->getDateOrNull(); + } + $data['dateOrNull'] = $value; + } + if (null !== $object->getDateOrNullOrInt()) { + $value_1 = $object->getDateOrNullOrInt(); + if (is_object($object->getDateOrNullOrInt())) { + $value_1 = $object->getDateOrNullOrInt()->format('d.m.Y'); + } elseif (is_null($object->getDateOrNullOrInt())) { + $value_1 = $object->getDateOrNullOrInt(); + } elseif (is_int($object->getDateOrNullOrInt())) { + $value_1 = $object->getDateOrNullOrInt(); + } + $data['dateOrNullOrInt'] = $value_1; + } + return $data; + } +} \ No newline at end of file diff --git a/Tests/fixtures/date-format/expected/Runtime/Normalizer/CheckArray.php b/Tests/fixtures/date-format/expected/Runtime/Normalizer/CheckArray.php new file mode 100644 index 0000000..3f60a9f --- /dev/null +++ b/Tests/fixtures/date-format/expected/Runtime/Normalizer/CheckArray.php @@ -0,0 +1,13 @@ +getReferenceUri(); + return $ref; + } + /** + * {@inheritdoc} + */ + public function supportsNormalization($data, $format = null) + { + return $data instanceof Reference; + } +} \ No newline at end of file diff --git a/Tests/fixtures/date-format/schema.json b/Tests/fixtures/date-format/schema.json new file mode 100644 index 0000000..d38846b --- /dev/null +++ b/Tests/fixtures/date-format/schema.json @@ -0,0 +1,20 @@ +{ + "id": "http://json-schema.org/draft-04/schema#", + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Schema for object with null values", + "type": "object", + "properties": { + "date": { + "type": "string", + "format": "date" + }, + "dateOrNull": { + "type": ["string", "null"], + "format": "date" + }, + "dateOrNullOrInt": { + "type": ["string", "null", "integer"], + "format": "date" + } + } +} diff --git a/Tests/fixtures/date/.jane b/Tests/fixtures/date/.jane new file mode 100644 index 0000000..bcd1207 --- /dev/null +++ b/Tests/fixtures/date/.jane @@ -0,0 +1,8 @@ + __DIR__ . '/schema.json', + 'root-class' => 'Test', + 'namespace' => 'Jane\Component\JsonSchema\Tests\Expected', + 'directory' => __DIR__ . '/generated', +]; diff --git a/Tests/fixtures/date/expected/Model/Test.php b/Tests/fixtures/date/expected/Model/Test.php new file mode 100644 index 0000000..c424d74 --- /dev/null +++ b/Tests/fixtures/date/expected/Model/Test.php @@ -0,0 +1,88 @@ +date; + } + /** + * + * + * @param \DateTime $date + * + * @return self + */ + public function setDate(\DateTime $date) : self + { + $this->date = $date; + return $this; + } + /** + * + * + * @return \DateTime|null + */ + public function getDateOrNull() : ?\DateTime + { + return $this->dateOrNull; + } + /** + * + * + * @param \DateTime|null $dateOrNull + * + * @return self + */ + public function setDateOrNull(?\DateTime $dateOrNull) : self + { + $this->dateOrNull = $dateOrNull; + return $this; + } + /** + * + * + * @return \DateTime|null|int + */ + public function getDateOrNullOrInt() + { + return $this->dateOrNullOrInt; + } + /** + * + * + * @param \DateTime|null|int $dateOrNullOrInt + * + * @return self + */ + public function setDateOrNullOrInt($dateOrNullOrInt) : self + { + $this->dateOrNullOrInt = $dateOrNullOrInt; + return $this; + } +} \ No newline at end of file diff --git a/Tests/fixtures/date/expected/Normalizer/JaneObjectNormalizer.php b/Tests/fixtures/date/expected/Normalizer/JaneObjectNormalizer.php new file mode 100644 index 0000000..9b98fa6 --- /dev/null +++ b/Tests/fixtures/date/expected/Normalizer/JaneObjectNormalizer.php @@ -0,0 +1,50 @@ + 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Normalizer\\TestNormalizer', '\\Jane\\Component\\JsonSchemaRuntime\\Reference' => '\\Jane\\Component\\JsonSchema\\Tests\\Expected\\Runtime\\Normalizer\\ReferenceNormalizer'), $normalizersCache = array(); + public function supportsDenormalization($data, $type, $format = null) + { + return array_key_exists($type, $this->normalizers); + } + public function supportsNormalization($data, $format = null) + { + return is_object($data) && array_key_exists(get_class($data), $this->normalizers); + } + public function normalize($object, $format = null, array $context = array()) + { + $normalizerClass = $this->normalizers[get_class($object)]; + $normalizer = $this->getNormalizer($normalizerClass); + return $normalizer->normalize($object, $format, $context); + } + public function denormalize($data, $class, $format = null, array $context = array()) + { + $denormalizerClass = $this->normalizers[$class]; + $denormalizer = $this->getNormalizer($denormalizerClass); + return $denormalizer->denormalize($data, $class, $format, $context); + } + private function getNormalizer(string $normalizerClass) + { + return $this->normalizersCache[$normalizerClass] ?? $this->initNormalizer($normalizerClass); + } + private function initNormalizer(string $normalizerClass) + { + $normalizer = new $normalizerClass(); + $normalizer->setNormalizer($this->normalizer); + $normalizer->setDenormalizer($this->denormalizer); + $this->normalizersCache[$normalizerClass] = $normalizer; + return $normalizer; + } +} \ No newline at end of file diff --git a/Tests/fixtures/date/expected/Normalizer/TestNormalizer.php b/Tests/fixtures/date/expected/Normalizer/TestNormalizer.php new file mode 100644 index 0000000..27a373c --- /dev/null +++ b/Tests/fixtures/date/expected/Normalizer/TestNormalizer.php @@ -0,0 +1,98 @@ +setDate(\DateTime::createFromFormat('Y-m-d', $data['date'])->setTime(0, 0, 0)); + } + if (\array_key_exists('dateOrNull', $data) && $data['dateOrNull'] !== null) { + $value = $data['dateOrNull']; + if (is_string($data['dateOrNull']) and false !== \DateTime::createFromFormat('Y-m-d', $data['dateOrNull'])->setTime(0, 0, 0)) { + $value = \DateTime::createFromFormat('Y-m-d', $data['dateOrNull'])->setTime(0, 0, 0); + } elseif (is_null($data['dateOrNull'])) { + $value = $data['dateOrNull']; + } + $object->setDateOrNull($value); + } + elseif (\array_key_exists('dateOrNull', $data) && $data['dateOrNull'] === null) { + $object->setDateOrNull(null); + } + if (\array_key_exists('dateOrNullOrInt', $data) && $data['dateOrNullOrInt'] !== null) { + $value_1 = $data['dateOrNullOrInt']; + if (is_string($data['dateOrNullOrInt']) and false !== \DateTime::createFromFormat('Y-m-d', $data['dateOrNullOrInt'])->setTime(0, 0, 0)) { + $value_1 = \DateTime::createFromFormat('Y-m-d', $data['dateOrNullOrInt'])->setTime(0, 0, 0); + } elseif (is_null($data['dateOrNullOrInt'])) { + $value_1 = $data['dateOrNullOrInt']; + } elseif (is_int($data['dateOrNullOrInt'])) { + $value_1 = $data['dateOrNullOrInt']; + } + $object->setDateOrNullOrInt($value_1); + } + elseif (\array_key_exists('dateOrNullOrInt', $data) && $data['dateOrNullOrInt'] === null) { + $object->setDateOrNullOrInt(null); + } + return $object; + } + public function normalize($object, $format = null, array $context = array()) + { + $data = array(); + if (null !== $object->getDate()) { + $data['date'] = $object->getDate()->format('Y-m-d'); + } + if (null !== $object->getDateOrNull()) { + $value = $object->getDateOrNull(); + if (is_object($object->getDateOrNull())) { + $value = $object->getDateOrNull()->format('Y-m-d'); + } elseif (is_null($object->getDateOrNull())) { + $value = $object->getDateOrNull(); + } + $data['dateOrNull'] = $value; + } + if (null !== $object->getDateOrNullOrInt()) { + $value_1 = $object->getDateOrNullOrInt(); + if (is_object($object->getDateOrNullOrInt())) { + $value_1 = $object->getDateOrNullOrInt()->format('Y-m-d'); + } elseif (is_null($object->getDateOrNullOrInt())) { + $value_1 = $object->getDateOrNullOrInt(); + } elseif (is_int($object->getDateOrNullOrInt())) { + $value_1 = $object->getDateOrNullOrInt(); + } + $data['dateOrNullOrInt'] = $value_1; + } + return $data; + } +} \ No newline at end of file diff --git a/Tests/fixtures/date/expected/Runtime/Normalizer/CheckArray.php b/Tests/fixtures/date/expected/Runtime/Normalizer/CheckArray.php new file mode 100644 index 0000000..3f60a9f --- /dev/null +++ b/Tests/fixtures/date/expected/Runtime/Normalizer/CheckArray.php @@ -0,0 +1,13 @@ +getReferenceUri(); + return $ref; + } + /** + * {@inheritdoc} + */ + public function supportsNormalization($data, $format = null) + { + return $data instanceof Reference; + } +} \ No newline at end of file diff --git a/Tests/fixtures/date/schema.json b/Tests/fixtures/date/schema.json new file mode 100644 index 0000000..d38846b --- /dev/null +++ b/Tests/fixtures/date/schema.json @@ -0,0 +1,20 @@ +{ + "id": "http://json-schema.org/draft-04/schema#", + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Schema for object with null values", + "type": "object", + "properties": { + "date": { + "type": "string", + "format": "date" + }, + "dateOrNull": { + "type": ["string", "null"], + "format": "date" + }, + "dateOrNullOrInt": { + "type": ["string", "null", "integer"], + "format": "date" + } + } +} diff --git a/Tests/fixtures/datetime-format/.jane b/Tests/fixtures/datetime-format/.jane new file mode 100644 index 0000000..92b047a --- /dev/null +++ b/Tests/fixtures/datetime-format/.jane @@ -0,0 +1,9 @@ + __DIR__ . '/schema.json', + 'root-class' => 'Test', + 'namespace' => 'Jane\Component\JsonSchema\Tests\Expected', + 'directory' => __DIR__ . '/generated', + 'date-format' => \DateTime::RFC850, +]; diff --git a/Tests/fixtures/datetime-format/expected/Model/Test.php b/Tests/fixtures/datetime-format/expected/Model/Test.php new file mode 100644 index 0000000..c424d74 --- /dev/null +++ b/Tests/fixtures/datetime-format/expected/Model/Test.php @@ -0,0 +1,88 @@ +date; + } + /** + * + * + * @param \DateTime $date + * + * @return self + */ + public function setDate(\DateTime $date) : self + { + $this->date = $date; + return $this; + } + /** + * + * + * @return \DateTime|null + */ + public function getDateOrNull() : ?\DateTime + { + return $this->dateOrNull; + } + /** + * + * + * @param \DateTime|null $dateOrNull + * + * @return self + */ + public function setDateOrNull(?\DateTime $dateOrNull) : self + { + $this->dateOrNull = $dateOrNull; + return $this; + } + /** + * + * + * @return \DateTime|null|int + */ + public function getDateOrNullOrInt() + { + return $this->dateOrNullOrInt; + } + /** + * + * + * @param \DateTime|null|int $dateOrNullOrInt + * + * @return self + */ + public function setDateOrNullOrInt($dateOrNullOrInt) : self + { + $this->dateOrNullOrInt = $dateOrNullOrInt; + return $this; + } +} \ No newline at end of file diff --git a/Tests/fixtures/datetime-format/expected/Normalizer/JaneObjectNormalizer.php b/Tests/fixtures/datetime-format/expected/Normalizer/JaneObjectNormalizer.php new file mode 100644 index 0000000..9b98fa6 --- /dev/null +++ b/Tests/fixtures/datetime-format/expected/Normalizer/JaneObjectNormalizer.php @@ -0,0 +1,50 @@ + 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Normalizer\\TestNormalizer', '\\Jane\\Component\\JsonSchemaRuntime\\Reference' => '\\Jane\\Component\\JsonSchema\\Tests\\Expected\\Runtime\\Normalizer\\ReferenceNormalizer'), $normalizersCache = array(); + public function supportsDenormalization($data, $type, $format = null) + { + return array_key_exists($type, $this->normalizers); + } + public function supportsNormalization($data, $format = null) + { + return is_object($data) && array_key_exists(get_class($data), $this->normalizers); + } + public function normalize($object, $format = null, array $context = array()) + { + $normalizerClass = $this->normalizers[get_class($object)]; + $normalizer = $this->getNormalizer($normalizerClass); + return $normalizer->normalize($object, $format, $context); + } + public function denormalize($data, $class, $format = null, array $context = array()) + { + $denormalizerClass = $this->normalizers[$class]; + $denormalizer = $this->getNormalizer($denormalizerClass); + return $denormalizer->denormalize($data, $class, $format, $context); + } + private function getNormalizer(string $normalizerClass) + { + return $this->normalizersCache[$normalizerClass] ?? $this->initNormalizer($normalizerClass); + } + private function initNormalizer(string $normalizerClass) + { + $normalizer = new $normalizerClass(); + $normalizer->setNormalizer($this->normalizer); + $normalizer->setDenormalizer($this->denormalizer); + $this->normalizersCache[$normalizerClass] = $normalizer; + return $normalizer; + } +} \ No newline at end of file diff --git a/Tests/fixtures/datetime-format/expected/Normalizer/TestNormalizer.php b/Tests/fixtures/datetime-format/expected/Normalizer/TestNormalizer.php new file mode 100644 index 0000000..cb88492 --- /dev/null +++ b/Tests/fixtures/datetime-format/expected/Normalizer/TestNormalizer.php @@ -0,0 +1,98 @@ +setDate(\DateTime::createFromFormat('l, d-M-y H:i:s T', $data['date'])); + } + if (\array_key_exists('dateOrNull', $data) && $data['dateOrNull'] !== null) { + $value = $data['dateOrNull']; + if (is_string($data['dateOrNull']) and false !== \DateTime::createFromFormat('l, d-M-y H:i:s T', $data['dateOrNull'])) { + $value = \DateTime::createFromFormat('l, d-M-y H:i:s T', $data['dateOrNull']); + } elseif (is_null($data['dateOrNull'])) { + $value = $data['dateOrNull']; + } + $object->setDateOrNull($value); + } + elseif (\array_key_exists('dateOrNull', $data) && $data['dateOrNull'] === null) { + $object->setDateOrNull(null); + } + if (\array_key_exists('dateOrNullOrInt', $data) && $data['dateOrNullOrInt'] !== null) { + $value_1 = $data['dateOrNullOrInt']; + if (is_string($data['dateOrNullOrInt']) and false !== \DateTime::createFromFormat('l, d-M-y H:i:s T', $data['dateOrNullOrInt'])) { + $value_1 = \DateTime::createFromFormat('l, d-M-y H:i:s T', $data['dateOrNullOrInt']); + } elseif (is_null($data['dateOrNullOrInt'])) { + $value_1 = $data['dateOrNullOrInt']; + } elseif (is_int($data['dateOrNullOrInt'])) { + $value_1 = $data['dateOrNullOrInt']; + } + $object->setDateOrNullOrInt($value_1); + } + elseif (\array_key_exists('dateOrNullOrInt', $data) && $data['dateOrNullOrInt'] === null) { + $object->setDateOrNullOrInt(null); + } + return $object; + } + public function normalize($object, $format = null, array $context = array()) + { + $data = array(); + if (null !== $object->getDate()) { + $data['date'] = $object->getDate()->format('l, d-M-y H:i:s T'); + } + if (null !== $object->getDateOrNull()) { + $value = $object->getDateOrNull(); + if (is_object($object->getDateOrNull())) { + $value = $object->getDateOrNull()->format('l, d-M-y H:i:s T'); + } elseif (is_null($object->getDateOrNull())) { + $value = $object->getDateOrNull(); + } + $data['dateOrNull'] = $value; + } + if (null !== $object->getDateOrNullOrInt()) { + $value_1 = $object->getDateOrNullOrInt(); + if (is_object($object->getDateOrNullOrInt())) { + $value_1 = $object->getDateOrNullOrInt()->format('l, d-M-y H:i:s T'); + } elseif (is_null($object->getDateOrNullOrInt())) { + $value_1 = $object->getDateOrNullOrInt(); + } elseif (is_int($object->getDateOrNullOrInt())) { + $value_1 = $object->getDateOrNullOrInt(); + } + $data['dateOrNullOrInt'] = $value_1; + } + return $data; + } +} \ No newline at end of file diff --git a/Tests/fixtures/datetime-format/expected/Runtime/Normalizer/CheckArray.php b/Tests/fixtures/datetime-format/expected/Runtime/Normalizer/CheckArray.php new file mode 100644 index 0000000..3f60a9f --- /dev/null +++ b/Tests/fixtures/datetime-format/expected/Runtime/Normalizer/CheckArray.php @@ -0,0 +1,13 @@ +getReferenceUri(); + return $ref; + } + /** + * {@inheritdoc} + */ + public function supportsNormalization($data, $format = null) + { + return $data instanceof Reference; + } +} \ No newline at end of file diff --git a/Tests/fixtures/datetime-format/schema.json b/Tests/fixtures/datetime-format/schema.json new file mode 100644 index 0000000..caa7f01 --- /dev/null +++ b/Tests/fixtures/datetime-format/schema.json @@ -0,0 +1,20 @@ +{ + "id": "http://json-schema.org/draft-04/schema#", + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Schema for object with null values", + "type": "object", + "properties": { + "date": { + "type": "string", + "format": "date-time" + }, + "dateOrNull": { + "type": ["string", "null"], + "format": "date-time" + }, + "dateOrNullOrInt": { + "type": ["string", "null", "integer"], + "format": "date-time" + } + } +} diff --git a/Tests/fixtures/datetime-interface/.jane b/Tests/fixtures/datetime-interface/.jane new file mode 100644 index 0000000..1c3d1fc --- /dev/null +++ b/Tests/fixtures/datetime-interface/.jane @@ -0,0 +1,9 @@ + __DIR__ . '/schema.json', + 'root-class' => 'Test', + 'namespace' => 'Jane\Component\JsonSchema\Tests\Expected', + 'directory' => __DIR__ . '/generated', + 'date-prefer-interface' => true, +]; diff --git a/Tests/fixtures/datetime-interface/expected/Model/Test.php b/Tests/fixtures/datetime-interface/expected/Model/Test.php new file mode 100644 index 0000000..632d92d --- /dev/null +++ b/Tests/fixtures/datetime-interface/expected/Model/Test.php @@ -0,0 +1,88 @@ +date; + } + /** + * + * + * @param \DateTimeInterface $date + * + * @return self + */ + public function setDate(\DateTimeInterface $date) : self + { + $this->date = $date; + return $this; + } + /** + * + * + * @return \DateTimeInterface|null + */ + public function getDateOrNull() : ?\DateTimeInterface + { + return $this->dateOrNull; + } + /** + * + * + * @param \DateTimeInterface|null $dateOrNull + * + * @return self + */ + public function setDateOrNull(?\DateTimeInterface $dateOrNull) : self + { + $this->dateOrNull = $dateOrNull; + return $this; + } + /** + * + * + * @return \DateTimeInterface|null|int + */ + public function getDateOrNullOrInt() + { + return $this->dateOrNullOrInt; + } + /** + * + * + * @param \DateTimeInterface|null|int $dateOrNullOrInt + * + * @return self + */ + public function setDateOrNullOrInt($dateOrNullOrInt) : self + { + $this->dateOrNullOrInt = $dateOrNullOrInt; + return $this; + } +} \ No newline at end of file diff --git a/Tests/fixtures/datetime-interface/expected/Normalizer/JaneObjectNormalizer.php b/Tests/fixtures/datetime-interface/expected/Normalizer/JaneObjectNormalizer.php new file mode 100644 index 0000000..9b98fa6 --- /dev/null +++ b/Tests/fixtures/datetime-interface/expected/Normalizer/JaneObjectNormalizer.php @@ -0,0 +1,50 @@ + 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Normalizer\\TestNormalizer', '\\Jane\\Component\\JsonSchemaRuntime\\Reference' => '\\Jane\\Component\\JsonSchema\\Tests\\Expected\\Runtime\\Normalizer\\ReferenceNormalizer'), $normalizersCache = array(); + public function supportsDenormalization($data, $type, $format = null) + { + return array_key_exists($type, $this->normalizers); + } + public function supportsNormalization($data, $format = null) + { + return is_object($data) && array_key_exists(get_class($data), $this->normalizers); + } + public function normalize($object, $format = null, array $context = array()) + { + $normalizerClass = $this->normalizers[get_class($object)]; + $normalizer = $this->getNormalizer($normalizerClass); + return $normalizer->normalize($object, $format, $context); + } + public function denormalize($data, $class, $format = null, array $context = array()) + { + $denormalizerClass = $this->normalizers[$class]; + $denormalizer = $this->getNormalizer($denormalizerClass); + return $denormalizer->denormalize($data, $class, $format, $context); + } + private function getNormalizer(string $normalizerClass) + { + return $this->normalizersCache[$normalizerClass] ?? $this->initNormalizer($normalizerClass); + } + private function initNormalizer(string $normalizerClass) + { + $normalizer = new $normalizerClass(); + $normalizer->setNormalizer($this->normalizer); + $normalizer->setDenormalizer($this->denormalizer); + $this->normalizersCache[$normalizerClass] = $normalizer; + return $normalizer; + } +} \ No newline at end of file diff --git a/Tests/fixtures/datetime-interface/expected/Normalizer/TestNormalizer.php b/Tests/fixtures/datetime-interface/expected/Normalizer/TestNormalizer.php new file mode 100644 index 0000000..e0e0e72 --- /dev/null +++ b/Tests/fixtures/datetime-interface/expected/Normalizer/TestNormalizer.php @@ -0,0 +1,98 @@ +setDate(\DateTime::createFromFormat('Y-m-d\\TH:i:sP', $data['date'])); + } + if (\array_key_exists('dateOrNull', $data) && $data['dateOrNull'] !== null) { + $value = $data['dateOrNull']; + if (is_string($data['dateOrNull']) and false !== \DateTime::createFromFormat('Y-m-d\\TH:i:sP', $data['dateOrNull'])) { + $value = \DateTime::createFromFormat('Y-m-d\\TH:i:sP', $data['dateOrNull']); + } elseif (is_null($data['dateOrNull'])) { + $value = $data['dateOrNull']; + } + $object->setDateOrNull($value); + } + elseif (\array_key_exists('dateOrNull', $data) && $data['dateOrNull'] === null) { + $object->setDateOrNull(null); + } + if (\array_key_exists('dateOrNullOrInt', $data) && $data['dateOrNullOrInt'] !== null) { + $value_1 = $data['dateOrNullOrInt']; + if (is_string($data['dateOrNullOrInt']) and false !== \DateTime::createFromFormat('Y-m-d\\TH:i:sP', $data['dateOrNullOrInt'])) { + $value_1 = \DateTime::createFromFormat('Y-m-d\\TH:i:sP', $data['dateOrNullOrInt']); + } elseif (is_null($data['dateOrNullOrInt'])) { + $value_1 = $data['dateOrNullOrInt']; + } elseif (is_int($data['dateOrNullOrInt'])) { + $value_1 = $data['dateOrNullOrInt']; + } + $object->setDateOrNullOrInt($value_1); + } + elseif (\array_key_exists('dateOrNullOrInt', $data) && $data['dateOrNullOrInt'] === null) { + $object->setDateOrNullOrInt(null); + } + return $object; + } + public function normalize($object, $format = null, array $context = array()) + { + $data = array(); + if (null !== $object->getDate()) { + $data['date'] = $object->getDate()->format('Y-m-d\\TH:i:sP'); + } + if (null !== $object->getDateOrNull()) { + $value = $object->getDateOrNull(); + if (is_object($object->getDateOrNull())) { + $value = $object->getDateOrNull()->format('Y-m-d\\TH:i:sP'); + } elseif (is_null($object->getDateOrNull())) { + $value = $object->getDateOrNull(); + } + $data['dateOrNull'] = $value; + } + if (null !== $object->getDateOrNullOrInt()) { + $value_1 = $object->getDateOrNullOrInt(); + if (is_object($object->getDateOrNullOrInt())) { + $value_1 = $object->getDateOrNullOrInt()->format('Y-m-d\\TH:i:sP'); + } elseif (is_null($object->getDateOrNullOrInt())) { + $value_1 = $object->getDateOrNullOrInt(); + } elseif (is_int($object->getDateOrNullOrInt())) { + $value_1 = $object->getDateOrNullOrInt(); + } + $data['dateOrNullOrInt'] = $value_1; + } + return $data; + } +} \ No newline at end of file diff --git a/Tests/fixtures/datetime-interface/expected/Runtime/Normalizer/CheckArray.php b/Tests/fixtures/datetime-interface/expected/Runtime/Normalizer/CheckArray.php new file mode 100644 index 0000000..3f60a9f --- /dev/null +++ b/Tests/fixtures/datetime-interface/expected/Runtime/Normalizer/CheckArray.php @@ -0,0 +1,13 @@ +getReferenceUri(); + return $ref; + } + /** + * {@inheritdoc} + */ + public function supportsNormalization($data, $format = null) + { + return $data instanceof Reference; + } +} \ No newline at end of file diff --git a/Tests/fixtures/datetime-interface/schema.json b/Tests/fixtures/datetime-interface/schema.json new file mode 100644 index 0000000..caa7f01 --- /dev/null +++ b/Tests/fixtures/datetime-interface/schema.json @@ -0,0 +1,20 @@ +{ + "id": "http://json-schema.org/draft-04/schema#", + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Schema for object with null values", + "type": "object", + "properties": { + "date": { + "type": "string", + "format": "date-time" + }, + "dateOrNull": { + "type": ["string", "null"], + "format": "date-time" + }, + "dateOrNullOrInt": { + "type": ["string", "null", "integer"], + "format": "date-time" + } + } +} diff --git a/Tests/fixtures/datetime-no-format/.jane b/Tests/fixtures/datetime-no-format/.jane new file mode 100644 index 0000000..fb28f0f --- /dev/null +++ b/Tests/fixtures/datetime-no-format/.jane @@ -0,0 +1,9 @@ + __DIR__ . '/schema.json', + 'root-class' => 'Test', + 'namespace' => 'Jane\Component\JsonSchema\Tests\Expected', + 'directory' => __DIR__ . '/generated', + 'date-input-format' => '', +]; diff --git a/Tests/fixtures/datetime-no-format/expected/Model/Test.php b/Tests/fixtures/datetime-no-format/expected/Model/Test.php new file mode 100644 index 0000000..c424d74 --- /dev/null +++ b/Tests/fixtures/datetime-no-format/expected/Model/Test.php @@ -0,0 +1,88 @@ +date; + } + /** + * + * + * @param \DateTime $date + * + * @return self + */ + public function setDate(\DateTime $date) : self + { + $this->date = $date; + return $this; + } + /** + * + * + * @return \DateTime|null + */ + public function getDateOrNull() : ?\DateTime + { + return $this->dateOrNull; + } + /** + * + * + * @param \DateTime|null $dateOrNull + * + * @return self + */ + public function setDateOrNull(?\DateTime $dateOrNull) : self + { + $this->dateOrNull = $dateOrNull; + return $this; + } + /** + * + * + * @return \DateTime|null|int + */ + public function getDateOrNullOrInt() + { + return $this->dateOrNullOrInt; + } + /** + * + * + * @param \DateTime|null|int $dateOrNullOrInt + * + * @return self + */ + public function setDateOrNullOrInt($dateOrNullOrInt) : self + { + $this->dateOrNullOrInt = $dateOrNullOrInt; + return $this; + } +} \ No newline at end of file diff --git a/Tests/fixtures/datetime-no-format/expected/Normalizer/JaneObjectNormalizer.php b/Tests/fixtures/datetime-no-format/expected/Normalizer/JaneObjectNormalizer.php new file mode 100644 index 0000000..9b98fa6 --- /dev/null +++ b/Tests/fixtures/datetime-no-format/expected/Normalizer/JaneObjectNormalizer.php @@ -0,0 +1,50 @@ + 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Normalizer\\TestNormalizer', '\\Jane\\Component\\JsonSchemaRuntime\\Reference' => '\\Jane\\Component\\JsonSchema\\Tests\\Expected\\Runtime\\Normalizer\\ReferenceNormalizer'), $normalizersCache = array(); + public function supportsDenormalization($data, $type, $format = null) + { + return array_key_exists($type, $this->normalizers); + } + public function supportsNormalization($data, $format = null) + { + return is_object($data) && array_key_exists(get_class($data), $this->normalizers); + } + public function normalize($object, $format = null, array $context = array()) + { + $normalizerClass = $this->normalizers[get_class($object)]; + $normalizer = $this->getNormalizer($normalizerClass); + return $normalizer->normalize($object, $format, $context); + } + public function denormalize($data, $class, $format = null, array $context = array()) + { + $denormalizerClass = $this->normalizers[$class]; + $denormalizer = $this->getNormalizer($denormalizerClass); + return $denormalizer->denormalize($data, $class, $format, $context); + } + private function getNormalizer(string $normalizerClass) + { + return $this->normalizersCache[$normalizerClass] ?? $this->initNormalizer($normalizerClass); + } + private function initNormalizer(string $normalizerClass) + { + $normalizer = new $normalizerClass(); + $normalizer->setNormalizer($this->normalizer); + $normalizer->setDenormalizer($this->denormalizer); + $this->normalizersCache[$normalizerClass] = $normalizer; + return $normalizer; + } +} \ No newline at end of file diff --git a/Tests/fixtures/datetime-no-format/expected/Normalizer/TestNormalizer.php b/Tests/fixtures/datetime-no-format/expected/Normalizer/TestNormalizer.php new file mode 100644 index 0000000..d92c41b --- /dev/null +++ b/Tests/fixtures/datetime-no-format/expected/Normalizer/TestNormalizer.php @@ -0,0 +1,98 @@ +setDate((new \DateTime($data['date']))->getTimezone()->getName() == 'Z' ? (new \DateTime($data['date']))->setTimezone(new \DateTimeZone('GMT')) : new \DateTime($data['date'])); + } + if (\array_key_exists('dateOrNull', $data) && $data['dateOrNull'] !== null) { + $value = $data['dateOrNull']; + if (is_string($data['dateOrNull']) and false !== ((new \DateTime($data['dateOrNull']))->getTimezone()->getName() == 'Z' ? (new \DateTime($data['dateOrNull']))->setTimezone(new \DateTimeZone('GMT')) : new \DateTime($data['dateOrNull']))) { + $value = (new \DateTime($data['dateOrNull']))->getTimezone()->getName() == 'Z' ? (new \DateTime($data['dateOrNull']))->setTimezone(new \DateTimeZone('GMT')) : new \DateTime($data['dateOrNull']); + } elseif (is_null($data['dateOrNull'])) { + $value = $data['dateOrNull']; + } + $object->setDateOrNull($value); + } + elseif (\array_key_exists('dateOrNull', $data) && $data['dateOrNull'] === null) { + $object->setDateOrNull(null); + } + if (\array_key_exists('dateOrNullOrInt', $data) && $data['dateOrNullOrInt'] !== null) { + $value_1 = $data['dateOrNullOrInt']; + if (is_string($data['dateOrNullOrInt']) and false !== ((new \DateTime($data['dateOrNullOrInt']))->getTimezone()->getName() == 'Z' ? (new \DateTime($data['dateOrNullOrInt']))->setTimezone(new \DateTimeZone('GMT')) : new \DateTime($data['dateOrNullOrInt']))) { + $value_1 = (new \DateTime($data['dateOrNullOrInt']))->getTimezone()->getName() == 'Z' ? (new \DateTime($data['dateOrNullOrInt']))->setTimezone(new \DateTimeZone('GMT')) : new \DateTime($data['dateOrNullOrInt']); + } elseif (is_null($data['dateOrNullOrInt'])) { + $value_1 = $data['dateOrNullOrInt']; + } elseif (is_int($data['dateOrNullOrInt'])) { + $value_1 = $data['dateOrNullOrInt']; + } + $object->setDateOrNullOrInt($value_1); + } + elseif (\array_key_exists('dateOrNullOrInt', $data) && $data['dateOrNullOrInt'] === null) { + $object->setDateOrNullOrInt(null); + } + return $object; + } + public function normalize($object, $format = null, array $context = array()) + { + $data = array(); + if (null !== $object->getDate()) { + $data['date'] = $object->getDate()->format('Y-m-d\\TH:i:sP'); + } + if (null !== $object->getDateOrNull()) { + $value = $object->getDateOrNull(); + if (is_object($object->getDateOrNull())) { + $value = $object->getDateOrNull()->format('Y-m-d\\TH:i:sP'); + } elseif (is_null($object->getDateOrNull())) { + $value = $object->getDateOrNull(); + } + $data['dateOrNull'] = $value; + } + if (null !== $object->getDateOrNullOrInt()) { + $value_1 = $object->getDateOrNullOrInt(); + if (is_object($object->getDateOrNullOrInt())) { + $value_1 = $object->getDateOrNullOrInt()->format('Y-m-d\\TH:i:sP'); + } elseif (is_null($object->getDateOrNullOrInt())) { + $value_1 = $object->getDateOrNullOrInt(); + } elseif (is_int($object->getDateOrNullOrInt())) { + $value_1 = $object->getDateOrNullOrInt(); + } + $data['dateOrNullOrInt'] = $value_1; + } + return $data; + } +} \ No newline at end of file diff --git a/Tests/fixtures/datetime-no-format/expected/Runtime/Normalizer/CheckArray.php b/Tests/fixtures/datetime-no-format/expected/Runtime/Normalizer/CheckArray.php new file mode 100644 index 0000000..3f60a9f --- /dev/null +++ b/Tests/fixtures/datetime-no-format/expected/Runtime/Normalizer/CheckArray.php @@ -0,0 +1,13 @@ +getReferenceUri(); + return $ref; + } + /** + * {@inheritdoc} + */ + public function supportsNormalization($data, $format = null) + { + return $data instanceof Reference; + } +} \ No newline at end of file diff --git a/Tests/fixtures/datetime-no-format/schema.json b/Tests/fixtures/datetime-no-format/schema.json new file mode 100644 index 0000000..caa7f01 --- /dev/null +++ b/Tests/fixtures/datetime-no-format/schema.json @@ -0,0 +1,20 @@ +{ + "id": "http://json-schema.org/draft-04/schema#", + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Schema for object with null values", + "type": "object", + "properties": { + "date": { + "type": "string", + "format": "date-time" + }, + "dateOrNull": { + "type": ["string", "null"], + "format": "date-time" + }, + "dateOrNullOrInt": { + "type": ["string", "null", "integer"], + "format": "date-time" + } + } +} diff --git a/Tests/fixtures/datetime/.jane b/Tests/fixtures/datetime/.jane new file mode 100644 index 0000000..bcd1207 --- /dev/null +++ b/Tests/fixtures/datetime/.jane @@ -0,0 +1,8 @@ + __DIR__ . '/schema.json', + 'root-class' => 'Test', + 'namespace' => 'Jane\Component\JsonSchema\Tests\Expected', + 'directory' => __DIR__ . '/generated', +]; diff --git a/Tests/fixtures/datetime/expected/Model/Test.php b/Tests/fixtures/datetime/expected/Model/Test.php new file mode 100644 index 0000000..c424d74 --- /dev/null +++ b/Tests/fixtures/datetime/expected/Model/Test.php @@ -0,0 +1,88 @@ +date; + } + /** + * + * + * @param \DateTime $date + * + * @return self + */ + public function setDate(\DateTime $date) : self + { + $this->date = $date; + return $this; + } + /** + * + * + * @return \DateTime|null + */ + public function getDateOrNull() : ?\DateTime + { + return $this->dateOrNull; + } + /** + * + * + * @param \DateTime|null $dateOrNull + * + * @return self + */ + public function setDateOrNull(?\DateTime $dateOrNull) : self + { + $this->dateOrNull = $dateOrNull; + return $this; + } + /** + * + * + * @return \DateTime|null|int + */ + public function getDateOrNullOrInt() + { + return $this->dateOrNullOrInt; + } + /** + * + * + * @param \DateTime|null|int $dateOrNullOrInt + * + * @return self + */ + public function setDateOrNullOrInt($dateOrNullOrInt) : self + { + $this->dateOrNullOrInt = $dateOrNullOrInt; + return $this; + } +} \ No newline at end of file diff --git a/Tests/fixtures/datetime/expected/Normalizer/JaneObjectNormalizer.php b/Tests/fixtures/datetime/expected/Normalizer/JaneObjectNormalizer.php new file mode 100644 index 0000000..9b98fa6 --- /dev/null +++ b/Tests/fixtures/datetime/expected/Normalizer/JaneObjectNormalizer.php @@ -0,0 +1,50 @@ + 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Normalizer\\TestNormalizer', '\\Jane\\Component\\JsonSchemaRuntime\\Reference' => '\\Jane\\Component\\JsonSchema\\Tests\\Expected\\Runtime\\Normalizer\\ReferenceNormalizer'), $normalizersCache = array(); + public function supportsDenormalization($data, $type, $format = null) + { + return array_key_exists($type, $this->normalizers); + } + public function supportsNormalization($data, $format = null) + { + return is_object($data) && array_key_exists(get_class($data), $this->normalizers); + } + public function normalize($object, $format = null, array $context = array()) + { + $normalizerClass = $this->normalizers[get_class($object)]; + $normalizer = $this->getNormalizer($normalizerClass); + return $normalizer->normalize($object, $format, $context); + } + public function denormalize($data, $class, $format = null, array $context = array()) + { + $denormalizerClass = $this->normalizers[$class]; + $denormalizer = $this->getNormalizer($denormalizerClass); + return $denormalizer->denormalize($data, $class, $format, $context); + } + private function getNormalizer(string $normalizerClass) + { + return $this->normalizersCache[$normalizerClass] ?? $this->initNormalizer($normalizerClass); + } + private function initNormalizer(string $normalizerClass) + { + $normalizer = new $normalizerClass(); + $normalizer->setNormalizer($this->normalizer); + $normalizer->setDenormalizer($this->denormalizer); + $this->normalizersCache[$normalizerClass] = $normalizer; + return $normalizer; + } +} \ No newline at end of file diff --git a/Tests/fixtures/datetime/expected/Normalizer/TestNormalizer.php b/Tests/fixtures/datetime/expected/Normalizer/TestNormalizer.php new file mode 100644 index 0000000..e0e0e72 --- /dev/null +++ b/Tests/fixtures/datetime/expected/Normalizer/TestNormalizer.php @@ -0,0 +1,98 @@ +setDate(\DateTime::createFromFormat('Y-m-d\\TH:i:sP', $data['date'])); + } + if (\array_key_exists('dateOrNull', $data) && $data['dateOrNull'] !== null) { + $value = $data['dateOrNull']; + if (is_string($data['dateOrNull']) and false !== \DateTime::createFromFormat('Y-m-d\\TH:i:sP', $data['dateOrNull'])) { + $value = \DateTime::createFromFormat('Y-m-d\\TH:i:sP', $data['dateOrNull']); + } elseif (is_null($data['dateOrNull'])) { + $value = $data['dateOrNull']; + } + $object->setDateOrNull($value); + } + elseif (\array_key_exists('dateOrNull', $data) && $data['dateOrNull'] === null) { + $object->setDateOrNull(null); + } + if (\array_key_exists('dateOrNullOrInt', $data) && $data['dateOrNullOrInt'] !== null) { + $value_1 = $data['dateOrNullOrInt']; + if (is_string($data['dateOrNullOrInt']) and false !== \DateTime::createFromFormat('Y-m-d\\TH:i:sP', $data['dateOrNullOrInt'])) { + $value_1 = \DateTime::createFromFormat('Y-m-d\\TH:i:sP', $data['dateOrNullOrInt']); + } elseif (is_null($data['dateOrNullOrInt'])) { + $value_1 = $data['dateOrNullOrInt']; + } elseif (is_int($data['dateOrNullOrInt'])) { + $value_1 = $data['dateOrNullOrInt']; + } + $object->setDateOrNullOrInt($value_1); + } + elseif (\array_key_exists('dateOrNullOrInt', $data) && $data['dateOrNullOrInt'] === null) { + $object->setDateOrNullOrInt(null); + } + return $object; + } + public function normalize($object, $format = null, array $context = array()) + { + $data = array(); + if (null !== $object->getDate()) { + $data['date'] = $object->getDate()->format('Y-m-d\\TH:i:sP'); + } + if (null !== $object->getDateOrNull()) { + $value = $object->getDateOrNull(); + if (is_object($object->getDateOrNull())) { + $value = $object->getDateOrNull()->format('Y-m-d\\TH:i:sP'); + } elseif (is_null($object->getDateOrNull())) { + $value = $object->getDateOrNull(); + } + $data['dateOrNull'] = $value; + } + if (null !== $object->getDateOrNullOrInt()) { + $value_1 = $object->getDateOrNullOrInt(); + if (is_object($object->getDateOrNullOrInt())) { + $value_1 = $object->getDateOrNullOrInt()->format('Y-m-d\\TH:i:sP'); + } elseif (is_null($object->getDateOrNullOrInt())) { + $value_1 = $object->getDateOrNullOrInt(); + } elseif (is_int($object->getDateOrNullOrInt())) { + $value_1 = $object->getDateOrNullOrInt(); + } + $data['dateOrNullOrInt'] = $value_1; + } + return $data; + } +} \ No newline at end of file diff --git a/Tests/fixtures/datetime/expected/Runtime/Normalizer/CheckArray.php b/Tests/fixtures/datetime/expected/Runtime/Normalizer/CheckArray.php new file mode 100644 index 0000000..3f60a9f --- /dev/null +++ b/Tests/fixtures/datetime/expected/Runtime/Normalizer/CheckArray.php @@ -0,0 +1,13 @@ +getReferenceUri(); + return $ref; + } + /** + * {@inheritdoc} + */ + public function supportsNormalization($data, $format = null) + { + return $data instanceof Reference; + } +} \ No newline at end of file diff --git a/Tests/fixtures/datetime/schema.json b/Tests/fixtures/datetime/schema.json new file mode 100644 index 0000000..caa7f01 --- /dev/null +++ b/Tests/fixtures/datetime/schema.json @@ -0,0 +1,20 @@ +{ + "id": "http://json-schema.org/draft-04/schema#", + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Schema for object with null values", + "type": "object", + "properties": { + "date": { + "type": "string", + "format": "date-time" + }, + "dateOrNull": { + "type": ["string", "null"], + "format": "date-time" + }, + "dateOrNullOrInt": { + "type": ["string", "null", "integer"], + "format": "date-time" + } + } +} diff --git a/Tests/fixtures/deep-object/.jane b/Tests/fixtures/deep-object/.jane new file mode 100644 index 0000000..bcd1207 --- /dev/null +++ b/Tests/fixtures/deep-object/.jane @@ -0,0 +1,8 @@ + __DIR__ . '/schema.json', + 'root-class' => 'Test', + 'namespace' => 'Jane\Component\JsonSchema\Tests\Expected', + 'directory' => __DIR__ . '/generated', +]; diff --git a/Tests/fixtures/deep-object/expected/Model/Test.php b/Tests/fixtures/deep-object/expected/Model/Test.php new file mode 100644 index 0000000..b6af351 --- /dev/null +++ b/Tests/fixtures/deep-object/expected/Model/Test.php @@ -0,0 +1,34 @@ +foo; + } + /** + * + * + * @param TestFooItem[] $foo + * + * @return self + */ + public function setFoo(array $foo) : self + { + $this->foo = $foo; + return $this; + } +} \ No newline at end of file diff --git a/Tests/fixtures/deep-object/expected/Model/TestFooItem.php b/Tests/fixtures/deep-object/expected/Model/TestFooItem.php new file mode 100644 index 0000000..bbea3dc --- /dev/null +++ b/Tests/fixtures/deep-object/expected/Model/TestFooItem.php @@ -0,0 +1,34 @@ +bar; + } + /** + * + * + * @param string $bar + * + * @return self + */ + public function setBar(string $bar) : self + { + $this->bar = $bar; + return $this; + } +} \ No newline at end of file diff --git a/Tests/fixtures/deep-object/expected/Normalizer/JaneObjectNormalizer.php b/Tests/fixtures/deep-object/expected/Normalizer/JaneObjectNormalizer.php new file mode 100644 index 0000000..0dbd4cc --- /dev/null +++ b/Tests/fixtures/deep-object/expected/Normalizer/JaneObjectNormalizer.php @@ -0,0 +1,50 @@ + 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Normalizer\\TestNormalizer', 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Model\\TestFooItem' => 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Normalizer\\TestFooItemNormalizer', '\\Jane\\Component\\JsonSchemaRuntime\\Reference' => '\\Jane\\Component\\JsonSchema\\Tests\\Expected\\Runtime\\Normalizer\\ReferenceNormalizer'), $normalizersCache = array(); + public function supportsDenormalization($data, $type, $format = null) + { + return array_key_exists($type, $this->normalizers); + } + public function supportsNormalization($data, $format = null) + { + return is_object($data) && array_key_exists(get_class($data), $this->normalizers); + } + public function normalize($object, $format = null, array $context = array()) + { + $normalizerClass = $this->normalizers[get_class($object)]; + $normalizer = $this->getNormalizer($normalizerClass); + return $normalizer->normalize($object, $format, $context); + } + public function denormalize($data, $class, $format = null, array $context = array()) + { + $denormalizerClass = $this->normalizers[$class]; + $denormalizer = $this->getNormalizer($denormalizerClass); + return $denormalizer->denormalize($data, $class, $format, $context); + } + private function getNormalizer(string $normalizerClass) + { + return $this->normalizersCache[$normalizerClass] ?? $this->initNormalizer($normalizerClass); + } + private function initNormalizer(string $normalizerClass) + { + $normalizer = new $normalizerClass(); + $normalizer->setNormalizer($this->normalizer); + $normalizer->setDenormalizer($this->denormalizer); + $this->normalizersCache[$normalizerClass] = $normalizer; + return $normalizer; + } +} \ No newline at end of file diff --git a/Tests/fixtures/deep-object/expected/Normalizer/TestFooItemNormalizer.php b/Tests/fixtures/deep-object/expected/Normalizer/TestFooItemNormalizer.php new file mode 100644 index 0000000..9ee1150 --- /dev/null +++ b/Tests/fixtures/deep-object/expected/Normalizer/TestFooItemNormalizer.php @@ -0,0 +1,52 @@ +setBar($data['bar']); + } + return $object; + } + public function normalize($object, $format = null, array $context = array()) + { + $data = array(); + if (null !== $object->getBar()) { + $data['bar'] = $object->getBar(); + } + return $data; + } +} \ No newline at end of file diff --git a/Tests/fixtures/deep-object/expected/Normalizer/TestNormalizer.php b/Tests/fixtures/deep-object/expected/Normalizer/TestNormalizer.php new file mode 100644 index 0000000..6ba5ba1 --- /dev/null +++ b/Tests/fixtures/deep-object/expected/Normalizer/TestNormalizer.php @@ -0,0 +1,60 @@ +denormalizer->denormalize($value, 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Model\\TestFooItem', 'json', $context); + } + $object->setFoo($values); + } + return $object; + } + public function normalize($object, $format = null, array $context = array()) + { + $data = array(); + if (null !== $object->getFoo()) { + $values = array(); + foreach ($object->getFoo() as $value) { + $values[] = $this->normalizer->normalize($value, 'json', $context); + } + $data['foo'] = $values; + } + return $data; + } +} \ No newline at end of file diff --git a/Tests/fixtures/deep-object/expected/Runtime/Normalizer/CheckArray.php b/Tests/fixtures/deep-object/expected/Runtime/Normalizer/CheckArray.php new file mode 100644 index 0000000..3f60a9f --- /dev/null +++ b/Tests/fixtures/deep-object/expected/Runtime/Normalizer/CheckArray.php @@ -0,0 +1,13 @@ +getReferenceUri(); + return $ref; + } + /** + * {@inheritdoc} + */ + public function supportsNormalization($data, $format = null) + { + return $data instanceof Reference; + } +} \ No newline at end of file diff --git a/Tests/fixtures/deep-object/schema.json b/Tests/fixtures/deep-object/schema.json new file mode 100644 index 0000000..b89859f --- /dev/null +++ b/Tests/fixtures/deep-object/schema.json @@ -0,0 +1,16 @@ +{ + "type": "object", + "properties": { + "foo": { + "type": "array", + "items": { + "type": "object", + "properties": { + "bar": { + "type": "string" + } + } + } + } + } +} diff --git a/Tests/fixtures/definitions/.jane b/Tests/fixtures/definitions/.jane new file mode 100644 index 0000000..bcd1207 --- /dev/null +++ b/Tests/fixtures/definitions/.jane @@ -0,0 +1,8 @@ + __DIR__ . '/schema.json', + 'root-class' => 'Test', + 'namespace' => 'Jane\Component\JsonSchema\Tests\Expected', + 'directory' => __DIR__ . '/generated', +]; diff --git a/Tests/fixtures/definitions/expected/Model/BarItem.php b/Tests/fixtures/definitions/expected/Model/BarItem.php new file mode 100644 index 0000000..04651a4 --- /dev/null +++ b/Tests/fixtures/definitions/expected/Model/BarItem.php @@ -0,0 +1,34 @@ +bar; + } + /** + * + * + * @param string $bar + * + * @return self + */ + public function setBar(string $bar) : self + { + $this->bar = $bar; + return $this; + } +} \ No newline at end of file diff --git a/Tests/fixtures/definitions/expected/Model/Foo.php b/Tests/fixtures/definitions/expected/Model/Foo.php new file mode 100644 index 0000000..f8de199 --- /dev/null +++ b/Tests/fixtures/definitions/expected/Model/Foo.php @@ -0,0 +1,34 @@ +foo; + } + /** + * + * + * @param string $foo + * + * @return self + */ + public function setFoo(string $foo) : self + { + $this->foo = $foo; + return $this; + } +} \ No newline at end of file diff --git a/Tests/fixtures/definitions/expected/Model/HelloWorld.php b/Tests/fixtures/definitions/expected/Model/HelloWorld.php new file mode 100644 index 0000000..cdc525a --- /dev/null +++ b/Tests/fixtures/definitions/expected/Model/HelloWorld.php @@ -0,0 +1,34 @@ +foo; + } + /** + * + * + * @param string $foo + * + * @return self + */ + public function setFoo(string $foo) : self + { + $this->foo = $foo; + return $this; + } +} \ No newline at end of file diff --git a/Tests/fixtures/definitions/expected/Normalizer/BarItemNormalizer.php b/Tests/fixtures/definitions/expected/Normalizer/BarItemNormalizer.php new file mode 100644 index 0000000..20900ed --- /dev/null +++ b/Tests/fixtures/definitions/expected/Normalizer/BarItemNormalizer.php @@ -0,0 +1,52 @@ +setBar($data['bar']); + } + return $object; + } + public function normalize($object, $format = null, array $context = array()) + { + $data = array(); + if (null !== $object->getBar()) { + $data['bar'] = $object->getBar(); + } + return $data; + } +} \ No newline at end of file diff --git a/Tests/fixtures/definitions/expected/Normalizer/FooNormalizer.php b/Tests/fixtures/definitions/expected/Normalizer/FooNormalizer.php new file mode 100644 index 0000000..1590e2d --- /dev/null +++ b/Tests/fixtures/definitions/expected/Normalizer/FooNormalizer.php @@ -0,0 +1,52 @@ +setFoo($data['foo']); + } + return $object; + } + public function normalize($object, $format = null, array $context = array()) + { + $data = array(); + if (null !== $object->getFoo()) { + $data['foo'] = $object->getFoo(); + } + return $data; + } +} \ No newline at end of file diff --git a/Tests/fixtures/definitions/expected/Normalizer/HelloWorldNormalizer.php b/Tests/fixtures/definitions/expected/Normalizer/HelloWorldNormalizer.php new file mode 100644 index 0000000..14d04c6 --- /dev/null +++ b/Tests/fixtures/definitions/expected/Normalizer/HelloWorldNormalizer.php @@ -0,0 +1,52 @@ +setFoo($data['foo']); + } + return $object; + } + public function normalize($object, $format = null, array $context = array()) + { + $data = array(); + if (null !== $object->getFoo()) { + $data['foo'] = $object->getFoo(); + } + return $data; + } +} \ No newline at end of file diff --git a/Tests/fixtures/definitions/expected/Normalizer/JaneObjectNormalizer.php b/Tests/fixtures/definitions/expected/Normalizer/JaneObjectNormalizer.php new file mode 100644 index 0000000..e3a3087 --- /dev/null +++ b/Tests/fixtures/definitions/expected/Normalizer/JaneObjectNormalizer.php @@ -0,0 +1,50 @@ + 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Normalizer\\FooNormalizer', 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Model\\BarItem' => 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Normalizer\\BarItemNormalizer', 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Model\\HelloWorld' => 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Normalizer\\HelloWorldNormalizer', '\\Jane\\Component\\JsonSchemaRuntime\\Reference' => '\\Jane\\Component\\JsonSchema\\Tests\\Expected\\Runtime\\Normalizer\\ReferenceNormalizer'), $normalizersCache = array(); + public function supportsDenormalization($data, $type, $format = null) + { + return array_key_exists($type, $this->normalizers); + } + public function supportsNormalization($data, $format = null) + { + return is_object($data) && array_key_exists(get_class($data), $this->normalizers); + } + public function normalize($object, $format = null, array $context = array()) + { + $normalizerClass = $this->normalizers[get_class($object)]; + $normalizer = $this->getNormalizer($normalizerClass); + return $normalizer->normalize($object, $format, $context); + } + public function denormalize($data, $class, $format = null, array $context = array()) + { + $denormalizerClass = $this->normalizers[$class]; + $denormalizer = $this->getNormalizer($denormalizerClass); + return $denormalizer->denormalize($data, $class, $format, $context); + } + private function getNormalizer(string $normalizerClass) + { + return $this->normalizersCache[$normalizerClass] ?? $this->initNormalizer($normalizerClass); + } + private function initNormalizer(string $normalizerClass) + { + $normalizer = new $normalizerClass(); + $normalizer->setNormalizer($this->normalizer); + $normalizer->setDenormalizer($this->denormalizer); + $this->normalizersCache[$normalizerClass] = $normalizer; + return $normalizer; + } +} \ No newline at end of file diff --git a/Tests/fixtures/definitions/expected/Runtime/Normalizer/CheckArray.php b/Tests/fixtures/definitions/expected/Runtime/Normalizer/CheckArray.php new file mode 100644 index 0000000..3f60a9f --- /dev/null +++ b/Tests/fixtures/definitions/expected/Runtime/Normalizer/CheckArray.php @@ -0,0 +1,13 @@ +getReferenceUri(); + return $ref; + } + /** + * {@inheritdoc} + */ + public function supportsNormalization($data, $format = null) + { + return $data instanceof Reference; + } +} \ No newline at end of file diff --git a/Tests/fixtures/definitions/schema.json b/Tests/fixtures/definitions/schema.json new file mode 100644 index 0000000..f9e647d --- /dev/null +++ b/Tests/fixtures/definitions/schema.json @@ -0,0 +1,34 @@ +{ + "id": "http://json-schema.org/draft-04/schema#", + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Schema with definitions", + "definitions": { + "Foo": { + "type": "object", + "properties": { + "foo": { + "type": "string" + } + } + }, + "Bar": { + "type": "array", + "items": { + "type": "object", + "properties": { + "bar": { + "type": "string" + } + } + } + }, + "hello.world": { + "type": "object", + "properties": { + "foo": { + "type": "string" + } + } + } + } +} diff --git a/Tests/fixtures/deprecated/.jane b/Tests/fixtures/deprecated/.jane new file mode 100644 index 0000000..bcd1207 --- /dev/null +++ b/Tests/fixtures/deprecated/.jane @@ -0,0 +1,8 @@ + __DIR__ . '/schema.json', + 'root-class' => 'Test', + 'namespace' => 'Jane\Component\JsonSchema\Tests\Expected', + 'directory' => __DIR__ . '/generated', +]; diff --git a/Tests/fixtures/deprecated/expected/Model/Foo.php b/Tests/fixtures/deprecated/expected/Model/Foo.php new file mode 100644 index 0000000..a55605a --- /dev/null +++ b/Tests/fixtures/deprecated/expected/Model/Foo.php @@ -0,0 +1,71 @@ +email; + } + /** + * + * + * @param string $email + * + * @return self + */ + public function setEmail(string $email) : self + { + $this->email = $email; + return $this; + } + /** + * + * + * @deprecated + * + * @return string + */ + public function getFoo() : string + { + return $this->foo; + } + /** + * + * + * @param string $foo + * + * @deprecated + * + * @return self + */ + public function setFoo(string $foo) : self + { + $this->foo = $foo; + return $this; + } +} \ No newline at end of file diff --git a/Tests/fixtures/deprecated/expected/Normalizer/FooNormalizer.php b/Tests/fixtures/deprecated/expected/Normalizer/FooNormalizer.php new file mode 100644 index 0000000..f396d99 --- /dev/null +++ b/Tests/fixtures/deprecated/expected/Normalizer/FooNormalizer.php @@ -0,0 +1,58 @@ +setEmail($data['email']); + } + if (\array_key_exists('foo', $data)) { + $object->setFoo($data['foo']); + } + return $object; + } + public function normalize($object, $format = null, array $context = array()) + { + $data = array(); + if (null !== $object->getEmail()) { + $data['email'] = $object->getEmail(); + } + if (null !== $object->getFoo()) { + $data['foo'] = $object->getFoo(); + } + return $data; + } +} \ No newline at end of file diff --git a/Tests/fixtures/deprecated/expected/Normalizer/JaneObjectNormalizer.php b/Tests/fixtures/deprecated/expected/Normalizer/JaneObjectNormalizer.php new file mode 100644 index 0000000..026073d --- /dev/null +++ b/Tests/fixtures/deprecated/expected/Normalizer/JaneObjectNormalizer.php @@ -0,0 +1,50 @@ + 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Normalizer\\FooNormalizer', '\\Jane\\Component\\JsonSchemaRuntime\\Reference' => '\\Jane\\Component\\JsonSchema\\Tests\\Expected\\Runtime\\Normalizer\\ReferenceNormalizer'), $normalizersCache = array(); + public function supportsDenormalization($data, $type, $format = null) + { + return array_key_exists($type, $this->normalizers); + } + public function supportsNormalization($data, $format = null) + { + return is_object($data) && array_key_exists(get_class($data), $this->normalizers); + } + public function normalize($object, $format = null, array $context = array()) + { + $normalizerClass = $this->normalizers[get_class($object)]; + $normalizer = $this->getNormalizer($normalizerClass); + return $normalizer->normalize($object, $format, $context); + } + public function denormalize($data, $class, $format = null, array $context = array()) + { + $denormalizerClass = $this->normalizers[$class]; + $denormalizer = $this->getNormalizer($denormalizerClass); + return $denormalizer->denormalize($data, $class, $format, $context); + } + private function getNormalizer(string $normalizerClass) + { + return $this->normalizersCache[$normalizerClass] ?? $this->initNormalizer($normalizerClass); + } + private function initNormalizer(string $normalizerClass) + { + $normalizer = new $normalizerClass(); + $normalizer->setNormalizer($this->normalizer); + $normalizer->setDenormalizer($this->denormalizer); + $this->normalizersCache[$normalizerClass] = $normalizer; + return $normalizer; + } +} \ No newline at end of file diff --git a/Tests/fixtures/deprecated/expected/Runtime/Normalizer/CheckArray.php b/Tests/fixtures/deprecated/expected/Runtime/Normalizer/CheckArray.php new file mode 100644 index 0000000..3f60a9f --- /dev/null +++ b/Tests/fixtures/deprecated/expected/Runtime/Normalizer/CheckArray.php @@ -0,0 +1,13 @@ +getReferenceUri(); + return $ref; + } + /** + * {@inheritdoc} + */ + public function supportsNormalization($data, $format = null) + { + return $data instanceof Reference; + } +} \ No newline at end of file diff --git a/Tests/fixtures/deprecated/schema.json b/Tests/fixtures/deprecated/schema.json new file mode 100644 index 0000000..39a060a --- /dev/null +++ b/Tests/fixtures/deprecated/schema.json @@ -0,0 +1,18 @@ +{ + "type": "object", + "definitions": { + "Foo": { + "type": "object", + "deprecated": true, + "properties": { + "email": { + "type": "string" + }, + "foo": { + "type": "string", + "deprecated": true + } + } + } + } +} diff --git a/Tests/fixtures/disabled-strict-required/.jane b/Tests/fixtures/disabled-strict-required/.jane new file mode 100644 index 0000000..6701250 --- /dev/null +++ b/Tests/fixtures/disabled-strict-required/.jane @@ -0,0 +1,10 @@ + __DIR__ . '/schema.json', + 'root-class' => 'Nullable', + 'namespace' => 'Jane\Component\JsonSchema\Tests\Expected', + 'directory' => __DIR__ . '/generated', + 'strict' => false, + 'skip-required-fields' => true, +]; diff --git a/Tests/fixtures/disabled-strict-required/expected/Model/Nullable.php b/Tests/fixtures/disabled-strict-required/expected/Model/Nullable.php new file mode 100644 index 0000000..bb28015 --- /dev/null +++ b/Tests/fixtures/disabled-strict-required/expected/Model/Nullable.php @@ -0,0 +1,115 @@ +onlyNull; + } + /** + * + * + * @param null $onlyNull + * + * @return self + */ + public function setOnlyNull($onlyNull) : self + { + $this->onlyNull = $onlyNull; + return $this; + } + /** + * + * + * @return string|null + */ + public function getNullOrString() : ?string + { + return $this->nullOrString; + } + /** + * + * + * @param string|null $nullOrString + * + * @return self + */ + public function setNullOrString(?string $nullOrString) : self + { + $this->nullOrString = $nullOrString; + return $this; + } + /** + * + * + * @return string|null + */ + public function getRequired() : ?string + { + return $this->required; + } + /** + * + * + * @param string|null $required + * + * @return self + */ + public function setRequired(?string $required) : self + { + $this->required = $required; + return $this; + } + /** + * + * + * @return string|null + */ + public function getRequiredNull() : ?string + { + return $this->requiredNull; + } + /** + * + * + * @param string|null $requiredNull + * + * @return self + */ + public function setRequiredNull(?string $requiredNull) : self + { + $this->requiredNull = $requiredNull; + return $this; + } +} \ No newline at end of file diff --git a/Tests/fixtures/disabled-strict-required/expected/Normalizer/JaneObjectNormalizer.php b/Tests/fixtures/disabled-strict-required/expected/Normalizer/JaneObjectNormalizer.php new file mode 100644 index 0000000..7e26b36 --- /dev/null +++ b/Tests/fixtures/disabled-strict-required/expected/Normalizer/JaneObjectNormalizer.php @@ -0,0 +1,50 @@ + 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Normalizer\\NullableNormalizer', '\\Jane\\Component\\JsonSchemaRuntime\\Reference' => '\\Jane\\Component\\JsonSchema\\Tests\\Expected\\Runtime\\Normalizer\\ReferenceNormalizer'), $normalizersCache = array(); + public function supportsDenormalization($data, $type, $format = null) + { + return array_key_exists($type, $this->normalizers); + } + public function supportsNormalization($data, $format = null) + { + return is_object($data) && array_key_exists(get_class($data), $this->normalizers); + } + public function normalize($object, $format = null, array $context = array()) + { + $normalizerClass = $this->normalizers[get_class($object)]; + $normalizer = $this->getNormalizer($normalizerClass); + return $normalizer->normalize($object, $format, $context); + } + public function denormalize($data, $class, $format = null, array $context = array()) + { + $denormalizerClass = $this->normalizers[$class]; + $denormalizer = $this->getNormalizer($denormalizerClass); + return $denormalizer->denormalize($data, $class, $format, $context); + } + private function getNormalizer(string $normalizerClass) + { + return $this->normalizersCache[$normalizerClass] ?? $this->initNormalizer($normalizerClass); + } + private function initNormalizer(string $normalizerClass) + { + $normalizer = new $normalizerClass(); + $normalizer->setNormalizer($this->normalizer); + $normalizer->setDenormalizer($this->denormalizer); + $this->normalizersCache[$normalizerClass] = $normalizer; + return $normalizer; + } +} \ No newline at end of file diff --git a/Tests/fixtures/disabled-strict-required/expected/Normalizer/NullableNormalizer.php b/Tests/fixtures/disabled-strict-required/expected/Normalizer/NullableNormalizer.php new file mode 100644 index 0000000..7a3a8f1 --- /dev/null +++ b/Tests/fixtures/disabled-strict-required/expected/Normalizer/NullableNormalizer.php @@ -0,0 +1,106 @@ +setOnlyNull($data['onlyNull']); + } + elseif (\array_key_exists('onlyNull', $data) && $data['onlyNull'] === null) { + $object->setOnlyNull(null); + } + if (\array_key_exists('nullOrString', $data) && $data['nullOrString'] !== null) { + $value = $data['nullOrString']; + if (is_string($data['nullOrString'])) { + $value = $data['nullOrString']; + } elseif (is_null($data['nullOrString'])) { + $value = $data['nullOrString']; + } + $object->setNullOrString($value); + } + elseif (\array_key_exists('nullOrString', $data) && $data['nullOrString'] === null) { + $object->setNullOrString(null); + } + if (\array_key_exists('required', $data) && $data['required'] !== null) { + $object->setRequired($data['required']); + } + elseif (\array_key_exists('required', $data) && $data['required'] === null) { + $object->setRequired(null); + } + if (\array_key_exists('requiredNull', $data) && $data['requiredNull'] !== null) { + $value_1 = $data['requiredNull']; + if (is_string($data['requiredNull'])) { + $value_1 = $data['requiredNull']; + } elseif (is_null($data['requiredNull'])) { + $value_1 = $data['requiredNull']; + } + $object->setRequiredNull($value_1); + } + elseif (\array_key_exists('requiredNull', $data) && $data['requiredNull'] === null) { + $object->setRequiredNull(null); + } + return $object; + } + public function normalize($object, $format = null, array $context = array()) + { + $data = array(); + if (null !== $object->getOnlyNull()) { + $data['onlyNull'] = $object->getOnlyNull(); + } + if (null !== $object->getNullOrString()) { + $value = $object->getNullOrString(); + if (is_string($object->getNullOrString())) { + $value = $object->getNullOrString(); + } elseif (is_null($object->getNullOrString())) { + $value = $object->getNullOrString(); + } + $data['nullOrString'] = $value; + } + if (null !== $object->getRequired()) { + $data['required'] = $object->getRequired(); + } + if (null !== $object->getRequiredNull()) { + $value_1 = $object->getRequiredNull(); + if (is_string($object->getRequiredNull())) { + $value_1 = $object->getRequiredNull(); + } elseif (is_null($object->getRequiredNull())) { + $value_1 = $object->getRequiredNull(); + } + $data['requiredNull'] = $value_1; + } + return $data; + } +} \ No newline at end of file diff --git a/Tests/fixtures/disabled-strict-required/expected/Runtime/Normalizer/CheckArray.php b/Tests/fixtures/disabled-strict-required/expected/Runtime/Normalizer/CheckArray.php new file mode 100644 index 0000000..3f60a9f --- /dev/null +++ b/Tests/fixtures/disabled-strict-required/expected/Runtime/Normalizer/CheckArray.php @@ -0,0 +1,13 @@ +getReferenceUri(); + return $ref; + } + /** + * {@inheritdoc} + */ + public function supportsNormalization($data, $format = null) + { + return $data instanceof Reference; + } +} \ No newline at end of file diff --git a/Tests/fixtures/disabled-strict-required/schema.json b/Tests/fixtures/disabled-strict-required/schema.json new file mode 100644 index 0000000..78c80e9 --- /dev/null +++ b/Tests/fixtures/disabled-strict-required/schema.json @@ -0,0 +1,24 @@ +{ + "id": "http://json-schema.org/draft-04/schema#", + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Schema for object with null values", + "type": "object", + "properties": { + "onlyNull": { + "type": "null" + }, + "nullOrString": { + "type": ["string", "null"] + }, + "required": { + "type": "string" + }, + "requiredNull": { + "type": ["string", "null"] + } + }, + "required": [ + "required", + "requiredNull" + ] +} diff --git a/Tests/fixtures/multi-files/.jane b/Tests/fixtures/multi-files/.jane new file mode 100644 index 0000000..9907db2 --- /dev/null +++ b/Tests/fixtures/multi-files/.jane @@ -0,0 +1,9 @@ + __DIR__ . '/schema.json', + 'root-class' => 'Test', + 'namespace' => 'Jane\Component\JsonSchema\Tests\Expected', + 'directory' => __DIR__ . '/generated', + 'reference' => false, +]; diff --git a/Tests/fixtures/multi-files/definitions.json b/Tests/fixtures/multi-files/definitions.json new file mode 100644 index 0000000..c752f62 --- /dev/null +++ b/Tests/fixtures/multi-files/definitions.json @@ -0,0 +1,12 @@ +{ + "definitions": { + "Foo": { + "type": "object", + "properties": { + "foo": { + "type": "string" + } + } + } + } +} diff --git a/Tests/fixtures/multi-files/expected/Model/Test.php b/Tests/fixtures/multi-files/expected/Model/Test.php new file mode 100644 index 0000000..8b91d96 --- /dev/null +++ b/Tests/fixtures/multi-files/expected/Model/Test.php @@ -0,0 +1,34 @@ +foo; + } + /** + * + * + * @param TestFoo $foo + * + * @return self + */ + public function setFoo(TestFoo $foo) : self + { + $this->foo = $foo; + return $this; + } +} \ No newline at end of file diff --git a/Tests/fixtures/multi-files/expected/Model/TestFoo.php b/Tests/fixtures/multi-files/expected/Model/TestFoo.php new file mode 100644 index 0000000..ddc8528 --- /dev/null +++ b/Tests/fixtures/multi-files/expected/Model/TestFoo.php @@ -0,0 +1,34 @@ +foo; + } + /** + * + * + * @param string $foo + * + * @return self + */ + public function setFoo(string $foo) : self + { + $this->foo = $foo; + return $this; + } +} \ No newline at end of file diff --git a/Tests/fixtures/multi-files/expected/Normalizer/JaneObjectNormalizer.php b/Tests/fixtures/multi-files/expected/Normalizer/JaneObjectNormalizer.php new file mode 100644 index 0000000..6e67b2a --- /dev/null +++ b/Tests/fixtures/multi-files/expected/Normalizer/JaneObjectNormalizer.php @@ -0,0 +1,50 @@ + 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Normalizer\\TestNormalizer', 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Model\\TestFoo' => 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Normalizer\\TestFooNormalizer'), $normalizersCache = array(); + public function supportsDenormalization($data, $type, $format = null) + { + return array_key_exists($type, $this->normalizers); + } + public function supportsNormalization($data, $format = null) + { + return is_object($data) && array_key_exists(get_class($data), $this->normalizers); + } + public function normalize($object, $format = null, array $context = array()) + { + $normalizerClass = $this->normalizers[get_class($object)]; + $normalizer = $this->getNormalizer($normalizerClass); + return $normalizer->normalize($object, $format, $context); + } + public function denormalize($data, $class, $format = null, array $context = array()) + { + $denormalizerClass = $this->normalizers[$class]; + $denormalizer = $this->getNormalizer($denormalizerClass); + return $denormalizer->denormalize($data, $class, $format, $context); + } + private function getNormalizer(string $normalizerClass) + { + return $this->normalizersCache[$normalizerClass] ?? $this->initNormalizer($normalizerClass); + } + private function initNormalizer(string $normalizerClass) + { + $normalizer = new $normalizerClass(); + $normalizer->setNormalizer($this->normalizer); + $normalizer->setDenormalizer($this->denormalizer); + $this->normalizersCache[$normalizerClass] = $normalizer; + return $normalizer; + } +} \ No newline at end of file diff --git a/Tests/fixtures/multi-files/expected/Normalizer/TestFooNormalizer.php b/Tests/fixtures/multi-files/expected/Normalizer/TestFooNormalizer.php new file mode 100644 index 0000000..992a578 --- /dev/null +++ b/Tests/fixtures/multi-files/expected/Normalizer/TestFooNormalizer.php @@ -0,0 +1,46 @@ +setFoo($data['foo']); + } + return $object; + } + public function normalize($object, $format = null, array $context = array()) + { + $data = array(); + if (null !== $object->getFoo()) { + $data['foo'] = $object->getFoo(); + } + return $data; + } +} \ No newline at end of file diff --git a/Tests/fixtures/multi-files/expected/Normalizer/TestNormalizer.php b/Tests/fixtures/multi-files/expected/Normalizer/TestNormalizer.php new file mode 100644 index 0000000..5f79323 --- /dev/null +++ b/Tests/fixtures/multi-files/expected/Normalizer/TestNormalizer.php @@ -0,0 +1,46 @@ +setFoo($this->denormalizer->denormalize($data['foo'], 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Model\\TestFoo', 'json', $context)); + } + return $object; + } + public function normalize($object, $format = null, array $context = array()) + { + $data = array(); + if (null !== $object->getFoo()) { + $data['foo'] = $this->normalizer->normalize($object->getFoo(), 'json', $context); + } + return $data; + } +} \ No newline at end of file diff --git a/Tests/fixtures/multi-files/expected/Runtime/Normalizer/CheckArray.php b/Tests/fixtures/multi-files/expected/Runtime/Normalizer/CheckArray.php new file mode 100644 index 0000000..3f60a9f --- /dev/null +++ b/Tests/fixtures/multi-files/expected/Runtime/Normalizer/CheckArray.php @@ -0,0 +1,13 @@ +getReferenceUri(); + return $ref; + } + /** + * {@inheritdoc} + */ + public function supportsNormalization($data, $format = null) + { + return $data instanceof Reference; + } +} \ No newline at end of file diff --git a/Tests/fixtures/multi-files/schema.json b/Tests/fixtures/multi-files/schema.json new file mode 100644 index 0000000..96a9a59 --- /dev/null +++ b/Tests/fixtures/multi-files/schema.json @@ -0,0 +1,8 @@ +{ + "type": "object", + "properties": { + "foo": { + "$ref": "definitions.json#/definitions/Foo" + } + } +} diff --git a/Tests/fixtures/multi-namespace/.jane b/Tests/fixtures/multi-namespace/.jane new file mode 100644 index 0000000..46cdcc0 --- /dev/null +++ b/Tests/fixtures/multi-namespace/.jane @@ -0,0 +1,18 @@ + [ + // Order of schema matters for naming + __DIR__ . '/schema2.json' => [ + 'root-class' => 'Test', + 'namespace' => 'Jane\Component\JsonSchema\Tests\Expected\Schema2', + 'directory' => __DIR__ . '/generated/Schema2', + ], + __DIR__ . '/schema1.json' => [ + 'root-class' => 'Test', + 'namespace' => 'Jane\Component\JsonSchema\Tests\Expected\Schema1', + 'directory' => __DIR__ . '/generated/Schema1', + ], + ], + 'reference' => false, +]; diff --git a/Tests/fixtures/multi-namespace/expected/Schema1/Model/Test.php b/Tests/fixtures/multi-namespace/expected/Schema1/Model/Test.php new file mode 100644 index 0000000..d990c63 --- /dev/null +++ b/Tests/fixtures/multi-namespace/expected/Schema1/Model/Test.php @@ -0,0 +1,34 @@ +foo; + } + /** + * + * + * @param \Jane\Component\JsonSchema\Tests\Expected\Schema2\Model\Foo $foo + * + * @return self + */ + public function setFoo(\Jane\Component\JsonSchema\Tests\Expected\Schema2\Model\Foo $foo) : self + { + $this->foo = $foo; + return $this; + } +} \ No newline at end of file diff --git a/Tests/fixtures/multi-namespace/expected/Schema1/Normalizer/JaneObjectNormalizer.php b/Tests/fixtures/multi-namespace/expected/Schema1/Normalizer/JaneObjectNormalizer.php new file mode 100644 index 0000000..50dfae6 --- /dev/null +++ b/Tests/fixtures/multi-namespace/expected/Schema1/Normalizer/JaneObjectNormalizer.php @@ -0,0 +1,50 @@ + 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Schema1\\Normalizer\\TestNormalizer'), $normalizersCache = array(); + public function supportsDenormalization($data, $type, $format = null) + { + return array_key_exists($type, $this->normalizers); + } + public function supportsNormalization($data, $format = null) + { + return is_object($data) && array_key_exists(get_class($data), $this->normalizers); + } + public function normalize($object, $format = null, array $context = array()) + { + $normalizerClass = $this->normalizers[get_class($object)]; + $normalizer = $this->getNormalizer($normalizerClass); + return $normalizer->normalize($object, $format, $context); + } + public function denormalize($data, $class, $format = null, array $context = array()) + { + $denormalizerClass = $this->normalizers[$class]; + $denormalizer = $this->getNormalizer($denormalizerClass); + return $denormalizer->denormalize($data, $class, $format, $context); + } + private function getNormalizer(string $normalizerClass) + { + return $this->normalizersCache[$normalizerClass] ?? $this->initNormalizer($normalizerClass); + } + private function initNormalizer(string $normalizerClass) + { + $normalizer = new $normalizerClass(); + $normalizer->setNormalizer($this->normalizer); + $normalizer->setDenormalizer($this->denormalizer); + $this->normalizersCache[$normalizerClass] = $normalizer; + return $normalizer; + } +} \ No newline at end of file diff --git a/Tests/fixtures/multi-namespace/expected/Schema1/Normalizer/TestNormalizer.php b/Tests/fixtures/multi-namespace/expected/Schema1/Normalizer/TestNormalizer.php new file mode 100644 index 0000000..73eeed6 --- /dev/null +++ b/Tests/fixtures/multi-namespace/expected/Schema1/Normalizer/TestNormalizer.php @@ -0,0 +1,46 @@ +setFoo($this->denormalizer->denormalize($data['foo'], 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Schema2\\Model\\Foo', 'json', $context)); + } + return $object; + } + public function normalize($object, $format = null, array $context = array()) + { + $data = array(); + if (null !== $object->getFoo()) { + $data['foo'] = $this->normalizer->normalize($object->getFoo(), 'json', $context); + } + return $data; + } +} \ No newline at end of file diff --git a/Tests/fixtures/multi-namespace/expected/Schema1/Runtime/Normalizer/CheckArray.php b/Tests/fixtures/multi-namespace/expected/Schema1/Runtime/Normalizer/CheckArray.php new file mode 100644 index 0000000..b6a895f --- /dev/null +++ b/Tests/fixtures/multi-namespace/expected/Schema1/Runtime/Normalizer/CheckArray.php @@ -0,0 +1,13 @@ +getReferenceUri(); + return $ref; + } + /** + * {@inheritdoc} + */ + public function supportsNormalization($data, $format = null) + { + return $data instanceof Reference; + } +} \ No newline at end of file diff --git a/Tests/fixtures/multi-namespace/expected/Schema2/Model/Foo.php b/Tests/fixtures/multi-namespace/expected/Schema2/Model/Foo.php new file mode 100644 index 0000000..f3d00f2 --- /dev/null +++ b/Tests/fixtures/multi-namespace/expected/Schema2/Model/Foo.php @@ -0,0 +1,34 @@ +foo; + } + /** + * + * + * @param string $foo + * + * @return self + */ + public function setFoo(string $foo) : self + { + $this->foo = $foo; + return $this; + } +} \ No newline at end of file diff --git a/Tests/fixtures/multi-namespace/expected/Schema2/Normalizer/FooNormalizer.php b/Tests/fixtures/multi-namespace/expected/Schema2/Normalizer/FooNormalizer.php new file mode 100644 index 0000000..79077de --- /dev/null +++ b/Tests/fixtures/multi-namespace/expected/Schema2/Normalizer/FooNormalizer.php @@ -0,0 +1,46 @@ +setFoo($data['foo']); + } + return $object; + } + public function normalize($object, $format = null, array $context = array()) + { + $data = array(); + if (null !== $object->getFoo()) { + $data['foo'] = $object->getFoo(); + } + return $data; + } +} \ No newline at end of file diff --git a/Tests/fixtures/multi-namespace/expected/Schema2/Normalizer/JaneObjectNormalizer.php b/Tests/fixtures/multi-namespace/expected/Schema2/Normalizer/JaneObjectNormalizer.php new file mode 100644 index 0000000..99aa9c0 --- /dev/null +++ b/Tests/fixtures/multi-namespace/expected/Schema2/Normalizer/JaneObjectNormalizer.php @@ -0,0 +1,50 @@ + 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Schema2\\Normalizer\\FooNormalizer'), $normalizersCache = array(); + public function supportsDenormalization($data, $type, $format = null) + { + return array_key_exists($type, $this->normalizers); + } + public function supportsNormalization($data, $format = null) + { + return is_object($data) && array_key_exists(get_class($data), $this->normalizers); + } + public function normalize($object, $format = null, array $context = array()) + { + $normalizerClass = $this->normalizers[get_class($object)]; + $normalizer = $this->getNormalizer($normalizerClass); + return $normalizer->normalize($object, $format, $context); + } + public function denormalize($data, $class, $format = null, array $context = array()) + { + $denormalizerClass = $this->normalizers[$class]; + $denormalizer = $this->getNormalizer($denormalizerClass); + return $denormalizer->denormalize($data, $class, $format, $context); + } + private function getNormalizer(string $normalizerClass) + { + return $this->normalizersCache[$normalizerClass] ?? $this->initNormalizer($normalizerClass); + } + private function initNormalizer(string $normalizerClass) + { + $normalizer = new $normalizerClass(); + $normalizer->setNormalizer($this->normalizer); + $normalizer->setDenormalizer($this->denormalizer); + $this->normalizersCache[$normalizerClass] = $normalizer; + return $normalizer; + } +} \ No newline at end of file diff --git a/Tests/fixtures/multi-namespace/expected/Schema2/Runtime/Normalizer/CheckArray.php b/Tests/fixtures/multi-namespace/expected/Schema2/Runtime/Normalizer/CheckArray.php new file mode 100644 index 0000000..a95f075 --- /dev/null +++ b/Tests/fixtures/multi-namespace/expected/Schema2/Runtime/Normalizer/CheckArray.php @@ -0,0 +1,13 @@ +getReferenceUri(); + return $ref; + } + /** + * {@inheritdoc} + */ + public function supportsNormalization($data, $format = null) + { + return $data instanceof Reference; + } +} \ No newline at end of file diff --git a/Tests/fixtures/multi-namespace/schema1.json b/Tests/fixtures/multi-namespace/schema1.json new file mode 100644 index 0000000..cf7931e --- /dev/null +++ b/Tests/fixtures/multi-namespace/schema1.json @@ -0,0 +1,8 @@ +{ + "type": "object", + "properties": { + "foo": { + "$ref": "schema2.json#/definitions/Foo" + } + } +} diff --git a/Tests/fixtures/multi-namespace/schema2.json b/Tests/fixtures/multi-namespace/schema2.json new file mode 100644 index 0000000..c752f62 --- /dev/null +++ b/Tests/fixtures/multi-namespace/schema2.json @@ -0,0 +1,12 @@ +{ + "definitions": { + "Foo": { + "type": "object", + "properties": { + "foo": { + "type": "string" + } + } + } + } +} diff --git a/Tests/fixtures/name-conflict/.jane b/Tests/fixtures/name-conflict/.jane new file mode 100644 index 0000000..bcd1207 --- /dev/null +++ b/Tests/fixtures/name-conflict/.jane @@ -0,0 +1,8 @@ + __DIR__ . '/schema.json', + 'root-class' => 'Test', + 'namespace' => 'Jane\Component\JsonSchema\Tests\Expected', + 'directory' => __DIR__ . '/generated', +]; diff --git a/Tests/fixtures/name-conflict/expected/Model/Test.php b/Tests/fixtures/name-conflict/expected/Model/Test.php new file mode 100644 index 0000000..e511c23 --- /dev/null +++ b/Tests/fixtures/name-conflict/expected/Model/Test.php @@ -0,0 +1,61 @@ +msgref; + } + /** + * Indicates the ID of the referenced original mail. + * + * @param string $msgref + * + * @return self + */ + public function setMsgref(string $msgref) : self + { + $this->msgref = $msgref; + return $this; + } + /** + * Message reference on reply/forward. + * + * @return string + */ + public function getMsgRef2() : string + { + return $this->msgRef2; + } + /** + * Message reference on reply/forward. + * + * @param string $msgRef2 + * + * @return self + */ + public function setMsgRef2(string $msgRef2) : self + { + $this->msgRef2 = $msgRef2; + return $this; + } +} \ No newline at end of file diff --git a/Tests/fixtures/name-conflict/expected/Normalizer/JaneObjectNormalizer.php b/Tests/fixtures/name-conflict/expected/Normalizer/JaneObjectNormalizer.php new file mode 100644 index 0000000..9b98fa6 --- /dev/null +++ b/Tests/fixtures/name-conflict/expected/Normalizer/JaneObjectNormalizer.php @@ -0,0 +1,50 @@ + 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Normalizer\\TestNormalizer', '\\Jane\\Component\\JsonSchemaRuntime\\Reference' => '\\Jane\\Component\\JsonSchema\\Tests\\Expected\\Runtime\\Normalizer\\ReferenceNormalizer'), $normalizersCache = array(); + public function supportsDenormalization($data, $type, $format = null) + { + return array_key_exists($type, $this->normalizers); + } + public function supportsNormalization($data, $format = null) + { + return is_object($data) && array_key_exists(get_class($data), $this->normalizers); + } + public function normalize($object, $format = null, array $context = array()) + { + $normalizerClass = $this->normalizers[get_class($object)]; + $normalizer = $this->getNormalizer($normalizerClass); + return $normalizer->normalize($object, $format, $context); + } + public function denormalize($data, $class, $format = null, array $context = array()) + { + $denormalizerClass = $this->normalizers[$class]; + $denormalizer = $this->getNormalizer($denormalizerClass); + return $denormalizer->denormalize($data, $class, $format, $context); + } + private function getNormalizer(string $normalizerClass) + { + return $this->normalizersCache[$normalizerClass] ?? $this->initNormalizer($normalizerClass); + } + private function initNormalizer(string $normalizerClass) + { + $normalizer = new $normalizerClass(); + $normalizer->setNormalizer($this->normalizer); + $normalizer->setDenormalizer($this->denormalizer); + $this->normalizersCache[$normalizerClass] = $normalizer; + return $normalizer; + } +} \ No newline at end of file diff --git a/Tests/fixtures/name-conflict/expected/Normalizer/TestNormalizer.php b/Tests/fixtures/name-conflict/expected/Normalizer/TestNormalizer.php new file mode 100644 index 0000000..b0bc3a0 --- /dev/null +++ b/Tests/fixtures/name-conflict/expected/Normalizer/TestNormalizer.php @@ -0,0 +1,56 @@ +setMsgref($data['msgref']); + } + if (\array_key_exists('msg_ref', $data)) { + $object->setMsgRef2($data['msg_ref']); + } + return $object; + } + public function normalize($object, $format = null, array $context = array()) + { + $data = array(); + $data['msgref'] = $object->getMsgref(); + if (null !== $object->getMsgRef2()) { + $data['msg_ref'] = $object->getMsgRef2(); + } + return $data; + } +} \ No newline at end of file diff --git a/Tests/fixtures/name-conflict/expected/Runtime/Normalizer/CheckArray.php b/Tests/fixtures/name-conflict/expected/Runtime/Normalizer/CheckArray.php new file mode 100644 index 0000000..3f60a9f --- /dev/null +++ b/Tests/fixtures/name-conflict/expected/Runtime/Normalizer/CheckArray.php @@ -0,0 +1,13 @@ +getReferenceUri(); + return $ref; + } + /** + * {@inheritdoc} + */ + public function supportsNormalization($data, $format = null) + { + return $data instanceof Reference; + } +} \ No newline at end of file diff --git a/Tests/fixtures/name-conflict/schema.json b/Tests/fixtures/name-conflict/schema.json new file mode 100644 index 0000000..02bd4f9 --- /dev/null +++ b/Tests/fixtures/name-conflict/schema.json @@ -0,0 +1,16 @@ +{ + "type": "object", + "properties": { + "msgref": { + "type": "string", + "description": "Indicates the ID of the referenced original mail." + }, + "msg_ref": { + "type": "string", + "description": "Message reference on reply/forward." + } + }, + "required": [ + "msgref" + ] +} diff --git a/Tests/fixtures/one-of/.jane b/Tests/fixtures/one-of/.jane new file mode 100644 index 0000000..bcd1207 --- /dev/null +++ b/Tests/fixtures/one-of/.jane @@ -0,0 +1,8 @@ + __DIR__ . '/schema.json', + 'root-class' => 'Test', + 'namespace' => 'Jane\Component\JsonSchema\Tests\Expected', + 'directory' => __DIR__ . '/generated', +]; diff --git a/Tests/fixtures/one-of/expected/Model/Foo.php b/Tests/fixtures/one-of/expected/Model/Foo.php new file mode 100644 index 0000000..94e113d --- /dev/null +++ b/Tests/fixtures/one-of/expected/Model/Foo.php @@ -0,0 +1,34 @@ +foo; + } + /** + * + * + * @param string|object|null[] $foo + * + * @return self + */ + public function setFoo($foo) : self + { + $this->foo = $foo; + return $this; + } +} \ No newline at end of file diff --git a/Tests/fixtures/one-of/expected/Normalizer/FooNormalizer.php b/Tests/fixtures/one-of/expected/Normalizer/FooNormalizer.php new file mode 100644 index 0000000..e724120 --- /dev/null +++ b/Tests/fixtures/one-of/expected/Normalizer/FooNormalizer.php @@ -0,0 +1,90 @@ + $value_1) { + if (preg_match('/^[a-zA-Z0-9._-]+$/', (string) $key) && isset($value_1)) { + $value_2 = $value_1; + if (is_array($value_1)) { + $value_2 = $value_1; + } elseif (is_null($value_1)) { + $value_2 = $value_1; + } + $values[$key] = $value_2; + continue; + } + } + $value = $values; + } + $object->setFoo($value); + } + return $object; + } + public function normalize($object, $format = null, array $context = array()) + { + $data = array(); + if (null !== $object->getFoo()) { + $value = $object->getFoo(); + if (is_string($object->getFoo())) { + $value = $object->getFoo(); + } elseif (!is_null($object->getFoo())) { + $values = array(); + foreach ($object->getFoo() as $key => $value_1) { + if (preg_match('/^[a-zA-Z0-9._-]+$/', (string) $key) && !is_null($value_1)) { + $value_2 = $value_1; + if (is_object($value_1)) { + $value_2 = $value_1; + } elseif (is_null($value_1)) { + $value_2 = $value_1; + } + $values[$key] = $value_2; + continue; + } + } + $value = $values; + } + $data['foo'] = $value; + } + return $data; + } +} \ No newline at end of file diff --git a/Tests/fixtures/one-of/expected/Normalizer/JaneObjectNormalizer.php b/Tests/fixtures/one-of/expected/Normalizer/JaneObjectNormalizer.php new file mode 100644 index 0000000..026073d --- /dev/null +++ b/Tests/fixtures/one-of/expected/Normalizer/JaneObjectNormalizer.php @@ -0,0 +1,50 @@ + 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Normalizer\\FooNormalizer', '\\Jane\\Component\\JsonSchemaRuntime\\Reference' => '\\Jane\\Component\\JsonSchema\\Tests\\Expected\\Runtime\\Normalizer\\ReferenceNormalizer'), $normalizersCache = array(); + public function supportsDenormalization($data, $type, $format = null) + { + return array_key_exists($type, $this->normalizers); + } + public function supportsNormalization($data, $format = null) + { + return is_object($data) && array_key_exists(get_class($data), $this->normalizers); + } + public function normalize($object, $format = null, array $context = array()) + { + $normalizerClass = $this->normalizers[get_class($object)]; + $normalizer = $this->getNormalizer($normalizerClass); + return $normalizer->normalize($object, $format, $context); + } + public function denormalize($data, $class, $format = null, array $context = array()) + { + $denormalizerClass = $this->normalizers[$class]; + $denormalizer = $this->getNormalizer($denormalizerClass); + return $denormalizer->denormalize($data, $class, $format, $context); + } + private function getNormalizer(string $normalizerClass) + { + return $this->normalizersCache[$normalizerClass] ?? $this->initNormalizer($normalizerClass); + } + private function initNormalizer(string $normalizerClass) + { + $normalizer = new $normalizerClass(); + $normalizer->setNormalizer($this->normalizer); + $normalizer->setDenormalizer($this->denormalizer); + $this->normalizersCache[$normalizerClass] = $normalizer; + return $normalizer; + } +} \ No newline at end of file diff --git a/Tests/fixtures/one-of/expected/Runtime/Normalizer/CheckArray.php b/Tests/fixtures/one-of/expected/Runtime/Normalizer/CheckArray.php new file mode 100644 index 0000000..3f60a9f --- /dev/null +++ b/Tests/fixtures/one-of/expected/Runtime/Normalizer/CheckArray.php @@ -0,0 +1,13 @@ +getReferenceUri(); + return $ref; + } + /** + * {@inheritdoc} + */ + public function supportsNormalization($data, $format = null) + { + return $data instanceof Reference; + } +} \ No newline at end of file diff --git a/Tests/fixtures/one-of/schema.json b/Tests/fixtures/one-of/schema.json new file mode 100644 index 0000000..32a844e --- /dev/null +++ b/Tests/fixtures/one-of/schema.json @@ -0,0 +1,53 @@ +{ + "id": "http://json-schema.org/draft-04/schema#", + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Schema with definitions", + "definitions": { + "Foo": { + "type": "object", + "properties": { + "foo": { + "oneOf": [ + { + "$ref":"#/definitions/list_of_strings" + }, + { + "type":"object", + "patternProperties":{ + "^[a-zA-Z0-9._-]+$":{ + "oneOf":[ + { + "type":"object", + "properties":{ + "aliases":{ + "$ref":"#/definitions/list_of_strings" + }, + "ipv4_address":{ + "type":"string" + }, + "ipv6_address":{ + "type":"string" + } + }, + "additionalProperties":false + }, + { + "type":"null" + } + ] + } + }, + "additionalProperties":false + } + ] + } + } + }, + "list_of_strings": { + "type": "string", + "enum": [ + "a", "b", "c" + ] + } + } +} diff --git a/Tests/fixtures/read-only/.jane b/Tests/fixtures/read-only/.jane new file mode 100644 index 0000000..bcd1207 --- /dev/null +++ b/Tests/fixtures/read-only/.jane @@ -0,0 +1,8 @@ + __DIR__ . '/schema.json', + 'root-class' => 'Test', + 'namespace' => 'Jane\Component\JsonSchema\Tests\Expected', + 'directory' => __DIR__ . '/generated', +]; diff --git a/Tests/fixtures/read-only/expected/Model/Foo.php b/Tests/fixtures/read-only/expected/Model/Foo.php new file mode 100644 index 0000000..6d6b6be --- /dev/null +++ b/Tests/fixtures/read-only/expected/Model/Foo.php @@ -0,0 +1,88 @@ +foo; + } + /** + * + * + * @param string $foo + * + * @return self + */ + public function setFoo(string $foo) : self + { + $this->foo = $foo; + return $this; + } + /** + * + * + * @return string + */ + public function getBar() : string + { + return $this->bar; + } + /** + * + * + * @param string $bar + * + * @return self + */ + public function setBar(string $bar) : self + { + $this->bar = $bar; + return $this; + } + /** + * + * + * @return string + */ + public function getFooBar() : string + { + return $this->fooBar; + } + /** + * + * + * @param string $fooBar + * + * @return self + */ + public function setFooBar(string $fooBar) : self + { + $this->fooBar = $fooBar; + return $this; + } +} \ No newline at end of file diff --git a/Tests/fixtures/read-only/expected/Normalizer/FooNormalizer.php b/Tests/fixtures/read-only/expected/Normalizer/FooNormalizer.php new file mode 100644 index 0000000..0ba913a --- /dev/null +++ b/Tests/fixtures/read-only/expected/Normalizer/FooNormalizer.php @@ -0,0 +1,61 @@ +setFoo($data['foo']); + } + if (\array_key_exists('bar', $data)) { + $object->setBar($data['bar']); + } + if (\array_key_exists('fooBar', $data)) { + $object->setFooBar($data['fooBar']); + } + return $object; + } + public function normalize($object, $format = null, array $context = array()) + { + $data = array(); + if (null !== $object->getBar()) { + $data['bar'] = $object->getBar(); + } + if (null !== $object->getFooBar()) { + $data['fooBar'] = $object->getFooBar(); + } + return $data; + } +} \ No newline at end of file diff --git a/Tests/fixtures/read-only/expected/Normalizer/JaneObjectNormalizer.php b/Tests/fixtures/read-only/expected/Normalizer/JaneObjectNormalizer.php new file mode 100644 index 0000000..026073d --- /dev/null +++ b/Tests/fixtures/read-only/expected/Normalizer/JaneObjectNormalizer.php @@ -0,0 +1,50 @@ + 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Normalizer\\FooNormalizer', '\\Jane\\Component\\JsonSchemaRuntime\\Reference' => '\\Jane\\Component\\JsonSchema\\Tests\\Expected\\Runtime\\Normalizer\\ReferenceNormalizer'), $normalizersCache = array(); + public function supportsDenormalization($data, $type, $format = null) + { + return array_key_exists($type, $this->normalizers); + } + public function supportsNormalization($data, $format = null) + { + return is_object($data) && array_key_exists(get_class($data), $this->normalizers); + } + public function normalize($object, $format = null, array $context = array()) + { + $normalizerClass = $this->normalizers[get_class($object)]; + $normalizer = $this->getNormalizer($normalizerClass); + return $normalizer->normalize($object, $format, $context); + } + public function denormalize($data, $class, $format = null, array $context = array()) + { + $denormalizerClass = $this->normalizers[$class]; + $denormalizer = $this->getNormalizer($denormalizerClass); + return $denormalizer->denormalize($data, $class, $format, $context); + } + private function getNormalizer(string $normalizerClass) + { + return $this->normalizersCache[$normalizerClass] ?? $this->initNormalizer($normalizerClass); + } + private function initNormalizer(string $normalizerClass) + { + $normalizer = new $normalizerClass(); + $normalizer->setNormalizer($this->normalizer); + $normalizer->setDenormalizer($this->denormalizer); + $this->normalizersCache[$normalizerClass] = $normalizer; + return $normalizer; + } +} \ No newline at end of file diff --git a/Tests/fixtures/read-only/expected/Runtime/Normalizer/CheckArray.php b/Tests/fixtures/read-only/expected/Runtime/Normalizer/CheckArray.php new file mode 100644 index 0000000..3f60a9f --- /dev/null +++ b/Tests/fixtures/read-only/expected/Runtime/Normalizer/CheckArray.php @@ -0,0 +1,13 @@ +getReferenceUri(); + return $ref; + } + /** + * {@inheritdoc} + */ + public function supportsNormalization($data, $format = null) + { + return $data instanceof Reference; + } +} \ No newline at end of file diff --git a/Tests/fixtures/read-only/schema.json b/Tests/fixtures/read-only/schema.json new file mode 100644 index 0000000..3518c5a --- /dev/null +++ b/Tests/fixtures/read-only/schema.json @@ -0,0 +1,23 @@ +{ + "id": "http://json-schema.org/draft-04/schema#", + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Schema with definitions", + "definitions": { + "Foo": { + "type": "object", + "properties": { + "foo": { + "type": "string", + "readOnly": true + }, + "bar": { + "type": "string", + "readOnly": false + }, + "fooBar": { + "type": "string" + } + } + } + } +} diff --git a/Tests/fixtures/reserved-words/.jane b/Tests/fixtures/reserved-words/.jane new file mode 100644 index 0000000..bcd1207 --- /dev/null +++ b/Tests/fixtures/reserved-words/.jane @@ -0,0 +1,8 @@ + __DIR__ . '/schema.json', + 'root-class' => 'Test', + 'namespace' => 'Jane\Component\JsonSchema\Tests\Expected', + 'directory' => __DIR__ . '/generated', +]; diff --git a/Tests/fixtures/reserved-words/expected/Model/_List.php b/Tests/fixtures/reserved-words/expected/Model/_List.php new file mode 100644 index 0000000..70764b8 --- /dev/null +++ b/Tests/fixtures/reserved-words/expected/Model/_List.php @@ -0,0 +1,34 @@ +foo; + } + /** + * + * + * @param string $foo + * + * @return self + */ + public function setFoo(string $foo) : self + { + $this->foo = $foo; + return $this; + } +} \ No newline at end of file diff --git a/Tests/fixtures/reserved-words/expected/Normalizer/JaneObjectNormalizer.php b/Tests/fixtures/reserved-words/expected/Normalizer/JaneObjectNormalizer.php new file mode 100644 index 0000000..cb70084 --- /dev/null +++ b/Tests/fixtures/reserved-words/expected/Normalizer/JaneObjectNormalizer.php @@ -0,0 +1,50 @@ + 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Normalizer\\_ListNormalizer', '\\Jane\\Component\\JsonSchemaRuntime\\Reference' => '\\Jane\\Component\\JsonSchema\\Tests\\Expected\\Runtime\\Normalizer\\ReferenceNormalizer'), $normalizersCache = array(); + public function supportsDenormalization($data, $type, $format = null) + { + return array_key_exists($type, $this->normalizers); + } + public function supportsNormalization($data, $format = null) + { + return is_object($data) && array_key_exists(get_class($data), $this->normalizers); + } + public function normalize($object, $format = null, array $context = array()) + { + $normalizerClass = $this->normalizers[get_class($object)]; + $normalizer = $this->getNormalizer($normalizerClass); + return $normalizer->normalize($object, $format, $context); + } + public function denormalize($data, $class, $format = null, array $context = array()) + { + $denormalizerClass = $this->normalizers[$class]; + $denormalizer = $this->getNormalizer($denormalizerClass); + return $denormalizer->denormalize($data, $class, $format, $context); + } + private function getNormalizer(string $normalizerClass) + { + return $this->normalizersCache[$normalizerClass] ?? $this->initNormalizer($normalizerClass); + } + private function initNormalizer(string $normalizerClass) + { + $normalizer = new $normalizerClass(); + $normalizer->setNormalizer($this->normalizer); + $normalizer->setDenormalizer($this->denormalizer); + $this->normalizersCache[$normalizerClass] = $normalizer; + return $normalizer; + } +} \ No newline at end of file diff --git a/Tests/fixtures/reserved-words/expected/Normalizer/ListNormalizer.php b/Tests/fixtures/reserved-words/expected/Normalizer/ListNormalizer.php new file mode 100644 index 0000000..1ce2c57 --- /dev/null +++ b/Tests/fixtures/reserved-words/expected/Normalizer/ListNormalizer.php @@ -0,0 +1,52 @@ +setFoo($data['foo']); + } + return $object; + } + public function normalize($object, $format = null, array $context = array()) + { + $data = array(); + if (null !== $object->getFoo()) { + $data['foo'] = $object->getFoo(); + } + return $data; + } +} \ No newline at end of file diff --git a/Tests/fixtures/reserved-words/expected/Runtime/Normalizer/CheckArray.php b/Tests/fixtures/reserved-words/expected/Runtime/Normalizer/CheckArray.php new file mode 100644 index 0000000..3f60a9f --- /dev/null +++ b/Tests/fixtures/reserved-words/expected/Runtime/Normalizer/CheckArray.php @@ -0,0 +1,13 @@ +getReferenceUri(); + return $ref; + } + /** + * {@inheritdoc} + */ + public function supportsNormalization($data, $format = null) + { + return $data instanceof Reference; + } +} \ No newline at end of file diff --git a/Tests/fixtures/reserved-words/schema.json b/Tests/fixtures/reserved-words/schema.json new file mode 100644 index 0000000..e56ea4b --- /dev/null +++ b/Tests/fixtures/reserved-words/schema.json @@ -0,0 +1,15 @@ +{ + "id": "http://json-schema.org/draft-04/schema#", + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Schema with definitions", + "definitions": { + "List": { + "type": "object", + "properties": { + "foo": { + "type": "string" + } + } + } + } +} diff --git a/Tests/fixtures/test-default/.jane b/Tests/fixtures/test-default/.jane new file mode 100644 index 0000000..66682c1 --- /dev/null +++ b/Tests/fixtures/test-default/.jane @@ -0,0 +1,9 @@ + __DIR__ . '/schema.json', + 'root-class' => 'Test', + 'namespace' => 'Jane\Component\JsonSchema\Tests\Expected', + 'directory' => __DIR__ . '/generated', + 'strict' => false, +]; diff --git a/Tests/fixtures/test-default/expected/Model/Test.php b/Tests/fixtures/test-default/expected/Model/Test.php new file mode 100644 index 0000000..493e781 --- /dev/null +++ b/Tests/fixtures/test-default/expected/Model/Test.php @@ -0,0 +1,196 @@ + 'value'); + /** + * + * + * @var mixed[]|null + */ + protected $object = array('key' => 'value'); + /** + * + * + * @var TestSubObject|null + */ + protected $subObject; + /** + * + * + * @return string|null + */ + public function getString() : ?string + { + return $this->string; + } + /** + * + * + * @param string|null $string + * + * @return self + */ + public function setString(?string $string) : self + { + $this->string = $string; + return $this; + } + /** + * + * + * @return bool|null + */ + public function getBool() : ?bool + { + return $this->bool; + } + /** + * + * + * @param bool|null $bool + * + * @return self + */ + public function setBool(?bool $bool) : self + { + $this->bool = $bool; + return $this; + } + /** + * + * + * @return int|null + */ + public function getInteger() : ?int + { + return $this->integer; + } + /** + * + * + * @param int|null $integer + * + * @return self + */ + public function setInteger(?int $integer) : self + { + $this->integer = $integer; + return $this; + } + /** + * + * + * @return float|null + */ + public function getFloat() : ?float + { + return $this->float; + } + /** + * + * + * @param float|null $float + * + * @return self + */ + public function setFloat(?float $float) : self + { + $this->float = $float; + return $this; + } + /** + * + * + * @return mixed[]|null + */ + public function getArray() : ?array + { + return $this->array; + } + /** + * + * + * @param mixed[]|null $array + * + * @return self + */ + public function setArray(?array $array) : self + { + $this->array = $array; + return $this; + } + /** + * + * + * @return mixed[]|null + */ + public function getObject() : ?array + { + return $this->object; + } + /** + * + * + * @param mixed[]|null $object + * + * @return self + */ + public function setObject(?array $object) : self + { + $this->object = $object; + return $this; + } + /** + * + * + * @return TestSubObject|null + */ + public function getSubObject() : ?TestSubObject + { + return $this->subObject; + } + /** + * + * + * @param TestSubObject|null $subObject + * + * @return self + */ + public function setSubObject(?TestSubObject $subObject) : self + { + $this->subObject = $subObject; + return $this; + } +} \ No newline at end of file diff --git a/Tests/fixtures/test-default/expected/Model/TestSubObject.php b/Tests/fixtures/test-default/expected/Model/TestSubObject.php new file mode 100644 index 0000000..9aa9974 --- /dev/null +++ b/Tests/fixtures/test-default/expected/Model/TestSubObject.php @@ -0,0 +1,34 @@ +foo; + } + /** + * + * + * @param string|null $foo + * + * @return self + */ + public function setFoo(?string $foo) : self + { + $this->foo = $foo; + return $this; + } +} \ No newline at end of file diff --git a/Tests/fixtures/test-default/expected/Normalizer/JaneObjectNormalizer.php b/Tests/fixtures/test-default/expected/Normalizer/JaneObjectNormalizer.php new file mode 100644 index 0000000..b2f5656 --- /dev/null +++ b/Tests/fixtures/test-default/expected/Normalizer/JaneObjectNormalizer.php @@ -0,0 +1,50 @@ + 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Normalizer\\TestNormalizer', 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Model\\TestSubObject' => 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Normalizer\\TestSubObjectNormalizer', '\\Jane\\Component\\JsonSchemaRuntime\\Reference' => '\\Jane\\Component\\JsonSchema\\Tests\\Expected\\Runtime\\Normalizer\\ReferenceNormalizer'), $normalizersCache = array(); + public function supportsDenormalization($data, $type, $format = null) + { + return array_key_exists($type, $this->normalizers); + } + public function supportsNormalization($data, $format = null) + { + return is_object($data) && array_key_exists(get_class($data), $this->normalizers); + } + public function normalize($object, $format = null, array $context = array()) + { + $normalizerClass = $this->normalizers[get_class($object)]; + $normalizer = $this->getNormalizer($normalizerClass); + return $normalizer->normalize($object, $format, $context); + } + public function denormalize($data, $class, $format = null, array $context = array()) + { + $denormalizerClass = $this->normalizers[$class]; + $denormalizer = $this->getNormalizer($denormalizerClass); + return $denormalizer->denormalize($data, $class, $format, $context); + } + private function getNormalizer(string $normalizerClass) + { + return $this->normalizersCache[$normalizerClass] ?? $this->initNormalizer($normalizerClass); + } + private function initNormalizer(string $normalizerClass) + { + $normalizer = new $normalizerClass(); + $normalizer->setNormalizer($this->normalizer); + $normalizer->setDenormalizer($this->denormalizer); + $this->normalizersCache[$normalizerClass] = $normalizer; + return $normalizer; + } +} \ No newline at end of file diff --git a/Tests/fixtures/test-default/expected/Normalizer/TestNormalizer.php b/Tests/fixtures/test-default/expected/Normalizer/TestNormalizer.php new file mode 100644 index 0000000..6a71fb1 --- /dev/null +++ b/Tests/fixtures/test-default/expected/Normalizer/TestNormalizer.php @@ -0,0 +1,125 @@ +setString($data['string']); + } + elseif (\array_key_exists('string', $data) && $data['string'] === null) { + $object->setString(null); + } + if (\array_key_exists('bool', $data) && $data['bool'] !== null) { + $object->setBool($data['bool']); + } + elseif (\array_key_exists('bool', $data) && $data['bool'] === null) { + $object->setBool(null); + } + if (\array_key_exists('integer', $data) && $data['integer'] !== null) { + $object->setInteger($data['integer']); + } + elseif (\array_key_exists('integer', $data) && $data['integer'] === null) { + $object->setInteger(null); + } + if (\array_key_exists('float', $data) && $data['float'] !== null) { + $object->setFloat($data['float']); + } + elseif (\array_key_exists('float', $data) && $data['float'] === null) { + $object->setFloat(null); + } + if (\array_key_exists('array', $data) && $data['array'] !== null) { + $values = array(); + foreach ($data['array'] as $value) { + $values[] = $value; + } + $object->setArray($values); + } + elseif (\array_key_exists('array', $data) && $data['array'] === null) { + $object->setArray(null); + } + if (\array_key_exists('object', $data) && $data['object'] !== null) { + $values_1 = array(); + foreach ($data['object'] as $value_1) { + $values_1[] = $value_1; + } + $object->setObject($values_1); + } + elseif (\array_key_exists('object', $data) && $data['object'] === null) { + $object->setObject(null); + } + if (\array_key_exists('subObject', $data) && $data['subObject'] !== null) { + $object->setSubObject($this->denormalizer->denormalize($data['subObject'], 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Model\\TestSubObject', 'json', $context)); + } + elseif (\array_key_exists('subObject', $data) && $data['subObject'] === null) { + $object->setSubObject(null); + } + return $object; + } + public function normalize($object, $format = null, array $context = array()) + { + $data = array(); + if (null !== $object->getString()) { + $data['string'] = $object->getString(); + } + if (null !== $object->getBool()) { + $data['bool'] = $object->getBool(); + } + if (null !== $object->getInteger()) { + $data['integer'] = $object->getInteger(); + } + if (null !== $object->getFloat()) { + $data['float'] = $object->getFloat(); + } + if (null !== $object->getArray()) { + $values = array(); + foreach ($object->getArray() as $value) { + $values[] = $value; + } + $data['array'] = $values; + } + if (null !== $object->getObject()) { + $values_1 = array(); + foreach ($object->getObject() as $value_1) { + $values_1[] = $value_1; + } + $data['object'] = $values_1; + } + if (null !== $object->getSubObject()) { + $data['subObject'] = $this->normalizer->normalize($object->getSubObject(), 'json', $context); + } + return $data; + } +} \ No newline at end of file diff --git a/Tests/fixtures/test-default/expected/Normalizer/TestSubObjectNormalizer.php b/Tests/fixtures/test-default/expected/Normalizer/TestSubObjectNormalizer.php new file mode 100644 index 0000000..08cef99 --- /dev/null +++ b/Tests/fixtures/test-default/expected/Normalizer/TestSubObjectNormalizer.php @@ -0,0 +1,55 @@ +setFoo($data['foo']); + } + elseif (\array_key_exists('foo', $data) && $data['foo'] === null) { + $object->setFoo(null); + } + return $object; + } + public function normalize($object, $format = null, array $context = array()) + { + $data = array(); + if (null !== $object->getFoo()) { + $data['foo'] = $object->getFoo(); + } + return $data; + } +} \ No newline at end of file diff --git a/Tests/fixtures/test-default/expected/Runtime/Normalizer/CheckArray.php b/Tests/fixtures/test-default/expected/Runtime/Normalizer/CheckArray.php new file mode 100644 index 0000000..3f60a9f --- /dev/null +++ b/Tests/fixtures/test-default/expected/Runtime/Normalizer/CheckArray.php @@ -0,0 +1,13 @@ +getReferenceUri(); + return $ref; + } + /** + * {@inheritdoc} + */ + public function supportsNormalization($data, $format = null) + { + return $data instanceof Reference; + } +} \ No newline at end of file diff --git a/Tests/fixtures/test-default/schema.json b/Tests/fixtures/test-default/schema.json new file mode 100644 index 0000000..9a46b92 --- /dev/null +++ b/Tests/fixtures/test-default/schema.json @@ -0,0 +1,43 @@ +{ + "id": "http://json-schema.org/draft-04/schema#", + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Schema for object with null values", + "type": "object", + "properties": { + "string": { + "type": "string", + "default": "content" + }, + "bool": { + "type": "boolean", + "default": true + }, + "integer": { + "type": "integer", + "default": 10 + }, + "float": { + "type": "number", + "default": 3.4 + }, + "array": { + "type": "array", + "default": ["value"] + }, + "object": { + "type": "array", + "default": { + "key": "value" + } + }, + "subObject": { + "type": "object", + "properties": { + "foo": { + "type": "string", + "default": "subContent" + } + } + } + } +} diff --git a/Tests/fixtures/test-no-reference/.jane b/Tests/fixtures/test-no-reference/.jane new file mode 100644 index 0000000..9907db2 --- /dev/null +++ b/Tests/fixtures/test-no-reference/.jane @@ -0,0 +1,9 @@ + __DIR__ . '/schema.json', + 'root-class' => 'Test', + 'namespace' => 'Jane\Component\JsonSchema\Tests\Expected', + 'directory' => __DIR__ . '/generated', + 'reference' => false, +]; diff --git a/Tests/fixtures/test-no-reference/expected/Model/Test.php b/Tests/fixtures/test-no-reference/expected/Model/Test.php new file mode 100644 index 0000000..e8d8d68 --- /dev/null +++ b/Tests/fixtures/test-no-reference/expected/Model/Test.php @@ -0,0 +1,61 @@ +string; + } + /** + * + * + * @param string $string + * + * @return self + */ + public function setString(string $string) : self + { + $this->string = $string; + return $this; + } + /** + * + * + * @return TestSubObject + */ + public function getSubObject() : TestSubObject + { + return $this->subObject; + } + /** + * + * + * @param TestSubObject $subObject + * + * @return self + */ + public function setSubObject(TestSubObject $subObject) : self + { + $this->subObject = $subObject; + return $this; + } +} \ No newline at end of file diff --git a/Tests/fixtures/test-no-reference/expected/Model/TestSubObject.php b/Tests/fixtures/test-no-reference/expected/Model/TestSubObject.php new file mode 100644 index 0000000..f25f6ac --- /dev/null +++ b/Tests/fixtures/test-no-reference/expected/Model/TestSubObject.php @@ -0,0 +1,34 @@ +foo; + } + /** + * + * + * @param string $foo + * + * @return self + */ + public function setFoo(string $foo) : self + { + $this->foo = $foo; + return $this; + } +} \ No newline at end of file diff --git a/Tests/fixtures/test-no-reference/expected/Normalizer/JaneObjectNormalizer.php b/Tests/fixtures/test-no-reference/expected/Normalizer/JaneObjectNormalizer.php new file mode 100644 index 0000000..a23cf56 --- /dev/null +++ b/Tests/fixtures/test-no-reference/expected/Normalizer/JaneObjectNormalizer.php @@ -0,0 +1,50 @@ + 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Normalizer\\TestNormalizer', 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Model\\TestSubObject' => 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Normalizer\\TestSubObjectNormalizer'), $normalizersCache = array(); + public function supportsDenormalization($data, $type, $format = null) + { + return array_key_exists($type, $this->normalizers); + } + public function supportsNormalization($data, $format = null) + { + return is_object($data) && array_key_exists(get_class($data), $this->normalizers); + } + public function normalize($object, $format = null, array $context = array()) + { + $normalizerClass = $this->normalizers[get_class($object)]; + $normalizer = $this->getNormalizer($normalizerClass); + return $normalizer->normalize($object, $format, $context); + } + public function denormalize($data, $class, $format = null, array $context = array()) + { + $denormalizerClass = $this->normalizers[$class]; + $denormalizer = $this->getNormalizer($denormalizerClass); + return $denormalizer->denormalize($data, $class, $format, $context); + } + private function getNormalizer(string $normalizerClass) + { + return $this->normalizersCache[$normalizerClass] ?? $this->initNormalizer($normalizerClass); + } + private function initNormalizer(string $normalizerClass) + { + $normalizer = new $normalizerClass(); + $normalizer->setNormalizer($this->normalizer); + $normalizer->setDenormalizer($this->denormalizer); + $this->normalizersCache[$normalizerClass] = $normalizer; + return $normalizer; + } +} \ No newline at end of file diff --git a/Tests/fixtures/test-no-reference/expected/Normalizer/TestNormalizer.php b/Tests/fixtures/test-no-reference/expected/Normalizer/TestNormalizer.php new file mode 100644 index 0000000..fa6bade --- /dev/null +++ b/Tests/fixtures/test-no-reference/expected/Normalizer/TestNormalizer.php @@ -0,0 +1,52 @@ +setString($data['string']); + } + if (\array_key_exists('subObject', $data)) { + $object->setSubObject($this->denormalizer->denormalize($data['subObject'], 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Model\\TestSubObject', 'json', $context)); + } + return $object; + } + public function normalize($object, $format = null, array $context = array()) + { + $data = array(); + if (null !== $object->getString()) { + $data['string'] = $object->getString(); + } + if (null !== $object->getSubObject()) { + $data['subObject'] = $this->normalizer->normalize($object->getSubObject(), 'json', $context); + } + return $data; + } +} \ No newline at end of file diff --git a/Tests/fixtures/test-no-reference/expected/Normalizer/TestSubObjectNormalizer.php b/Tests/fixtures/test-no-reference/expected/Normalizer/TestSubObjectNormalizer.php new file mode 100644 index 0000000..2094786 --- /dev/null +++ b/Tests/fixtures/test-no-reference/expected/Normalizer/TestSubObjectNormalizer.php @@ -0,0 +1,46 @@ +setFoo($data['foo']); + } + return $object; + } + public function normalize($object, $format = null, array $context = array()) + { + $data = array(); + if (null !== $object->getFoo()) { + $data['foo'] = $object->getFoo(); + } + return $data; + } +} \ No newline at end of file diff --git a/Tests/fixtures/test-no-reference/expected/Runtime/Normalizer/CheckArray.php b/Tests/fixtures/test-no-reference/expected/Runtime/Normalizer/CheckArray.php new file mode 100644 index 0000000..3f60a9f --- /dev/null +++ b/Tests/fixtures/test-no-reference/expected/Runtime/Normalizer/CheckArray.php @@ -0,0 +1,13 @@ +getReferenceUri(); + return $ref; + } + /** + * {@inheritdoc} + */ + public function supportsNormalization($data, $format = null) + { + return $data instanceof Reference; + } +} \ No newline at end of file diff --git a/Tests/fixtures/test-no-reference/schema.json b/Tests/fixtures/test-no-reference/schema.json new file mode 100644 index 0000000..bd1214f --- /dev/null +++ b/Tests/fixtures/test-no-reference/schema.json @@ -0,0 +1,19 @@ +{ + "id": "http://json-schema.org/draft-04/schema#", + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Schema for object with null values", + "type": "object", + "properties": { + "string": { + "type": "string" + }, + "subObject": { + "type": "object", + "properties": { + "foo": { + "type": "string" + } + } + } + } +} diff --git a/Tests/fixtures/test-not-strict/.jane b/Tests/fixtures/test-not-strict/.jane new file mode 100644 index 0000000..66682c1 --- /dev/null +++ b/Tests/fixtures/test-not-strict/.jane @@ -0,0 +1,9 @@ + __DIR__ . '/schema.json', + 'root-class' => 'Test', + 'namespace' => 'Jane\Component\JsonSchema\Tests\Expected', + 'directory' => __DIR__ . '/generated', + 'strict' => false, +]; diff --git a/Tests/fixtures/test-not-strict/expected/Model/Test.php b/Tests/fixtures/test-not-strict/expected/Model/Test.php new file mode 100644 index 0000000..9190d14 --- /dev/null +++ b/Tests/fixtures/test-not-strict/expected/Model/Test.php @@ -0,0 +1,115 @@ +onlyNull; + } + /** + * + * + * @param null $onlyNull + * + * @return self + */ + public function setOnlyNull($onlyNull) : self + { + $this->onlyNull = $onlyNull; + return $this; + } + /** + * + * + * @return string|null + */ + public function getNullOrString() : ?string + { + return $this->nullOrString; + } + /** + * + * + * @param string|null $nullOrString + * + * @return self + */ + public function setNullOrString(?string $nullOrString) : self + { + $this->nullOrString = $nullOrString; + return $this; + } + /** + * + * + * @return string[]|null + */ + public function getArray() : ?array + { + return $this->array; + } + /** + * + * + * @param string[]|null $array + * + * @return self + */ + public function setArray(?array $array) : self + { + $this->array = $array; + return $this; + } + /** + * + * + * @return string[]|null + */ + public function getObject() : ?iterable + { + return $this->object; + } + /** + * + * + * @param string[]|null $object + * + * @return self + */ + public function setObject(?iterable $object) : self + { + $this->object = $object; + return $this; + } +} \ No newline at end of file diff --git a/Tests/fixtures/test-not-strict/expected/Normalizer/JaneObjectNormalizer.php b/Tests/fixtures/test-not-strict/expected/Normalizer/JaneObjectNormalizer.php new file mode 100644 index 0000000..9b98fa6 --- /dev/null +++ b/Tests/fixtures/test-not-strict/expected/Normalizer/JaneObjectNormalizer.php @@ -0,0 +1,50 @@ + 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Normalizer\\TestNormalizer', '\\Jane\\Component\\JsonSchemaRuntime\\Reference' => '\\Jane\\Component\\JsonSchema\\Tests\\Expected\\Runtime\\Normalizer\\ReferenceNormalizer'), $normalizersCache = array(); + public function supportsDenormalization($data, $type, $format = null) + { + return array_key_exists($type, $this->normalizers); + } + public function supportsNormalization($data, $format = null) + { + return is_object($data) && array_key_exists(get_class($data), $this->normalizers); + } + public function normalize($object, $format = null, array $context = array()) + { + $normalizerClass = $this->normalizers[get_class($object)]; + $normalizer = $this->getNormalizer($normalizerClass); + return $normalizer->normalize($object, $format, $context); + } + public function denormalize($data, $class, $format = null, array $context = array()) + { + $denormalizerClass = $this->normalizers[$class]; + $denormalizer = $this->getNormalizer($denormalizerClass); + return $denormalizer->denormalize($data, $class, $format, $context); + } + private function getNormalizer(string $normalizerClass) + { + return $this->normalizersCache[$normalizerClass] ?? $this->initNormalizer($normalizerClass); + } + private function initNormalizer(string $normalizerClass) + { + $normalizer = new $normalizerClass(); + $normalizer->setNormalizer($this->normalizer); + $normalizer->setDenormalizer($this->denormalizer); + $this->normalizersCache[$normalizerClass] = $normalizer; + return $normalizer; + } +} \ No newline at end of file diff --git a/Tests/fixtures/test-not-strict/expected/Normalizer/TestNormalizer.php b/Tests/fixtures/test-not-strict/expected/Normalizer/TestNormalizer.php new file mode 100644 index 0000000..4b99867 --- /dev/null +++ b/Tests/fixtures/test-not-strict/expected/Normalizer/TestNormalizer.php @@ -0,0 +1,110 @@ +setOnlyNull($data['onlyNull']); + } + elseif (\array_key_exists('onlyNull', $data) && $data['onlyNull'] === null) { + $object->setOnlyNull(null); + } + if (\array_key_exists('nullOrString', $data) && $data['nullOrString'] !== null) { + $value = $data['nullOrString']; + if (is_string($data['nullOrString'])) { + $value = $data['nullOrString']; + } elseif (is_null($data['nullOrString'])) { + $value = $data['nullOrString']; + } + $object->setNullOrString($value); + } + elseif (\array_key_exists('nullOrString', $data) && $data['nullOrString'] === null) { + $object->setNullOrString(null); + } + if (\array_key_exists('array', $data) && $data['array'] !== null) { + $values = array(); + foreach ($data['array'] as $value_1) { + $values[] = $value_1; + } + $object->setArray($values); + } + elseif (\array_key_exists('array', $data) && $data['array'] === null) { + $object->setArray(null); + } + if (\array_key_exists('object', $data) && $data['object'] !== null) { + $values_1 = new \ArrayObject(array(), \ArrayObject::ARRAY_AS_PROPS); + foreach ($data['object'] as $key => $value_2) { + $values_1[$key] = $value_2; + } + $object->setObject($values_1); + } + elseif (\array_key_exists('object', $data) && $data['object'] === null) { + $object->setObject(null); + } + return $object; + } + public function normalize($object, $format = null, array $context = array()) + { + $data = array(); + if (null !== $object->getOnlyNull()) { + $data['onlyNull'] = $object->getOnlyNull(); + } + if (null !== $object->getNullOrString()) { + $value = $object->getNullOrString(); + if (is_string($object->getNullOrString())) { + $value = $object->getNullOrString(); + } elseif (is_null($object->getNullOrString())) { + $value = $object->getNullOrString(); + } + $data['nullOrString'] = $value; + } + if (null !== $object->getArray()) { + $values = array(); + foreach ($object->getArray() as $value_1) { + $values[] = $value_1; + } + $data['array'] = $values; + } + if (null !== $object->getObject()) { + $values_1 = array(); + foreach ($object->getObject() as $key => $value_2) { + $values_1[$key] = $value_2; + } + $data['object'] = $values_1; + } + return $data; + } +} \ No newline at end of file diff --git a/Tests/fixtures/test-not-strict/expected/Runtime/Normalizer/CheckArray.php b/Tests/fixtures/test-not-strict/expected/Runtime/Normalizer/CheckArray.php new file mode 100644 index 0000000..3f60a9f --- /dev/null +++ b/Tests/fixtures/test-not-strict/expected/Runtime/Normalizer/CheckArray.php @@ -0,0 +1,13 @@ +getReferenceUri(); + return $ref; + } + /** + * {@inheritdoc} + */ + public function supportsNormalization($data, $format = null) + { + return $data instanceof Reference; + } +} \ No newline at end of file diff --git a/Tests/fixtures/test-not-strict/schema.json b/Tests/fixtures/test-not-strict/schema.json new file mode 100644 index 0000000..3b08020 --- /dev/null +++ b/Tests/fixtures/test-not-strict/schema.json @@ -0,0 +1,26 @@ +{ + "id": "http://json-schema.org/draft-04/schema#", + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Schema for object with null values", + "type": "object", + "properties": { + "onlyNull": { + "type": "null" + }, + "nullOrString": { + "type": ["string", "null"] + }, + "array": { + "type": "array", + "items": { + "type": "string" + } + }, + "object": { + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } +} diff --git a/Tests/fixtures/test-null/.jane b/Tests/fixtures/test-null/.jane new file mode 100644 index 0000000..9ee4304 --- /dev/null +++ b/Tests/fixtures/test-null/.jane @@ -0,0 +1,8 @@ + __DIR__ . '/schema.json', + 'root-class' => 'Nullable', + 'namespace' => 'Jane\Component\JsonSchema\Tests\Expected', + 'directory' => __DIR__ . '/generated', +]; diff --git a/Tests/fixtures/test-null/expected/Model/Nullable.php b/Tests/fixtures/test-null/expected/Model/Nullable.php new file mode 100644 index 0000000..f444835 --- /dev/null +++ b/Tests/fixtures/test-null/expected/Model/Nullable.php @@ -0,0 +1,115 @@ +onlyNull; + } + /** + * + * + * @param null $onlyNull + * + * @return self + */ + public function setOnlyNull($onlyNull) : self + { + $this->onlyNull = $onlyNull; + return $this; + } + /** + * + * + * @return string|null + */ + public function getNullOrString() : ?string + { + return $this->nullOrString; + } + /** + * + * + * @param string|null $nullOrString + * + * @return self + */ + public function setNullOrString(?string $nullOrString) : self + { + $this->nullOrString = $nullOrString; + return $this; + } + /** + * + * + * @return string + */ + public function getRequired() : string + { + return $this->required; + } + /** + * + * + * @param string $required + * + * @return self + */ + public function setRequired(string $required) : self + { + $this->required = $required; + return $this; + } + /** + * + * + * @return string|null + */ + public function getRequiredNull() : ?string + { + return $this->requiredNull; + } + /** + * + * + * @param string|null $requiredNull + * + * @return self + */ + public function setRequiredNull(?string $requiredNull) : self + { + $this->requiredNull = $requiredNull; + return $this; + } +} \ No newline at end of file diff --git a/Tests/fixtures/test-null/expected/Normalizer/JaneObjectNormalizer.php b/Tests/fixtures/test-null/expected/Normalizer/JaneObjectNormalizer.php new file mode 100644 index 0000000..7e26b36 --- /dev/null +++ b/Tests/fixtures/test-null/expected/Normalizer/JaneObjectNormalizer.php @@ -0,0 +1,50 @@ + 'Jane\\Component\\JsonSchema\\Tests\\Expected\\Normalizer\\NullableNormalizer', '\\Jane\\Component\\JsonSchemaRuntime\\Reference' => '\\Jane\\Component\\JsonSchema\\Tests\\Expected\\Runtime\\Normalizer\\ReferenceNormalizer'), $normalizersCache = array(); + public function supportsDenormalization($data, $type, $format = null) + { + return array_key_exists($type, $this->normalizers); + } + public function supportsNormalization($data, $format = null) + { + return is_object($data) && array_key_exists(get_class($data), $this->normalizers); + } + public function normalize($object, $format = null, array $context = array()) + { + $normalizerClass = $this->normalizers[get_class($object)]; + $normalizer = $this->getNormalizer($normalizerClass); + return $normalizer->normalize($object, $format, $context); + } + public function denormalize($data, $class, $format = null, array $context = array()) + { + $denormalizerClass = $this->normalizers[$class]; + $denormalizer = $this->getNormalizer($denormalizerClass); + return $denormalizer->denormalize($data, $class, $format, $context); + } + private function getNormalizer(string $normalizerClass) + { + return $this->normalizersCache[$normalizerClass] ?? $this->initNormalizer($normalizerClass); + } + private function initNormalizer(string $normalizerClass) + { + $normalizer = new $normalizerClass(); + $normalizer->setNormalizer($this->normalizer); + $normalizer->setDenormalizer($this->denormalizer); + $this->normalizersCache[$normalizerClass] = $normalizer; + return $normalizer; + } +} \ No newline at end of file diff --git a/Tests/fixtures/test-null/expected/Normalizer/NullableNormalizer.php b/Tests/fixtures/test-null/expected/Normalizer/NullableNormalizer.php new file mode 100644 index 0000000..81f7f83 --- /dev/null +++ b/Tests/fixtures/test-null/expected/Normalizer/NullableNormalizer.php @@ -0,0 +1,99 @@ +setOnlyNull($data['onlyNull']); + } + elseif (\array_key_exists('onlyNull', $data) && $data['onlyNull'] === null) { + $object->setOnlyNull(null); + } + if (\array_key_exists('nullOrString', $data) && $data['nullOrString'] !== null) { + $value = $data['nullOrString']; + if (is_string($data['nullOrString'])) { + $value = $data['nullOrString']; + } elseif (is_null($data['nullOrString'])) { + $value = $data['nullOrString']; + } + $object->setNullOrString($value); + } + elseif (\array_key_exists('nullOrString', $data) && $data['nullOrString'] === null) { + $object->setNullOrString(null); + } + if (\array_key_exists('required', $data)) { + $object->setRequired($data['required']); + } + if (\array_key_exists('requiredNull', $data) && $data['requiredNull'] !== null) { + $value_1 = $data['requiredNull']; + if (is_string($data['requiredNull'])) { + $value_1 = $data['requiredNull']; + } elseif (is_null($data['requiredNull'])) { + $value_1 = $data['requiredNull']; + } + $object->setRequiredNull($value_1); + } + elseif (\array_key_exists('requiredNull', $data) && $data['requiredNull'] === null) { + $object->setRequiredNull(null); + } + return $object; + } + public function normalize($object, $format = null, array $context = array()) + { + $data = array(); + if (null !== $object->getOnlyNull()) { + $data['onlyNull'] = $object->getOnlyNull(); + } + if (null !== $object->getNullOrString()) { + $value = $object->getNullOrString(); + if (is_string($object->getNullOrString())) { + $value = $object->getNullOrString(); + } elseif (is_null($object->getNullOrString())) { + $value = $object->getNullOrString(); + } + $data['nullOrString'] = $value; + } + $data['required'] = $object->getRequired(); + $value_1 = $object->getRequiredNull(); + if (is_string($object->getRequiredNull())) { + $value_1 = $object->getRequiredNull(); + } elseif (is_null($object->getRequiredNull())) { + $value_1 = $object->getRequiredNull(); + } + $data['requiredNull'] = $value_1; + return $data; + } +} \ No newline at end of file diff --git a/Tests/fixtures/test-null/expected/Runtime/Normalizer/CheckArray.php b/Tests/fixtures/test-null/expected/Runtime/Normalizer/CheckArray.php new file mode 100644 index 0000000..3f60a9f --- /dev/null +++ b/Tests/fixtures/test-null/expected/Runtime/Normalizer/CheckArray.php @@ -0,0 +1,13 @@ +getReferenceUri(); + return $ref; + } + /** + * {@inheritdoc} + */ + public function supportsNormalization($data, $format = null) + { + return $data instanceof Reference; + } +} \ No newline at end of file diff --git a/Tests/fixtures/test-null/schema.json b/Tests/fixtures/test-null/schema.json new file mode 100644 index 0000000..78c80e9 --- /dev/null +++ b/Tests/fixtures/test-null/schema.json @@ -0,0 +1,24 @@ +{ + "id": "http://json-schema.org/draft-04/schema#", + "$schema": "http://json-schema.org/draft-04/schema#", + "description": "Schema for object with null values", + "type": "object", + "properties": { + "onlyNull": { + "type": "null" + }, + "nullOrString": { + "type": ["string", "null"] + }, + "required": { + "type": "string" + }, + "requiredNull": { + "type": ["string", "null"] + } + }, + "required": [ + "required", + "requiredNull" + ] +} diff --git a/Tests/generated/.gitkeep b/Tests/generated/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/Tools/InflectorTrait.php b/Tools/InflectorTrait.php new file mode 100644 index 0000000..d93fe92 --- /dev/null +++ b/Tools/InflectorTrait.php @@ -0,0 +1,20 @@ +inflector) { + $this->inflector = InflectorFactory::create()->build(); + } + + return $this->inflector; + } +} diff --git a/Tools/JsonSchemaMerger.php b/Tools/JsonSchemaMerger.php new file mode 100644 index 0000000..98c12c8 --- /dev/null +++ b/Tools/JsonSchemaMerger.php @@ -0,0 +1,55 @@ +getType() && null !== $right->getType() && $left->getType() !== $right->getType()) { + throw new \RuntimeException('Both types are defined and different, merge is not possible'); + } + + if (null === $right->getType() && null !== $left->getType()) { + $merged->setType($left->getType()); + } + + $merged->setProperties($this->arrayMerge($left->getProperties(), $right->getProperties())); + $merged->setRequired($this->arrayUnique($this->arrayMerge($left->getRequired(), $right->getRequired()))); + + return $merged; + } + + private function arrayMerge($left, $right) + { + if (!\is_array($left)) { + return $right; + } + + if (!\is_array($right)) { + return $left; + } + + return array_merge($left, $right); + } + + private function arrayUnique($array) + { + if (!\is_array($array)) { + return $array; + } + + return array_unique($array); + } +} diff --git a/bin/jane b/bin/jane new file mode 100755 index 0000000..fcf0d7b --- /dev/null +++ b/bin/jane @@ -0,0 +1,22 @@ +#!/usr/bin/env php +run(); diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..c8bda1c --- /dev/null +++ b/composer.json @@ -0,0 +1,60 @@ +{ + "name": "jane-php/json-schema", + "description": "Generate a serializable / deserializable object model given a json schema", + "license": "MIT", + "authors": [ + { + "name": "Joel Wurtz", + "email": "jwurtz@jolicode.com" + }, + { + "name": "Baptiste Leduc", + "email": "baptiste.leduc@gmail.com" + } + ], + "autoload": { + "psr-4": { + "Jane\\Component\\JsonSchema\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "require": { + "php": ">=7.2", + "ext-json": "*", + "doctrine/inflector": "^1.4 || ^2.0", + "jane-php/json-schema-runtime": "~6.2.0", + "nikic/php-parser": "^4.0", + "symfony/console": "^4.4 || ^5.0", + "symfony/filesystem": "^4.4 || ^5.0", + "symfony/options-resolver": "^4.4 || ^5.0", + "symfony/serializer": "^4.4 || ^5.0", + "symfony/var-dumper": "^4.4 || ^5.0", + "symfony/yaml": "~4.4.9 || ^5.0" + }, + "require-dev": { + "phpunit/phpunit": "^8.0", + "symfony/finder": "^4.4 || ^5.0" + }, + "suggest": { + "friendsofphp/php-cs-fixer": "Allow to automatically fix cs on generated code for better visualisation" + }, + "conflict": { + "symfony/framework-bundle": "5.1.0" + }, + "extra": { + "branch-alias": { + "dev-next": "6-dev" + } + }, + "bin": [ + "bin/jane" + ], + "config": { + "process-timeout": 1800, + "sort-packages": true + }, + "minimum-stability": "dev", + "prefer-stable": true +} diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..dfd1d0f --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,13 @@ + + + + + + + Tests + Tests/fixtures + + +