Security update for Core, with self-updated composer
[yaffs-website] / vendor / nikic / php-parser / test / PhpParser / NodeVisitor / NameResolverTest.php
1 <?php
2
3 namespace PhpParser\NodeVisitor;
4
5 use PhpParser;
6 use PhpParser\Node;
7 use PhpParser\Node\Expr;
8 use PhpParser\Node\Name;
9 use PhpParser\Node\Stmt;
10
11 class NameResolverTest extends \PHPUnit_Framework_TestCase
12 {
13     private function canonicalize($string) {
14         return str_replace("\r\n", "\n", $string);
15     }
16
17     /**
18      * @covers PhpParser\NodeVisitor\NameResolver
19      */
20     public function testResolveNames() {
21         $code = <<<'EOC'
22 <?php
23
24 namespace Foo {
25     use Hallo as Hi;
26
27     new Bar();
28     new Hi();
29     new Hi\Bar();
30     new \Bar();
31     new namespace\Bar();
32
33     bar();
34     hi();
35     Hi\bar();
36     foo\bar();
37     \bar();
38     namespace\bar();
39 }
40 namespace {
41     use Hallo as Hi;
42
43     new Bar();
44     new Hi();
45     new Hi\Bar();
46     new \Bar();
47     new namespace\Bar();
48
49     bar();
50     hi();
51     Hi\bar();
52     foo\bar();
53     \bar();
54     namespace\bar();
55 }
56 namespace Bar {
57     use function foo\bar as baz;
58     use const foo\BAR as BAZ;
59     use foo as bar;
60
61     bar();
62     baz();
63     bar\foo();
64     baz\foo();
65     BAR();
66     BAZ();
67     BAR\FOO();
68     BAZ\FOO();
69
70     bar;
71     baz;
72     bar\foo;
73     baz\foo;
74     BAR;
75     BAZ;
76     BAR\FOO;
77     BAZ\FOO;
78 }
79 namespace Baz {
80     use A\T\{B\C, D\E};
81     use function X\T\{b\c, d\e};
82     use const Y\T\{B\C, D\E};
83     use Z\T\{G, function f, const K};
84
85     new C;
86     new E;
87     new C\D;
88     new E\F;
89     new G;
90
91     c();
92     e();
93     f();
94     C;
95     E;
96     K;
97 }
98 EOC;
99         $expectedCode = <<<'EOC'
100 namespace Foo {
101     use Hallo as Hi;
102     new \Foo\Bar();
103     new \Hallo();
104     new \Hallo\Bar();
105     new \Bar();
106     new \Foo\Bar();
107     bar();
108     hi();
109     \Hallo\bar();
110     \Foo\foo\bar();
111     \bar();
112     \Foo\bar();
113 }
114 namespace {
115     use Hallo as Hi;
116     new \Bar();
117     new \Hallo();
118     new \Hallo\Bar();
119     new \Bar();
120     new \Bar();
121     \bar();
122     \hi();
123     \Hallo\bar();
124     \foo\bar();
125     \bar();
126     \bar();
127 }
128 namespace Bar {
129     use function foo\bar as baz;
130     use const foo\BAR as BAZ;
131     use foo as bar;
132     bar();
133     \foo\bar();
134     \foo\foo();
135     \Bar\baz\foo();
136     BAR();
137     \foo\bar();
138     \foo\FOO();
139     \Bar\BAZ\FOO();
140     bar;
141     baz;
142     \foo\foo;
143     \Bar\baz\foo;
144     BAR;
145     \foo\BAR;
146     \foo\FOO;
147     \Bar\BAZ\FOO;
148 }
149 namespace Baz {
150     use A\T\{B\C, D\E};
151     use function X\T\{b\c, d\e};
152     use const Y\T\{B\C, D\E};
153     use Z\T\{G, function f, const K};
154     new \A\T\B\C();
155     new \A\T\D\E();
156     new \A\T\B\C\D();
157     new \A\T\D\E\F();
158     new \Z\T\G();
159     \X\T\b\c();
160     \X\T\d\e();
161     \Z\T\f();
162     \Y\T\B\C;
163     \Y\T\D\E;
164     \Z\T\K;
165 }
166 EOC;
167
168         $parser        = new PhpParser\Parser\Php7(new PhpParser\Lexer\Emulative);
169         $prettyPrinter = new PhpParser\PrettyPrinter\Standard;
170         $traverser     = new PhpParser\NodeTraverser;
171         $traverser->addVisitor(new NameResolver);
172
173         $stmts = $parser->parse($code);
174         $stmts = $traverser->traverse($stmts);
175
176         $this->assertSame(
177             $this->canonicalize($expectedCode),
178             $prettyPrinter->prettyPrint($stmts)
179         );
180     }
181
182     /**
183      * @covers PhpParser\NodeVisitor\NameResolver
184      */
185     public function testResolveLocations() {
186         $code = <<<'EOC'
187 <?php
188 namespace NS;
189
190 class A extends B implements C, D {
191     use E, F, G {
192         f as private g;
193         E::h as i;
194         E::j insteadof F, G;
195     }
196 }
197
198 interface A extends C, D {
199     public function a(A $a) : A;
200 }
201
202 function fn(A $a) : A {}
203 function fn2(array $a) : array {}
204 function(A $a) : A {};
205
206 function fn3(?A $a) : ?A {}
207 function fn4(?array $a) : ?array {}
208
209 A::b();
210 A::$b;
211 A::B;
212 new A;
213 $a instanceof A;
214
215 namespace\a();
216 namespace\A;
217
218 try {
219     $someThing;
220 } catch (A $a) {
221     $someThingElse;
222 }
223 EOC;
224         $expectedCode = <<<'EOC'
225 namespace NS;
226
227 class A extends \NS\B implements \NS\C, \NS\D
228 {
229     use \NS\E, \NS\F, \NS\G {
230         f as private g;
231         \NS\E::h as i;
232         \NS\E::j insteadof \NS\F, \NS\G;
233     }
234 }
235 interface A extends \NS\C, \NS\D
236 {
237     public function a(\NS\A $a) : \NS\A;
238 }
239 function fn(\NS\A $a) : \NS\A
240 {
241 }
242 function fn2(array $a) : array
243 {
244 }
245 function (\NS\A $a) : \NS\A {
246 };
247 function fn3(?\NS\A $a) : ?\NS\A
248 {
249 }
250 function fn4(?array $a) : ?array
251 {
252 }
253 \NS\A::b();
254 \NS\A::$b;
255 \NS\A::B;
256 new \NS\A();
257 $a instanceof \NS\A;
258 \NS\a();
259 \NS\A;
260 try {
261     $someThing;
262 } catch (\NS\A $a) {
263     $someThingElse;
264 }
265 EOC;
266
267         $parser        = new PhpParser\Parser\Php7(new PhpParser\Lexer\Emulative);
268         $prettyPrinter = new PhpParser\PrettyPrinter\Standard;
269         $traverser     = new PhpParser\NodeTraverser;
270         $traverser->addVisitor(new NameResolver);
271
272         $stmts = $parser->parse($code);
273         $stmts = $traverser->traverse($stmts);
274
275         $this->assertSame(
276             $this->canonicalize($expectedCode),
277             $prettyPrinter->prettyPrint($stmts)
278         );
279     }
280
281     public function testNoResolveSpecialName() {
282         $stmts = array(new Node\Expr\New_(new Name('self')));
283
284         $traverser = new PhpParser\NodeTraverser;
285         $traverser->addVisitor(new NameResolver);
286
287         $this->assertEquals($stmts, $traverser->traverse($stmts));
288     }
289
290     public function testAddDeclarationNamespacedName() {
291         $nsStmts = array(
292             new Stmt\Class_('A'),
293             new Stmt\Interface_('B'),
294             new Stmt\Function_('C'),
295             new Stmt\Const_(array(
296                 new Node\Const_('D', new Node\Scalar\LNumber(42))
297             )),
298             new Stmt\Trait_('E'),
299             new Expr\New_(new Stmt\Class_(null)),
300         );
301
302         $traverser = new PhpParser\NodeTraverser;
303         $traverser->addVisitor(new NameResolver);
304
305         $stmts = $traverser->traverse([new Stmt\Namespace_(new Name('NS'), $nsStmts)]);
306         $this->assertSame('NS\\A', (string) $stmts[0]->stmts[0]->namespacedName);
307         $this->assertSame('NS\\B', (string) $stmts[0]->stmts[1]->namespacedName);
308         $this->assertSame('NS\\C', (string) $stmts[0]->stmts[2]->namespacedName);
309         $this->assertSame('NS\\D', (string) $stmts[0]->stmts[3]->consts[0]->namespacedName);
310         $this->assertSame('NS\\E', (string) $stmts[0]->stmts[4]->namespacedName);
311         $this->assertObjectNotHasAttribute('namespacedName', $stmts[0]->stmts[5]->class);
312
313         $stmts = $traverser->traverse([new Stmt\Namespace_(null, $nsStmts)]);
314         $this->assertSame('A',     (string) $stmts[0]->stmts[0]->namespacedName);
315         $this->assertSame('B',     (string) $stmts[0]->stmts[1]->namespacedName);
316         $this->assertSame('C',     (string) $stmts[0]->stmts[2]->namespacedName);
317         $this->assertSame('D',     (string) $stmts[0]->stmts[3]->consts[0]->namespacedName);
318         $this->assertSame('E',     (string) $stmts[0]->stmts[4]->namespacedName);
319         $this->assertObjectNotHasAttribute('namespacedName', $stmts[0]->stmts[5]->class);
320     }
321
322     public function testAddRuntimeResolvedNamespacedName() {
323         $stmts = array(
324             new Stmt\Namespace_(new Name('NS'), array(
325                 new Expr\FuncCall(new Name('foo')),
326                 new Expr\ConstFetch(new Name('FOO')),
327             )),
328             new Stmt\Namespace_(null, array(
329                 new Expr\FuncCall(new Name('foo')),
330                 new Expr\ConstFetch(new Name('FOO')),
331             )),
332         );
333
334         $traverser = new PhpParser\NodeTraverser;
335         $traverser->addVisitor(new NameResolver);
336         $stmts = $traverser->traverse($stmts);
337         
338         $this->assertSame('NS\\foo', (string) $stmts[0]->stmts[0]->name->getAttribute('namespacedName'));
339         $this->assertSame('NS\\FOO', (string) $stmts[0]->stmts[1]->name->getAttribute('namespacedName'));
340
341         $this->assertFalse($stmts[1]->stmts[0]->name->hasAttribute('namespacedName'));
342         $this->assertFalse($stmts[1]->stmts[1]->name->hasAttribute('namespacedName'));
343     }
344
345     /**
346      * @dataProvider provideTestError
347      */
348     public function testError(Node $stmt, $errorMsg) {
349         $this->setExpectedException('PhpParser\Error', $errorMsg);
350
351         $traverser = new PhpParser\NodeTraverser;
352         $traverser->addVisitor(new NameResolver);
353         $traverser->traverse(array($stmt));
354     }
355
356     public function provideTestError() {
357         return array(
358             array(
359                 new Stmt\Use_(array(
360                     new Stmt\UseUse(new Name('A\B'), 'B', 0, array('startLine' => 1)),
361                     new Stmt\UseUse(new Name('C\D'), 'B', 0, array('startLine' => 2)),
362                 ), Stmt\Use_::TYPE_NORMAL),
363                 'Cannot use C\D as B because the name is already in use on line 2'
364             ),
365             array(
366                 new Stmt\Use_(array(
367                     new Stmt\UseUse(new Name('a\b'), 'b', 0, array('startLine' => 1)),
368                     new Stmt\UseUse(new Name('c\d'), 'B', 0, array('startLine' => 2)),
369                 ), Stmt\Use_::TYPE_FUNCTION),
370                 'Cannot use function c\d as B because the name is already in use on line 2'
371             ),
372             array(
373                 new Stmt\Use_(array(
374                     new Stmt\UseUse(new Name('A\B'), 'B', 0, array('startLine' => 1)),
375                     new Stmt\UseUse(new Name('C\D'), 'B', 0, array('startLine' => 2)),
376                 ), Stmt\Use_::TYPE_CONSTANT),
377                 'Cannot use const C\D as B because the name is already in use on line 2'
378             ),
379             array(
380                 new Expr\New_(new Name\FullyQualified('self', array('startLine' => 3))),
381                 "'\\self' is an invalid class name on line 3"
382             ),
383             array(
384                 new Expr\New_(new Name\Relative('self', array('startLine' => 3))),
385                 "'\\self' is an invalid class name on line 3"
386             ),
387             array(
388                 new Expr\New_(new Name\FullyQualified('PARENT', array('startLine' => 3))),
389                 "'\\PARENT' is an invalid class name on line 3"
390             ),
391             array(
392                 new Expr\New_(new Name\Relative('STATIC', array('startLine' => 3))),
393                 "'\\STATIC' is an invalid class name on line 3"
394             ),
395         );
396     }
397
398     public function testClassNameIsCaseInsensitive()
399     {
400         $source = <<<'EOC'
401 <?php
402 namespace Foo;
403 use Bar\Baz;
404 $test = new baz();
405 EOC;
406
407         $parser = new PhpParser\Parser\Php7(new PhpParser\Lexer\Emulative);
408         $stmts = $parser->parse($source);
409
410         $traverser = new PhpParser\NodeTraverser;
411         $traverser->addVisitor(new NameResolver);
412
413         $stmts = $traverser->traverse($stmts);
414         $stmt = $stmts[0];
415
416         $this->assertSame(array('Bar', 'Baz'), $stmt->stmts[1]->expr->class->parts);
417     }
418
419     public function testSpecialClassNamesAreCaseInsensitive() {
420         $source = <<<'EOC'
421 <?php
422 namespace Foo;
423
424 class Bar
425 {
426     public static function method()
427     {
428         SELF::method();
429         PARENT::method();
430         STATIC::method();
431     }
432 }
433 EOC;
434
435         $parser = new PhpParser\Parser\Php7(new PhpParser\Lexer\Emulative);
436         $stmts = $parser->parse($source);
437
438         $traverser = new PhpParser\NodeTraverser;
439         $traverser->addVisitor(new NameResolver);
440
441         $stmts = $traverser->traverse($stmts);
442         $classStmt = $stmts[0];
443         $methodStmt = $classStmt->stmts[0]->stmts[0];
444
445         $this->assertSame('SELF', (string)$methodStmt->stmts[0]->class);
446         $this->assertSame('PARENT', (string)$methodStmt->stmts[1]->class);
447         $this->assertSame('STATIC', (string)$methodStmt->stmts[2]->class);
448     }
449
450     public function testAddOriginalNames() {
451         $traverser = new PhpParser\NodeTraverser;
452         $traverser->addVisitor(new NameResolver(null, ['preserveOriginalNames' => true]));
453
454         $n1 = new Name('Bar');
455         $n2 = new Name('bar');
456         $origStmts = [
457             new Stmt\Namespace_(new Name('Foo'), [
458                 new Expr\ClassConstFetch($n1, 'FOO'),
459                 new Expr\FuncCall($n2),
460             ])
461         ];
462
463         $stmts = $traverser->traverse($origStmts);
464
465         $this->assertSame($n1, $stmts[0]->stmts[0]->class->getAttribute('originalName'));
466         $this->assertSame($n2, $stmts[0]->stmts[1]->name->getAttribute('originalName'));
467     }
468 }