Skip to content

Commit e29bd96

Browse files
committed
Add BCP 47 value object factory - Close #20
1 parent 38fad8d commit e29bd96

File tree

2 files changed

+350
-0
lines changed

2 files changed

+350
-0
lines changed

src/ValueObject/Bcp47Factory.php

Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
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\Builder\ClassBuilder;
14+
use OpenCodeModeling\CodeAst\Code\BodyGenerator;
15+
use OpenCodeModeling\CodeAst\Code\MethodGenerator;
16+
use OpenCodeModeling\CodeAst\Code\ParameterGenerator;
17+
use OpenCodeModeling\CodeAst\NodeVisitor\ClassMethod;
18+
use OpenCodeModeling\JsonSchemaToPhp\Type\StringType;
19+
use OpenCodeModeling\JsonSchemaToPhpAst\PropertyFactory;
20+
use PhpParser\NodeVisitor;
21+
use PhpParser\Parser;
22+
23+
/**
24+
* This file creates node visitors for a value object of type string with format BCP 47.
25+
*/
26+
final class Bcp47Factory
27+
{
28+
private Parser $parser;
29+
private PropertyFactory $propertyFactory;
30+
private bool $typed;
31+
32+
/**
33+
* @var callable
34+
*/
35+
private $propertyNameFilter;
36+
37+
public function __construct(Parser $parser, bool $typed, callable $propertyNameFilter)
38+
{
39+
$this->parser = $parser;
40+
$this->typed = $typed;
41+
$this->propertyNameFilter = $propertyNameFilter;
42+
$this->propertyFactory = new PropertyFactory($typed, $propertyNameFilter);
43+
}
44+
45+
/**
46+
* @param StringType $typeDefinition
47+
* @return array<NodeVisitor>
48+
*/
49+
public function nodeVisitors(StringType $typeDefinition): array
50+
{
51+
$name = $typeDefinition->name() ?: 'bcp47';
52+
53+
return $this->nodeVisitorsFromNative($name);
54+
}
55+
56+
public function classBuilder(StringType $typeDefinition): ClassBuilder
57+
{
58+
$name = $typeDefinition->name() ?: 'bcp47';
59+
60+
return $this->classBuilderFromNative($name)->setTyped($this->typed);
61+
}
62+
63+
/**
64+
* @param string $name
65+
* @return array<NodeVisitor>
66+
*/
67+
public function nodeVisitorsFromNative(string $name): array
68+
{
69+
$nodeVisitors = $this->propertyFactory->nodeVisitorFromNative($name, 'string');
70+
$nodeVisitors = \array_merge($nodeVisitors, $this->propertyFactory->nodeVisitorFromNative('language', '?string'));
71+
$nodeVisitors = \array_merge($nodeVisitors, $this->propertyFactory->nodeVisitorFromNative('region', '?string'));
72+
$nodeVisitors[] = new ClassMethod($this->methodFromString($name));
73+
$nodeVisitors[] = new ClassMethod($this->methodMagicConstruct($name));
74+
$nodeVisitors[] = new ClassMethod($this->methodToString($name));
75+
$nodeVisitors[] = new ClassMethod($this->methodEquals($name));
76+
$nodeVisitors[] = new ClassMethod($this->methodMagicToString($name));
77+
$nodeVisitors[] = new ClassMethod($this->methodLanguage());
78+
$nodeVisitors[] = new ClassMethod($this->methodRegion());
79+
80+
return $nodeVisitors;
81+
}
82+
83+
public function classBuilderFromNative(string $name): ClassBuilder
84+
{
85+
return ClassBuilder::fromNodes(
86+
$this->propertyFactory->propertyGenerator($name, 'string')->generate(),
87+
$this->propertyFactory->propertyGenerator('language', '?string')->generate(),
88+
$this->propertyFactory->propertyGenerator('region', '?string')->generate(),
89+
$this->methodFromString($name)->generate(),
90+
$this->methodMagicConstruct($name)->generate(),
91+
$this->methodToString($name)->generate(),
92+
$this->methodEquals($name)->generate(),
93+
$this->methodMagicToString($name)->generate(),
94+
$this->methodLanguage()->generate(),
95+
$this->methodRegion()->generate(),
96+
)->setTyped($this->typed);
97+
}
98+
99+
public function methodFromString(string $argumentName): MethodGenerator
100+
{
101+
$argumentName = ($this->propertyNameFilter)($argumentName);
102+
103+
$method = new MethodGenerator(
104+
'fromString',
105+
[
106+
new ParameterGenerator($argumentName, 'string'),
107+
],
108+
MethodGenerator::FLAG_STATIC | MethodGenerator::FLAG_PUBLIC,
109+
new BodyGenerator($this->parser, 'return new self($' . $argumentName . ');')
110+
);
111+
$method->setTyped($this->typed);
112+
$method->setReturnType('self');
113+
114+
return $method;
115+
}
116+
117+
public function methodMagicConstruct(string $argumentName): MethodGenerator
118+
{
119+
$argumentName = ($this->propertyNameFilter)($argumentName);
120+
121+
$body = \sprintf('$this->%s = $%s;', $argumentName, $argumentName);
122+
$body .= \sprintf('$parsedLocale = locale_parse($%s);', $argumentName);
123+
$body .= <<<'PHP'
124+
$this->language = $parsedLocale['language'] ?? null;
125+
$this->region = $parsedLocale['region'] ?? null;
126+
PHP;
127+
128+
$method = new MethodGenerator(
129+
'__construct',
130+
[
131+
new ParameterGenerator($argumentName, 'string'),
132+
],
133+
MethodGenerator::FLAG_PRIVATE,
134+
new BodyGenerator($this->parser, $body)
135+
);
136+
$method->setTyped($this->typed);
137+
138+
return $method;
139+
}
140+
141+
public function methodToString(string $argumentName): MethodGenerator
142+
{
143+
$argumentName = ($this->propertyNameFilter)($argumentName);
144+
145+
$method = new MethodGenerator(
146+
'toString',
147+
[],
148+
MethodGenerator::FLAG_PUBLIC,
149+
new BodyGenerator($this->parser, 'return $this->' . $argumentName . ';')
150+
);
151+
$method->setTyped($this->typed);
152+
$method->setReturnType('string');
153+
154+
return $method;
155+
}
156+
157+
public function methodEquals(string $propertyName, string $argumentName = 'other'): MethodGenerator
158+
{
159+
$argumentName = ($this->propertyNameFilter)($argumentName);
160+
$propertyName = ($this->propertyNameFilter)($propertyName);
161+
162+
$body = <<<PHP
163+
if(!\$$argumentName instanceof self) {
164+
return false;
165+
}
166+
167+
return \$this->$propertyName === \$$argumentName->$propertyName;
168+
PHP;
169+
170+
$method = new MethodGenerator(
171+
'equals',
172+
[
173+
new ParameterGenerator($argumentName),
174+
],
175+
MethodGenerator::FLAG_PUBLIC,
176+
new BodyGenerator($this->parser, $body)
177+
);
178+
$method->setTyped($this->typed);
179+
$method->setReturnType('bool');
180+
181+
return $method;
182+
}
183+
184+
public function methodMagicToString(string $argumentName): MethodGenerator
185+
{
186+
$argumentName = ($this->propertyNameFilter)($argumentName);
187+
188+
$method = new MethodGenerator(
189+
'__toString',
190+
[],
191+
MethodGenerator::FLAG_PUBLIC,
192+
new BodyGenerator($this->parser, 'return $this->' . $argumentName . ';')
193+
);
194+
$method->setTyped($this->typed);
195+
$method->setReturnType('string');
196+
197+
return $method;
198+
}
199+
200+
public function methodLanguage(): MethodGenerator
201+
{
202+
$method = new MethodGenerator(
203+
'language',
204+
[],
205+
MethodGenerator::FLAG_PUBLIC,
206+
new BodyGenerator($this->parser, 'return $this->language;')
207+
);
208+
$method->setTyped($this->typed);
209+
$method->setReturnType('?string');
210+
211+
return $method;
212+
}
213+
214+
public function methodRegion(): MethodGenerator
215+
{
216+
$method = new MethodGenerator(
217+
'region',
218+
[],
219+
MethodGenerator::FLAG_PUBLIC,
220+
new BodyGenerator($this->parser, 'return $this->region;')
221+
);
222+
$method->setTyped($this->typed);
223+
$method->setReturnType('?string');
224+
225+
return $method;
226+
}
227+
}
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
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 OpenCodeModelingTest\JsonSchemaToPhpAst\ValueObject;
12+
13+
use OpenCodeModeling\JsonSchemaToPhp\Type\StringType;
14+
use OpenCodeModeling\JsonSchemaToPhpAst\ValueObject\Bcp47Factory;
15+
use PhpParser\NodeTraverser;
16+
use PhpParser\NodeVisitor;
17+
18+
final class Bcp47FactoryTest extends BaseTestCase
19+
{
20+
private Bcp47Factory $stringFactory;
21+
22+
public function setUp(): void
23+
{
24+
parent::setUp();
25+
$this->stringFactory = new Bcp47Factory($this->parser, true, $this->propertyNameFilter);
26+
}
27+
28+
/**
29+
* @test
30+
*/
31+
public function it_generates_code_from_native(): void
32+
{
33+
$this->assertCode($this->stringFactory->nodeVisitorsFromNative('bcp47'));
34+
}
35+
36+
/**
37+
* @test
38+
*/
39+
public function it_generates_code_via_value_object_factory(): void
40+
{
41+
$this->assertCode(
42+
$this->voFactory->nodeVisitors(
43+
StringType::fromDefinition(['type' => 'string', 'name' => 'bcp47', 'format' => 'BCP 47'])
44+
)
45+
);
46+
}
47+
48+
/**
49+
* @test
50+
*/
51+
public function it_generates_code_via_value_object_factory_with_class_builder(): void
52+
{
53+
$classBuilder = $this->voFactory->classBuilder(
54+
StringType::fromDefinition(['type' => 'string', 'name' => 'bcp47', 'format' => 'BCP 47'])
55+
);
56+
$classBuilder->setName('LocaleVO');
57+
58+
$this->assertCode(
59+
$classBuilder->generate($this->parser)
60+
);
61+
}
62+
63+
/**
64+
* @param array<NodeVisitor> $nodeVisitors
65+
*/
66+
private function assertCode(array $nodeVisitors): void
67+
{
68+
$ast = $this->parser->parse('<?php final class LocaleVO {}');
69+
70+
$nodeTraverser = new NodeTraverser();
71+
72+
foreach ($nodeVisitors as $nodeVisitor) {
73+
$nodeTraverser->addVisitor($nodeVisitor);
74+
}
75+
76+
$expected = <<<'EOF'
77+
<?php
78+
79+
final class LocaleVO
80+
{
81+
private string $bcp47;
82+
private ?string $language;
83+
private ?string $region;
84+
public static function fromString(string $bcp47) : self
85+
{
86+
return new self($bcp47);
87+
}
88+
private function __construct(string $bcp47)
89+
{
90+
$this->bcp47 = $bcp47;
91+
$parsedLocale = locale_parse($bcp47);
92+
$this->language = $parsedLocale['language'] ?? null;
93+
$this->region = $parsedLocale['region'] ?? null;
94+
}
95+
public function toString() : string
96+
{
97+
return $this->bcp47;
98+
}
99+
public function equals($other) : bool
100+
{
101+
if (!$other instanceof self) {
102+
return false;
103+
}
104+
return $this->bcp47 === $other->bcp47;
105+
}
106+
public function __toString() : string
107+
{
108+
return $this->bcp47;
109+
}
110+
public function language() : ?string
111+
{
112+
return $this->language;
113+
}
114+
public function region() : ?string
115+
{
116+
return $this->region;
117+
}
118+
}
119+
EOF;
120+
121+
$this->assertSame($expected, $this->printer->prettyPrintFile($nodeTraverser->traverse($ast)));
122+
}
123+
}

0 commit comments

Comments
 (0)