Skip to content

Commit cfd82c0

Browse files
committed
Add code generation for value objects of type string with format date-time
1 parent 0304a06 commit cfd82c0

File tree

4 files changed

+386
-2
lines changed

4 files changed

+386
-2
lines changed

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
"require": {
3535
"php": "^7.4 || ^8.0",
3636
"open-code-modeling/json-schema-to-php": "dev-master",
37-
"open-code-modeling/php-code-ast": "^0.2.0"
37+
"open-code-modeling/php-code-ast": "^0.2.1"
3838
},
3939
"require-dev": {
4040
"jangregor/phpstan-prophecy": "^0.8.0",

src/ValueObject/DateTimeFactory.php

Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
<?php
2+
3+
/**
4+
* @see https://github.com/open-code-modeling/json-schema-to-php-ast for the canonical source repository
5+
* @copyright https://github.com/open-code-modeling/json-schema-to-php-ast/blob/master/COPYRIGHT.md
6+
* @license https://github.com/open-code-modeling/json-schema-to-php-ast/blob/master/LICENSE.md MIT License
7+
*/
8+
9+
declare(strict_types=1);
10+
11+
namespace OpenCodeModeling\JsonSchemaToPhpAst\ValueObject;
12+
13+
use OpenCodeModeling\CodeAst\Code\BodyGenerator;
14+
use OpenCodeModeling\CodeAst\Code\ClassConstGenerator;
15+
use OpenCodeModeling\CodeAst\Code\IdentifierGenerator;
16+
use OpenCodeModeling\CodeAst\Code\MethodGenerator;
17+
use OpenCodeModeling\CodeAst\Code\ParameterGenerator;
18+
use OpenCodeModeling\CodeAst\NodeVisitor\ClassConstant;
19+
use OpenCodeModeling\CodeAst\NodeVisitor\ClassMethod;
20+
use OpenCodeModeling\JsonSchemaToPhp\Type\StringType;
21+
use OpenCodeModeling\JsonSchemaToPhpAst\PropertyFactory;
22+
use PhpParser\NodeVisitor;
23+
use PhpParser\Parser;
24+
25+
/**
26+
* This file creates node visitors for a value object of type bool.
27+
*
28+
* The following code will be generated:
29+
*
30+
* private const OUTPUT_FORMAT = 'Y-m-d\TH:i:s.uP';
31+
*
32+
* private DateTimeImmutable $dateTime;
33+
*
34+
* public static function fromDateTime(DateTimeImmutable $dateTime): self
35+
* {
36+
* return new self(self::ensureUtc($dateTime));
37+
* }
38+
*
39+
* public static function fromString(string $dateTime): self
40+
* {
41+
* try {
42+
* $dateTimeImmutable = new DateTimeImmutable($dateTime);
43+
* } catch (\Exception $e) {
44+
* throw new InvalidArgumentException(
45+
* sprintf(
46+
* 'String "%s" is not supported. Use a date time format which is compatible with ISO 8601.',
47+
* $dateTime
48+
* )
49+
* );
50+
* }
51+
*
52+
* $dateTimeImmutable = self::ensureUtc($dateTimeImmutable);
53+
*
54+
* return new self($dateTimeImmutable);
55+
* }
56+
*
57+
* private function __construct(DateTimeImmutable $dateTime)
58+
* {
59+
* $this->dateTime = $dateTime;
60+
* }
61+
*
62+
* public function toString(): string
63+
* {
64+
* return $this->dateTime->format(self::OUTPUT_FORMAT);
65+
* }
66+
*
67+
* public function dateTime(): DateTimeImmutable
68+
* {
69+
* return $this->dateTime;
70+
* }
71+
*
72+
* public function __toString(): string
73+
* {
74+
* return $this->toString();
75+
* }
76+
*
77+
* private static function ensureUtc(DateTimeImmutable $dateTime): DateTimeImmutable
78+
* {
79+
* if ($dateTime->getTimezone()->getName() !== 'UTC') {
80+
* $dateTime = $dateTime->setTimezone(new \DateTimeZone('UTC'));
81+
* }
82+
*
83+
* return $dateTime;
84+
* }
85+
*/
86+
final class DateTimeFactory
87+
{
88+
private Parser $parser;
89+
private PropertyFactory $propertyFactory;
90+
91+
public function __construct(Parser $parser, bool $typed)
92+
{
93+
$this->parser = $parser;
94+
$this->propertyFactory = new PropertyFactory($typed);
95+
}
96+
97+
/**
98+
* @param StringType $typeDefinition
99+
* @return array<NodeVisitor>
100+
*/
101+
public function nodeVisitors(StringType $typeDefinition): array
102+
{
103+
$name = $typeDefinition->name() ?: 'dateTime';
104+
105+
return $this->nodeVisitorsFromNative($name);
106+
}
107+
108+
/**
109+
* @param string $name
110+
* @param string $outputFormat
111+
* @return array<NodeVisitor>
112+
*/
113+
public function nodeVisitorsFromNative(string $name, string $outputFormat = DATE_ATOM): array
114+
{
115+
$nodeVisitors = $this->propertyFactory->nodeVisitorFromNative($name, 'DateTimeImmutable');
116+
117+
\array_unshift(
118+
$nodeVisitors,
119+
new ClassConstant(
120+
new IdentifierGenerator(
121+
'OUTPUT_FORMAT',
122+
new ClassConstGenerator(
123+
'OUTPUT_FORMAT',
124+
$outputFormat,
125+
ClassConstGenerator::FLAG_PRIVATE
126+
)
127+
)
128+
)
129+
);
130+
131+
$nodeVisitors[] = $this->methodFromDateTime($name);
132+
$nodeVisitors[] = $this->methodFromString($name);
133+
$nodeVisitors[] = $this->methodMagicConstruct($name);
134+
$nodeVisitors[] = $this->methodToString($name);
135+
$nodeVisitors[] = $this->methodDateTime($name);
136+
$nodeVisitors[] = $this->methodMagicToString();
137+
$nodeVisitors[] = $this->methodEnsureUtc($name);
138+
139+
return $nodeVisitors;
140+
}
141+
142+
public function methodFromDateTime(string $argumentName): NodeVisitor
143+
{
144+
$method = new MethodGenerator(
145+
'fromDateTime',
146+
[
147+
new ParameterGenerator($argumentName, 'DateTimeImmutable'),
148+
],
149+
MethodGenerator::FLAG_STATIC | MethodGenerator::FLAG_PUBLIC,
150+
new BodyGenerator($this->parser, 'return new self(self::ensureUtc($' . $argumentName . '));')
151+
);
152+
$method->setReturnType('self');
153+
154+
return new ClassMethod($method);
155+
}
156+
157+
public function methodFromString(string $argumentName): NodeVisitor
158+
{
159+
$body = <<<PHP
160+
try {
161+
\$dateTimeImmutable = new DateTimeImmutable(\$$argumentName);
162+
} catch (\Exception \$e) {
163+
throw new InvalidArgumentException(sprintf('String "%s" is not supported. Use a date time format which is compatible with ISO 8601.', \$$argumentName));
164+
}
165+
166+
\$dateTimeImmutable = self::ensureUtc(\$dateTimeImmutable);
167+
168+
return new self(\$dateTimeImmutable);
169+
PHP;
170+
171+
$method = new MethodGenerator(
172+
'fromString',
173+
[
174+
new ParameterGenerator($argumentName, 'string'),
175+
],
176+
MethodGenerator::FLAG_STATIC | MethodGenerator::FLAG_PUBLIC,
177+
new BodyGenerator($this->parser, $body)
178+
);
179+
$method->setReturnType('self');
180+
181+
return new ClassMethod($method);
182+
}
183+
184+
public function methodMagicConstruct(string $argumentName): NodeVisitor
185+
{
186+
$method = new MethodGenerator(
187+
'__construct',
188+
[
189+
new ParameterGenerator($argumentName, 'DateTimeImmutable'),
190+
],
191+
MethodGenerator::FLAG_PRIVATE,
192+
new BodyGenerator($this->parser, \sprintf('$this->%s = $%s;', $argumentName, $argumentName))
193+
);
194+
195+
return new ClassMethod($method);
196+
}
197+
198+
public function methodToString(string $argumentName): NodeVisitor
199+
{
200+
$method = new MethodGenerator(
201+
'toString',
202+
[],
203+
MethodGenerator::FLAG_PUBLIC,
204+
new BodyGenerator($this->parser, 'return $this->' . $argumentName . '->format(self::OUTPUT_FORMAT);')
205+
);
206+
$method->setReturnType('string');
207+
208+
return new ClassMethod($method);
209+
}
210+
211+
public function methodDateTime(string $argumentName): NodeVisitor
212+
{
213+
$method = new MethodGenerator(
214+
'dateTime',
215+
[],
216+
MethodGenerator::FLAG_PUBLIC,
217+
new BodyGenerator($this->parser, 'return $this->' . $argumentName . ';')
218+
);
219+
$method->setReturnType('DateTimeImmutable');
220+
221+
return new ClassMethod($method);
222+
}
223+
224+
public function methodMagicToString(): NodeVisitor
225+
{
226+
$method = new MethodGenerator(
227+
'__toString',
228+
[],
229+
MethodGenerator::FLAG_PUBLIC,
230+
new BodyGenerator($this->parser, 'return $this->toString();')
231+
);
232+
$method->setReturnType('string');
233+
234+
return new ClassMethod($method);
235+
}
236+
237+
public function methodEnsureUtc(string $argumentName): NodeVisitor
238+
{
239+
$body = <<<'PHP'
240+
if ($argumentName->getTimezone()->getName() !== 'UTC') {
241+
$argumentName = $argumentName->setTimezone(new \DateTimeZone('UTC'));
242+
}
243+
244+
return $argumentName;
245+
PHP;
246+
$body = \str_replace('argumentName', $argumentName, $body);
247+
248+
$method = new MethodGenerator(
249+
'ensureUtc',
250+
[
251+
new ParameterGenerator($argumentName, 'DateTimeImmutable'),
252+
],
253+
MethodGenerator::FLAG_STATIC | MethodGenerator::FLAG_PRIVATE,
254+
new BodyGenerator($this->parser, $body)
255+
);
256+
$method->setReturnType('DateTimeImmutable');
257+
258+
return new ClassMethod($method);
259+
}
260+
}

src/ValueObjectFactory.php

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use OpenCodeModeling\JsonSchemaToPhp\Type\StringType;
1717
use OpenCodeModeling\JsonSchemaToPhp\Type\TypeDefinition;
1818
use OpenCodeModeling\JsonSchemaToPhpAst\ValueObject\BooleanFactory;
19+
use OpenCodeModeling\JsonSchemaToPhpAst\ValueObject\DateTimeFactory;
1920
use OpenCodeModeling\JsonSchemaToPhpAst\ValueObject\IntegerFactory;
2021
use OpenCodeModeling\JsonSchemaToPhpAst\ValueObject\NumberFactory;
2122
use OpenCodeModeling\JsonSchemaToPhpAst\ValueObject\StringFactory;
@@ -28,13 +29,15 @@ final class ValueObjectFactory
2829
private IntegerFactory $integerFactory;
2930
private BooleanFactory $booleanFactory;
3031
private NumberFactory $numberFactory;
32+
private DateTimeFactory $dateTimeFactory;
3133

3234
public function __construct(Parser $parser, bool $typed)
3335
{
3436
$this->stringFactory = new StringFactory($parser, $typed);
3537
$this->integerFactory = new IntegerFactory($parser, $typed);
3638
$this->booleanFactory = new BooleanFactory($parser, $typed);
3739
$this->numberFactory = new NumberFactory($parser, $typed);
40+
$this->dateTimeFactory = new DateTimeFactory($parser, $typed);
3841
}
3942

4043
/**
@@ -45,7 +48,13 @@ public function nodeVisitors(TypeDefinition $typeDefinition): array
4548
{
4649
switch (true) {
4750
case $typeDefinition instanceof StringType:
48-
return $this->stringFactory->nodeVisitors($typeDefinition);
51+
switch ($typeDefinition->format()) {
52+
case TypeDefinition::FORMAT_DATETIME:
53+
return $this->dateTimeFactory->nodeVisitors($typeDefinition);
54+
default:
55+
return $this->stringFactory->nodeVisitors($typeDefinition);
56+
}
57+
// no break
4958
case $typeDefinition instanceof IntegerType:
5059
return $this->integerFactory->nodeVisitors($typeDefinition);
5160
case $typeDefinition instanceof BooleanType:

0 commit comments

Comments
 (0)