Skip to content

Commit 93efc64

Browse files
authored
Merge pull request #1 from open-code-modeling/feature/shorthand_support
Add shorthand support
2 parents 4dde7ed + fce284b commit 93efc64

File tree

13 files changed

+689
-4
lines changed

13 files changed

+689
-4
lines changed

src/Exception/ExceptionInterface.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
/**
4+
* @see https://github.com/open-code-modeling/json-schema-to-php for the canonical source repository
5+
* @copyright https://github.com/open-code-modeling/json-schema-to-php/blob/master/COPYRIGHT.md
6+
* @license https://github.com/open-code-modeling/json-schema-to-php/blob/master/LICENSE.md MIT License
7+
*/
8+
9+
declare(strict_types = 1);
10+
11+
namespace OpenCodeModeling\JsonSchemaToPhp\Exception;
12+
13+
/**
14+
* Base exception interface
15+
*
16+
* All exceptions must implements this exception to catch exceptions of this library
17+
*/
18+
interface ExceptionInterface
19+
{
20+
}

src/Exception/InvalidShorthand.php

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
<?php
2+
3+
/**
4+
* @see https://github.com/open-code-modeling/json-schema-to-php for the canonical source repository
5+
* @copyright https://github.com/open-code-modeling/json-schema-to-php/blob/master/COPYRIGHT.md
6+
* @license https://github.com/open-code-modeling/json-schema-to-php/blob/master/LICENSE.md MIT License
7+
*/
8+
9+
declare(strict_types=1);
10+
11+
namespace OpenCodeModeling\JsonSchemaToPhp\Exception;
12+
13+
final class InvalidShorthand extends LogicException
14+
{
15+
/**
16+
* @var array<string, mixed>
17+
*/
18+
private array $shorthand;
19+
20+
/**
21+
* @param string $schemaProperty
22+
* @param array<string, mixed> $shorthand
23+
* @return static
24+
*/
25+
public static function cannotParseProperty(string $schemaProperty, array $shorthand): self
26+
{
27+
$self = new static(
28+
\sprintf(
29+
'I tried to parse JSONSchema for property: "%s", but it is neither a string nor an object.',
30+
$schemaProperty
31+
)
32+
);
33+
$self->shorthand = $shorthand;
34+
35+
return $self;
36+
}
37+
38+
/**
39+
* @param array<string, mixed> $shorthand $shorthand
40+
* @return static
41+
*/
42+
public static function emptyString(array $shorthand): self
43+
{
44+
$self = new static('Shorthand contains an empty or non string property. Cannot deal with that!');
45+
$self->shorthand = $shorthand;
46+
47+
return $self;
48+
}
49+
50+
/**
51+
* @param array<string, mixed> $shorthand $shorthand
52+
* @return static
53+
*/
54+
public static function refNotString(array $shorthand): self
55+
{
56+
$self = new static(
57+
'Detected a top level shorthand reference using a "$ref" property, but the value of the property is not a string.'
58+
);
59+
$self->shorthand = $shorthand;
60+
61+
return $self;
62+
}
63+
64+
/**
65+
* @param array<string, mixed> $shorthand $shorthand
66+
* @return static
67+
*/
68+
public static function itemsNotString(array $shorthand): self
69+
{
70+
$self = new static(
71+
'Detected a top level shorthand array using an "$items" property, but the value of the property is not a string.'
72+
);
73+
$self->shorthand = $shorthand;
74+
75+
return $self;
76+
}
77+
78+
/**
79+
* @param array<string, mixed> $shorthand $shorthand
80+
* @return static
81+
*/
82+
public static function refWithOtherProperties(array $shorthand): self
83+
{
84+
$self = new static(
85+
'Shorthand contains a top level ref property "$ref", but it is not the only property!'
86+
. ' A top level reference cannot have other properties then "$ref".'
87+
);
88+
$self->shorthand = $shorthand;
89+
90+
return $self;
91+
}
92+
93+
/**
94+
* @param array<string, mixed> $shorthand $shorthand
95+
* @return static
96+
*/
97+
public static function itemsWithOtherProperties(array $shorthand): self
98+
{
99+
$self = new static(
100+
'Shorthand %s contains a top level array property "$items", but it is not the only property!'
101+
. ' A top level array cannot have other properties then "$items".'
102+
);
103+
$self->shorthand = $shorthand;
104+
105+
return $self;
106+
}
107+
108+
/**
109+
* @return array<string, mixed>
110+
*/
111+
public function shorthand(): array
112+
{
113+
return $this->shorthand;
114+
}
115+
}

src/Exception/LogicException.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
/**
4+
* @see https://github.com/open-code-modeling/json-schema-to-php for the canonical source repository
5+
* @copyright https://github.com/open-code-modeling/json-schema-to-php/blob/master/COPYRIGHT.md
6+
* @license https://github.com/open-code-modeling/json-schema-to-php/blob/master/LICENSE.md MIT License
7+
*/
8+
9+
declare(strict_types = 1);
10+
11+
namespace OpenCodeModeling\JsonSchemaToPhp\Exception;
12+
13+
use LogicException as PhpLogicException;
14+
15+
class LogicException extends PhpLogicException implements ExceptionInterface
16+
{
17+
}

src/Exception/RuntimeException.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
/**
4+
* @see https://github.com/open-code-modeling/json-schema-to-php for the canonical source repository
5+
* @copyright https://github.com/open-code-modeling/json-schema-to-php/blob/master/COPYRIGHT.md
6+
* @license https://github.com/open-code-modeling/json-schema-to-php/blob/master/LICENSE.md MIT License
7+
*/
8+
9+
declare(strict_types = 1);
10+
11+
namespace OpenCodeModeling\JsonSchemaToPhp\Exception;
12+
13+
use RuntimeException as PhpRuntimeException;
14+
15+
/**
16+
* Runtime exception
17+
*
18+
* Use this exception if the code has not the capacity to handle the request.
19+
*/
20+
class RuntimeException extends PhpRuntimeException implements ExceptionInterface
21+
{
22+
}

src/Shorthand/Shorthand.php

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
<?php
2+
3+
/**
4+
* @see https://github.com/open-code-modeling/json-schema-to-php for the canonical source repository
5+
* @copyright https://github.com/open-code-modeling/json-schema-to-php/blob/master/COPYRIGHT.md
6+
* @license https://github.com/open-code-modeling/json-schema-to-php/blob/master/LICENSE.md MIT License
7+
*/
8+
9+
declare(strict_types=1);
10+
11+
namespace OpenCodeModeling\JsonSchemaToPhp\Shorthand;
12+
13+
use OpenCodeModeling\JsonSchemaToPhp\Exception\InvalidShorthand;
14+
use OpenCodeModeling\JsonSchemaToPhp\Exception\LogicException;
15+
16+
final class Shorthand
17+
{
18+
/**
19+
* @param array<string, mixed> $shorthand
20+
* @return array<string, mixed>
21+
*/
22+
public static function convertToJsonSchema(array $shorthand): array
23+
{
24+
$schema = [
25+
'type' => 'object',
26+
'properties' => [
27+
28+
],
29+
'required' => [],
30+
'additionalProperties' => false,
31+
];
32+
33+
foreach ($shorthand as $property => $shorthandDefinition) {
34+
if (! \is_string($property) || empty($property)) {
35+
throw InvalidShorthand::emptyString($shorthand);
36+
}
37+
$schemaProperty = $property;
38+
39+
switch (true) {
40+
case \mb_substr($property, -1) === '?':
41+
$schemaProperty = \mb_substr($property, 0, -1);
42+
break;
43+
case $schemaProperty === '$ref':
44+
if (\count($shorthand) > 1) {
45+
throw InvalidShorthand::refWithOtherProperties($shorthand);
46+
}
47+
48+
if (! \is_string($shorthandDefinition)) {
49+
throw InvalidShorthand::refNotString($shorthand);
50+
}
51+
52+
$shorthandDefinition = \str_replace('#/definitions/', '', $shorthandDefinition);
53+
54+
return [
55+
'$ref' => "#/definitions/$shorthandDefinition",
56+
];
57+
case $schemaProperty === '$items':
58+
if (\count($shorthand) > 1) {
59+
throw InvalidShorthand::itemsWithOtherProperties($shorthand);
60+
}
61+
62+
if (! \is_string($shorthandDefinition)) {
63+
throw InvalidShorthand::itemsNotString($shorthand);
64+
}
65+
66+
if (\mb_substr($shorthandDefinition, -2) !== '[]') {
67+
$shorthandDefinition .= '[]';
68+
}
69+
70+
return self::convertShorthandStringToJsonSchema($shorthandDefinition);
71+
case $schemaProperty === '$title':
72+
$schema['title'] = $shorthandDefinition;
73+
continue 2;
74+
default:
75+
$schema['required'][] = $schemaProperty;
76+
break;
77+
}
78+
79+
if (\is_array($shorthandDefinition)) {
80+
$schema['properties'][$schemaProperty] = self::convertToJsonSchema($shorthandDefinition);
81+
} elseif (\is_string($shorthandDefinition)) {
82+
$schema['properties'][$schemaProperty] = self::convertShorthandStringToJsonSchema($shorthandDefinition);
83+
} else {
84+
throw InvalidShorthand::cannotParseProperty($schemaProperty, $shorthand);
85+
}
86+
}
87+
88+
return $schema;
89+
}
90+
91+
/**
92+
* @param string $shorthandStr
93+
* @return array<string, mixed>
94+
*/
95+
private static function convertShorthandStringToJsonSchema(string $shorthandStr): array
96+
{
97+
if ($shorthandStr === '') {
98+
return ['type' => 'string'];
99+
}
100+
101+
$parts = \explode('|', $shorthandStr);
102+
103+
if ($parts[0] === 'enum') {
104+
return ['enum' => \array_slice($parts, 1)];
105+
}
106+
107+
if (\mb_substr($parts[0], -2) === '[]') {
108+
$itemsParts = [\mb_substr($parts[0], 0, -2)];
109+
\array_push($itemsParts, ...\array_slice($parts, 1));
110+
111+
return [
112+
'type' => 'array',
113+
'items' => self::convertShorthandStringToJsonSchema(\implode('|', $itemsParts)),
114+
];
115+
}
116+
117+
switch ($parts[0]) {
118+
case 'string':
119+
case 'integer':
120+
case 'number':
121+
case 'boolean':
122+
$type = $parts[0];
123+
124+
if (isset($parts[1]) && $parts[1] === 'null') {
125+
$type = [$type, 'null'];
126+
127+
\array_splice($parts, 1, 1);
128+
}
129+
130+
$schema = ['type' => $type];
131+
132+
if (\count($parts) > 1) {
133+
$parts = \array_slice($parts, 1);
134+
135+
foreach ($parts as $part) {
136+
[$validationKey, $validationValue] = self::parseShorthandValidation($part);
137+
138+
$schema[$validationKey] = $validationValue;
139+
}
140+
}
141+
142+
return $schema;
143+
default:
144+
return [
145+
'$ref' => '#/definitions/'.$parts[0],
146+
];
147+
}
148+
}
149+
150+
/**
151+
* @param string $shorthandValidation
152+
* @return array<mixed>
153+
*/
154+
private static function parseShorthandValidation(string $shorthandValidation): array
155+
{
156+
$parts = \explode(':', $shorthandValidation);
157+
158+
if (\count($parts) !== 2) {
159+
throw new LogicException(\sprintf(
160+
'Cannot parse shorthand validation: "%s". Expected format "validationKey:value". Please check again!',
161+
$shorthandValidation
162+
));
163+
}
164+
165+
[$validationKey, $value] = $parts;
166+
167+
if ($value === 'true') {
168+
return [$validationKey, true];
169+
}
170+
171+
if ($value === 'false') {
172+
return [$validationKey, false];
173+
}
174+
175+
if ((string) (int) $value === $value) {
176+
return [$validationKey, (int) $value];
177+
}
178+
179+
if ((string) (float) $value === $value) {
180+
return [$validationKey, (float) $value];
181+
}
182+
183+
return [$validationKey, $value];
184+
}
185+
}

src/Type/ArrayType.php

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
namespace OpenCodeModeling\JsonSchemaToPhp\Type;
1212

13-
final class ArrayType implements TypeDefinition
13+
final class ArrayType implements TypeDefinition, TitleAware
1414
{
1515
use PopulateRequired;
1616

@@ -36,6 +36,7 @@ final class ArrayType implements TypeDefinition
3636
protected ?string $name = null;
3737
protected bool $isRequired = false;
3838
protected bool $nullable = false;
39+
protected ?string $title = null;
3940

4041
private function __construct()
4142
{
@@ -231,6 +232,16 @@ public function additionalItems(): ?TypeSet
231232
return $this->additionalItems;
232233
}
233234

235+
public function title(): ?string
236+
{
237+
return $this->title;
238+
}
239+
240+
public function setTitle(string $title): void
241+
{
242+
$this->title = $title;
243+
}
244+
234245
public static function type(): string
235246
{
236247
return self::TYPE_ARRAY;

0 commit comments

Comments
 (0)