Skip to content

Commit d181276

Browse files
committed
Fix default value code generation for properties and parameters - Close #68
1 parent 1c5fce7 commit d181276

8 files changed

+328
-45
lines changed

src/Builder/ClassPropertyBuilder.php

Lines changed: 46 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,15 @@ final class ClassPropertyBuilder
5353
*/
5454
private ?DocBlock $docBlock = null;
5555

56+
private PropertyGenerator $propertyGenerator;
57+
5658
private function __construct()
5759
{
5860
}
5961

6062
public static function fromNode(Node\Stmt\Property $node, bool $typed = true): self
6163
{
6264
$self = new self();
63-
6465
$type = null;
6566

6667
switch (true) {
@@ -76,10 +77,33 @@ public static function fromNode(Node\Stmt\Property $node, bool $typed = true): s
7677
}
7778

7879
$self->name = $node->props[0]->name->name;
79-
$self->defaultValue = $node->props[0]->default;
8080
$self->type = $type;
8181
$self->visibility = $node->flags;
8282
$self->typed = $typed;
83+
$self->propertyGenerator = new PropertyGenerator($self->name, $self->type);
84+
85+
$defaultValue = $node->props[0]->default;
86+
87+
switch (true) {
88+
case $defaultValue instanceof Node\Expr\ConstFetch:
89+
$self->defaultValue = $defaultValue->name->toString();
90+
91+
if ($self->defaultValue === 'null') {
92+
$self->defaultValue = null;
93+
}
94+
$self->propertyGenerator->setDefaultValue($self->defaultValue);
95+
break;
96+
case $defaultValue instanceof Node\Expr\ClassConstFetch:
97+
$self->defaultValue = $defaultValue->class->toString() . '::'. $defaultValue->name->toString();
98+
$self->propertyGenerator->setDefaultValue($self->defaultValue);
99+
break;
100+
default:
101+
if ($defaultValue !== null) {
102+
$self->defaultValue = $defaultValue;
103+
$self->propertyGenerator->setDefaultValue($self->defaultValue);
104+
}
105+
break;
106+
}
83107

84108
$comments = $node->getAttribute('comments');
85109

@@ -105,6 +129,7 @@ public static function fromScratch(string $name, string $type, bool $typed = tru
105129
$self->type = $type;
106130
$self->typed = $typed;
107131
$self->visibility = ClassConstGenerator::FLAG_PRIVATE;
132+
$self->propertyGenerator = new PropertyGenerator($self->name, $self->type);
108133

109134
return $self;
110135
}
@@ -203,6 +228,20 @@ public function overrideDocBlock(?DocBlock $docBlock): self
203228
return $this;
204229
}
205230

231+
/**
232+
* @param mixed $defaultValue
233+
*/
234+
public function setDefaultValue($defaultValue): void
235+
{
236+
$this->defaultValue = $defaultValue;
237+
$this->propertyGenerator->setDefaultValue($this->defaultValue);
238+
}
239+
240+
public function getDefaultValue()
241+
{
242+
return $this->defaultValue;
243+
}
244+
206245
public function generate(): NodeVisitor
207246
{
208247
return new Property($this->propertyGenerator());
@@ -211,14 +250,12 @@ public function generate(): NodeVisitor
211250
private function propertyGenerator(): PropertyGenerator
212251
{
213252
$flags = $this->visibility;
253+
$this->propertyGenerator->setFlags($flags);
254+
$this->propertyGenerator->setDocBlockComment($this->docBlockComment);
255+
$this->propertyGenerator->setTypeDocBlockHint($this->typeDocBlockHint);
256+
$this->propertyGenerator->overrideDocBlock($this->docBlock);
214257

215-
$propertyGenerator = new PropertyGenerator($this->name, $this->type, $this->defaultValue, $this->typed, $flags);
216-
217-
$propertyGenerator->setDocBlockComment($this->docBlockComment);
218-
$propertyGenerator->setTypeDocBlockHint($this->typeDocBlockHint);
219-
$propertyGenerator->overrideDocBlock($this->docBlock);
220-
221-
return $propertyGenerator;
258+
return $this->propertyGenerator;
222259
}
223260

224261
public function injectVisitors(NodeTraverser $nodeTraverser): void

src/Builder/ParameterBuilder.php

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ final class ParameterBuilder
4141
*/
4242
private ?string $typeDocBlockHint = null;
4343

44+
private ParameterGenerator $parameterGenerator;
45+
4446
private function __construct()
4547
{
4648
}
@@ -66,19 +68,40 @@ public static function fromNode(Node\Param $node): self
6668
$self->type = $type;
6769
$self->variadic = $node->variadic;
6870
$self->passedByReference = $node->byRef;
71+
$self->parameterGenerator = new ParameterGenerator($self->name, $self->type);
72+
73+
$defaultValue = $node->default;
74+
75+
switch (true) {
76+
case $defaultValue instanceof Node\Expr\ConstFetch:
77+
$self->defaultValue = $defaultValue->name->toString();
78+
79+
if ($self->defaultValue === 'null') {
80+
$self->defaultValue = null;
81+
}
82+
$self->parameterGenerator->setDefaultValue($self->defaultValue);
83+
break;
84+
case $defaultValue instanceof Node\Expr\ClassConstFetch:
85+
$self->defaultValue = $defaultValue->class->toString() . '::'. $defaultValue->name->toString();
86+
$self->parameterGenerator->setDefaultValue($self->defaultValue);
87+
break;
88+
default:
89+
if ($defaultValue !== null) {
90+
$self->defaultValue = $defaultValue;
91+
$self->parameterGenerator->setDefaultValue($self->defaultValue);
92+
}
93+
break;
94+
}
6995

7096
return $self;
7197
}
7298

73-
public static function fromScratch(
74-
string $name,
75-
?string $type = null,
76-
$defaultValue = null
77-
): self {
99+
public static function fromScratch(string $name, ?string $type = null): self
100+
{
78101
$self = new self();
79102
$self->name = $name;
80103
$self->type = $type;
81-
$self->defaultValue = $defaultValue;
104+
$self->parameterGenerator = new ParameterGenerator($self->name, $self->type);
82105

83106
return $self;
84107
}
@@ -112,6 +135,7 @@ public function getDefaultValue()
112135
public function setDefaultValue($defaultValue): self
113136
{
114137
$this->defaultValue = $defaultValue;
138+
$this->parameterGenerator->setDefaultValue($this->defaultValue);
115139

116140
return $this;
117141
}
@@ -166,11 +190,10 @@ public function setTypeDocBlockHint(?string $typeDocBlockHint): self
166190

167191
public function generate(): ParameterGenerator
168192
{
169-
$methodGenerator = new ParameterGenerator($this->name, $this->type, $this->defaultValue, $this->passedByReference);
170-
171-
$methodGenerator->setTypeDocBlockHint($this->typeDocBlockHint);
172-
$methodGenerator->setVariadic($this->variadic);
193+
$this->parameterGenerator->setPassedByReference($this->passedByReference);
194+
$this->parameterGenerator->setTypeDocBlockHint($this->typeDocBlockHint);
195+
$this->parameterGenerator->setVariadic($this->variadic);
173196

174-
return $methodGenerator;
197+
return $this->parameterGenerator;
175198
}
176199
}

src/Code/ParameterGenerator.php

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,23 +55,18 @@ final class ParameterGenerator
5555
/**
5656
* @param string $name
5757
* @param string|null $type
58-
* @param mixed $defaultValue
5958
* @param bool $passByReference
6059
*/
6160
public function __construct(
6261
string $name,
6362
string $type = null,
64-
$defaultValue = null,
6563
bool $passByReference = false
6664
) {
6765
$this->setName($name);
6866

6967
if (null !== $type) {
7068
$this->setType($type);
7169
}
72-
if (null !== $defaultValue) {
73-
$this->setDefaultValue($defaultValue);
74-
}
7570
if (false !== $passByReference) {
7671
$this->setPassedByReference(true);
7772
}
@@ -111,8 +106,6 @@ public function getName(): string
111106
/**
112107
* Set the default value of the parameter.
113108
*
114-
* Certain variables are difficult to express
115-
*
116109
* @param ValueGenerator|mixed $defaultValue
117110
* @return ParameterGenerator
118111
*/

src/Code/PropertyGenerator.php

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@ final class PropertyGenerator extends AbstractMemberGenerator
5959
public function __construct(
6060
string $name = null,
6161
string $type = null,
62-
$defaultValue = null,
6362
bool $typed = true,
6463
int $flags = self::FLAG_PRIVATE
6564
) {
@@ -71,10 +70,6 @@ public function __construct(
7170
$this->setType($type);
7271
}
7372

74-
if (null !== $defaultValue) {
75-
$this->setDefaultValue($defaultValue);
76-
}
77-
7873
$this->typed = $typed;
7974

8075
if ($flags !== self::FLAG_PUBLIC) {
@@ -117,19 +112,16 @@ public function overrideDocBlock(?DocBlock $docBlock): void
117112
}
118113

119114
/**
120-
* @param ValueGenerator|mixed $defaultValue
121-
* @param string $defaultValueType
115+
* Set the default value of the parameter.
122116
*
117+
* @param ValueGenerator|mixed $defaultValue
123118
* @return PropertyGenerator
124119
*/
125-
public function setDefaultValue(
126-
$defaultValue,
127-
$defaultValueType = ValueGenerator::TYPE_AUTO
128-
): self {
120+
public function setDefaultValue($defaultValue): self
121+
{
129122
if (! $defaultValue instanceof ValueGenerator) {
130-
$defaultValue = new ValueGenerator($defaultValue, $defaultValueType);
123+
$defaultValue = new ValueGenerator($defaultValue);
131124
}
132-
133125
$this->defaultValue = $defaultValue;
134126

135127
return $this;

tests/Builder/ClassPropertyBuilderTest.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
use OpenCodeModeling\CodeAst\Builder\ClassBuilder;
1414
use OpenCodeModeling\CodeAst\Builder\ClassPropertyBuilder;
15+
use OpenCodeModeling\CodeAst\Code\PropertyGenerator;
1516
use PhpParser\NodeTraverser;
1617
use PhpParser\Parser;
1718
use PhpParser\ParserFactory;
@@ -207,4 +208,32 @@ class TestClass
207208

208209
$this->assertSame($expected, $this->printer->prettyPrintFile($nodeTraverser->traverse($this->parser->parse(''))));
209210
}
211+
212+
/**
213+
* @test
214+
*/
215+
public function it_generates_property_with_default_value_from_node(): void
216+
{
217+
$classBuilder = ClassBuilder::fromScratch('TestClass');
218+
$classBuilder->addProperty(
219+
ClassPropertyBuilder::fromNode(
220+
(new PropertyGenerator('recordData', 'array'))->setDefaultValue(null)->generate()
221+
)
222+
);
223+
224+
$nodeTraverser = new NodeTraverser();
225+
$classBuilder->injectVisitors($nodeTraverser, $this->parser);
226+
227+
$expected = <<<'EOF'
228+
<?php
229+
230+
declare (strict_types=1);
231+
class TestClass
232+
{
233+
private array $recordData = null;
234+
}
235+
EOF;
236+
237+
$this->assertSame($expected, $this->printer->prettyPrintFile($nodeTraverser->traverse($this->parser->parse(''))));
238+
}
210239
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<?php
2+
3+
/**
4+
* @see https://github.com/open-code-modeling/php-code-ast for the canonical source repository
5+
* @copyright https://github.com/open-code-modeling/php-code-ast/blob/master/COPYRIGHT.md
6+
* @license https://github.com/open-code-modeling/php-code-ast/blob/master/LICENSE.md MIT License
7+
*/
8+
9+
declare(strict_types=1);
10+
11+
namespace OpenCodeModelingTest\CodeAst\Builder;
12+
13+
use OpenCodeModeling\CodeAst\Builder\ClassBuilder;
14+
use OpenCodeModeling\CodeAst\Builder\ClassMethodBuilder;
15+
use OpenCodeModeling\CodeAst\Builder\ParameterBuilder;
16+
use PhpParser\NodeTraverser;
17+
use PhpParser\Parser;
18+
use PhpParser\ParserFactory;
19+
use PhpParser\PrettyPrinter\Standard;
20+
use PHPUnit\Framework\TestCase;
21+
22+
final class ParameterBuilderTest extends TestCase
23+
{
24+
/**
25+
* @var Parser
26+
*/
27+
private Parser $parser;
28+
29+
/**
30+
* @var Standard
31+
*/
32+
private Standard $printer;
33+
34+
public function setUp(): void
35+
{
36+
$this->parser = (new ParserFactory())->create(ParserFactory::ONLY_PHP7);
37+
$this->printer = new Standard(['shortArraySyntax' => true]);
38+
}
39+
40+
/**
41+
* @test
42+
*/
43+
public function it_generates_property_with_default_value_from_node(): void
44+
{
45+
$method = ClassMethodBuilder::fromScratch('setRecordData')
46+
->setReturnType('void')
47+
->setParameters(
48+
ParameterBuilder::fromScratch('recordData', 'array')->setDefaultValue(null)
49+
);
50+
$classBuilder = ClassBuilder::fromScratch('TestClass');
51+
$classBuilder->addMethod($method);
52+
53+
$nodeTraverser = new NodeTraverser();
54+
$classBuilder->injectVisitors($nodeTraverser, $this->parser);
55+
56+
$expected = <<<'EOF'
57+
<?php
58+
59+
declare (strict_types=1);
60+
class TestClass
61+
{
62+
public function setRecordData(array $recordData = null) : void
63+
{
64+
}
65+
}
66+
EOF;
67+
$this->assertSame($expected, $this->printer->prettyPrintFile($nodeTraverser->traverse($this->parser->parse(''))));
68+
69+
$classBuilder = ClassBuilder::fromNodes(...$nodeTraverser->traverse($this->parser->parse('')));
70+
$nodeTraverser = new NodeTraverser();
71+
$classBuilder->injectVisitors($nodeTraverser, $this->parser);
72+
73+
$this->assertSame($expected, $this->printer->prettyPrintFile($nodeTraverser->traverse($this->parser->parse(''))));
74+
}
75+
}

0 commit comments

Comments
 (0)