PageRenderTime 22ms CodeModel.GetById 10ms app.highlight 9ms RepoModel.GetById 1ms app.codeStats 0ms

/vendor/nikic/php-parser/test/PhpParser/NodeVisitor/NameResolverTest.php

https://bitbucket.org/alan_cordova/api-sb-map
PHP | 468 lines | 402 code | 57 blank | 9 comment | 0 complexity | 6c8281c9ecc6caa0470e69f8f9ef44b5 MD5 | raw file
  1<?php
  2
  3namespace PhpParser\NodeVisitor;
  4
  5use PhpParser;
  6use PhpParser\Node;
  7use PhpParser\Node\Expr;
  8use PhpParser\Node\Name;
  9use PhpParser\Node\Stmt;
 10
 11class 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
 24namespace 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}
 40namespace {
 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}
 56namespace 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}
 79namespace 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}
 98EOC;
 99        $expectedCode = <<<'EOC'
100namespace 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}
114namespace {
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}
128namespace 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}
149namespace 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}
166EOC;
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
188namespace NS;
189
190class 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
198interface A extends C, D {
199    public function a(A $a) : A;
200}
201
202function fn(A $a) : A {}
203function fn2(array $a) : array {}
204function(A $a) : A {};
205
206function fn3(?A $a) : ?A {}
207function fn4(?array $a) : ?array {}
208
209A::b();
210A::$b;
211A::B;
212new A;
213$a instanceof A;
214
215namespace\a();
216namespace\A;
217
218try {
219    $someThing;
220} catch (A $a) {
221    $someThingElse;
222}
223EOC;
224        $expectedCode = <<<'EOC'
225namespace NS;
226
227class 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}
235interface A extends \NS\C, \NS\D
236{
237    public function a(\NS\A $a) : \NS\A;
238}
239function fn(\NS\A $a) : \NS\A
240{
241}
242function fn2(array $a) : array
243{
244}
245function (\NS\A $a) : \NS\A {
246};
247function fn3(?\NS\A $a) : ?\NS\A
248{
249}
250function fn4(?array $a) : ?array
251{
252}
253\NS\A::b();
254\NS\A::$b;
255\NS\A::B;
256new \NS\A();
257$a instanceof \NS\A;
258\NS\a();
259\NS\A;
260try {
261    $someThing;
262} catch (\NS\A $a) {
263    $someThingElse;
264}
265EOC;
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
402namespace Foo;
403use Bar\Baz;
404$test = new baz();
405EOC;
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
422namespace Foo;
423
424class Bar
425{
426    public static function method()
427    {
428        SELF::method();
429        PARENT::method();
430        STATIC::method();
431    }
432}
433EOC;
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}