Mercurial > hg > isophonics-drupal-site
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 |