Chris@13
|
1 <?php declare(strict_types=1);
|
Chris@0
|
2
|
Chris@0
|
3 namespace PhpParser;
|
Chris@0
|
4
|
Chris@13
|
5 use PhpParser\Node\Arg;
|
Chris@0
|
6 use PhpParser\Node\Expr;
|
Chris@13
|
7 use PhpParser\Node\Expr\BinaryOp\Concat;
|
Chris@13
|
8 use PhpParser\Node\Identifier;
|
Chris@13
|
9 use PhpParser\Node\Name;
|
Chris@13
|
10 use PhpParser\Node\Scalar\LNumber;
|
Chris@13
|
11 use PhpParser\Node\Scalar\String_;
|
Chris@0
|
12
|
Chris@17
|
13 class BuilderFactoryTest extends \PHPUnit\Framework\TestCase
|
Chris@0
|
14 {
|
Chris@0
|
15 /**
|
Chris@0
|
16 * @dataProvider provideTestFactory
|
Chris@0
|
17 */
|
Chris@0
|
18 public function testFactory($methodName, $className) {
|
Chris@0
|
19 $factory = new BuilderFactory;
|
Chris@0
|
20 $this->assertInstanceOf($className, $factory->$methodName('test'));
|
Chris@0
|
21 }
|
Chris@0
|
22
|
Chris@0
|
23 public function provideTestFactory() {
|
Chris@13
|
24 return [
|
Chris@17
|
25 ['namespace', Builder\Namespace_::class],
|
Chris@17
|
26 ['class', Builder\Class_::class],
|
Chris@17
|
27 ['interface', Builder\Interface_::class],
|
Chris@17
|
28 ['trait', Builder\Trait_::class],
|
Chris@17
|
29 ['method', Builder\Method::class],
|
Chris@17
|
30 ['function', Builder\Function_::class],
|
Chris@17
|
31 ['property', Builder\Property::class],
|
Chris@17
|
32 ['param', Builder\Param::class],
|
Chris@17
|
33 ['use', Builder\Use_::class],
|
Chris@17
|
34 ['useFunction', Builder\Use_::class],
|
Chris@17
|
35 ['useConst', Builder\Use_::class],
|
Chris@13
|
36 ];
|
Chris@13
|
37 }
|
Chris@13
|
38
|
Chris@13
|
39 public function testVal() {
|
Chris@13
|
40 // This method is a wrapper around BuilderHelpers::normalizeValue(),
|
Chris@13
|
41 // which is already tested elsewhere
|
Chris@13
|
42 $factory = new BuilderFactory();
|
Chris@13
|
43 $this->assertEquals(
|
Chris@13
|
44 new String_("foo"),
|
Chris@13
|
45 $factory->val("foo")
|
Chris@0
|
46 );
|
Chris@0
|
47 }
|
Chris@0
|
48
|
Chris@13
|
49 public function testConcat() {
|
Chris@0
|
50 $factory = new BuilderFactory();
|
Chris@13
|
51 $varA = new Expr\Variable('a');
|
Chris@13
|
52 $varB = new Expr\Variable('b');
|
Chris@13
|
53 $varC = new Expr\Variable('c');
|
Chris@13
|
54
|
Chris@13
|
55 $this->assertEquals(
|
Chris@13
|
56 new Concat($varA, $varB),
|
Chris@13
|
57 $factory->concat($varA, $varB)
|
Chris@13
|
58 );
|
Chris@13
|
59 $this->assertEquals(
|
Chris@13
|
60 new Concat(new Concat($varA, $varB), $varC),
|
Chris@13
|
61 $factory->concat($varA, $varB, $varC)
|
Chris@13
|
62 );
|
Chris@13
|
63 $this->assertEquals(
|
Chris@13
|
64 new Concat(new Concat(new String_("a"), $varB), new String_("c")),
|
Chris@13
|
65 $factory->concat("a", $varB, "c")
|
Chris@13
|
66 );
|
Chris@13
|
67 }
|
Chris@13
|
68
|
Chris@13
|
69 public function testConcatOneError() {
|
Chris@17
|
70 $this->expectException(\LogicException::class);
|
Chris@17
|
71 $this->expectExceptionMessage('Expected at least two expressions');
|
Chris@13
|
72 (new BuilderFactory())->concat("a");
|
Chris@13
|
73 }
|
Chris@13
|
74
|
Chris@13
|
75 public function testConcatInvalidExpr() {
|
Chris@17
|
76 $this->expectException(\LogicException::class);
|
Chris@17
|
77 $this->expectExceptionMessage('Expected string or Expr');
|
Chris@13
|
78 (new BuilderFactory())->concat("a", 42);
|
Chris@13
|
79 }
|
Chris@13
|
80
|
Chris@13
|
81 public function testArgs() {
|
Chris@13
|
82 $factory = new BuilderFactory();
|
Chris@13
|
83 $unpack = new Arg(new Expr\Variable('c'), false, true);
|
Chris@13
|
84 $this->assertEquals(
|
Chris@13
|
85 [
|
Chris@13
|
86 new Arg(new Expr\Variable('a')),
|
Chris@13
|
87 new Arg(new String_('b')),
|
Chris@13
|
88 $unpack
|
Chris@13
|
89 ],
|
Chris@13
|
90 $factory->args([new Expr\Variable('a'), 'b', $unpack])
|
Chris@13
|
91 );
|
Chris@13
|
92 }
|
Chris@13
|
93
|
Chris@13
|
94 public function testCalls() {
|
Chris@13
|
95 $factory = new BuilderFactory();
|
Chris@13
|
96
|
Chris@13
|
97 // Simple function call
|
Chris@13
|
98 $this->assertEquals(
|
Chris@13
|
99 new Expr\FuncCall(
|
Chris@13
|
100 new Name('var_dump'),
|
Chris@13
|
101 [new Arg(new String_('str'))]
|
Chris@13
|
102 ),
|
Chris@13
|
103 $factory->funcCall('var_dump', ['str'])
|
Chris@13
|
104 );
|
Chris@13
|
105 // Dynamic function call
|
Chris@13
|
106 $this->assertEquals(
|
Chris@13
|
107 new Expr\FuncCall(new Expr\Variable('fn')),
|
Chris@13
|
108 $factory->funcCall(new Expr\Variable('fn'))
|
Chris@13
|
109 );
|
Chris@13
|
110
|
Chris@13
|
111 // Simple method call
|
Chris@13
|
112 $this->assertEquals(
|
Chris@13
|
113 new Expr\MethodCall(
|
Chris@13
|
114 new Expr\Variable('obj'),
|
Chris@13
|
115 new Identifier('method'),
|
Chris@13
|
116 [new Arg(new LNumber(42))]
|
Chris@13
|
117 ),
|
Chris@13
|
118 $factory->methodCall(new Expr\Variable('obj'), 'method', [42])
|
Chris@13
|
119 );
|
Chris@13
|
120 // Explicitly pass Identifier node
|
Chris@13
|
121 $this->assertEquals(
|
Chris@13
|
122 new Expr\MethodCall(
|
Chris@13
|
123 new Expr\Variable('obj'),
|
Chris@13
|
124 new Identifier('method')
|
Chris@13
|
125 ),
|
Chris@13
|
126 $factory->methodCall(new Expr\Variable('obj'), new Identifier('method'))
|
Chris@13
|
127 );
|
Chris@13
|
128 // Dynamic method call
|
Chris@13
|
129 $this->assertEquals(
|
Chris@13
|
130 new Expr\MethodCall(
|
Chris@13
|
131 new Expr\Variable('obj'),
|
Chris@13
|
132 new Expr\Variable('method')
|
Chris@13
|
133 ),
|
Chris@13
|
134 $factory->methodCall(new Expr\Variable('obj'), new Expr\Variable('method'))
|
Chris@13
|
135 );
|
Chris@13
|
136
|
Chris@13
|
137 // Simple static method call
|
Chris@13
|
138 $this->assertEquals(
|
Chris@13
|
139 new Expr\StaticCall(
|
Chris@13
|
140 new Name\FullyQualified('Foo'),
|
Chris@13
|
141 new Identifier('bar'),
|
Chris@13
|
142 [new Arg(new Expr\Variable('baz'))]
|
Chris@13
|
143 ),
|
Chris@13
|
144 $factory->staticCall('\Foo', 'bar', [new Expr\Variable('baz')])
|
Chris@13
|
145 );
|
Chris@13
|
146 // Dynamic static method call
|
Chris@13
|
147 $this->assertEquals(
|
Chris@13
|
148 new Expr\StaticCall(
|
Chris@13
|
149 new Expr\Variable('foo'),
|
Chris@13
|
150 new Expr\Variable('bar')
|
Chris@13
|
151 ),
|
Chris@13
|
152 $factory->staticCall(new Expr\Variable('foo'), new Expr\Variable('bar'))
|
Chris@13
|
153 );
|
Chris@13
|
154
|
Chris@13
|
155 // Simple new call
|
Chris@13
|
156 $this->assertEquals(
|
Chris@13
|
157 new Expr\New_(new Name\FullyQualified('stdClass')),
|
Chris@13
|
158 $factory->new('\stdClass')
|
Chris@13
|
159 );
|
Chris@13
|
160 // Dynamic new call
|
Chris@13
|
161 $this->assertEquals(
|
Chris@13
|
162 new Expr\New_(
|
Chris@13
|
163 new Expr\Variable('foo'),
|
Chris@13
|
164 [new Arg(new String_('bar'))]
|
Chris@13
|
165 ),
|
Chris@13
|
166 $factory->new(new Expr\Variable('foo'), ['bar'])
|
Chris@13
|
167 );
|
Chris@13
|
168 }
|
Chris@13
|
169
|
Chris@13
|
170 public function testConstFetches() {
|
Chris@13
|
171 $factory = new BuilderFactory();
|
Chris@13
|
172 $this->assertEquals(
|
Chris@13
|
173 new Expr\ConstFetch(new Name('FOO')),
|
Chris@13
|
174 $factory->constFetch('FOO')
|
Chris@13
|
175 );
|
Chris@13
|
176 $this->assertEquals(
|
Chris@13
|
177 new Expr\ClassConstFetch(new Name('Foo'), new Identifier('BAR')),
|
Chris@13
|
178 $factory->classConstFetch('Foo', 'BAR')
|
Chris@13
|
179 );
|
Chris@13
|
180 $this->assertEquals(
|
Chris@13
|
181 new Expr\ClassConstFetch(new Expr\Variable('foo'), new Identifier('BAR')),
|
Chris@13
|
182 $factory->classConstFetch(new Expr\Variable('foo'), 'BAR')
|
Chris@13
|
183 );
|
Chris@13
|
184 }
|
Chris@13
|
185
|
Chris@17
|
186 public function testVar() {
|
Chris@17
|
187 $factory = new BuilderFactory();
|
Chris@17
|
188 $this->assertEquals(
|
Chris@17
|
189 new Expr\Variable("foo"),
|
Chris@17
|
190 $factory->var("foo")
|
Chris@17
|
191 );
|
Chris@17
|
192 $this->assertEquals(
|
Chris@17
|
193 new Expr\Variable(new Expr\Variable("foo")),
|
Chris@17
|
194 $factory->var($factory->var("foo"))
|
Chris@17
|
195 );
|
Chris@17
|
196 }
|
Chris@17
|
197
|
Chris@17
|
198 public function testPropertyFetch() {
|
Chris@17
|
199 $f = new BuilderFactory();
|
Chris@17
|
200 $this->assertEquals(
|
Chris@17
|
201 new Expr\PropertyFetch(new Expr\Variable('foo'), 'bar'),
|
Chris@17
|
202 $f->propertyFetch($f->var('foo'), 'bar')
|
Chris@17
|
203 );
|
Chris@17
|
204 $this->assertEquals(
|
Chris@17
|
205 new Expr\PropertyFetch(new Expr\Variable('foo'), 'bar'),
|
Chris@17
|
206 $f->propertyFetch($f->var('foo'), new Identifier('bar'))
|
Chris@17
|
207 );
|
Chris@17
|
208 $this->assertEquals(
|
Chris@17
|
209 new Expr\PropertyFetch(new Expr\Variable('foo'), new Expr\Variable('bar')),
|
Chris@17
|
210 $f->propertyFetch($f->var('foo'), $f->var('bar'))
|
Chris@17
|
211 );
|
Chris@17
|
212 }
|
Chris@17
|
213
|
Chris@13
|
214 public function testInvalidIdentifier() {
|
Chris@17
|
215 $this->expectException(\LogicException::class);
|
Chris@17
|
216 $this->expectExceptionMessage('Expected string or instance of Node\Identifier');
|
Chris@13
|
217 (new BuilderFactory())->classConstFetch('Foo', new Expr\Variable('foo'));
|
Chris@13
|
218 }
|
Chris@13
|
219
|
Chris@13
|
220 public function testInvalidIdentifierOrExpr() {
|
Chris@17
|
221 $this->expectException(\LogicException::class);
|
Chris@17
|
222 $this->expectExceptionMessage('Expected string or instance of Node\Identifier or Node\Expr');
|
Chris@13
|
223 (new BuilderFactory())->staticCall('Foo', new Name('bar'));
|
Chris@13
|
224 }
|
Chris@13
|
225
|
Chris@13
|
226 public function testInvalidNameOrExpr() {
|
Chris@17
|
227 $this->expectException(\LogicException::class);
|
Chris@17
|
228 $this->expectExceptionMessage('Name must be a string or an instance of Node\Name or Node\Expr');
|
Chris@13
|
229 (new BuilderFactory())->funcCall(new Node\Stmt\Return_());
|
Chris@0
|
230 }
|
Chris@0
|
231
|
Chris@17
|
232 public function testInvalidVar() {
|
Chris@17
|
233 $this->expectException(\LogicException::class);
|
Chris@17
|
234 $this->expectExceptionMessage('Variable name must be string or Expr');
|
Chris@17
|
235 (new BuilderFactory())->var(new Node\Stmt\Return_());
|
Chris@17
|
236 }
|
Chris@17
|
237
|
Chris@0
|
238 public function testIntegration() {
|
Chris@0
|
239 $factory = new BuilderFactory;
|
Chris@0
|
240 $node = $factory->namespace('Name\Space')
|
Chris@0
|
241 ->addStmt($factory->use('Foo\Bar\SomeOtherClass'))
|
Chris@0
|
242 ->addStmt($factory->use('Foo\Bar')->as('A'))
|
Chris@17
|
243 ->addStmt($factory->useFunction('strlen'))
|
Chris@17
|
244 ->addStmt($factory->useConst('PHP_VERSION'))
|
Chris@0
|
245 ->addStmt($factory
|
Chris@0
|
246 ->class('SomeClass')
|
Chris@0
|
247 ->extend('SomeOtherClass')
|
Chris@0
|
248 ->implement('A\Few', '\Interfaces')
|
Chris@0
|
249 ->makeAbstract()
|
Chris@0
|
250
|
Chris@17
|
251 ->addStmt($factory->useTrait('FirstTrait'))
|
Chris@17
|
252
|
Chris@17
|
253 ->addStmt($factory->useTrait('SecondTrait', 'ThirdTrait')
|
Chris@17
|
254 ->and('AnotherTrait')
|
Chris@17
|
255 ->with($factory->traitUseAdaptation('foo')->as('bar'))
|
Chris@17
|
256 ->with($factory->traitUseAdaptation('AnotherTrait', 'baz')->as('test'))
|
Chris@17
|
257 ->with($factory->traitUseAdaptation('AnotherTrait', 'func')->insteadof('SecondTrait')))
|
Chris@17
|
258
|
Chris@0
|
259 ->addStmt($factory->method('firstMethod'))
|
Chris@0
|
260
|
Chris@0
|
261 ->addStmt($factory->method('someMethod')
|
Chris@0
|
262 ->makePublic()
|
Chris@0
|
263 ->makeAbstract()
|
Chris@17
|
264 ->addParam($factory->param('someParam')->setType('SomeClass'))
|
Chris@0
|
265 ->setDocComment('/**
|
Chris@0
|
266 * This method does something.
|
Chris@0
|
267 *
|
Chris@0
|
268 * @param SomeClass And takes a parameter
|
Chris@0
|
269 */'))
|
Chris@0
|
270
|
Chris@0
|
271 ->addStmt($factory->method('anotherMethod')
|
Chris@0
|
272 ->makeProtected()
|
Chris@0
|
273 ->addParam($factory->param('someParam')->setDefault('test'))
|
Chris@0
|
274 ->addStmt(new Expr\Print_(new Expr\Variable('someParam'))))
|
Chris@0
|
275
|
Chris@0
|
276 ->addStmt($factory->property('someProperty')->makeProtected())
|
Chris@0
|
277 ->addStmt($factory->property('anotherProperty')
|
Chris@0
|
278 ->makePrivate()
|
Chris@13
|
279 ->setDefault([1, 2, 3])))
|
Chris@0
|
280 ->getNode()
|
Chris@0
|
281 ;
|
Chris@0
|
282
|
Chris@0
|
283 $expected = <<<'EOC'
|
Chris@0
|
284 <?php
|
Chris@0
|
285
|
Chris@0
|
286 namespace Name\Space;
|
Chris@0
|
287
|
Chris@0
|
288 use Foo\Bar\SomeOtherClass;
|
Chris@0
|
289 use Foo\Bar as A;
|
Chris@17
|
290 use function strlen;
|
Chris@17
|
291 use const PHP_VERSION;
|
Chris@0
|
292 abstract class SomeClass extends SomeOtherClass implements A\Few, \Interfaces
|
Chris@0
|
293 {
|
Chris@17
|
294 use FirstTrait;
|
Chris@17
|
295 use SecondTrait, ThirdTrait, AnotherTrait {
|
Chris@17
|
296 foo as bar;
|
Chris@17
|
297 AnotherTrait::baz as test;
|
Chris@17
|
298 AnotherTrait::func insteadof SecondTrait;
|
Chris@17
|
299 }
|
Chris@0
|
300 protected $someProperty;
|
Chris@0
|
301 private $anotherProperty = array(1, 2, 3);
|
Chris@0
|
302 function firstMethod()
|
Chris@0
|
303 {
|
Chris@0
|
304 }
|
Chris@0
|
305 /**
|
Chris@0
|
306 * This method does something.
|
Chris@0
|
307 *
|
Chris@0
|
308 * @param SomeClass And takes a parameter
|
Chris@0
|
309 */
|
Chris@0
|
310 public abstract function someMethod(SomeClass $someParam);
|
Chris@0
|
311 protected function anotherMethod($someParam = 'test')
|
Chris@0
|
312 {
|
Chris@0
|
313 print $someParam;
|
Chris@0
|
314 }
|
Chris@0
|
315 }
|
Chris@0
|
316 EOC;
|
Chris@0
|
317
|
Chris@13
|
318 $stmts = [$node];
|
Chris@0
|
319 $prettyPrinter = new PrettyPrinter\Standard();
|
Chris@0
|
320 $generated = $prettyPrinter->prettyPrintFile($stmts);
|
Chris@0
|
321
|
Chris@0
|
322 $this->assertEquals(
|
Chris@0
|
323 str_replace("\r\n", "\n", $expected),
|
Chris@0
|
324 str_replace("\r\n", "\n", $generated)
|
Chris@0
|
325 );
|
Chris@0
|
326 }
|
Chris@0
|
327 }
|