Upstream nikic/PHP-Parser state of the touched files before 9f4a64fbfd
This commit is contained in:
commit
09376a3b68
3 files changed
+2020
No files matched your search
@@ -0,0 +1,117 @@
|
||||
Pretty printing
|
||||
===============
|
||||
|
||||
Pretty printing is the process of converting a syntax tree back to PHP code. In its basic mode of
|
||||
operation the pretty printer provided by this library will print the AST using a certain predefined
|
||||
code style and will discard (nearly) all formatting of the original code. Because programmers tend
|
||||
to be rather picky about their code formatting, this mode of operation is not very suitable for
|
||||
refactoring code, but can be used for automatically generated code, which is usually only read for
|
||||
debugging purposes.
|
||||
|
||||
Basic usage
|
||||
-----------
|
||||
|
||||
```php
|
||||
$stmts = $parser->parse($code);
|
||||
|
||||
// MODIFY $stmts here
|
||||
|
||||
$prettyPrinter = new PhpParser\PrettyPrinter\Standard;
|
||||
$newCode = $prettyPrinter->prettyPrintFile($stmts);
|
||||
```
|
||||
|
||||
The pretty printer has three basic printing methods: `prettyPrint()`, `prettyPrintFile()` and
|
||||
`prettyPrintExpr()`. The one that is most commonly useful is `prettyPrintFile()`, which takes an
|
||||
array of statements and produces a full PHP file, including opening `<?php`.
|
||||
|
||||
`prettyPrint()` also takes a statement array, but produces code which is valid inside an already
|
||||
open `<?php` context. Lastly, `prettyPrintExpr()` takes an `Expr` node and prints only a single
|
||||
expression.
|
||||
|
||||
Customizing the formatting
|
||||
--------------------------
|
||||
|
||||
The pretty printer respects a number of attributes used by some nodes:
|
||||
|
||||
* `kind` on `Scalar\String_` to use single quotes (default), double quotes, heredoc or nowdoc.
|
||||
In the latter two cases, the heredoc/nowdoc label from the `docLabel` attribute is used.
|
||||
* `kind` on `Scalar\Int_` to use decimal (default), binary, octal or hexadecimal representation.
|
||||
* `shouldPrintRawValue` and `rawValue` on `Scalar\Int_` to preserve the original formatting of
|
||||
integer literals (e.g., numeric separators like `1_000`). When `shouldPrintRawValue` is set to
|
||||
`true`, the value from `rawValue` is used instead of the computed representation. This works for
|
||||
all integer formats (decimal, binary, octal, hexadecimal).
|
||||
* `kind` on `Cast\Double` to use `(double)` (default), `(float)` or `(real)`.
|
||||
* `kind` on `Expr\List_` to use `[]` or `list()` (default depends on `phpVersion` option).
|
||||
* `kind` on `Expr\Array_` to use `[]` or `array()` (default depends on `shortArraySyntax` option).
|
||||
* `kind` on `Expr\Exit_` to use `die` (default) or `exit`.
|
||||
* `hasLeadingNewline` on `Stmt\InlineHTML` to determine whether a newline is emitted after `?>`
|
||||
or not.
|
||||
|
||||
Additionally, the pretty printer supports three options:
|
||||
|
||||
* `phpVersion` (defaults to 7.4) allows opting into formatting that is not supported by older PHP
|
||||
versions.
|
||||
* `newline` (defaults to `"\n"`) can be set to `"\r\n"` in order to produce Windows newlines.
|
||||
* `indent` (defaults to four spaces `" "`) can be set to any number of spaces or a single tab.
|
||||
* `shortArraySyntax` determines the used array syntax if the `kind` attribute is not set. This is
|
||||
a legacy option, and `phpVersion` should be used to control this behavior instead.
|
||||
|
||||
The behaviors controlled by `phpVersion` (defaults to PHP 7.4) are:
|
||||
|
||||
* For PHP >= 7.0, short array syntax `[]` will be used by default. This does not affect nodes that
|
||||
specify an explicit array syntax using the `kind` attribute.
|
||||
* For PHP >= 7.0, parentheses around `yield` expressions will only be printed when necessary.
|
||||
* For PHP >= 7.1, the short array syntax `[]` will be used for destructuring by default (instead of
|
||||
`list()`). This does not affect nodes that specify and explicit syntax using the `kind` attribute.
|
||||
* For PHP >= 7.3, a newline is no longer forced after heredoc/nowdoc strings, as the requirement
|
||||
for this has been removed with the introduction of flexible heredoc/nowdoc strings.
|
||||
* For PHP >= 7.3, heredoc/nowdoc strings are indented just like regular code. This was allowed with
|
||||
the introduction of flexible heredoc/nowdoc strings.
|
||||
|
||||
The default pretty printer does not provide functionality for fine-grained customization of code
|
||||
formatting.
|
||||
|
||||
If you want to make minor changes to the formatting, the easiest way is to extend the pretty printer
|
||||
and override the methods responsible for the node types you are interested in.
|
||||
|
||||
If you want to have more fine-grained formatting control, the recommended method is to combine the
|
||||
default pretty printer with an existing library for code reformatting, such as
|
||||
[PHP-CS-Fixer](https://github.com/FriendsOfPHP/PHP-CS-Fixer).
|
||||
|
||||
Formatting-preserving pretty printing
|
||||
-------------------------------------
|
||||
|
||||
For automated code refactoring, migration and similar, you will usually only want to modify a small
|
||||
portion of the code and leave the remainder alone. The basic pretty printer is not suitable for
|
||||
this, because it will also reformat parts of the code which have not been modified.
|
||||
|
||||
Since PHP-Parser 4.0, a formatting-preserving pretty-printing mode is available, which
|
||||
attempts to preserve the formatting of code (those AST nodes that have not changed) and only reformat
|
||||
code which has been modified or newly inserted.
|
||||
|
||||
Use of the formatting-preservation functionality requires some additional preparatory steps:
|
||||
|
||||
```php
|
||||
use PhpParser\{NodeTraverser, NodeVisitor, ParserFactory, PrettyPrinter};
|
||||
|
||||
$parser = (new ParserFactory())->createForHostVersion();
|
||||
$oldStmts = $parser->parse($code);
|
||||
$oldTokens = $parser->getTokens();
|
||||
|
||||
// Run CloningVisitor before making changes to the AST.
|
||||
$traverser = new NodeTraverser(new NodeVisitor\CloningVisitor());
|
||||
$newStmts = $traverser->traverse($oldStmts);
|
||||
|
||||
// MODIFY $newStmts HERE
|
||||
|
||||
$printer = new PrettyPrinter\Standard();
|
||||
$newCode = $printer->printFormatPreserving($newStmts, $oldStmts, $oldTokens);
|
||||
```
|
||||
|
||||
If you make use of the name resolution functionality, you will likely want to disable the
|
||||
`replaceNodes` option. This will add resolved names as attributes, instead of directly modifying
|
||||
the AST and causing spurious changes to the pretty printed code. For more information, see the
|
||||
[name resolution documentation](Name_resolution.markdown).
|
||||
|
||||
The formatting-preservation works on a best-effort basis and may sometimes reformat more code than
|
||||
necessary. If you encounter problems while using this functionality, please open an issue.
|
||||
@@ -0,0 +1,175 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace PhpParser;
|
||||
|
||||
/**
|
||||
* A PHP version, representing only the major and minor version components.
|
||||
*/
|
||||
class PhpVersion {
|
||||
/** @var int Version ID in PHP_VERSION_ID format */
|
||||
public int $id;
|
||||
|
||||
/** @var int[] Minimum versions for builtin types */
|
||||
private const BUILTIN_TYPE_VERSIONS = [
|
||||
'array' => 50100,
|
||||
'callable' => 50400,
|
||||
'bool' => 70000,
|
||||
'int' => 70000,
|
||||
'float' => 70000,
|
||||
'string' => 70000,
|
||||
'iterable' => 70100,
|
||||
'void' => 70100,
|
||||
'object' => 70200,
|
||||
'null' => 80000,
|
||||
'false' => 80000,
|
||||
'mixed' => 80000,
|
||||
'never' => 80100,
|
||||
'true' => 80200,
|
||||
];
|
||||
|
||||
private function __construct(int $id) {
|
||||
$this->id = $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a PhpVersion object from major and minor version components.
|
||||
*/
|
||||
public static function fromComponents(int $major, int $minor): self {
|
||||
return new self($major * 10000 + $minor * 100);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the newest PHP version supported by this library. Support for this version may be partial,
|
||||
* if it is still under development.
|
||||
*/
|
||||
public static function getNewestSupported(): self {
|
||||
return self::fromComponents(8, 5);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the host PHP version, that is the PHP version we're currently running on.
|
||||
*/
|
||||
public static function getHostVersion(): self {
|
||||
return self::fromComponents(\PHP_MAJOR_VERSION, \PHP_MINOR_VERSION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the version from a string like "8.1".
|
||||
*/
|
||||
public static function fromString(string $version): self {
|
||||
if (!preg_match('/^(\d+)\.(\d+)/', $version, $matches)) {
|
||||
throw new \LogicException("Invalid PHP version \"$version\"");
|
||||
}
|
||||
return self::fromComponents((int) $matches[1], (int) $matches[2]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether two versions are the same.
|
||||
*/
|
||||
public function equals(PhpVersion $other): bool {
|
||||
return $this->id === $other->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether this version is greater than or equal to the argument.
|
||||
*/
|
||||
public function newerOrEqual(PhpVersion $other): bool {
|
||||
return $this->id >= $other->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether this version is older than the argument.
|
||||
*/
|
||||
public function older(PhpVersion $other): bool {
|
||||
return $this->id < $other->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether this is the host PHP version.
|
||||
*/
|
||||
public function isHostVersion(): bool {
|
||||
return $this->equals(self::getHostVersion());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether this PHP version supports the given builtin type. Type name must be lowercase.
|
||||
*/
|
||||
public function supportsBuiltinType(string $type): bool {
|
||||
$minVersion = self::BUILTIN_TYPE_VERSIONS[$type] ?? null;
|
||||
return $minVersion !== null && $this->id >= $minVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this version supports [] array literals.
|
||||
*/
|
||||
public function supportsShortArraySyntax(): bool {
|
||||
return $this->id >= 50400;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this version supports [] for destructuring.
|
||||
*/
|
||||
public function supportsShortArrayDestructuring(): bool {
|
||||
return $this->id >= 70100;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this version supports flexible heredoc/nowdoc.
|
||||
*/
|
||||
public function supportsFlexibleHeredoc(): bool {
|
||||
return $this->id >= 70300;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this version supports trailing commas in parameter lists.
|
||||
*/
|
||||
public function supportsTrailingCommaInParamList(): bool {
|
||||
return $this->id >= 80000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this version allows "$var =& new Obj".
|
||||
*/
|
||||
public function allowsAssignNewByReference(): bool {
|
||||
return $this->id < 70000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this version allows invalid octals like "08".
|
||||
*/
|
||||
public function allowsInvalidOctals(): bool {
|
||||
return $this->id < 70000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this version allows DEL (\x7f) to occur in identifiers.
|
||||
*/
|
||||
public function allowsDelInIdentifiers(): bool {
|
||||
return $this->id < 70100;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this version supports yield in expression context without parentheses.
|
||||
*/
|
||||
public function supportsYieldWithoutParentheses(): bool {
|
||||
return $this->id >= 70000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this version supports unicode escape sequences in strings.
|
||||
*/
|
||||
public function supportsUnicodeEscapes(): bool {
|
||||
return $this->id >= 70000;
|
||||
}
|
||||
|
||||
/*
|
||||
* Whether this version supports attributes.
|
||||
*/
|
||||
public function supportsAttributes(): bool {
|
||||
return $this->id >= 80000;
|
||||
}
|
||||
|
||||
public function supportsNewDereferenceWithoutParentheses(): bool {
|
||||
return $this->id >= 80400;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user