comparison vendor/nikic/php-parser/doc/2_Usage_of_basic_components.markdown @ 13:5fb285c0d0e3

Update Drupal core to 8.4.7 via Composer. Security update; I *think* we've been lucky to get away with this so far, as we don't support self-registration which seems to be used by the so-called "drupalgeddon 2" attack that 8.4.5 was vulnerable to.
author Chris Cannam
date Mon, 23 Apr 2018 09:33:26 +0100
parents 4c8ae668cc8c
children c2387f117808
comparison
equal deleted inserted replaced
12:7a779792577d 13:5fb285c0d0e3
39 `ParserFactory::PREFER_PHP7` | Try to parse code as PHP 7. If this fails, try to parse it as PHP 5. 39 `ParserFactory::PREFER_PHP7` | Try to parse code as PHP 7. If this fails, try to parse it as PHP 5.
40 `ParserFactory::PREFER_PHP5` | Try to parse code as PHP 5. If this fails, try to parse it as PHP 7. 40 `ParserFactory::PREFER_PHP5` | Try to parse code as PHP 5. If this fails, try to parse it as PHP 7.
41 `ParserFactory::ONLY_PHP7` | Parse code as PHP 7. 41 `ParserFactory::ONLY_PHP7` | Parse code as PHP 7.
42 `ParserFactory::ONLY_PHP5` | Parse code as PHP 5. 42 `ParserFactory::ONLY_PHP5` | Parse code as PHP 5.
43 43
44 Unless you have strong reason to use something else, `PREFER_PHP7` is a reasonable default. 44 Unless you have a strong reason to use something else, `PREFER_PHP7` is a reasonable default.
45 45
46 The `create()` method optionally accepts a `Lexer` instance as the second argument. Some use cases 46 The `create()` method optionally accepts a `Lexer` instance as the second argument. Some use cases
47 that require customized lexers are discussed in the [lexer documentation](component/Lexer.markdown). 47 that require customized lexers are discussed in the [lexer documentation](component/Lexer.markdown).
48 48
49 Subsequently you can pass PHP code (including the opening `<?php` tag) to the `parse` method in order to 49 Subsequently you can pass PHP code (including the opening `<?php` tag) to the `parse` method in order to
50 create a syntax tree. If a syntax error is encountered, an `PhpParser\Error` exception will be thrown: 50 create a syntax tree. If a syntax error is encountered, an `PhpParser\Error` exception will be thrown:
51 51
52 ```php 52 ```php
53 <?php
53 use PhpParser\Error; 54 use PhpParser\Error;
54 use PhpParser\ParserFactory; 55 use PhpParser\ParserFactory;
55 56
56 $code = '<?php // some code'; 57 $code = <<<'CODE'
58 <?php
59 function printLine($msg) {
60 echo $msg, "\n";
61 }
62 printLine('Hello World!!!');
63 CODE;
64
57 $parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7); 65 $parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
58 66
59 try { 67 try {
60 $stmts = $parser->parse($code); 68 $stmts = $parser->parse($code);
61 // $stmts is an array of statement nodes 69 // $stmts is an array of statement nodes
64 } 72 }
65 ``` 73 ```
66 74
67 A parser instance can be reused to parse multiple files. 75 A parser instance can be reused to parse multiple files.
68 76
69 Node tree 77 Node dumping
70 --------- 78 ------------
71 79
72 If you use the above code with `$code = "<?php echo 'Hi ', hi\\getTarget();"` the parser will 80 To dump the abstact syntax tree in human readable form, a `NodeDumper` can be used:
73 generate a node tree looking like this: 81
82 ```php
83 <?php
84 use PhpParser\NodeDumper;
85
86 $nodeDumper = new NodeDumper;
87 echo $nodeDumper->dump($stmts), "\n";
88 ```
89
90 For the sample code from the previous section, this will produce the following output:
74 91
75 ``` 92 ```
76 array( 93 array(
77 0: Stmt_Echo( 94 0: Stmt_Function(
78 exprs: array( 95 byRef: false
79 0: Scalar_String( 96 name: Identifier(
80 value: Hi 97 name: printLine
98 )
99 params: array(
100 0: Param(
101 type: null
102 byRef: false
103 variadic: false
104 var: Expr_Variable(
105 name: msg
106 )
107 default: null
81 ) 108 )
82 1: Expr_FuncCall( 109 )
83 name: Name( 110 returnType: null
84 parts: array( 111 stmts: array(
85 0: hi 112 0: Stmt_Echo(
86 1: getTarget 113 exprs: array(
114 0: Expr_Variable(
115 name: msg
116 )
117 1: Scalar_String(
118 value:
119
87 ) 120 )
88 ) 121 )
89 args: array( 122 )
123 )
124 )
125 1: Stmt_Expression(
126 expr: Expr_FuncCall(
127 name: Name(
128 parts: array(
129 0: printLine
130 )
131 )
132 args: array(
133 0: Arg(
134 value: Scalar_String(
135 value: Hello World!!!
136 )
137 byRef: false
138 unpack: false
90 ) 139 )
91 ) 140 )
92 ) 141 )
93 ) 142 )
94 ) 143 )
95 ``` 144 ```
96 145
97 Thus `$stmts` will contain an array with only one node, with this node being an instance of 146 You can also use the `php-parse` script to obtain such a node dump by calling it either with a file
98 `PhpParser\Node\Stmt\Echo_`. 147 name or code string:
99 148
100 As PHP is a large language there are approximately 140 different nodes. In order to make work 149 ```sh
150 vendor/bin/php-parse file.php
151 vendor/bin/php-parse "<?php foo();"
152 ```
153
154 This can be very helpful if you want to quickly check how certain syntax is represented in the AST.
155
156 Node tree structure
157 -------------------
158
159 Looking at the node dump above, you can see that `$stmts` for this example code is an array of two
160 nodes, a `Stmt_Function` and a `Stmt_Expression`. The corresponding class names are:
161
162 * `Stmt_Function -> PhpParser\Node\Stmt\Function_`
163 * `Stmt_Expression -> PhpParser\Node\Stmt\Expression`
164
165 The additional `_` at the end of the first class name is necessary, because `Function` is a
166 reserved keyword. Many node class names in this library have a trailing `_` to avoid clashing with
167 a keyword.
168
169 As PHP is a large language there are approximately 140 different nodes. In order to make working
101 with them easier they are grouped into three categories: 170 with them easier they are grouped into three categories:
102 171
103 * `PhpParser\Node\Stmt`s are statement nodes, i.e. language constructs that do not return 172 * `PhpParser\Node\Stmt`s are statement nodes, i.e. language constructs that do not return
104 a value and can not occur in an expression. For example a class definition is a statement. 173 a value and can not occur in an expression. For example a class definition is a statement.
105 It doesn't return a value and you can't write something like `func(class A {});`. 174 It doesn't return a value and you can't write something like `func(class A {});`.
111 like `__FILE__` (`PhpParser\Node\Scalar\MagicConst\File`). All `PhpParser\Node\Scalar`s extend 180 like `__FILE__` (`PhpParser\Node\Scalar\MagicConst\File`). All `PhpParser\Node\Scalar`s extend
112 `PhpParser\Node\Expr`, as scalars are expressions, too. 181 `PhpParser\Node\Expr`, as scalars are expressions, too.
113 * There are some nodes not in either of these groups, for example names (`PhpParser\Node\Name`) 182 * There are some nodes not in either of these groups, for example names (`PhpParser\Node\Name`)
114 and call arguments (`PhpParser\Node\Arg`). 183 and call arguments (`PhpParser\Node\Arg`).
115 184
116 Some node class names have a trailing `_`. This is used whenever the class name would otherwise clash 185 The `Node\Stmt\Expression` node is somewhat confusing in that it contains both the terms "statement"
117 with a PHP keyword. 186 and "expression". This node distinguishes `expr`, which is a `Node\Expr`, from `expr;`, which is
187 an "expression statement" represented by `Node\Stmt\Expression` and containing `expr` as a sub-node.
118 188
119 Every node has a (possibly zero) number of subnodes. You can access subnodes by writing 189 Every node has a (possibly zero) number of subnodes. You can access subnodes by writing
120 `$node->subNodeName`. The `Stmt\Echo_` node has only one subnode `exprs`. So in order to access it 190 `$node->subNodeName`. The `Stmt\Echo_` node has only one subnode `exprs`. So in order to access it
121 in the above example you would write `$stmts[0]->exprs`. If you wanted to access the name of the function 191 in the above example you would write `$stmts[0]->exprs`. If you wanted to access the name of the function
122 call, you would write `$stmts[0]->exprs[1]->name`. 192 call, you would write `$stmts[0]->exprs[1]->name`.
171 } 241 }
172 ``` 242 ```
173 243
174 The above code will output: 244 The above code will output:
175 245
176 <?php echo 'Hello ', hi\getTarget(); 246 echo 'Hello ', hi\getTarget();
177 247
178 As you can see the source code was first parsed using `PhpParser\Parser->parse()`, then changed and then 248 As you can see the source code was first parsed using `PhpParser\Parser->parse()`, then changed and then
179 again converted to code using `PhpParser\PrettyPrinter\Standard->prettyPrint()`. 249 again converted to code using `PhpParser\PrettyPrinter\Standard->prettyPrint()`.
180 250
181 The `prettyPrint()` method pretty prints a statements array. It is also possible to pretty print only a 251 The `prettyPrint()` method pretty prints a statements array. It is also possible to pretty print only a
182 single expression using `prettyPrintExpr()`. 252 single expression using `prettyPrintExpr()`.
183 253
184 The `prettyPrintFile()` method can be used to print an entire file. This will include the opening `<?php` tag 254 The `prettyPrintFile()` method can be used to print an entire file. This will include the opening `<?php` tag
185 and handle inline HTML as the first/last statement more gracefully. 255 and handle inline HTML as the first/last statement more gracefully.
256
257 > Read more: [Pretty printing documentation](component/Pretty_printing.markdown)
186 258
187 Node traversation 259 Node traversation
188 ----------------- 260 -----------------
189 261
190 The above pretty printing example used the fact that the source code was known and thus it was easy to 262 The above pretty printing example used the fact that the source code was known and thus it was easy to
276 be `array(A, X, Y, Z, C)`. 348 be `array(A, X, Y, Z, C)`.
277 349
278 Instead of manually implementing the `NodeVisitor` interface you can also extend the `NodeVisitorAbstract` 350 Instead of manually implementing the `NodeVisitor` interface you can also extend the `NodeVisitorAbstract`
279 class, which will define empty default implementations for all the above methods. 351 class, which will define empty default implementations for all the above methods.
280 352
353 > Read more: [Walking the AST](component/Walking_the_AST.markdown)
354
281 The NameResolver node visitor 355 The NameResolver node visitor
282 ----------------------------- 356 -----------------------------
283 357
284 One visitor is already bundled with the package: `PhpParser\NodeVisitor\NameResolver`. This visitor 358 One visitor that is already bundled with the package is `PhpParser\NodeVisitor\NameResolver`. This visitor
285 helps you work with namespaced code by trying to resolve most names to fully qualified ones. 359 helps you work with namespaced code by trying to resolve most names to fully qualified ones.
286 360
287 For example, consider the following code: 361 For example, consider the following code:
288 362
289 use A as B; 363 use A as B;
290 new B\C(); 364 new B\C();
291 365
292 In order to know that `B\C` really is `A\C` you would need to track aliases and namespaces yourself. 366 In order to know that `B\C` really is `A\C` you would need to track aliases and namespaces yourself.
293 The `NameResolver` takes care of that and resolves names as far as possible. 367 The `NameResolver` takes care of that and resolves names as far as possible.
294 368
295 After running it most names will be fully qualified. The only names that will stay unqualified are 369 After running it, most names will be fully qualified. The only names that will stay unqualified are
296 unqualified function and constant names. These are resolved at runtime and thus the visitor can't 370 unqualified function and constant names. These are resolved at runtime and thus the visitor can't
297 know which function they are referring to. In most cases this is a non-issue as the global functions 371 know which function they are referring to. In most cases this is a non-issue as the global functions
298 are meant. 372 are meant.
299 373
300 Also the `NameResolver` adds a `namespacedName` subnode to class, function and constant declarations 374 Also the `NameResolver` adds a `namespacedName` subnode to class, function and constant declarations
301 that contains the namespaced name instead of only the shortname that is available via `name`. 375 that contains the namespaced name instead of only the shortname that is available via `name`.
376
377 > Read more: [Name resolution documentation](component/Name_resolution.markdown)
302 378
303 Example: Converting namespaced code to pseudo namespaces 379 Example: Converting namespaced code to pseudo namespaces
304 -------------------------------------------------------- 380 --------------------------------------------------------
305 381
306 A small example to understand the concept: We want to convert namespaced code to pseudo namespaces 382 A small example to understand the concept: We want to convert namespaced code to pseudo namespaces
363 439
364 class NamespaceConverter extends \PhpParser\NodeVisitorAbstract 440 class NamespaceConverter extends \PhpParser\NodeVisitorAbstract
365 { 441 {
366 public function leaveNode(Node $node) { 442 public function leaveNode(Node $node) {
367 if ($node instanceof Node\Name) { 443 if ($node instanceof Node\Name) {
368 return new Node\Name($node->toString('_')); 444 return new Node\Name(str_replace('\\', '_', $node->toString()));
369 } 445 }
370 } 446 }
371 } 447 }
372 ``` 448 ```
373 449
374 The above code profits from the fact that the `NameResolver` already resolved all names as far as 450 The above code profits from the fact that the `NameResolver` already resolved all names as far as
375 possible, so we don't need to do that. We only need to create a string with the name parts separated 451 possible, so we don't need to do that. We only need to create a string with the name parts separated
376 by underscores instead of backslashes. This is what `$node->toString('_')` does. (If you want to 452 by underscores instead of backslashes. This is what `str_replace('\\', '_', $node->toString())` does. (If you want to
377 create a name with backslashes either write `$node->toString()` or `(string) $node`.) Then we create 453 create a name with backslashes either write `$node->toString()` or `(string) $node`.) Then we create
378 a new name from the string and return it. Returning a new node replaces the old node. 454 a new name from the string and return it. Returning a new node replaces the old node.
379 455
380 Another thing we need to do is change the class/function/const declarations. Currently they contain 456 Another thing we need to do is change the class/function/const declarations. Currently they contain
381 only the shortname (i.e. the last part of the name), but they need to contain the complete name including 457 only the shortname (i.e. the last part of the name), but they need to contain the complete name including
387 463
388 class NodeVisitor_NamespaceConverter extends \PhpParser\NodeVisitorAbstract 464 class NodeVisitor_NamespaceConverter extends \PhpParser\NodeVisitorAbstract
389 { 465 {
390 public function leaveNode(Node $node) { 466 public function leaveNode(Node $node) {
391 if ($node instanceof Node\Name) { 467 if ($node instanceof Node\Name) {
392 return new Node\Name($node->toString('_')); 468 return new Node\Name(str_replace('\\', '_', $node->toString()));
393 } elseif ($node instanceof Stmt\Class_ 469 } elseif ($node instanceof Stmt\Class_
394 || $node instanceof Stmt\Interface_ 470 || $node instanceof Stmt\Interface_
395 || $node instanceof Stmt\Function_) { 471 || $node instanceof Stmt\Function_) {
396 $node->name = $node->namespacedName->toString('_'); 472 $node->name = str_replace('\\', '_', $node->namespacedName->toString());
397 } elseif ($node instanceof Stmt\Const_) { 473 } elseif ($node instanceof Stmt\Const_) {
398 foreach ($node->consts as $const) { 474 foreach ($node->consts as $const) {
399 $const->name = $const->namespacedName->toString('_'); 475 $const->name = str_replace('\\', '_', $const->namespacedName->toString());
400 } 476 }
401 } 477 }
402 } 478 }
403 } 479 }
404 ``` 480 ```
408 The last thing we need to do is remove the `namespace` and `use` statements: 484 The last thing we need to do is remove the `namespace` and `use` statements:
409 485
410 ```php 486 ```php
411 use PhpParser\Node; 487 use PhpParser\Node;
412 use PhpParser\Node\Stmt; 488 use PhpParser\Node\Stmt;
489 use PhpParser\NodeTraverser;
413 490
414 class NodeVisitor_NamespaceConverter extends \PhpParser\NodeVisitorAbstract 491 class NodeVisitor_NamespaceConverter extends \PhpParser\NodeVisitorAbstract
415 { 492 {
416 public function leaveNode(Node $node) { 493 public function leaveNode(Node $node) {
417 if ($node instanceof Node\Name) { 494 if ($node instanceof Node\Name) {
418 return new Node\Name($node->toString('_')); 495 return new Node\Name(str_replace('\\', '_', $node->toString()));
419 } elseif ($node instanceof Stmt\Class_ 496 } elseif ($node instanceof Stmt\Class_
420 || $node instanceof Stmt\Interface_ 497 || $node instanceof Stmt\Interface_
421 || $node instanceof Stmt\Function_) { 498 || $node instanceof Stmt\Function_) {
422 $node->name = $node->namespacedName->toString('_'); 499 $node->name = str_replace('\\', '_', $node->namespacedName->toString();
423 } elseif ($node instanceof Stmt\Const_) { 500 } elseif ($node instanceof Stmt\Const_) {
424 foreach ($node->consts as $const) { 501 foreach ($node->consts as $const) {
425 $const->name = $const->namespacedName->toString('_'); 502 $const->name = str_replace('\\', '_', $const->namespacedName->toString());
426 } 503 }
427 } elseif ($node instanceof Stmt\Namespace_) { 504 } elseif ($node instanceof Stmt\Namespace_) {
428 // returning an array merges is into the parent array 505 // returning an array merges is into the parent array
429 return $node->stmts; 506 return $node->stmts;
430 } elseif ($node instanceof Stmt\Use_) { 507 } elseif ($node instanceof Stmt\Use_) {
431 // returning false removed the node altogether 508 // remove use nodes altogether
432 return false; 509 return NodeTraverser::REMOVE_NODE;
433 } 510 }
434 } 511 }
435 } 512 }
436 ``` 513 ```
437 514