PK PQN\Tc CONTRIBUTING.mdnu W+A # Contributions Are Welcome!
If you need any help, don't hesitate to ask the community on [Gitter](https://gitter.im/PHP-CS-Fixer/Lobby).
## Quick Guide
* [Fork](https://help.github.com/articles/fork-a-repo/) the repo.
* [Checkout](https://git-scm.com/docs/git-checkout) the branch you want to make changes on:
* If you are fixing a bug or typo, improving tests or for any small tweak: the lowest branch where the changes can be applied. Once your Pull Request is accepted, the changes will get merged up to highest branches.
* `master` in other cases (new feature, deprecation, or backwards compatibility breaking changes). Note that most of the time, `master` represents the next minor release of PHP CS Fixer, so Pull Requests that break backwards compatibility might be postponed.
* Install dependencies: `composer install`.
* Create a new branch, e.g. `feature-foo` or `bugfix-bar`.
* Make changes.
* If you are adding functionality or fixing a bug - add a test! Prefer adding new test cases over modifying existing ones.
* Make sure there is no trailing spaces in code: `./check_trailing_spaces.sh`.
* Regenerate README: `php php-cs-fixer readme > README.rst`. Do not modify `README.rst` manually!
* Check if tests pass: `vendor/bin/phpunit`.
* Fix project itself: `php php-cs-fixer fix`.
## Opening a [Pull Request](https://help.github.com/articles/about-pull-requests/)
You can do some things to increase the chance that your Pull Request is accepted the first time:
* Submit one Pull Request per fix or feature.
* If your changes are not up to date, [rebase](https://git-scm.com/docs/git-rebase) your branch onto the parent branch.
* Follow the conventions used in the project.
* Remember about tests and documentation.
* Don't bump version.
## Making New Fixers
There is a [cookbook](doc/COOKBOOK-FIXERS.md) with basic instructions on how to build a new fixer. Consider reading it
before opening a PR.
## Project's Standards
* [PSR-1: Basic Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md)
* [PSR-2: Coding Style Guide](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)
* [PSR-4: Autoloading Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader.md)
* [PSR-5: PHPDoc (draft)](https://github.com/phpDocumentor/fig-standards/blob/master/proposed/phpdoc.md)
* [Symfony Coding Standards](https://symfony.com/doc/current/contributing/code/standards.html)
PK PQN*Mv2 2
UPGRADE.mdnu W+A UPGRADE GUIDE FROM 1.x to 2.0
=============================
This is guide for upgrade from version 1.x to 2.0 for using the CLI tool.
Rules and sets
--------------
To configure which fixers should be used you must now set rules and sets instead of fixers and level. This affects both configuration file and CLI arguments.
Default ruleset was changed from Symfony standard to more generic PSR2. You can still use Symfony standard, which in fact extends PSR2.
The term of risky fixers was introduced. Risky fixer is a fixer that may change the meaning of code (like `StrictComparisonFixer` fixer, which will change `==` into `===`). No rules that are followed by risky fixers are run by default. You need to explicitly permit risky fixers to run them.
Default configuration changes
----------------------------
By default, PSR2 rules are used instead of Symfony rules.
Files that will be fixed are php/phpt/twig instead of php/twig/xml/yml.
Finally, the caching mechanism is enabled by default.
CLI options
-----------
| 1.x | 2.0 | Description | Note |
| --------------- | --------------- | ------------------------------------------------------------------------------ | ------------------------------- |
| | --allow-risky | Are risky fixers allowed | |
| | --cache-file | The path to the cache file | option was added |
| --config | | Config class codename | option was removed |
| --config-file | --config | The path to a .php_cs file | option was renamed |
| --diff | --diff | Show diff | |
| --dry-run | --dry-run | Run in dry-run mode | |
| --fixers | | Coding standard fixers | option was removed, see --rules |
| --format | --format | Choose format | |
| --level | | Coding standard level | option was removed, see --rules |
| | --path-mode | Should the finder from config be
overridden or intersected with `path` arg | option was added |
| | --rules | Rules to be used | option was added |
| | --using-cache | Does cache should be used | option was added |
CLI argument
------------
On 2.x line `path` argument is an array, so you may pass multiple paths.
Intersection path mode makes the `path` argument a mask for finder you have defined in your configuration file.
Only files pointed by both finder and CLI `path` argument will be fixed.
Exit codes
----------
Exit codes for `fix` command have been changed and are build using the following bit flags:
| 1.x bit | 2.0 bit | Description | Note |
| -------:| -------:| ----------------------------------------------------------- | ---------------------------------------------------------------- |
| 0 | 0 | OK | |
| 1 | 1 | General error (or PHP/HHVM minimal requirement not matched) | no longer used for other states, never combined with other flags |
| | 4 | Some files have invalid syntax | flag was added, works only in dry-run mode |
| | 8 | Some files need fixing | flag was added, works only in dry-run mode |
| 16 | 16 | Configuration error of the application | |
| 32 | 32 | Configuration error of a Fixer | |
| | 64 | Exception within the application | flag was added |
Namespace
---------
`Symfony\CS` namespace was renamed into `PhpCsFixer`.
Config file
-----------
From now you can create new configuration file: `.php_cs.dist`. This file is used if no `.php_cs` file was found. It is recommended to create `.php_cs.dist` file attached in your repository and add `.php_cs` file to `.gitignore` for allowing your contributors to have theirs own configuration file.
Config and Finder classes
-------------------------
All off `Symfony\CS\Config\*` and `Symfony\CS\Finder\*` classes have been removed, instead use `PhpCsFixer\Config` and `PhpCsFixer\Finder`.
For that reason you can not set config class by `--config` CLI argument, from now it is used to set configuration file. Therefor the `--config-file` CLI argument is no longer available.
Renamed rules
-------------
Old name | New name | Note
-------- | -------- | ----
align_double_arrow | binary_operator_spaces | use configuration ['align_double_arrow' => true]
align_equals | binary_operator_spaces | use configuration ['align_equals' => true]
array_element_no_space_before_comma | no_whitespace_before_comma_in_array
array_element_white_space_after_comma | whitespace_after_comma_in_array
blankline_after_open_tag | blank_line_after_opening_tag
concat_with_spaces | concat_space | use configuration ['spacing' => 'one']
concat_without_spaces | concat_space | use configuration ['spacing' => 'none']
double_arrow_multiline_whitespaces | no_multiline_whitespace_around_double_arrow
duplicate_semicolon | no_empty_statement | new one fixes more cases
empty_return | simplified_null_return
echo_to_print | no_mixed_echo_print | use configuration ['use' => 'print']
eof_ending | single_blank_line_at_eof
extra_empty_lines | no_extra_consecutive_blank_lines
function_call_space | no_spaces_after_function_name
general_phpdoc_annotation_rename | phpdoc_no_alias_tag | use configuration ['property-read' => 'property', 'property-write' => 'property']
indentation | indentation_type
join_function | no_alias_functions | new one fixes more aliases
line_after_namespace | blank_line_after_namespace
linefeed | line_ending | whitespaces type aware
list_commas | no_trailing_comma_in_list_call
logical_not_operators_with_spaces | not_operator_with_space
logical_not_operators_with_successor_space | not_operator_with_successor_space
long_array_syntax | array_syntax | use configuration ['syntax' => 'long']
method_argument_default_value | no_unreachable_default_argument_value
multiline_array_trailing_comma | trailing_comma_in_multiline_array
multiline_spaces_before_semicolon | no_multiline_whitespace_before_semicolons
multiple_use | single_import_per_statement
namespace_no_leading_whitespace | no_leading_namespace_whitespace
newline_after_open_tag | linebreak_after_opening_tag
no_empty_lines_after_phpdocs | no_blank_lines_after_phpdoc
object_operator | object_operator_without_whitespace
operators_spaces | binary_operator_spaces
ordered_use | ordered_imports
parenthesis | no_spaces_inside_parenthesis
php4_constructor | no_php4_constructor
php_closing_tag | no_closing_tag
phpdoc_params | phpdoc_align
phpdoc_property | phpdoc_no_alias_tag | use configuration ['type' => 'var']
phpdoc_short_description | phpdoc_summary
phpdoc_type_to_var | phpdoc_no_alias_tag | use configuration ['type' => 'var']
phpdoc_var_to_type | phpdoc_no_alias_tag | use configuration ['var' => 'type']
print_to_echo | no_mixed_echo_print | use configuration ['use' => 'echo']
remove_leading_slash_use | no_leading_import_slash
remove_lines_between_uses | no_extra_consecutive_blank_lines | use configuration ['use']
return | blank_line_before_return
short_array_syntax | array_syntax | use configuration ['syntax' => 'short']
short_bool_cast | no_short_bool_cast
short_echo_tag | no_short_echo_tag
short_tag | full_opening_tag
single_array_no_trailing_comma | no_trailing_comma_in_singleline_array
spaces_after_semicolon | space_after_semicolon
spaces_before_semicolon | no_singleline_whitespace_before_semicolons
spaces_cast | cast_spaces
standardize_not_equal | standardize_not_equals
strict | strict_comparison
ternary_spaces | ternary_operator_spaces
trailing_spaces | no_trailing_whitespace
unalign_double_arrow | binary_operator_spaces | use configuration ['align_double_arrow' => false]
unalign_equals | binary_operator_spaces | use configuration ['align_equals' => false]
unary_operators_spaces | unary_operator_spaces
unneeded_control_parentheses | no_unneeded_control_parentheses
unused_use | no_unused_imports
visibility | visibility_required
whitespacy_lines | no_whitespace_in_blank_line
Changes to Fixers
-----------------
Fixer | Note
----- | ----
psr0 | Fixer no longer takes base dir from `ConfigInterface::getDir`, instead you may configure the fixer with `['dir' => 'my/path']`.
Custom fixers
-------------
If you have registered custom fixers in your config file `*.php_cs` using `addCustomFixer()` method...
```
fixers([
'blankline_after_open_tag',
// ...
])
->addCustomFixer(new ShopSys\CodingStandards\CsFixer\MissingButtonTypeFixer())
->addCustomFixer(new ShopSys\CodingStandards\CsFixer\OrmJoinColumnRequireNullableFixer());
```
...now you have to use `registerCustomFixers()` method instead and enable the custom fixers by their names in the `setRules()` method:
```
registerCustomFixers([
new ShopSys\CodingStandards\CsFixer\MissingButtonTypeFixer(),
new ShopSys\CodingStandards\CsFixer\OrmJoinColumnRequireNullableFixer(),
])
->setRules([
'blankline_after_open_tag',
'Shopsys/missing_button_type' => true,
'Shopsys/orm_join_column_require_nullable' => true,
// ...
]);
```
PK PQN.> " src/AbstractNoUselessElseFixer.phpnu W+A
* Dariusz Rumiński
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace PhpCsFixer;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
/**
* @author SpacePossum
*/
abstract class AbstractNoUselessElseFixer extends AbstractFixer
{
/**
* {@inheritdoc}
*/
public function getPriority()
{
// should be run before NoWhitespaceInBlankLineFixer, NoExtraBlankLinesFixer, BracesFixer and after NoEmptyStatementFixer.
return 25;
}
/**
* @param Tokens $tokens
* @param int $index
*
* @return bool
*/
protected function isSuperfluousElse(Tokens $tokens, $index)
{
$previousBlockStart = $index;
do {
// Check if all 'if', 'else if ' and 'elseif' blocks above this 'else' always end,
// if so this 'else' is overcomplete.
list($previousBlockStart, $previousBlockEnd) = $this->getPreviousBlock($tokens, $previousBlockStart);
// short 'if' detection
$previous = $previousBlockEnd;
if ($tokens[$previous]->equals('}')) {
$previous = $tokens->getPrevMeaningfulToken($previous);
}
if (
!$tokens[$previous]->equals(';') || // 'if' block doesn't end with semicolon, keep 'else'
$tokens[$tokens->getPrevMeaningfulToken($previous)]->equals('{') // empty 'if' block, keep 'else'
) {
return false;
}
$candidateIndex = $tokens->getPrevTokenOfKind(
$previous,
[
';',
[T_BREAK],
[T_CLOSE_TAG],
[T_CONTINUE],
[T_EXIT],
[T_GOTO],
[T_IF],
[T_RETURN],
[T_THROW],
]
);
if (
null === $candidateIndex
|| $tokens[$candidateIndex]->equalsAny([';', [T_CLOSE_TAG], [T_IF]])
|| $this->isInConditional($tokens, $candidateIndex, $previousBlockStart)
|| $this->isInConditionWithoutBraces($tokens, $candidateIndex, $previousBlockStart)
) {
return false;
}
// implicit continue, i.e. delete candidate
} while (!$tokens[$previousBlockStart]->isGivenKind(T_IF));
return true;
}
/**
* Return the first and last token index of the previous block.
*
* [0] First is either T_IF, T_ELSE or T_ELSEIF
* [1] Last is either '}' or ';' / T_CLOSE_TAG for short notation blocks
*
* @param Tokens $tokens
* @param int $index T_IF, T_ELSE, T_ELSEIF
*
* @return int[]
*/
private function getPreviousBlock(Tokens $tokens, $index)
{
$close = $previous = $tokens->getPrevMeaningfulToken($index);
// short 'if' detection
if ($tokens[$close]->equals('}')) {
$previous = $tokens->findBlockStart(Tokens::BLOCK_TYPE_CURLY_BRACE, $close);
}
$open = $tokens->getPrevTokenOfKind($previous, [[T_IF], [T_ELSE], [T_ELSEIF]]);
if ($tokens[$open]->isGivenKind(T_IF)) {
$elseCandidate = $tokens->getPrevMeaningfulToken($open);
if ($tokens[$elseCandidate]->isGivenKind(T_ELSE)) {
$open = $elseCandidate;
}
}
return [$open, $close];
}
/**
* @param Tokens $tokens
* @param int $index Index of the token to check
* @param int $lowerLimitIndex Lower limit index. Since the token to check will always be in a conditional we must stop checking at this index
*
* @return bool
*/
private function isInConditional(Tokens $tokens, $index, $lowerLimitIndex)
{
$candidateIndex = $tokens->getPrevTokenOfKind($index, [')', ';', ':']);
if ($tokens[$candidateIndex]->equals(':')) {
return true;
}
if (!$tokens[$candidateIndex]->equals(')')) {
return false; // token is ';' or close tag
}
// token is always ')' here.
// If it is part of the condition the token is always in, return false.
// If it is not it is a nested condition so return true
$open = $tokens->findBlockStart(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $candidateIndex);
return $tokens->getPrevMeaningfulToken($open) > $lowerLimitIndex;
}
/**
* For internal use only, as it is not perfect.
*
* Returns if the token at given index is part of a if/elseif/else statement
* without {}. Assumes not passing the last `;`/close tag of the statement, not
* out of range index, etc.
*
* @param Tokens $tokens
* @param int $index Index of the token to check
* @param int $lowerLimitIndex
*
* @return bool
*/
private function isInConditionWithoutBraces(Tokens $tokens, $index, $lowerLimitIndex)
{
do {
if ($tokens[$index]->isComment() || $tokens[$index]->isWhitespace()) {
$index = $tokens->getPrevMeaningfulToken($index);
}
$token = $tokens[$index];
if ($token->isGivenKind([T_IF, T_ELSEIF, T_ELSE])) {
return true;
}
if ($token->equals(';', '}')) {
return false;
}
if ($token->equals('{')) {
$index = $tokens->getPrevMeaningfulToken($index);
// OK if belongs to: for, do, while, foreach
// Not OK if belongs to: if, else, elseif
if ($tokens[$index]->isGivenKind(T_DO)) {
--$index;
continue;
}
if (!$tokens[$index]->equals(')')) {
return false; // like `else {`
}
$index = $tokens->findBlockStart(
Tokens::BLOCK_TYPE_PARENTHESIS_BRACE,
$index
);
$index = $tokens->getPrevMeaningfulToken($index);
if ($tokens[$index]->isGivenKind([T_IF, T_ELSEIF])) {
return false;
}
} elseif ($token->equals(')')) {
$type = Tokens::detectBlockType($token);
$index = $tokens->findBlockStart(
$type['type'],
$index
);
$index = $tokens->getPrevMeaningfulToken($index);
} else {
--$index;
}
} while ($index > $lowerLimitIndex);
return false;
}
}
PK PQNH src/AbstractAlignFixerHelper.phpnu W+A
* Dariusz Rumiński
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace PhpCsFixer;
use PhpCsFixer\Tokenizer\Tokens;
/**
* @author Carlos Cirello
*
* @internal
*
* @deprecated
*/
abstract class AbstractAlignFixerHelper
{
/**
* @const Placeholder used as anchor for right alignment.
*/
const ALIGNABLE_PLACEHOLDER = "\x2 ALIGNABLE%d \x3";
/**
* Keep track of the deepest level ever achieved while
* parsing the code. Used later to replace alignment
* placeholders with spaces.
*
* @var int
*/
protected $deepestLevel = 0;
public function fix(Tokens $tokens)
{
// This fixer works partially on Tokens and partially on string representation of code.
// During the process of fixing internal state of single Token may be affected by injecting ALIGNABLE_PLACEHOLDER to its content.
// The placeholder will be resolved by `replacePlaceholder` method by removing placeholder or changing it into spaces.
// That way of fixing the code causes disturbances in marking Token as changed - if code is perfectly valid then placeholder
// still be injected and removed, which will cause the `changed` flag to be set.
// To handle that unwanted behavior we work on clone of Tokens collection and then override original collection with fixed collection.
$tokensClone = clone $tokens;
$this->injectAlignmentPlaceholders($tokensClone, 0, \count($tokens));
$content = $this->replacePlaceholder($tokensClone);
$tokens->setCode($content);
}
/**
* Inject into the text placeholders of candidates of vertical alignment.
*
* @param Tokens $tokens
* @param int $startAt
* @param int $endAt
*/
abstract protected function injectAlignmentPlaceholders(Tokens $tokens, $startAt, $endAt);
/**
* Look for group of placeholders, and provide vertical alignment.
*
* @param Tokens $tokens
*
* @return string
*/
protected function replacePlaceholder(Tokens $tokens)
{
$tmpCode = $tokens->generateCode();
for ($j = 0; $j <= $this->deepestLevel; ++$j) {
$placeholder = sprintf(self::ALIGNABLE_PLACEHOLDER, $j);
if (false === strpos($tmpCode, $placeholder)) {
continue;
}
$lines = explode("\n", $tmpCode);
$linesWithPlaceholder = [];
$blockSize = 0;
$linesWithPlaceholder[$blockSize] = [];
foreach ($lines as $index => $line) {
if (substr_count($line, $placeholder) > 0) {
$linesWithPlaceholder[$blockSize][] = $index;
} else {
++$blockSize;
$linesWithPlaceholder[$blockSize] = [];
}
}
foreach ($linesWithPlaceholder as $group) {
if (\count($group) < 1) {
continue;
}
$rightmostSymbol = 0;
foreach ($group as $index) {
$rightmostSymbol = max($rightmostSymbol, strpos(utf8_decode($lines[$index]), $placeholder));
}
foreach ($group as $index) {
$line = $lines[$index];
$currentSymbol = strpos(utf8_decode($line), $placeholder);
$delta = abs($rightmostSymbol - $currentSymbol);
if ($delta > 0) {
$line = str_replace($placeholder, str_repeat(' ', $delta).$placeholder, $line);
$lines[$index] = $line;
}
}
}
$tmpCode = str_replace($placeholder, '', implode("\n", $lines));
}
return $tmpCode;
}
}
PK PQN2i i src/ToolInfoInterface.phpnu W+A
* Dariusz Rumiński
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace PhpCsFixer;
/**
* @internal
*/
interface ToolInfoInterface
{
public function getComposerInstallationDetails();
public function getComposerVersion();
public function getVersion();
public function isInstalledAsPhar();
public function isInstalledByComposer();
public function getPharDownloadUri($version);
}
PK PQNh ;P P src/Tokenizer/TokensAnalyzer.phpnu W+A
* Dariusz Rumiński
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace PhpCsFixer\Tokenizer;
/**
* Analyzer of Tokens collection.
*
* Its role is to provide the ability to analyze collection.
*
* @author Dariusz Rumiński
* @author Gregor Harlan
* @author SpacePossum
*
* @internal
*/
final class TokensAnalyzer
{
/**
* Tokens collection instance.
*
* @var Tokens
*/
private $tokens;
public function __construct(Tokens $tokens)
{
$this->tokens = $tokens;
}
/**
* Get indexes of methods and properties in classy code (classes, interfaces and traits).
*
* @return array[]
*/
public function getClassyElements()
{
$this->tokens->rewind();
$elements = [];
for ($index = 1, $count = \count($this->tokens) - 2; $index < $count; ++$index) {
if ($this->tokens[$index]->isClassy()) {
list($index, $newElements) = $this->findClassyElements($index);
$elements += $newElements;
}
}
ksort($elements);
return $elements;
}
/**
* Get indexes of namespace uses.
*
* @param bool $perNamespace Return namespace uses per namespace
*
* @return int[]|int[][]
*/
public function getImportUseIndexes($perNamespace = false)
{
$tokens = $this->tokens;
$tokens->rewind();
$uses = [];
$namespaceIndex = 0;
for ($index = 0, $limit = $tokens->count(); $index < $limit; ++$index) {
$token = $tokens[$index];
if ($token->isGivenKind(T_NAMESPACE)) {
$nextTokenIndex = $tokens->getNextTokenOfKind($index, [';', '{']);
$nextToken = $tokens[$nextTokenIndex];
if ($nextToken->equals('{')) {
$index = $nextTokenIndex;
}
if ($perNamespace) {
++$namespaceIndex;
}
continue;
}
if ($token->isGivenKind(T_USE)) {
$uses[$namespaceIndex][] = $index;
}
}
if (!$perNamespace && isset($uses[$namespaceIndex])) {
return $uses[$namespaceIndex];
}
return $uses;
}
/**
* Check if there is an array at given index.
*
* @param int $index
*
* @return bool
*/
public function isArray($index)
{
return $this->tokens[$index]->isGivenKind([T_ARRAY, CT::T_ARRAY_SQUARE_BRACE_OPEN]);
}
/**
* Check if the array at index is multiline.
*
* This only checks the root-level of the array.
*
* @param int $index
*
* @return bool
*/
public function isArrayMultiLine($index)
{
if (!$this->isArray($index)) {
throw new \InvalidArgumentException(sprintf('Not an array at given index %d.', $index));
}
$tokens = $this->tokens;
// Skip only when its an array, for short arrays we need the brace for correct
// level counting
if ($tokens[$index]->isGivenKind(T_ARRAY)) {
$index = $tokens->getNextMeaningfulToken($index);
}
$endIndex = $tokens[$index]->equals('(')
? $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $index)
: $tokens->findBlockEnd(Tokens::BLOCK_TYPE_ARRAY_SQUARE_BRACE, $index)
;
for (++$index; $index < $endIndex; ++$index) {
$token = $tokens[$index];
$blockType = Tokens::detectBlockType($token);
if ($blockType && $blockType['isStart']) {
$index = $tokens->findBlockEnd($blockType['type'], $index);
continue;
}
if (
$token->isWhitespace() &&
!$tokens[$index - 1]->isGivenKind(T_END_HEREDOC) &&
false !== strpos($token->getContent(), "\n")
) {
return true;
}
}
return false;
}
/**
* Returns the attributes of the method under the given index.
*
* The array has the following items:
* 'visibility' int|null T_PRIVATE, T_PROTECTED or T_PUBLIC
* 'static' bool
* 'abstract' bool
* 'final' bool
*
* @param int $index Token index of the method (T_FUNCTION)
*
* @return array
*/
public function getMethodAttributes($index)
{
$tokens = $this->tokens;
$token = $tokens[$index];
if (!$token->isGivenKind(T_FUNCTION)) {
throw new \LogicException(sprintf('No T_FUNCTION at given index %d, got %s.', $index, $token->getName()));
}
$attributes = [
'visibility' => null,
'static' => false,
'abstract' => false,
'final' => false,
];
for ($i = $index; $i >= 0; --$i) {
$tokenIndex = $tokens->getPrevMeaningfulToken($i);
$i = $tokenIndex;
$token = $tokens[$tokenIndex];
if ($token->isGivenKind(T_STATIC)) {
$attributes['static'] = true;
continue;
}
if ($token->isGivenKind(T_FINAL)) {
$attributes['final'] = true;
continue;
}
if ($token->isGivenKind(T_ABSTRACT)) {
$attributes['abstract'] = true;
continue;
}
// visibility
if ($token->isGivenKind(T_PRIVATE)) {
$attributes['visibility'] = T_PRIVATE;
continue;
}
if ($token->isGivenKind(T_PROTECTED)) {
$attributes['visibility'] = T_PROTECTED;
continue;
}
if ($token->isGivenKind(T_PUBLIC)) {
$attributes['visibility'] = T_PUBLIC;
continue;
}
// found a meaningful token that is not part of
// the function signature; stop looking
break;
}
return $attributes;
}
/**
* Check if there is an anonymous class under given index.
*
* @param int $index
*
* @return bool
*/
public function isAnonymousClass($index)
{
$tokens = $this->tokens;
$token = $tokens[$index];
if (!$token->isClassy()) {
throw new \LogicException(sprintf('No classy token at given index %d.', $index));
}
if (!$token->isGivenKind(T_CLASS)) {
return false;
}
return $tokens[$tokens->getPrevMeaningfulToken($index)]->isGivenKind(T_NEW);
}
/**
* Check if the function under given index is a lambda.
*
* @param int $index
*
* @return bool
*/
public function isLambda($index)
{
if (!$this->tokens[$index]->isGivenKind(T_FUNCTION)) {
throw new \LogicException(sprintf('No T_FUNCTION at given index %d, got %s.', $index, $this->tokens[$index]->getName()));
}
$startParenthesisIndex = $this->tokens->getNextMeaningfulToken($index);
$startParenthesisToken = $this->tokens[$startParenthesisIndex];
// skip & for `function & () {}` syntax
if ($startParenthesisToken->isGivenKind(CT::T_RETURN_REF)) {
$startParenthesisIndex = $this->tokens->getNextMeaningfulToken($startParenthesisIndex);
$startParenthesisToken = $this->tokens[$startParenthesisIndex];
}
return $startParenthesisToken->equals('(');
}
/**
* Check if the T_STRING under given index is a constant invocation.
*
* @param int $index
*
* @return bool
*/
public function isConstantInvocation($index)
{
if (!$this->tokens[$index]->isGivenKind(T_STRING)) {
throw new \LogicException(sprintf('No T_STRING at given index %d, got %s.', $index, $this->tokens[$index]->getName()));
}
$nextIndex = $this->tokens->getNextMeaningfulToken($index);
if (
$this->tokens[$nextIndex]->equalsAny(['(', '{']) ||
$this->tokens[$nextIndex]->isGivenKind([T_AS, T_DOUBLE_COLON, T_ELLIPSIS, T_NS_SEPARATOR, CT::T_RETURN_REF, CT::T_TYPE_ALTERNATION, T_VARIABLE])
) {
return false;
}
$prevIndex = $this->tokens->getPrevMeaningfulToken($index);
if ($this->tokens[$prevIndex]->isGivenKind([T_AS, T_CLASS, T_CONST, T_DOUBLE_COLON, T_FUNCTION, T_GOTO, CT::T_GROUP_IMPORT_BRACE_OPEN, T_INTERFACE, T_OBJECT_OPERATOR, T_TRAIT])) {
return false;
}
while ($this->tokens[$prevIndex]->isGivenKind([CT::T_NAMESPACE_OPERATOR, T_NS_SEPARATOR, T_STRING])) {
$prevIndex = $this->tokens->getPrevMeaningfulToken($prevIndex);
}
if ($this->tokens[$prevIndex]->isGivenKind([CT::T_CONST_IMPORT, T_EXTENDS, CT::T_FUNCTION_IMPORT, T_IMPLEMENTS, T_INSTANCEOF, T_INSTEADOF, T_NAMESPACE, T_NEW, T_USE, CT::T_USE_TRAIT])) {
return false;
}
// `FOO & $bar` could be:
// - function reference parameter: function baz(Foo & $bar) {}
// - bit operator: $x = FOO & $bar;
if ($this->tokens[$nextIndex]->equals('&') && $this->tokens[$this->tokens->getNextMeaningfulToken($nextIndex)]->isGivenKind(T_VARIABLE)) {
$checkIndex = $this->tokens->getPrevTokenOfKind($prevIndex, [';', '{', '}', [T_FUNCTION], [T_OPEN_TAG], [T_OPEN_TAG_WITH_ECHO]]);
if ($this->tokens[$checkIndex]->isGivenKind(T_FUNCTION)) {
return false;
}
}
// check for `implements`/`use` list
if ($this->tokens[$prevIndex]->equals(',')) {
$checkIndex = $prevIndex;
while ($this->tokens[$checkIndex]->equalsAny([',', [T_AS], [CT::T_NAMESPACE_OPERATOR], [T_NS_SEPARATOR], [T_STRING]])) {
$checkIndex = $this->tokens->getPrevMeaningfulToken($checkIndex);
}
if ($this->tokens[$checkIndex]->isGivenKind([CT::T_GROUP_IMPORT_BRACE_OPEN, T_IMPLEMENTS, CT::T_USE_TRAIT])) {
return false;
}
}
// check for array in double quoted string: `"..$foo[bar].."`
if ($this->tokens[$prevIndex]->equals('[') && $this->tokens[$nextIndex]->equals(']')) {
$checkToken = $this->tokens[$this->tokens->getNextMeaningfulToken($nextIndex)];
if ($checkToken->equals('"') || $checkToken->isGivenKind([T_CURLY_OPEN, T_DOLLAR_OPEN_CURLY_BRACES, T_ENCAPSED_AND_WHITESPACE, T_VARIABLE])) {
return false;
}
}
// check for goto label
if ($this->tokens[$nextIndex]->equals(':') && $this->tokens[$prevIndex]->equalsAny([';', '}', [T_OPEN_TAG], [T_OPEN_TAG_WITH_ECHO]])) {
return false;
}
return true;
}
/**
* Checks if there is an unary successor operator under given index.
*
* @param int $index
*
* @return bool
*/
public function isUnarySuccessorOperator($index)
{
static $allowedPrevToken = [
']',
[T_STRING],
[T_VARIABLE],
[CT::T_DYNAMIC_PROP_BRACE_CLOSE],
[CT::T_DYNAMIC_VAR_BRACE_CLOSE],
];
$tokens = $this->tokens;
$token = $tokens[$index];
if (!$token->isGivenKind([T_INC, T_DEC])) {
return false;
}
$prevToken = $tokens[$tokens->getPrevMeaningfulToken($index)];
return $prevToken->equalsAny($allowedPrevToken);
}
/**
* Checks if there is an unary predecessor operator under given index.
*
* @param int $index
*
* @return bool
*/
public function isUnaryPredecessorOperator($index)
{
static $potentialSuccessorOperator = [T_INC, T_DEC];
static $potentialBinaryOperator = ['+', '-', '&', [CT::T_RETURN_REF]];
static $otherOperators;
if (null === $otherOperators) {
$otherOperators = ['!', '~', '@', [T_ELLIPSIS]];
}
static $disallowedPrevTokens;
if (null === $disallowedPrevTokens) {
$disallowedPrevTokens = [
']',
'}',
')',
'"',
'`',
[CT::T_ARRAY_SQUARE_BRACE_CLOSE],
[CT::T_DYNAMIC_PROP_BRACE_CLOSE],
[CT::T_DYNAMIC_VAR_BRACE_CLOSE],
[T_CLASS_C],
[T_CONSTANT_ENCAPSED_STRING],
[T_DEC],
[T_DIR],
[T_DNUMBER],
[T_FILE],
[T_FUNC_C],
[T_INC],
[T_LINE],
[T_LNUMBER],
[T_METHOD_C],
[T_NS_C],
[T_STRING],
[T_TRAIT_C],
[T_VARIABLE],
];
}
$tokens = $this->tokens;
$token = $tokens[$index];
if ($token->isGivenKind($potentialSuccessorOperator)) {
return !$this->isUnarySuccessorOperator($index);
}
if ($token->equalsAny($otherOperators)) {
return true;
}
if (!$token->equalsAny($potentialBinaryOperator)) {
return false;
}
$prevToken = $tokens[$tokens->getPrevMeaningfulToken($index)];
if (!$prevToken->equalsAny($disallowedPrevTokens)) {
return true;
}
if (!$token->equals('&') || !$prevToken->isGivenKind(T_STRING)) {
return false;
}
static $searchTokens = [
';',
'{',
'}',
[T_FUNCTION],
[T_OPEN_TAG],
[T_OPEN_TAG_WITH_ECHO],
];
$prevToken = $tokens[$tokens->getPrevTokenOfKind($index, $searchTokens)];
return $prevToken->isGivenKind(T_FUNCTION);
}
/**
* Checks if there is a binary operator under given index.
*
* @param int $index
*
* @return bool
*/
public function isBinaryOperator($index)
{
static $nonArrayOperators = [
'=' => true,
'*' => true,
'/' => true,
'%' => true,
'<' => true,
'>' => true,
'|' => true,
'^' => true,
];
static $potentialUnaryNonArrayOperators = [
'+' => true,
'-' => true,
'&' => true,
];
static $arrayOperators;
if (null === $arrayOperators) {
$arrayOperators = [
T_AND_EQUAL => true, // &=
T_BOOLEAN_AND => true, // &&
T_BOOLEAN_OR => true, // ||
T_CONCAT_EQUAL => true, // .=
T_DIV_EQUAL => true, // /=
T_DOUBLE_ARROW => true, // =>
T_IS_EQUAL => true, // ==
T_IS_GREATER_OR_EQUAL => true, // >=
T_IS_IDENTICAL => true, // ===
T_IS_NOT_EQUAL => true, // !=, <>
T_IS_NOT_IDENTICAL => true, // !==
T_IS_SMALLER_OR_EQUAL => true, // <=
T_LOGICAL_AND => true, // and
T_LOGICAL_OR => true, // or
T_LOGICAL_XOR => true, // xor
T_MINUS_EQUAL => true, // -=
T_MOD_EQUAL => true, // %=
T_MUL_EQUAL => true, // *=
T_OR_EQUAL => true, // |=
T_PLUS_EQUAL => true, // +=
T_POW => true, // **
T_POW_EQUAL => true, // **=
T_SL => true, // <<
T_SL_EQUAL => true, // <<=
T_SR => true, // >>
T_SR_EQUAL => true, // >>=
T_XOR_EQUAL => true, // ^=
CT::T_TYPE_ALTERNATION => true, // |
];
if (\defined('T_SPACESHIP')) {
$arrayOperators[T_SPACESHIP] = true; // <=>
}
if (\defined('T_COALESCE')) {
$arrayOperators[T_COALESCE] = true; // ??
}
}
$tokens = $this->tokens;
$token = $tokens[$index];
if ($token->isArray()) {
return isset($arrayOperators[$token->getId()]);
}
if (isset($nonArrayOperators[$token->getContent()])) {
return true;
}
if (isset($potentialUnaryNonArrayOperators[$token->getContent()])) {
return !$this->isUnaryPredecessorOperator($index);
}
return false;
}
/**
* Check if `T_WHILE` token at given index is `do { ... } while ();` syntax
* and not `while () { ...}`.
*
* @param int $index
*
* @return bool
*/
public function isWhilePartOfDoWhile($index)
{
$tokens = $this->tokens;
$token = $tokens[$index];
if (!$token->isGivenKind(T_WHILE)) {
throw new \LogicException(sprintf('No T_WHILE at given index %d, got %s.', $index, $token->getName()));
}
$endIndex = $tokens->getPrevMeaningfulToken($index);
if (!$tokens[$endIndex]->equals('}')) {
return false;
}
$startIndex = $tokens->findBlockStart(Tokens::BLOCK_TYPE_CURLY_BRACE, $endIndex);
$beforeStartIndex = $tokens->getPrevMeaningfulToken($startIndex);
return $tokens[$beforeStartIndex]->isGivenKind(T_DO);
}
/**
* Find classy elements.
*
* Searches in tokens from the classy (start) index till the end (index) of the classy.
* Returns an array; first value is the index until the method has analysed (int), second the found classy elements (array).
*
* @param int $index classy index
*
* @return array
*/
private function findClassyElements($index)
{
$elements = [];
$curlyBracesLevel = 0;
$bracesLevel = 0;
$classIndex = $index;
++$index; // skip the classy index itself
for ($count = \count($this->tokens); $index < $count; ++$index) {
$token = $this->tokens[$index];
if ($token->isGivenKind(T_ENCAPSED_AND_WHITESPACE)) {
continue;
}
if ($token->isClassy()) { // anonymous class in class
list($index, $newElements) = $this->findClassyElements($index);
$elements += $newElements;
continue;
}
if ($token->equals('(')) {
++$bracesLevel;
continue;
}
if ($token->equals(')')) {
--$bracesLevel;
continue;
}
if ($token->equals('{')) {
++$curlyBracesLevel;
continue;
}
if ($token->equals('}')) {
--$curlyBracesLevel;
if (0 === $curlyBracesLevel) {
break;
}
continue;
}
if (1 !== $curlyBracesLevel || !$token->isArray()) {
continue;
}
if (0 === $bracesLevel && $token->isGivenKind(T_VARIABLE)) {
$elements[$index] = [
'token' => $token,
'type' => 'property',
'classIndex' => $classIndex,
];
continue;
}
if ($token->isGivenKind(T_FUNCTION)) {
$elements[$index] = [
'token' => $token,
'type' => 'method',
'classIndex' => $classIndex,
];
} elseif ($token->isGivenKind(T_CONST)) {
$elements[$index] = [
'token' => $token,
'type' => 'const',
'classIndex' => $classIndex,
];
}
}
return [$index, $elements];
}
}
PK PQNޅ src/Tokenizer/CodeHasher.phpnu W+A
* Dariusz Rumiński
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace PhpCsFixer\Tokenizer;
/**
* @author Dariusz Rumiński
*
* @internal
*/
final class CodeHasher
{
private function __construct()
{
// cannot create instance of util. class
}
/**
* Calculate hash for code.
*
* @param string $code
*
* @return string
*/
public static function calculateCodeHash($code)
{
return (string) crc32($code);
}
}
PK PQN)q src/Tokenizer/CT.phpnu W+A
* Dariusz Rumiński
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace PhpCsFixer\Tokenizer;
/**
* @author Dariusz Rumiński
*/
final class CT
{
const T_ARRAY_INDEX_CURLY_BRACE_CLOSE = 10001;
const T_ARRAY_INDEX_CURLY_BRACE_OPEN = 10002;
const T_ARRAY_SQUARE_BRACE_CLOSE = 10003;
const T_ARRAY_SQUARE_BRACE_OPEN = 10004;
const T_ARRAY_TYPEHINT = 10005;
const T_BRACE_CLASS_INSTANTIATION_CLOSE = 10006;
const T_BRACE_CLASS_INSTANTIATION_OPEN = 10007;
const T_CLASS_CONSTANT = 10008;
const T_CONST_IMPORT = 10009;
const T_CURLY_CLOSE = 10010;
const T_DESTRUCTURING_SQUARE_BRACE_CLOSE = 10011;
const T_DESTRUCTURING_SQUARE_BRACE_OPEN = 10012;
const T_DOLLAR_CLOSE_CURLY_BRACES = 10013;
const T_DYNAMIC_PROP_BRACE_CLOSE = 10014;
const T_DYNAMIC_PROP_BRACE_OPEN = 10015;
const T_DYNAMIC_VAR_BRACE_CLOSE = 10016;
const T_DYNAMIC_VAR_BRACE_OPEN = 10017;
const T_FUNCTION_IMPORT = 10018;
const T_GROUP_IMPORT_BRACE_CLOSE = 10019;
const T_GROUP_IMPORT_BRACE_OPEN = 10020;
const T_NAMESPACE_OPERATOR = 10021;
const T_NULLABLE_TYPE = 10022;
const T_RETURN_REF = 10023;
const T_TYPE_ALTERNATION = 10024;
const T_TYPE_COLON = 10025;
const T_USE_LAMBDA = 10026;
const T_USE_TRAIT = 10027;
private function __construct()
{
}
/**
* Get name for custom token.
*
* @param int $value custom token value
*
* @return string
*/
public static function getName($value)
{
if (!self::has($value)) {
throw new \InvalidArgumentException(sprintf('No custom token was found for "%s".', $value));
}
$tokens = self::getMapById();
return 'CT::'.$tokens[$value];
}
/**
* Check if given custom token exists.
*
* @param int $value custom token value
*
* @return bool
*/
public static function has($value)
{
$tokens = self::getMapById();
return isset($tokens[$value]);
}
private static function getMapById()
{
static $constants;
if (null === $constants) {
$reflection = new \ReflectionClass(__CLASS__);
$constants = array_flip($reflection->getConstants());
}
return $constants;
}
}
PK PQN5)\
, src/Tokenizer/Transformer/UseTransformer.phpnu W+A
* Dariusz Rumiński
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace PhpCsFixer\Tokenizer\Transformer;
use PhpCsFixer\Tokenizer\AbstractTransformer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
/**
* Transform T_USE into:
* - CT::T_USE_TRAIT for imports,
* - CT::T_USE_LAMBDA for lambda variable uses.
*
* @author Dariusz Rumiński
*
* @internal
*/
final class UseTransformer extends AbstractTransformer
{
/**
* {@inheritdoc}
*/
public function getCustomTokens()
{
return [CT::T_USE_TRAIT, CT::T_USE_LAMBDA];
}
/**
* {@inheritdoc}
*/
public function getPriority()
{
// Should run after CurlyBraceTransformer
return -5;
}
/**
* {@inheritdoc}
*/
public function getRequiredPhpVersionId()
{
return 50300;
}
/**
* {@inheritdoc}
*/
public function process(Tokens $tokens, Token $token, $index)
{
if ($token->isGivenKind(T_USE) && $this->isUseForLambda($tokens, $index)) {
$tokens[$index] = new Token([CT::T_USE_LAMBDA, $token->getContent()]);
return;
}
// Only search inside class/trait body for `T_USE` for traits.
// Cannot import traits inside interfaces or anywhere else
if (!$token->isGivenKind([T_CLASS, T_TRAIT])) {
return;
}
if ($tokens[$tokens->getPrevMeaningfulToken($index)]->isGivenKind(T_DOUBLE_COLON)) {
return;
}
$index = $tokens->getNextTokenOfKind($index, ['{']);
$innerLimit = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $index);
while ($index < $innerLimit) {
$token = $tokens[++$index];
if (!$token->isGivenKind(T_USE)) {
continue;
}
if ($this->isUseForLambda($tokens, $index)) {
$tokens[$index] = new Token([CT::T_USE_LAMBDA, $token->getContent()]);
} else {
$tokens[$index] = new Token([CT::T_USE_TRAIT, $token->getContent()]);
}
}
}
/**
* Check if token under given index is `use` statement for lambda function.
*
* @param Tokens $tokens
* @param int $index
*
* @return bool
*/
private function isUseForLambda(Tokens $tokens, $index)
{
$nextToken = $tokens[$tokens->getNextMeaningfulToken($index)];
// test `function () use ($foo) {}` case
return $nextToken->equals('(');
}
}
PK PQNmX X 4 src/Tokenizer/Transformer/SquareBraceTransformer.phpnu W+A
* Dariusz Rumiński
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace PhpCsFixer\Tokenizer\Transformer;
use PhpCsFixer\Tokenizer\AbstractTransformer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
/**
* Transform discriminate overloaded square braces tokens.
*
* Performed transformations:
* - in `[1, 2, 3]` into CT::T_ARRAY_SQUARE_BRACE_OPEN and CT::T_ARRAY_SQUARE_BRACE_CLOSE,
* - in `[$a, &$b, [$c]] = array(1, 2, array(3))` into CT::T_DESTRUCTURING_SQUARE_BRACE_OPEN and CT::T_DESTRUCTURING_SQUARE_BRACE_CLOSE.
*
* @author Dariusz Rumiński
* @author SpacePossum
*
* @internal
*/
final class SquareBraceTransformer extends AbstractTransformer
{
/**
* {@inheritdoc}
*/
public function getCustomTokens()
{
return [
CT::T_ARRAY_SQUARE_BRACE_OPEN,
CT::T_ARRAY_SQUARE_BRACE_CLOSE,
CT::T_DESTRUCTURING_SQUARE_BRACE_OPEN,
CT::T_DESTRUCTURING_SQUARE_BRACE_CLOSE,
];
}
/**
* {@inheritdoc}
*/
public function getRequiredPhpVersionId()
{
// Short array syntax was introduced in PHP 5.4, but the fixer is smart
// enough to handle it even before 5.4.
// Same for array destructing syntax sugar `[` introduced in PHP 7.1.
return 50000;
}
/**
* {@inheritdoc}
*/
public function process(Tokens $tokens, Token $token, $index)
{
if ($this->isArrayDestructing($tokens, $index)) {
$this->transformIntoDestructuringSquareBrace($tokens, $index);
return;
}
if ($this->isShortArray($tokens, $index)) {
$this->transformIntoArraySquareBrace($tokens, $index);
}
}
/**
* @param Tokens $tokens
* @param int $index
*/
private function transformIntoArraySquareBrace(Tokens $tokens, $index)
{
$endIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_INDEX_SQUARE_BRACE, $index);
$tokens[$index] = new Token([CT::T_ARRAY_SQUARE_BRACE_OPEN, '[']);
$tokens[$endIndex] = new Token([CT::T_ARRAY_SQUARE_BRACE_CLOSE, ']']);
}
/**
* @param Tokens $tokens
* @param int $index
*/
private function transformIntoDestructuringSquareBrace(Tokens $tokens, $index)
{
$endIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_INDEX_SQUARE_BRACE, $index);
$tokens[$index] = new Token([CT::T_DESTRUCTURING_SQUARE_BRACE_OPEN, '[']);
$tokens[$endIndex] = new Token([CT::T_DESTRUCTURING_SQUARE_BRACE_CLOSE, ']']);
$previousMeaningfulIndex = $index;
$index = $tokens->getNextMeaningfulToken($index);
while ($index < $endIndex) {
if ($tokens[$index]->equals('[') && $tokens[$previousMeaningfulIndex]->equalsAny([[CT::T_DESTRUCTURING_SQUARE_BRACE_OPEN], ','])) {
$tokens[$tokens->findBlockEnd(Tokens::BLOCK_TYPE_INDEX_SQUARE_BRACE, $index)] = new Token([CT::T_DESTRUCTURING_SQUARE_BRACE_CLOSE, ']']);
$tokens[$index] = new Token([CT::T_DESTRUCTURING_SQUARE_BRACE_OPEN, '[']);
}
$previousMeaningfulIndex = $index;
$index = $tokens->getNextMeaningfulToken($index);
}
}
/**
* Check if token under given index is short array opening.
*
* @param Tokens $tokens
* @param int $index
*
* @return bool
*/
private function isShortArray(Tokens $tokens, $index)
{
if (!$tokens[$index]->equals('[')) {
return false;
}
static $disallowedPrevTokens = [
')',
']',
'}',
'"',
[T_CONSTANT_ENCAPSED_STRING],
[T_STRING],
[T_STRING_VARNAME],
[T_VARIABLE],
[CT::T_ARRAY_SQUARE_BRACE_CLOSE],
[CT::T_DYNAMIC_PROP_BRACE_CLOSE],
[CT::T_DYNAMIC_VAR_BRACE_CLOSE],
[CT::T_ARRAY_INDEX_CURLY_BRACE_CLOSE],
];
$prevToken = $tokens[$tokens->getPrevMeaningfulToken($index)];
if ($prevToken->equalsAny($disallowedPrevTokens)) {
return false;
}
$nextToken = $tokens[$tokens->getNextMeaningfulToken($index)];
if ($nextToken->equals(']')) {
return true;
}
return !$this->isArrayDestructing($tokens, $index);
}
/**
* @param Tokens $tokens
* @param int $index
*
* @return bool
*/
private function isArrayDestructing(Tokens $tokens, $index)
{
if (\PHP_VERSION_ID < 70100 || !$tokens[$index]->equals('[')) {
return false;
}
static $disallowedPrevTokens = [
')',
']',
'"',
[T_CONSTANT_ENCAPSED_STRING],
[T_STRING],
[T_STRING_VARNAME],
[T_VARIABLE],
[CT::T_ARRAY_SQUARE_BRACE_CLOSE],
[CT::T_DYNAMIC_PROP_BRACE_CLOSE],
[CT::T_DYNAMIC_VAR_BRACE_CLOSE],
[CT::T_ARRAY_INDEX_CURLY_BRACE_CLOSE],
];
$prevToken = $tokens[$tokens->getPrevMeaningfulToken($index)];
if ($prevToken->equalsAny($disallowedPrevTokens)) {
return false;
}
$type = Tokens::detectBlockType($tokens[$index]);
$end = $tokens->findBlockEnd($type['type'], $index);
$nextToken = $tokens[$tokens->getNextMeaningfulToken($end)];
return $nextToken->equals('=');
}
}
PK PQN 5 src/Tokenizer/Transformer/NullableTypeTransformer.phpnu W+A
* Dariusz Rumiński
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace PhpCsFixer\Tokenizer\Transformer;
use PhpCsFixer\Tokenizer\AbstractTransformer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
/**
* Transform `?` operator into CT::T_NULLABLE_TYPE in `function foo(?Bar $b) {}`.
*
* @author Dariusz Rumiński
*
* @internal
*/
final class NullableTypeTransformer extends AbstractTransformer
{
/**
* {@inheritdoc}
*/
public function getCustomTokens()
{
return [CT::T_NULLABLE_TYPE];
}
/**
* {@inheritdoc}
*/
public function getPriority()
{
// needs to run after TypeColonTransformer
return -20;
}
/**
* {@inheritdoc}
*/
public function getRequiredPhpVersionId()
{
return 70100;
}
/**
* {@inheritdoc}
*/
public function process(Tokens $tokens, Token $token, $index)
{
if (!$token->equals('?')) {
return;
}
$prevIndex = $tokens->getPrevMeaningfulToken($index);
$prevToken = $tokens[$prevIndex];
if ($prevToken->equalsAny(['(', ',', [CT::T_TYPE_COLON]])) {
$tokens[$index] = new Token([CT::T_NULLABLE_TYPE, '?']);
}
}
}
PK PQN: / src/Tokenizer/Transformer/ImportTransformer.phpnu W+A
* Dariusz Rumiński
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace PhpCsFixer\Tokenizer\Transformer;
use PhpCsFixer\Tokenizer\AbstractTransformer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
/**
* Transform const/function import tokens.
*
* Performed transformations:
* - T_CONST into CT::T_CONST_IMPORT
* - T_FUNCTION into CT::T_FUNCTION_IMPORT
*
* @author Gregor Harlan
*
* @internal
*/
final class ImportTransformer extends AbstractTransformer
{
/**
* {@inheritdoc}
*/
public function getCustomTokens()
{
return [CT::T_CONST_IMPORT, CT::T_FUNCTION_IMPORT];
}
/**
* {@inheritdoc}
*/
public function getRequiredPhpVersionId()
{
return 50600;
}
/**
* {@inheritdoc}
*/
public function process(Tokens $tokens, Token $token, $index)
{
if (!$token->isGivenKind([T_CONST, T_FUNCTION])) {
return;
}
$prevToken = $tokens[$tokens->getPrevMeaningfulToken($index)];
if ($prevToken->isGivenKind(T_USE)) {
$tokens[$index] = new Token([
$token->isGivenKind(T_FUNCTION) ? CT::T_FUNCTION_IMPORT : CT::T_CONST_IMPORT,
$token->getContent(),
]);
}
}
}
PK PQNd# N N 6 src/Tokenizer/Transformer/ArrayTypehintTransformer.phpnu W+A
* Dariusz Rumiński
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace PhpCsFixer\Tokenizer\Transformer;
use PhpCsFixer\Tokenizer\AbstractTransformer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
/**
* Transform `array` typehint from T_ARRAY into CT::T_ARRAY_TYPEHINT.
*
* @author Dariusz Rumiński
*
* @internal
*/
final class ArrayTypehintTransformer extends AbstractTransformer
{
/**
* {@inheritdoc}
*/
public function getCustomTokens()
{
return [CT::T_ARRAY_TYPEHINT];
}
/**
* {@inheritdoc}
*/
public function getRequiredPhpVersionId()
{
return 50000;
}
/**
* {@inheritdoc}
*/
public function process(Tokens $tokens, Token $token, $index)
{
if (!$token->isGivenKind(T_ARRAY)) {
return;
}
$nextIndex = $tokens->getNextMeaningfulToken($index);
$nextToken = $tokens[$nextIndex];
if (!$nextToken->equals('(')) {
$tokens[$index] = new Token([CT::T_ARRAY_TYPEHINT, $token->getContent()]);
}
}
}
PK PQN 9f : src/Tokenizer/Transformer/WhitespacyCommentTransformer.phpnu W+A
* Dariusz Rumiński
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace PhpCsFixer\Tokenizer\Transformer;
use PhpCsFixer\Tokenizer\AbstractTransformer;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
/**
* Move trailing whitespaces from comments and docs into following T_WHITESPACE token.
*
* @author Dariusz Rumiński
*
* @internal
*/
final class WhitespacyCommentTransformer extends AbstractTransformer
{
/**
* {@inheritdoc}
*/
public function getCustomTokens()
{
return [];
}
/**
* {@inheritdoc}
*/
public function getRequiredPhpVersionId()
{
return 50000;
}
/**
* {@inheritdoc}
*/
public function process(Tokens $tokens, Token $token, $index)
{
if (!$token->isComment()) {
return;
}
$content = $token->getContent();
$trimmedContent = rtrim($content);
// nothing trimmed, nothing to do
if ($content === $trimmedContent) {
return;
}
$whitespaces = substr($content, \strlen($trimmedContent));
$tokens[$index] = new Token([$token->getId(), $trimmedContent]);
if (isset($tokens[$index + 1]) && $tokens[$index + 1]->isWhitespace()) {
$tokens[$index + 1] = new Token([T_WHITESPACE, $whitespaces.$tokens[$index + 1]->getContent()]);
} else {
$tokens->insertAt($index + 1, new Token([T_WHITESPACE, $whitespaces]));
}
}
}
PK PQNi i : src/Tokenizer/Transformer/NamespaceOperatorTransformer.phpnu W+A
* Dariusz Rumiński
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace PhpCsFixer\Tokenizer\Transformer;
use PhpCsFixer\Tokenizer\AbstractTransformer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
/**
* Transform `namespace` operator from T_NAMESPACE into CT::T_NAMESPACE_OPERATOR.
*
* @author Gregor Harlan
*
* @internal
*/
final class NamespaceOperatorTransformer extends AbstractTransformer
{
/**
* {@inheritdoc}
*/
public function getCustomTokens()
{
return [CT::T_NAMESPACE_OPERATOR];
}
/**
* {@inheritdoc}
*/
public function getRequiredPhpVersionId()
{
return 50300;
}
/**
* {@inheritdoc}
*/
public function process(Tokens $tokens, Token $token, $index)
{
if (!$token->isGivenKind(T_NAMESPACE)) {
return;
}
$nextIndex = $tokens->getNextMeaningfulToken($index);
$nextToken = $tokens[$nextIndex];
if ($nextToken->isGivenKind(T_NS_SEPARATOR)) {
$tokens[$index] = new Token([CT::T_NAMESPACE_OPERATOR, $token->getContent()]);
}
}
}
PK PQNh# 8 src/Tokenizer/Transformer/TypeAlternationTransformer.phpnu W+A
* Dariusz Rumiński
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace PhpCsFixer\Tokenizer\Transformer;
use PhpCsFixer\Tokenizer\AbstractTransformer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
/**
* Transform `|` operator into CT::T_TYPE_ALTERNATION in `} catch (ExceptionType1 | ExceptionType2 $e) {`.
*
* @author Dariusz Rumiński
*
* @internal
*/
final class TypeAlternationTransformer extends AbstractTransformer
{
/**
* {@inheritdoc}
*/
public function getCustomTokens()
{
return [CT::T_TYPE_ALTERNATION];
}
/**
* {@inheritdoc}
*/
public function getRequiredPhpVersionId()
{
return 70100;
}
/**
* {@inheritdoc}
*/
public function process(Tokens $tokens, Token $token, $index)
{
if (!$token->equals('|')) {
return;
}
$prevIndex = $tokens->getPrevMeaningfulToken($index);
$prevToken = $tokens[$prevIndex];
if (!$prevToken->isGivenKind(T_STRING)) {
return;
}
do {
$prevIndex = $tokens->getPrevMeaningfulToken($prevIndex);
if (null === $prevIndex) {
break;
}
$prevToken = $tokens[$prevIndex];
if ($prevToken->isGivenKind([T_NS_SEPARATOR, T_STRING])) {
continue;
}
if (
$prevToken->isGivenKind(CT::T_TYPE_ALTERNATION)
|| (
$prevToken->equals('(')
&& $tokens[$tokens->getPrevMeaningfulToken($prevIndex)]->isGivenKind(T_CATCH)
)
) {
$tokens[$index] = new Token([CT::T_TYPE_ALTERNATION, '|']);
}
break;
} while (true);
}
}
PK PQN. 3 src/Tokenizer/Transformer/CurlyBraceTransformer.phpnu W+A
* Dariusz Rumiński
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace PhpCsFixer\Tokenizer\Transformer;
use PhpCsFixer\Tokenizer\AbstractTransformer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
/**
* Transform discriminate overloaded curly braces tokens.
*
* Performed transformations:
* - closing `}` for T_CURLY_OPEN into CT::T_CURLY_CLOSE,
* - closing `}` for T_DOLLAR_OPEN_CURLY_BRACES into CT::T_DOLLAR_CLOSE_CURLY_BRACES,
* - in `$foo->{$bar}` into CT::T_DYNAMIC_PROP_BRACE_OPEN and CT::T_DYNAMIC_PROP_BRACE_CLOSE,
* - in `${$foo}` into CT::T_DYNAMIC_VAR_BRACE_OPEN and CT::T_DYNAMIC_VAR_BRACE_CLOSE,
* - in `$array{$index}` into CT::T_ARRAY_INDEX_CURLY_BRACE_OPEN and CT::T_ARRAY_INDEX_CURLY_BRACE_CLOSE,
* - in `use some\a\{ClassA, ClassB, ClassC as C}` into CT::T_GROUP_IMPORT_BRACE_OPEN, CT::T_GROUP_IMPORT_BRACE_CLOSE.
*
* @author Dariusz Rumiński
*
* @internal
*/
final class CurlyBraceTransformer extends AbstractTransformer
{
/**
* {@inheritdoc}
*/
public function getCustomTokens()
{
return [
CT::T_CURLY_CLOSE,
CT::T_DOLLAR_CLOSE_CURLY_BRACES,
CT::T_DYNAMIC_PROP_BRACE_OPEN,
CT::T_DYNAMIC_PROP_BRACE_CLOSE,
CT::T_DYNAMIC_VAR_BRACE_OPEN,
CT::T_DYNAMIC_VAR_BRACE_CLOSE,
CT::T_ARRAY_INDEX_CURLY_BRACE_OPEN,
CT::T_ARRAY_INDEX_CURLY_BRACE_CLOSE,
CT::T_GROUP_IMPORT_BRACE_OPEN,
CT::T_GROUP_IMPORT_BRACE_CLOSE,
];
}
/**
* {@inheritdoc}
*/
public function getRequiredPhpVersionId()
{
return 50000;
}
/**
* {@inheritdoc}
*/
public function process(Tokens $tokens, Token $token, $index)
{
$this->transformIntoCurlyCloseBrace($tokens, $token, $index);
$this->transformIntoDollarCloseBrace($tokens, $token, $index);
$this->transformIntoDynamicPropBraces($tokens, $token, $index);
$this->transformIntoDynamicVarBraces($tokens, $token, $index);
$this->transformIntoCurlyIndexBraces($tokens, $token, $index);
if (\PHP_VERSION_ID >= 70000) {
$this->transformIntoGroupUseBraces($tokens, $token, $index);
}
}
/**
* Transform closing `}` for T_CURLY_OPEN into CT::T_CURLY_CLOSE.
*
* This should be done at very beginning of curly braces transformations.
*
* @param Tokens $tokens
* @param Token $token
* @param int $index
*/
private function transformIntoCurlyCloseBrace(Tokens $tokens, Token $token, $index)
{
if (!$token->isGivenKind(T_CURLY_OPEN)) {
return;
}
$level = 1;
$nestIndex = $index;
while (0 < $level) {
++$nestIndex;
// we count all kind of {
if ($tokens[$nestIndex]->equals('{')) {
++$level;
continue;
}
// we count all kind of }
if ($tokens[$nestIndex]->equals('}')) {
--$level;
}
}
$tokens[$nestIndex] = new Token([CT::T_CURLY_CLOSE, '}']);
}
private function transformIntoDollarCloseBrace(Tokens $tokens, Token $token, $index)
{
if ($token->isGivenKind(T_DOLLAR_OPEN_CURLY_BRACES)) {
$nextIndex = $tokens->getNextTokenOfKind($index, ['}']);
$tokens[$nextIndex] = new Token([CT::T_DOLLAR_CLOSE_CURLY_BRACES, '}']);
}
}
private function transformIntoDynamicPropBraces(Tokens $tokens, Token $token, $index)
{
if (!$token->isGivenKind(T_OBJECT_OPERATOR)) {
return;
}
if (!$tokens[$index + 1]->equals('{')) {
return;
}
$openIndex = $index + 1;
$closeIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $openIndex);
$tokens[$openIndex] = new Token([CT::T_DYNAMIC_PROP_BRACE_OPEN, '{']);
$tokens[$closeIndex] = new Token([CT::T_DYNAMIC_PROP_BRACE_CLOSE, '}']);
}
private function transformIntoDynamicVarBraces(Tokens $tokens, Token $token, $index)
{
if (!$token->equals('$')) {
return;
}
$openIndex = $tokens->getNextMeaningfulToken($index);
if (null === $openIndex) {
return;
}
$openToken = $tokens[$openIndex];
if (!$openToken->equals('{')) {
return;
}
$closeIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $openIndex);
$tokens[$openIndex] = new Token([CT::T_DYNAMIC_VAR_BRACE_OPEN, '{']);
$tokens[$closeIndex] = new Token([CT::T_DYNAMIC_VAR_BRACE_CLOSE, '}']);
}
private function transformIntoCurlyIndexBraces(Tokens $tokens, Token $token, $index)
{
if (!$token->equals('{')) {
return;
}
$prevIndex = $tokens->getPrevMeaningfulToken($index);
if (!$tokens[$prevIndex]->equalsAny([
[T_STRING],
[T_VARIABLE],
[CT::T_ARRAY_INDEX_CURLY_BRACE_CLOSE],
[CT::T_ARRAY_SQUARE_BRACE_CLOSE],
']',
')',
])) {
return;
}
if (
$tokens[$prevIndex]->isGivenKind(T_STRING)
&& !$tokens[$tokens->getPrevMeaningfulToken($prevIndex)]->isGivenKind(T_OBJECT_OPERATOR)
) {
return;
}
if (
$tokens[$prevIndex]->equals(')')
&& !$tokens[$tokens->getPrevMeaningfulToken(
$tokens->findBlockStart(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $prevIndex)
)]->isGivenKind(T_ARRAY)
) {
return;
}
$closeIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $index);
$tokens[$index] = new Token([CT::T_ARRAY_INDEX_CURLY_BRACE_OPEN, '{']);
$tokens[$closeIndex] = new Token([CT::T_ARRAY_INDEX_CURLY_BRACE_CLOSE, '}']);
}
private function transformIntoGroupUseBraces(Tokens $tokens, Token $token, $index)
{
if (!$token->equals('{')) {
return;
}
$prevIndex = $tokens->getPrevMeaningfulToken($index);
if (!$tokens[$prevIndex]->isGivenKind(T_NS_SEPARATOR)) {
return;
}
$closeIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $index);
$tokens[$index] = new Token([CT::T_GROUP_IMPORT_BRACE_OPEN, '{']);
$tokens[$closeIndex] = new Token([CT::T_GROUP_IMPORT_BRACE_CLOSE, '}']);
}
}
PK PQN9m 2 src/Tokenizer/Transformer/ReturnRefTransformer.phpnu W+A
* Dariusz Rumiński
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace PhpCsFixer\Tokenizer\Transformer;
use PhpCsFixer\Tokenizer\AbstractTransformer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
/**
* Transform `&` operator into CT::T_RETURN_REF in `function & foo() {}`.
*
* @author Dariusz Rumiński
*
* @internal
*/
final class ReturnRefTransformer extends AbstractTransformer
{
/**
* {@inheritdoc}
*/
public function getCustomTokens()
{
return [CT::T_RETURN_REF];
}
/**
* {@inheritdoc}
*/
public function getRequiredPhpVersionId()
{
return 50000;
}
/**
* {@inheritdoc}
*/
public function process(Tokens $tokens, Token $token, $index)
{
if (
$token->equals('&')
&& $tokens[$tokens->getPrevMeaningfulToken($index)]->isGivenKind(T_FUNCTION)
) {
$tokens[$index] = new Token([CT::T_RETURN_REF, '&']);
}
}
}
PK PQN[M @ src/Tokenizer/Transformer/BraceClassInstantiationTransformer.phpnu W+A
* Dariusz Rumiński
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace PhpCsFixer\Tokenizer\Transformer;
use PhpCsFixer\Tokenizer\AbstractTransformer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
/**
* Transform braced class instantiation braces in `(new Foo())` into CT::T_BRACE_CLASS_INSTANTIATION_OPEN
* and CT::T_BRACE_CLASS_INSTANTIATION_CLOSE.
*
* @author Sebastiaans Stok
*
* @internal
*/
final class BraceClassInstantiationTransformer extends AbstractTransformer
{
/**
* {@inheritdoc}
*/
public function getCustomTokens()
{
return [CT::T_BRACE_CLASS_INSTANTIATION_OPEN, CT::T_BRACE_CLASS_INSTANTIATION_CLOSE];
}
/**
* {@inheritdoc}
*/
public function getPriority()
{
// must run after CurlyBraceTransformer and SquareBraceTransformer
return -1;
}
/**
* {@inheritdoc}
*/
public function getRequiredPhpVersionId()
{
return 50000;
}
/**
* {@inheritdoc}
*/
public function process(Tokens $tokens, Token $token, $index)
{
if (!$tokens[$index]->equals('(') || !$tokens[$tokens->getNextMeaningfulToken($index)]->equals([T_NEW])) {
return;
}
if ($tokens[$tokens->getPrevMeaningfulToken($index)]->equalsAny([
']',
[CT::T_ARRAY_INDEX_CURLY_BRACE_CLOSE],
[CT::T_ARRAY_SQUARE_BRACE_CLOSE],
[T_ARRAY],
[T_CLASS],
[T_ELSEIF],
[T_FOR],
[T_FOREACH],
[T_IF],
[T_STATIC],
[T_STRING],
[T_SWITCH],
[T_VARIABLE],
[T_WHILE],
])) {
return;
}
$closeIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $index);
$tokens[$index] = new Token([CT::T_BRACE_CLASS_INSTANTIATION_OPEN, '(']);
$tokens[$closeIndex] = new Token([CT::T_BRACE_CLASS_INSTANTIATION_CLOSE, ')']);
}
}
PK PQNB0 6 src/Tokenizer/Transformer/ClassConstantTransformer.phpnu W+A
* Dariusz Rumiński
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace PhpCsFixer\Tokenizer\Transformer;
use PhpCsFixer\Tokenizer\AbstractTransformer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
/**
* Transform `class` class' constant from T_CLASS into CT::T_CLASS_CONSTANT.
*
* @author Dariusz Rumiński
*
* @internal
*/
final class ClassConstantTransformer extends AbstractTransformer
{
/**
* {@inheritdoc}
*/
public function getCustomTokens()
{
return [CT::T_CLASS_CONSTANT];
}
/**
* {@inheritdoc}
*/
public function getRequiredPhpVersionId()
{
return 50500;
}
/**
* {@inheritdoc}
*/
public function process(Tokens $tokens, Token $token, $index)
{
if (!$token->equalsAny([
[T_CLASS, 'class'],
[T_STRING, 'class'],
], false)) {
return;
}
$prevIndex = $tokens->getPrevMeaningfulToken($index);
$prevToken = $tokens[$prevIndex];
if ($prevToken->isGivenKind(T_DOUBLE_COLON)) {
$tokens[$index] = new Token([CT::T_CLASS_CONSTANT, $token->getContent()]);
}
}
}
PK PQNꊳ 2 src/Tokenizer/Transformer/TypeColonTransformer.phpnu W+A
* Dariusz Rumiński
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace PhpCsFixer\Tokenizer\Transformer;
use PhpCsFixer\Tokenizer\AbstractTransformer;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
/**
* Transform `:` operator into CT::T_TYPE_COLON in `function foo() : int {}`.
*
* @author Dariusz Rumiński
*
* @internal
*/
final class TypeColonTransformer extends AbstractTransformer
{
/**
* {@inheritdoc}
*/
public function getCustomTokens()
{
return [CT::T_TYPE_COLON];
}
/**
* {@inheritdoc}
*/
public function getPriority()
{
// needs to run after ReturnRefTransformer and UseTransformer
return -10;
}
/**
* {@inheritdoc}
*/
public function getRequiredPhpVersionId()
{
return 70000;
}
/**
* {@inheritdoc}
*/
public function process(Tokens $tokens, Token $token, $index)
{
if (!$token->equals(':')) {
return;
}
$endIndex = $tokens->getPrevMeaningfulToken($index);
if (!$tokens[$endIndex]->equals(')')) {
return;
}
$startIndex = $tokens->findBlockStart(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $endIndex);
$prevIndex = $tokens->getPrevMeaningfulToken($startIndex);
$prevToken = $tokens[$prevIndex];
// if this could be a function name we need to take one more step
if ($prevToken->isGivenKind(T_STRING)) {
$prevIndex = $tokens->getPrevMeaningfulToken($prevIndex);
$prevToken = $tokens[$prevIndex];
}
if ($prevToken->isGivenKind([T_FUNCTION, CT::T_RETURN_REF, CT::T_USE_LAMBDA])) {
$tokens[$index] = new Token([CT::T_TYPE_COLON, ':']);
}
}
}
PK PQNG)F 0 src/Tokenizer/Resolver/TypeShortNameResolver.phpnu W+A
* Dariusz Rumiński
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace PhpCsFixer\Tokenizer\Resolver;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\Analyzer\Analysis\NamespaceAnalysis;
use PhpCsFixer\Tokenizer\Analyzer\NamespacesAnalyzer;
use PhpCsFixer\Tokenizer\Analyzer\NamespaceUsesAnalyzer;
use PhpCsFixer\Tokenizer\Tokens;
/**
* @internal
*/
final class TypeShortNameResolver
{
/**
* This method will resolve the shortName of a FQCN if possible or otherwise return the inserted type name.
* E.g.: use Foo\Bar => "Bar".
*
* @param Tokens $tokens
* @param string $typeName
*
* @return string
*/
public function resolve(Tokens $tokens, $typeName)
{
// First match explicit imports:
$useMap = $this->getUseMapFromTokens($tokens);
foreach ($useMap as $shortName => $fullName) {
$regex = '/^\\\\?'.preg_quote($fullName, '/').'$/';
if (Preg::match($regex, $typeName)) {
return $shortName;
}
}
// Next try to match (partial) classes inside the same namespace
// For now only support one namespace per file:
$namespaces = $this->getNamespacesFromTokens($tokens);
if (1 === \count($namespaces)) {
foreach ($namespaces as $fullName) {
$matches = [];
$regex = '/^\\\\?'.preg_quote($fullName, '/').'\\\\(?P.+)$/';
if (Preg::match($regex, $typeName, $matches)) {
return $matches['className'];
}
}
}
// Next: Try to match partial use statements:
foreach ($useMap as $shortName => $fullName) {
$matches = [];
$regex = '/^\\\\?'.preg_quote($fullName, '/').'\\\\(?P.+)$/';
if (Preg::match($regex, $typeName, $matches)) {
return $shortName.'\\'.$matches['className'];
}
}
return $typeName;
}
/**
* @param Tokens $tokens
*
* @return array A list of all FQN namespaces in the file with the short name as key
*/
private function getNamespacesFromTokens(Tokens $tokens)
{
return array_map(function (NamespaceAnalysis $info) {
return $info->getFullName();
}, (new NamespacesAnalyzer())->getDeclarations($tokens));
}
/**
* @param Tokens $tokens
*
* @return array A list of all FQN use statements in the file with the short name as key
*/
private function getUseMapFromTokens(Tokens $tokens)
{
$map = [];
foreach ((new NamespaceUsesAnalyzer())->getDeclarationsFromTokens($tokens) as $useDeclaration) {
$map[$useDeclaration->getShortName()] = $useDeclaration->getFullName();
}
return $map;
}
}
PK PQN]K' 0 src/Tokenizer/Analyzer/NamespaceUsesAnalyzer.phpnu W+A
* Dariusz Rumiński
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace PhpCsFixer\Tokenizer\Analyzer;
use PhpCsFixer\Tokenizer\Analyzer\Analysis\NamespaceUseAnalysis;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Tokenizer\TokensAnalyzer;
/**
* @internal
*/
final class NamespaceUsesAnalyzer
{
/**
* @param Tokens $tokens
*
* @return NamespaceUseAnalysis[]
*/
public function getDeclarationsFromTokens(Tokens $tokens)
{
$tokenAnalyzer = new TokensAnalyzer($tokens);
$useIndexes = $tokenAnalyzer->getImportUseIndexes();
return $this->getDeclarations($tokens, $useIndexes);
}
/**
* @param Tokens $tokens
* @param array $useIndexes
*
* @return NamespaceUseAnalysis[]
*/
private function getDeclarations(Tokens $tokens, array $useIndexes)
{
$uses = [];
foreach ($useIndexes as $index) {
$endIndex = $tokens->getNextTokenOfKind($index, [';', [T_CLOSE_TAG]]);
$analysis = $this->parseDeclaration($tokens, $index, $endIndex);
if ($analysis) {
$uses[] = $analysis;
}
}
return $uses;
}
/**
* @param Tokens $tokens
* @param int $startIndex
* @param int $endIndex
*
* @return null|NamespaceUseAnalysis
*/
private function parseDeclaration(Tokens $tokens, $startIndex, $endIndex)
{
$fullName = $shortName = '';
$aliased = false;
$type = NamespaceUseAnalysis::TYPE_CLASS;
for ($i = $startIndex; $i <= $endIndex; ++$i) {
$token = $tokens[$i];
if ($token->equals(',') || $token->isGivenKind(CT::T_GROUP_IMPORT_BRACE_CLOSE)) {
// do not touch group use declarations until the logic of this is added (for example: `use some\a\{ClassD};`)
// ignore multiple use statements that should be split into few separate statements (for example: `use BarB, BarC as C;`)
return null;
}
if ($token->isGivenKind(CT::T_FUNCTION_IMPORT)) {
$type = NamespaceUseAnalysis::TYPE_FUNCTION;
} elseif ($token->isGivenKind(CT::T_CONST_IMPORT)) {
$type = NamespaceUseAnalysis::TYPE_CONSTANT;
}
if ($token->isWhitespace() || $token->isComment() || $token->isGivenKind(T_USE)) {
continue;
}
if ($token->isGivenKind(T_STRING)) {
$shortName = $token->getContent();
if (!$aliased) {
$fullName .= $shortName;
}
} elseif ($token->isGivenKind(T_NS_SEPARATOR)) {
$fullName .= $token->getContent();
} elseif ($token->isGivenKind(T_AS)) {
$aliased = true;
}
}
return new NamespaceUseAnalysis(
trim($fullName),
$shortName,
$aliased,
$startIndex,
$endIndex,
$type
);
}
}
PK PQNQ Q , src/Tokenizer/Analyzer/FunctionsAnalyzer.phpnu W+A
* Dariusz Rumiński
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace PhpCsFixer\Tokenizer\Analyzer;
use PhpCsFixer\Tokenizer\Analyzer\Analysis\ArgumentAnalysis;
use PhpCsFixer\Tokenizer\Analyzer\Analysis\TypeAnalysis;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Tokens;
/**
* @internal
*/
final class FunctionsAnalyzer
{
/**
* @param Tokens $tokens
* @param int $index
*
* @return bool
*/
public function isGlobalFunctionCall(Tokens $tokens, $index)
{
if (!$tokens[$index]->isGivenKind(T_STRING)) {
return false;
}
$prevIndex = $tokens->getPrevMeaningfulToken($index);
if ($tokens[$prevIndex]->isGivenKind(T_NS_SEPARATOR)) {
$prevIndex = $tokens->getPrevMeaningfulToken($prevIndex);
}
$nextIndex = $tokens->getNextMeaningfulToken($index);
return !$tokens[$prevIndex]->isGivenKind([T_DOUBLE_COLON, T_FUNCTION, CT::T_NAMESPACE_OPERATOR, T_NEW, T_OBJECT_OPERATOR, CT::T_RETURN_REF, T_STRING])
&& $tokens[$nextIndex]->equals('(');
}
/**
* @param Tokens $tokens
* @param int $methodIndex
*
* @return ArgumentAnalysis[]
*/
public function getFunctionArguments(Tokens $tokens, $methodIndex)
{
$argumentsStart = $tokens->getNextTokenOfKind($methodIndex, ['(']);
$argumentsEnd = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $argumentsStart);
$argumentAnalyzer = new ArgumentsAnalyzer();
$arguments = [];
foreach ($argumentAnalyzer->getArguments($tokens, $argumentsStart, $argumentsEnd) as $start => $end) {
$argumentInfo = $argumentAnalyzer->getArgumentInfo($tokens, $start, $end);
$arguments[$argumentInfo->getName()] = $argumentInfo;
}
return $arguments;
}
/**
* @param Tokens $tokens
* @param int $methodIndex
*
* @return null|TypeAnalysis
*/
public function getFunctionReturnType(Tokens $tokens, $methodIndex)
{
$argumentsStart = $tokens->getNextTokenOfKind($methodIndex, ['(']);
$argumentsEnd = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $argumentsStart);
$typeColonIndex = $tokens->getNextMeaningfulToken($argumentsEnd);
if (':' !== $tokens[$typeColonIndex]->getContent()) {
return null;
}
$type = '';
$typeStartIndex = $tokens->getNextNonWhitespace($typeColonIndex);
$typeEndIndex = $typeStartIndex;
$functionBodyStart = $tokens->getNextTokenOfKind($typeColonIndex, ['{', ';']);
for ($i = $typeStartIndex; $i < $functionBodyStart; ++$i) {
if ($tokens[$i]->isWhitespace()) {
continue;
}
$type .= $tokens[$i]->getContent();
$typeEndIndex = $i;
}
return new TypeAnalysis($type, $typeStartIndex, $typeEndIndex);
}
}
PK PQNx , src/Tokenizer/Analyzer/ArgumentsAnalyzer.phpnu W+A
* Dariusz Rumiński
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace PhpCsFixer\Tokenizer\Analyzer;
use PhpCsFixer\Tokenizer\Analyzer\Analysis\ArgumentAnalysis;
use PhpCsFixer\Tokenizer\Analyzer\Analysis\TypeAnalysis;
use PhpCsFixer\Tokenizer\Tokens;
/**
* @author Dariusz Rumiński
* @author Vladimir Reznichenko
*
* @internal
*/
final class ArgumentsAnalyzer
{
/**
* Count amount of parameters in a function/method reference.
*
* @param Tokens $tokens
* @param int $openParenthesis
* @param int $closeParenthesis
*
* @return int
*/
public function countArguments(Tokens $tokens, $openParenthesis, $closeParenthesis)
{
return \count($this->getArguments($tokens, $openParenthesis, $closeParenthesis));
}
/**
* Returns start and end token indexes of arguments.
*
* Returns an array with each key being the first token of an
* argument and the value the last. Including non-function tokens
* such as comments and white space tokens, but without the separation
* tokens like '(', ',' and ')'.
*
* @param Tokens $tokens
* @param int $openParenthesis
* @param int $closeParenthesis
*
* @return array
*/
public function getArguments(Tokens $tokens, $openParenthesis, $closeParenthesis)
{
$arguments = [];
$firstSensibleToken = $tokens->getNextMeaningfulToken($openParenthesis);
if ($tokens[$firstSensibleToken]->equals(')')) {
return $arguments;
}
$paramContentIndex = $openParenthesis + 1;
$argumentsStart = $paramContentIndex;
for (; $paramContentIndex < $closeParenthesis; ++$paramContentIndex) {
$token = $tokens[$paramContentIndex];
// skip nested (), [], {} constructs
$blockDefinitionProbe = Tokens::detectBlockType($token);
if (null !== $blockDefinitionProbe && true === $blockDefinitionProbe['isStart']) {
$paramContentIndex = $tokens->findBlockEnd($blockDefinitionProbe['type'], $paramContentIndex);
continue;
}
// if comma matched, increase arguments counter
if ($token->equals(',')) {
if ($tokens->getNextMeaningfulToken($paramContentIndex) === $closeParenthesis) {
break; // trailing ',' in function call (PHP 7.3)
}
$arguments[$argumentsStart] = $paramContentIndex - 1;
$argumentsStart = $paramContentIndex + 1;
}
}
$arguments[$argumentsStart] = $paramContentIndex - 1;
return $arguments;
}
/**
* @param Tokens $tokens
* @param int $argumentStart
* @param int $argumentEnd
*
* @return ArgumentAnalysis
*/
public function getArgumentInfo(Tokens $tokens, $argumentStart, $argumentEnd)
{
$info = [
'default' => null,
'name' => null,
'name_index' => null,
'type' => null,
'type_index_start' => null,
'type_index_end' => null,
];
$sawName = false;
for ($index = $argumentStart; $index <= $argumentEnd; ++$index) {
$token = $tokens[$index];
if ($token->isComment() || $token->isWhitespace() || $token->isGivenKind(T_ELLIPSIS) || $token->equals('&')) {
continue;
}
if ($token->isGivenKind(T_VARIABLE)) {
$sawName = true;
$info['name_index'] = $index;
$info['name'] = $token->getContent();
continue;
}
if ($token->equals('=')) {
continue;
}
if ($sawName) {
$info['default'] .= $token->getContent();
} else {
$info['type_index_start'] = ($info['type_index_start'] > 0) ? $info['type_index_start'] : $index;
$info['type_index_end'] = $index;
$info['type'] .= $token->getContent();
}
}
return new ArgumentAnalysis(
$info['name'],
$info['name_index'],
$info['default'],
$info['type'] ? new TypeAnalysis($info['type'], $info['type_index_start'], $info['type_index_end']) : null
);
}
}
PK PQNN2i + src/Tokenizer/Analyzer/CommentsAnalyzer.phpnu W+A
* Dariusz Rumiński
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace PhpCsFixer\Tokenizer\Analyzer;
use PhpCsFixer\Preg;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
/**
* @author Kuba Werłos
* @author SpacePossum
*
* @internal
*/
final class CommentsAnalyzer
{
const TYPE_HASH = 1;
const TYPE_DOUBLE_SLASH = 2;
const TYPE_SLASH_ASTERISK = 3;
/**
* @param Tokens $tokens
* @param int $index
*
* @return bool
*/
public function isHeaderComment(Tokens $tokens, $index)
{
if (!$tokens[$index]->isGivenKind([T_COMMENT, T_DOC_COMMENT])) {
throw new \InvalidArgumentException('Given index must point to a comment.');
}
$prevIndex = $tokens->getPrevMeaningfulToken($index);
return $tokens[$prevIndex]->isGivenKind(T_OPEN_TAG) && null !== $tokens->getNextMeaningfulToken($index);
}
/**
* Check if comment at given index precedes structural element.
*
* @see https://github.com/php-fig/fig-standards/blob/master/proposed/phpdoc.md#3-definitions
*
* @param Tokens $tokens
* @param int $index
*
* @return bool
*/
public function isBeforeStructuralElement(Tokens $tokens, $index)
{
$token = $tokens[$index];
if (!$token->isGivenKind([T_COMMENT, T_DOC_COMMENT])) {
throw new \InvalidArgumentException('Given index must point to a comment.');
}
$nextIndex = $index;
do {
$nextIndex = $tokens->getNextMeaningfulToken($nextIndex);
} while (null !== $nextIndex && $tokens[$nextIndex]->equals('('));
if (null === $nextIndex || $tokens[$nextIndex]->equals('}')) {
return false;
}
$nextToken = $tokens[$nextIndex];
if ($this->isStructuralElement($nextToken)) {
return true;
}
if ($this->isValidControl($tokens, $token, $nextIndex)) {
return true;
}
if ($this->isValidVariable($tokens, $nextIndex)) {
return true;
}
if ($this->isValidLanguageConstruct($tokens, $token, $nextIndex)) {
return true;
}
return false;
}
/**
* Return array of indices that are part of a comment started at given index.
*
* @param Tokens $tokens
* @param int $index T_COMMENT index
*
* @return null|array
*/
public function getCommentBlockIndices(Tokens $tokens, $index)
{
if (!$tokens[$index]->isGivenKind(T_COMMENT)) {
throw new \InvalidArgumentException('Given index must point to a comment.');
}
$commentType = $this->getCommentType($tokens[$index]->getContent());
$indices = [$index];
if (self::TYPE_SLASH_ASTERISK === $commentType) {
return $indices;
}
$count = \count($tokens);
++$index;
for (; $index < $count; ++$index) {
if ($tokens[$index]->isComment()) {
if ($commentType === $this->getCommentType($tokens[$index]->getContent())) {
$indices[] = $index;
continue;
}
break;
}
if (!$tokens[$index]->isWhitespace() || $this->getLineBreakCount($tokens, $index, $index + 1) > 1) {
break;
}
}
return $indices;
}
/**
* @see https://github.com/phpDocumentor/fig-standards/blob/master/proposed/phpdoc.md#3-definitions
*
* @param Token $token
*
* @return bool
*/
private function isStructuralElement(Token $token)
{
static $skip = [
T_PRIVATE,
T_PROTECTED,
T_PUBLIC,
T_VAR,
T_FUNCTION,
T_ABSTRACT,
T_CONST,
T_NAMESPACE,
T_REQUIRE,
T_REQUIRE_ONCE,
T_INCLUDE,
T_INCLUDE_ONCE,
T_FINAL,
T_STATIC,
];
return $token->isClassy() || $token->isGivenKind($skip);
}
/**
* Checks control structures (for, foreach, if, switch, while) for correct docblock usage.
*
* @param Tokens $tokens
* @param Token $docsToken docs Token
* @param int $controlIndex index of control structure Token
*
* @return bool
*/
private function isValidControl(Tokens $tokens, Token $docsToken, $controlIndex)
{
static $controlStructures = [
T_FOR,
T_FOREACH,
T_IF,
T_SWITCH,
T_WHILE,
];
if (!$tokens[$controlIndex]->isGivenKind($controlStructures)) {
return false;
}
$index = $tokens->getNextMeaningfulToken($controlIndex);
$endIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $index);
$docsContent = $docsToken->getContent();
for ($index = $index + 1; $index < $endIndex; ++$index) {
$token = $tokens[$index];
if (
$token->isGivenKind(T_VARIABLE) &&
false !== strpos($docsContent, $token->getContent())
) {
return true;
}
}
return false;
}
/**
* Checks variable assignments through `list()`, `print()` etc. calls for correct docblock usage.
*
* @param Tokens $tokens
* @param Token $docsToken docs Token
* @param int $languageConstructIndex index of variable Token
*
* @return bool
*/
private function isValidLanguageConstruct(Tokens $tokens, Token $docsToken, $languageConstructIndex)
{
static $languageStructures = [
T_LIST,
T_PRINT,
T_ECHO,
CT::T_DESTRUCTURING_SQUARE_BRACE_OPEN,
];
if (!$tokens[$languageConstructIndex]->isGivenKind($languageStructures)) {
return false;
}
$endKind = $tokens[$languageConstructIndex]->isGivenKind(CT::T_DESTRUCTURING_SQUARE_BRACE_OPEN)
? [CT::T_DESTRUCTURING_SQUARE_BRACE_CLOSE]
: ')'
;
$endIndex = $tokens->getNextTokenOfKind($languageConstructIndex, [$endKind]);
$docsContent = $docsToken->getContent();
for ($index = $languageConstructIndex + 1; $index < $endIndex; ++$index) {
$token = $tokens[$index];
if ($token->isGivenKind(T_VARIABLE) && false !== strpos($docsContent, $token->getContent())) {
return true;
}
}
return false;
}
/**
* Checks variable assignments for correct docblock usage.
*
* @param Tokens $tokens
* @param int $index index of variable Token
*
* @return bool
*/
private function isValidVariable(Tokens $tokens, $index)
{
if (!$tokens[$index]->isGivenKind(T_VARIABLE)) {
return false;
}
$nextIndex = $tokens->getNextMeaningfulToken($index);
return $tokens[$nextIndex]->equals('=');
}
/**
* @param string $content
*
* @return int
*/
private function getCommentType($content)
{
if ('#' === $content[0]) {
return self::TYPE_HASH;
}
if ('*' === $content[1]) {
return self::TYPE_SLASH_ASTERISK;
}
return self::TYPE_DOUBLE_SLASH;
}
/**
* @param Tokens $tokens
* @param int $whiteStart
* @param int $whiteEnd
*
* @return int
*/
private function getLineBreakCount(Tokens $tokens, $whiteStart, $whiteEnd)
{
$lineCount = 0;
for ($i = $whiteStart; $i < $whiteEnd; ++$i) {
$lineCount += Preg::matchAll('/\R/u', $tokens[$i]->getContent());
}
return $lineCount;
}
}
PK PQNPq > src/Tokenizer/Analyzer/Analysis/StartEndTokenAwareAnalysis.phpnu W+A
* Dariusz Rumiński
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace PhpCsFixer\Tokenizer\Analyzer\Analysis;
interface StartEndTokenAwareAnalysis
{
/**
* The start index of the analyzed subject inside of the Tokens.
*
* @return int
*/
public function getStartIndex();
/**
* The end index of the analyzed subject inside of the Tokens.
*
* @return int
*/
public function getEndIndex();
}
PK PQNz\YG
8 src/Tokenizer/Analyzer/Analysis/NamespaceUseAnalysis.phpnu W+A
* Dariusz Rumiński
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace PhpCsFixer\Tokenizer\Analyzer\Analysis;
/**
* @internal
*/
final class NamespaceUseAnalysis implements StartEndTokenAwareAnalysis
{
const TYPE_CLASS = 1;
const TYPE_FUNCTION = 2;
const TYPE_CONSTANT = 3;
/**
* The fully qualified use namespace.
*
* @var string
*/
private $fullName;
/**
* The short version of use namespace or the alias name in case of aliased use statements.
*
* @var string
*/
private $shortName;
/**
* Is the use statement being aliased?
*
* @var bool
*/
private $isAliased;
/**
* The start index of the namespace declaration in the analyzed Tokens.
*
* @var int
*/
private $startIndex;
/**
* The end index of the namespace declaration in the analyzed Tokens.
*
* @var int
*/
private $endIndex;
/**
* The type of import: class, function or constant.
*
* @var int
*/
private $type;
/**
* @param string $fullName
* @param string $shortName
* @param bool $isAliased
* @param int $startIndex
* @param int $endIndex
* @param int $type
*/
public function __construct($fullName, $shortName, $isAliased, $startIndex, $endIndex, $type)
{
$this->fullName = $fullName;
$this->shortName = $shortName;
$this->isAliased = $isAliased;
$this->startIndex = $startIndex;
$this->endIndex = $endIndex;
$this->type = $type;
}
/**
* @return string
*/
public function getFullName()
{
return $this->fullName;
}
/**
* @return string
*/
public function getShortName()
{
return $this->shortName;
}
/**
* @return bool
*/
public function isAliased()
{
return $this->isAliased;
}
/**
* @return int
*/
public function getStartIndex()
{
return $this->startIndex;
}
/**
* @return int
*/
public function getEndIndex()
{
return $this->endIndex;
}
/**
* @return bool
*/
public function isClass()
{
return self::TYPE_CLASS === $this->type;
}
/**
* @return bool
*/
public function isFunction()
{
return self::TYPE_FUNCTION === $this->type;
}
/**
* @return bool
*/
public function isConstant()
{
return self::TYPE_CONSTANT === $this->type;
}
}
PK PQNJ 4 src/Tokenizer/Analyzer/Analysis/ArgumentAnalysis.phpnu W+A
* Dariusz Rumiński
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace PhpCsFixer\Tokenizer\Analyzer\Analysis;
/**
* @internal
*/
final class ArgumentAnalysis
{
/**
* The default value of the argument.
*
* @var null|string
*/
private $default;
/**
* The name of the argument.
*
* @var string
*/
private $name;
/**
* The index where the name is located in the supplied Tokens object.
*
* @var int
*/
private $nameIndex;
/**
* The type analysis of the argument.
*
* @var null|TypeAnalysis
*/
private $typeAnalysis;
/**
* @param string $name
* @param int $nameIndex
* @param null|string $default
* @param null|TypeAnalysis $typeAnalysis
*/
public function __construct($name, $nameIndex, $default, TypeAnalysis $typeAnalysis = null)
{
$this->name = $name;
$this->nameIndex = $nameIndex;
$this->default = $default ?: null;
$this->typeAnalysis = $typeAnalysis ?: null;
}
/**
* @return null|string
*/
public function getDefault()
{
return $this->default;
}
/**
* @return bool
*/
public function hasDefault()
{
return null !== $this->default;
}
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @return int
*/
public function getNameIndex()
{
return $this->nameIndex;
}
/**
* @return null|TypeAnalysis
*/
public function getTypeAnalysis()
{
return $this->typeAnalysis;
}
/**
* @return bool
*/
public function hasTypeAnalysis()
{
return null !== $this->typeAnalysis;
}
}
PK PQN 0 src/Tokenizer/Analyzer/Analysis/TypeAnalysis.phpnu W+A
* Dariusz Rumiński
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace PhpCsFixer\Tokenizer\Analyzer\Analysis;
/**
* @internal
*/
final class TypeAnalysis implements StartEndTokenAwareAnalysis
{
/**
* This list contains soft and hard reserved types that can be used or will be used by PHP at some point.
*
* More info:
*
* @see https://php.net/manual/en/functions.arguments.php#functions.arguments.type-declaration.types
* @see https://php.net/manual/en/reserved.other-reserved-words.php
* @see https://php.net/manual/en/language.pseudo-types.php
*
* @var array
*/
private static $reservedTypes = [
'array',
'bool',
'callable',
'int',
'iterable',
'float',
'mixed',
'numeric',
'object',
'resource',
'self',
'string',
'void',
];
/**
* @var string
*/
private $name;
/**
* @var int
*/
private $startIndex;
/**
* @var int
*/
private $endIndex;
/**
* @param string $name
* @param int $startIndex
* @param int $endIndex
*/
public function __construct($name, $startIndex, $endIndex)
{
$this->name = $name;
$this->startIndex = $startIndex;
$this->endIndex = $endIndex;
}
/**
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* @return int
*/
public function getStartIndex()
{
return $this->startIndex;
}
/**
* @return int
*/
public function getEndIndex()
{
return $this->endIndex;
}
/**
* @return bool
*/
public function isReservedType()
{
return \in_array($this->name, self::$reservedTypes, true);
}
}
PK PQN@)U 5 src/Tokenizer/Analyzer/Analysis/NamespaceAnalysis.phpnu W+A
* Dariusz Rumiński
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace PhpCsFixer\Tokenizer\Analyzer\Analysis;
/**
* @internal
*/
final class NamespaceAnalysis implements StartEndTokenAwareAnalysis
{
/**
* The fully qualified namespace name.
*
* @var string
*/
private $fullName;
/**
* The short version of the namespace.
*
* @var string
*/
private $shortName;
/**
* The start index of the namespace declaration in the analyzed Tokens.
*
* @var int
*/
private $startIndex;
/**
* The end index of the namespace declaration in the analyzed Tokens.
*
* @var int
*/
private $endIndex;
/**
* The start index of the scope of the namespace in the analyzed Tokens.
*
* @var int
*/
private $scopeStartIndex;
/**
* The end index of the scope of the namespace in the analyzed Tokens.
*
* @var int
*/
private $scopeEndIndex;
/**
* @param string $fullName
* @param string $shortName
* @param int $startIndex
* @param int $endIndex
* @param int $scopeStartIndex
* @param int $scopeEndIndex
*/
public function __construct($fullName, $shortName, $startIndex, $endIndex, $scopeStartIndex, $scopeEndIndex)
{
$this->fullName = $fullName;
$this->shortName = $shortName;
$this->startIndex = $startIndex;
$this->endIndex = $endIndex;
$this->scopeStartIndex = $scopeStartIndex;
$this->scopeEndIndex = $scopeEndIndex;
}
/**
* @return string
*/
public function getFullName()
{
return $this->fullName;
}
/**
* @return string
*/
public function getShortName()
{
return $this->shortName;
}
/**
* @return int
*/
public function getStartIndex()
{
return $this->startIndex;
}
/**
* @return int
*/
public function getEndIndex()
{
return $this->endIndex;
}
/**
* @return int
*/
public function getScopeStartIndex()
{
return $this->scopeStartIndex;
}
/**
* @return int
*/
public function getScopeEndIndex()
{
return $this->scopeEndIndex;
}
}
PK PQN4l l - src/Tokenizer/Analyzer/NamespacesAnalyzer.phpnu W+A
* Dariusz Rumiński
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace PhpCsFixer\Tokenizer\Analyzer;
use PhpCsFixer\Tokenizer\Analyzer\Analysis\NamespaceAnalysis;
use PhpCsFixer\Tokenizer\Tokens;
/**
* @internal
*/
final class NamespacesAnalyzer
{
/**
* @param Tokens $tokens
*
* @return NamespaceAnalysis[]
*/
public function getDeclarations(Tokens $tokens)
{
$namespaces = [];
for ($index = 1, $count = \count($tokens); $index < $count; ++$index) {
$token = $tokens[$index];
if (!$token->isGivenKind(T_NAMESPACE)) {
continue;
}
$declarationEndIndex = $tokens->getNextTokenOfKind($index, [';', '{']);
$namespace = trim($tokens->generatePartialCode($index + 1, $declarationEndIndex - 1));
$declarationParts = explode('\\', $namespace);
$shortName = end($declarationParts);
if ($tokens[$declarationEndIndex]->equals('{')) {
$scopeEndIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $declarationEndIndex);
} else {
$scopeEndIndex = $tokens->getNextTokenOfKind($declarationEndIndex, [[T_NAMESPACE]]);
if (null === $scopeEndIndex) {
$scopeEndIndex = \count($tokens);
}
--$scopeEndIndex;
}
$namespaces[] = new NamespaceAnalysis(
$namespace,
$shortName,
$index,
$declarationEndIndex,
$index,
$scopeEndIndex
);
// Continue the analysis after the end of this namespace to find the next one
$index = $scopeEndIndex;
}
if (0 === \count($namespaces)) {
$namespaces[] = new NamespaceAnalysis('', '', 0, 0, 0, \count($tokens) - 1);
}
return $namespaces;
}
}
PK PQNTA A % src/Tokenizer/AbstractTransformer.phpnu W+A
* Dariusz Rumiński
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace PhpCsFixer\Tokenizer;
use PhpCsFixer\Utils;
/**
* @author Dariusz Rumiński
*
* @internal
*/
abstract class AbstractTransformer implements TransformerInterface
{
/**
* {@inheritdoc}
*/
public function getName()
{
$nameParts = explode('\\', static::class);
$name = substr(end($nameParts), 0, -\strlen('Transformer'));
return Utils::camelCaseToUnderscore($name);
}
/**
* {@inheritdoc}
*/
public function getPriority()
{
return 0;
}
}
PK PQN> : src/Tokenizer/Generator/NamespacedStringTokenGenerator.phpnu W+A
* Dariusz Rumiński
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace PhpCsFixer\Tokenizer\Generator;
use PhpCsFixer\Tokenizer\Token;
/**
* @internal
*/
final class NamespacedStringTokenGenerator
{
/**
* Parse a string that contains a namespace into tokens.
*
* @param string $input
*
* @return Token[]
*/
public function generate($input)
{
$tokens = [];
$parts = explode('\\', $input);
foreach ($parts as $index => $part) {
$tokens[] = new Token([T_STRING, $part]);
if ($index !== \count($parts) - 1) {
$tokens[] = new Token([T_NS_SEPARATOR, '\\']);
}
}
return $tokens;
}
}
PK PQNbdO
O
src/Tokenizer/Transformers.phpnu W+A
* Dariusz Rumiński
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/
namespace PhpCsFixer\Tokenizer;
use PhpCsFixer\Utils;
use Symfony\Component\Finder\Finder;
/**
* Collection of Transformer classes.
*
* @author Dariusz Rumiński
*
* @internal
*/
final class Transformers
{
/**
* The registered transformers.
*
* @var TransformerInterface[]
*/
private $items = [];
/**
* Register built in Transformers.
*/
private function __construct()
{
$this->registerBuiltInTransformers();
usort($this->items, static function (TransformerInterface $a, TransformerInterface $b) {
return Utils::cmpInt($b->getPriority(), $a->getPriority());
});
}
/**
* @return Transformers
*/
public static function create()
{
static $instance = null;
if (!$instance) {
$instance = new self();
}
return $instance;
}
/**
* Transform given Tokens collection through all Transformer classes.
*
* @param Tokens $tokens Tokens collection
*/
public function transform(Tokens $tokens)
{
foreach ($this->items as $transformer) {
foreach ($tokens as $index => $token) {
$transformer->process($tokens, $token, $index);
}
}
}
/**
* @param TransformerInterface $transformer Transformer
*/
private function registerTransformer(TransformerInterface $transformer)
{
if (\PHP_VERSION_ID >= $transformer->getRequiredPhpVersionId()) {
$this->items[] = $transformer;
}
}
private function registerBuiltInTransformers()
{
static $registered = false;
if ($registered) {
return;
}
$registered = true;
foreach ($this->findBuiltInTransformers() as $transformer) {
$this->registerTransformer($transformer);
}
}
/**
* @return \Generator|TransformerInterface[]
*/
private function findBuiltInTransformers()
{
foreach (Finder::create()->files()->in(__DIR__.'/Transformer') as $file) {
$relativeNamespace = $file->getRelativePath();
$class = __NAMESPACE__.'\\Transformer\\'.($relativeNamespace ? $relativeNamespace.'\\' : '').$file->getBasename('.php');
yield new $class();
}
}
}
PK PQN^TH &