Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions src/Generator/Definitions/Definition.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

namespace Helmich\Schema2Class\Generator\Definitions;

readonly class Definition
{
public function __construct(
public string $namespace,
public string $directory,
public string $classFQN,
public string $className,
public array $schema,
)
{
}
}
57 changes: 57 additions & 0 deletions src/Generator/Definitions/DefinitionsCollector.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php

declare(strict_types=1);

namespace Helmich\Schema2Class\Generator\Definitions;

use Helmich\Schema2Class\Generator\GeneratorRequest;

class DefinitionsCollector
{
public function __construct(protected readonly GeneratorRequest $generatorRequest)
{
}

/**
* @return \Generator<string, Definition>
*/
public function collect(array $schema, string $path = ''): \Generator {
if (isset($schema['definitions'])) {
yield from $this->findNestedDefinitions($schema['definitions'], ($path ?: '#') . '/definitions');
}

if (isset($schema['$defs'])) {
yield from $this->findNestedDefinitions($schema['$defs'], ($path ?: '#') . '/$defs');
}
}

private function findNestedDefinitions(array $definitions, string $path): \Generator {
foreach ($definitions as $key => $value) {
$newPath = $path . '/' . $key;
yield $newPath => $this->pathToClassName($newPath, $value);

if (is_array($value)) {
yield from $this->collect($value, $newPath);
}
}
}

private function pathToClassName(string $path, array $schema): Definition {
$parts = array_map(
fn ($part) => str_replace(' ', '', ucwords(str_replace('_', ' ', $part))),
explode('/', ltrim(str_replace('$defs', 'Defs', $path), '#/'))
);

$className = array_pop($parts);
$namespace = $this->generatorRequest->getTargetNamespace() . '\\' . implode('\\', $parts);
$directory = $this->generatorRequest->getTargetDirectory() . '/' . implode('/', $parts);

return new Definition(
namespace: $namespace,
directory: $directory,
classFQN: $namespace . '\\' . $className,
className: $className,
schema: $schema,
);
}
}
33 changes: 33 additions & 0 deletions src/Generator/Definitions/DefinitionsGenerator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

declare(strict_types=1);

namespace Helmich\Schema2Class\Generator\Definitions;

use Helmich\Schema2Class\Generator\GeneratorRequest;
use Helmich\Schema2Class\Generator\SchemaToClass;

class DefinitionsGenerator
{
public function __construct(
private SchemaToClass $schemaToClass,
)
{
}

/**
* @param array<string, Definition> $definitions
*/
public function generate(array $definitions, GeneratorRequest $generatorRequest): void
{
foreach ($definitions as $definition) {
$newRequest = $generatorRequest->withClass($definition->className)
->withSchema($definition->schema)
->withNamespace($definition->namespace)
->withDirectory($definition->directory)
;

$this->schemaToClass->schemaToClass($newRequest);
}
}
}
38 changes: 38 additions & 0 deletions src/Generator/Definitions/DefinitionsReferenceLookup.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

declare(strict_types=1);

namespace Helmich\Schema2Class\Generator\Definitions;

use Helmich\Schema2Class\Generator\ReferencedType;
use Helmich\Schema2Class\Generator\ReferencedTypeClass;
use Helmich\Schema2Class\Generator\ReferencedTypeUnknown;
use Helmich\Schema2Class\Generator\ReferenceLookup;

readonly class DefinitionsReferenceLookup implements ReferenceLookup
{
/**
* @param array<string, Definition> $definitions
*/
public function __construct(
private array $definitions
)
{
}

public function lookupReference(string $reference): ReferencedType
{
if (isset($this->definitions[$reference])) {
return new ReferencedTypeClass($this->definitions[$reference]->classFQN);
}
return new ReferencedTypeUnknown();
}

public function lookupSchema(string $reference): array
{
if (isset($this->definitions[$reference])) {
return $this->definitions[$reference]->schema;
}
return [];
}
}
64 changes: 58 additions & 6 deletions src/Generator/GeneratorRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ class GeneratorRequest
private array $schema;
private ValidatedSpecificationFilesItem $spec;
private SpecificationOptions $opts;
private ?ReferenceLookup $referenceLookup = null;
/** @var array<class-string, ReferenceLookup> */
private array $referenceLookup = [];

use GeneratorHookRunner;

Expand Down Expand Up @@ -46,11 +47,28 @@ private static function semversifyVersionNumber(string|int $versionNumber): stri
public function withReferenceLookup(ReferenceLookup $referenceLookup): self
{
$clone = clone $this;
$clone->referenceLookup = $referenceLookup;
$clone->referenceLookup = [];
$clone->referenceLookup[$referenceLookup::class] = $referenceLookup;

return $clone;
}

public function withAdditionalReferenceLookup(ReferenceLookup $referenceLookup): self
{
$clone = clone $this;
$clone->referenceLookup[$referenceLookup::class] = $referenceLookup;

return $clone;
}

/**
* @param class-string $referenceLookup
*/
public function hasReferenceLookup(string $referenceLookup): bool
{
return isset($this->referenceLookup[$referenceLookup]);
}

public function withSchema(array $schema): self
{
$clone = clone $this;
Expand All @@ -69,6 +87,26 @@ public function withClass(string $targetClass): self
return $clone;
}

public function withNamespace(string $targetNamespace): self
{
$clone = clone $this;
$clone->spec = $this->spec->withTargetNamespace($targetNamespace);

$clone->clearNonPropagatingHooks();

return $clone;
}

public function withDirectory(string $targetDirectory): self
{
$clone = clone $this;
$clone->spec = $this->spec->withTargetDirectory($targetDirectory);

$clone->clearNonPropagatingHooks();

return $clone;
}

public function withPHPVersion(string $targetPHPVersion): self
{
$clone = clone $this;
Expand Down Expand Up @@ -182,19 +220,33 @@ public function getOptions(): SpecificationOptions

public function lookupReference(string $ref): ReferencedType
{
if ($this->referenceLookup === null) {
if (empty($this->referenceLookup)) {
return new ReferencedTypeUnknown();
}

return $this->referenceLookup->lookupReference($ref);
foreach ($this->referenceLookup as $referenceLookup) {
$reference = $referenceLookup->lookupReference($ref);
if (!$reference instanceof ReferencedTypeUnknown) {
return $reference;
}
}

return new ReferencedTypeUnknown();
}

public function lookupSchema(string $ref): array
{
if ($this->referenceLookup === null) {
if (empty($this->referenceLookup)) {
return [];
}

return $this->referenceLookup->lookupSchema($ref);
foreach ($this->referenceLookup as $referenceLookup) {
$schema = $referenceLookup->lookupSchema($ref);
if (!empty($schema)) {
return $schema;
}
}

return [];
}
}
22 changes: 22 additions & 0 deletions src/Generator/SchemaToClass.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
namespace Helmich\Schema2Class\Generator;

use Helmich\Schema2Class\Codegen\PropertyGenerator;
use Helmich\Schema2Class\Generator\Definitions\DefinitionsCollector;
use Helmich\Schema2Class\Generator\Definitions\DefinitionsGenerator;
use Helmich\Schema2Class\Generator\Definitions\DefinitionsReferenceLookup;
use Helmich\Schema2Class\Generator\Property\IntersectProperty;
use Helmich\Schema2Class\Generator\Property\NestedObjectProperty;
use Helmich\Schema2Class\Generator\Property\PropertyCollection;
Expand Down Expand Up @@ -38,6 +41,8 @@ public function schemaToClass(GeneratorRequest $req): void
{
$schema = $req->getSchema();

$this->definitionsToSchemas($req);

if (isset($schema["enum"])) {
$this->enumGenerator->schemaToEnum($req);
return;
Expand Down Expand Up @@ -129,4 +134,21 @@ public function schemaToClass(GeneratorRequest $req): void
$this->writer->writeFile($filename, $content);
}

private function definitionsToSchemas(GeneratorRequest &$req): void
{
if ($req->hasReferenceLookup(DefinitionsReferenceLookup::class)) {
return;
}

$collector = new DefinitionsCollector($req);
$collectedDefinitions = iterator_to_array($collector->collect($req->getSchema()));

$req = $req->withAdditionalReferenceLookup(new DefinitionsReferenceLookup(
$collectedDefinitions,
));

$generator = new DefinitionsGenerator($this);
$generator->generate($collectedDefinitions, $req);
}

}
16 changes: 16 additions & 0 deletions src/Spec/ValidatedSpecificationFilesItem.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,20 @@ public function withTargetClass(string $targetClass): self
return $c;
}

public function withTargetNamespace(string $targetNamespace): self
{
$c = clone $this;
$c->targetNamespace = $targetNamespace;

return $c;
}

public function withTargetDirectory(string $targetDirectory): self
{
$c = clone $this;
$c->targetDirectory = $targetDirectory;

return $c;
}

}
Loading
Loading