1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace RunOpenCode\AbstractBuilder\Generator; |
4
|
|
|
|
5
|
|
|
use PhpParser\BuilderFactory; |
6
|
|
|
use PhpParser\Parser; |
7
|
|
|
use PhpParser\ParserFactory; |
8
|
|
|
use PhpParser\PrettyPrinter\Standard; |
9
|
|
|
use RunOpenCode\AbstractBuilder\Ast\Metadata\ClassMetadata; |
10
|
|
|
use RunOpenCode\AbstractBuilder\Ast\Metadata\FileMetadata; |
11
|
|
|
use RunOpenCode\AbstractBuilder\Ast\Metadata\MethodMetadata; |
12
|
|
|
use RunOpenCode\AbstractBuilder\Ast\MetadataLoader; |
13
|
|
|
use RunOpenCode\AbstractBuilder\Command\Question\ClassChoice; |
14
|
|
|
use RunOpenCode\AbstractBuilder\ReflectiveAbstractBuilder; |
15
|
|
|
use RunOpenCode\AbstractBuilder\Utils\ClassUtils; |
16
|
|
|
use PHPParser\Node; |
17
|
|
|
|
18
|
|
|
class BuilderGenerator |
19
|
|
|
{ |
20
|
|
|
/** |
21
|
|
|
* @var BuilderFactory |
22
|
|
|
*/ |
23
|
|
|
private $factory; |
24
|
|
|
|
25
|
|
|
/** |
26
|
|
|
* @var Parser |
27
|
|
|
*/ |
28
|
|
|
private $parser; |
29
|
|
|
|
30
|
|
|
/** |
31
|
|
|
* @var MetadataLoader |
32
|
|
|
*/ |
33
|
|
|
private $loader; |
34
|
|
|
|
35
|
|
|
/** |
36
|
|
|
* @var Standard |
37
|
|
|
*/ |
38
|
|
|
private $printer; |
39
|
|
|
|
40
|
|
|
public function __construct() |
41
|
|
|
{ |
42
|
|
|
$this->factory = new BuilderFactory(); |
43
|
|
|
$this->parser = (new ParserFactory())->create(ParserFactory::ONLY_PHP7); |
44
|
|
|
$this->loader = new MetadataLoader(); |
45
|
|
|
$this->printer = new Standard(); |
46
|
|
|
} |
47
|
|
|
|
48
|
|
|
public function create(ClassChoice $buildingClassChoice, $builderFilename, $builderClassName) |
49
|
|
|
{ |
50
|
|
|
|
51
|
|
|
$namespace = $this->factory->namespace($ns = ClassUtils::getNamespace($builderClassName)); |
52
|
|
|
|
53
|
|
|
$namespace->addStmt($this->factory->use(ReflectiveAbstractBuilder::class)); |
54
|
|
|
|
55
|
|
|
$builderClass = $this->factory->class($builderClassName) |
56
|
|
|
->extend('ReflectiveAbstractBuilder') |
57
|
|
|
->setDocComment(sprintf( |
58
|
|
|
' |
59
|
|
|
/** |
60
|
|
|
* Class %s |
61
|
|
|
* |
62
|
|
|
* This class is implementation of builder pattern |
63
|
|
|
* for class %s. |
64
|
|
|
* |
65
|
|
|
* This class is autogenerated by runopencode/abstract-builder library. |
66
|
|
|
* |
67
|
|
|
* @package %s |
68
|
|
|
* |
69
|
|
|
* @see %s |
70
|
|
|
* @see https://en.wikipedia.org/wiki/Builder_pattern |
71
|
|
|
*/', $builderClassName, $buildingClassChoice->getClass()->getName(), $ns, ReflectiveAbstractBuilder::class)); |
72
|
|
|
|
73
|
|
|
if ($buildingClassChoice->getClass()->isFinal()) { |
74
|
|
|
$builderClass->makeFinal(); |
75
|
|
|
} |
76
|
|
|
|
77
|
|
|
if ($buildingClassChoice->getClass()->isAbstract()) { |
78
|
|
|
$builderClass->makeAbstract(); |
79
|
|
|
} |
80
|
|
|
|
81
|
|
|
if (!$buildingClassChoice->getClass()->isAbstract()) { |
82
|
|
|
|
83
|
|
|
$buildMethod = $this->factory->method('build') |
84
|
|
|
->makePublic() |
85
|
|
|
->addStmts($this->parser->parse( |
|
|
|
|
86
|
|
|
<<<'CODE' |
87
|
|
|
<?php return parent::build(); |
88
|
|
|
CODE |
89
|
|
|
|
90
|
|
|
)) |
91
|
|
|
->setDocComment(sprintf( |
92
|
|
|
' |
93
|
|
|
/** |
94
|
|
|
* Builds new instance of %s from provided arguments. |
95
|
|
|
* |
96
|
|
|
* @return %s |
97
|
|
|
*/', $buildingClassChoice->getClass()->getName(), $buildingClassChoice->getClass()->getName() |
98
|
|
|
)); |
99
|
|
|
|
100
|
|
|
$builderClass->addStmt($buildMethod->getNode()); |
101
|
|
|
} |
102
|
|
|
|
103
|
|
|
$getObjectFqcnMethod = $this->factory->method('getObjectFqcn') |
104
|
|
|
->makeProtected() |
105
|
|
|
->addStmt(new Node\Stmt\Return_(new Node\Scalar\String_($builderClassName))) |
106
|
|
|
->setDocComment( |
107
|
|
|
' |
108
|
|
|
/** |
109
|
|
|
* {@inheritdoc} |
110
|
|
|
*/' |
111
|
|
|
); |
112
|
|
|
|
113
|
|
|
$builderClass->addStmt($getObjectFqcnMethod->getNode()); |
114
|
|
|
|
115
|
|
|
$configureParametersMethod = $this->factory->method('configureParameters') |
116
|
|
|
->makeProtected() |
117
|
|
|
->addStmts($this->parser->parse( |
|
|
|
|
118
|
|
|
<<<'CODE' |
119
|
|
|
<?php $defaults = parent::configureParameters(); |
120
|
|
|
// Modify default values here |
121
|
|
|
return $defaults; |
122
|
|
|
CODE |
123
|
|
|
|
124
|
|
|
)) |
125
|
|
|
->setDocComment( |
126
|
|
|
' |
127
|
|
|
/** |
128
|
|
|
* You can override default building parameter values here |
129
|
|
|
* |
130
|
|
|
* {@inheritdoc} |
131
|
|
|
*/' |
132
|
|
|
); |
133
|
|
|
|
134
|
|
|
$builderClass->addStmt($configureParametersMethod->getNode()); |
135
|
|
|
|
136
|
|
|
$namespace->addStmt($builderClass->getNode()); |
137
|
|
|
|
138
|
|
|
$classMetadata = new ClassMetadata( |
139
|
|
|
$builderClassName, |
140
|
|
|
$this->loader->load(ReflectiveAbstractBuilder::class)->getClass(ReflectiveAbstractBuilder::class), |
141
|
|
|
[], |
142
|
|
|
$buildingClassChoice->getClass()->isFinal(), |
143
|
|
|
$buildingClassChoice->getClass()->isAbstract(), |
144
|
|
|
[ |
145
|
|
|
MethodMetadata::fromClassMethod($configureParametersMethod->getNode()), |
146
|
|
|
MethodMetadata::fromClassMethod($getObjectFqcnMethod->getNode()) |
147
|
|
|
], |
148
|
|
|
$builderClass->getNode() |
149
|
|
|
); |
150
|
|
|
|
151
|
|
|
$fileMetadata = new FileMetadata($builderFilename, [], [$classMetadata], [], [$namespace->getNode()]); |
152
|
|
|
|
153
|
|
|
return $fileMetadata; |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
public function write(FileMetadata $file) |
157
|
|
|
{ |
158
|
|
|
return $this->printer->prettyPrint($file->getAst()); |
159
|
|
|
} |
160
|
|
|
} |
161
|
|
|
|
This check looks at variables that are passed out again to other methods.
If the outgoing method call has stricter type requirements than the method itself, an issue is raised.
An additional type check may prevent trouble.