PK qRzηr r logo.mdnu ٘ The logo is © 2010-2014 Sensio Labs.
Original resolution can be found at https://github.com/PHP-CS-Fixer/logo .
PK qR~hH H
.yamllint.ymlnu ٘ extends: relaxed
rules:
line-length:
max: 180
level: warning
PK qR3^ phpmd.xmlnu ٘
PK qRvڥ benchmark.shnu ٘ #!/bin/sh
set -eu
command -v php >/dev/null 2>&1 || { echo "I require \`php\` but it's not available. Aborting." >&2; exit 255; }
command -v grep >/dev/null 2>&1 || { echo "I require \`grep\` but it's not available. Aborting." >&2; exit 255; }
command -v awk >/dev/null 2>&1 || { echo "I require \`awk\` but it's not available. Aborting." >&2; exit 255; }
BRANCH1=${1:-''}
BRANCH2=${2:-''}
if [ "" = "$BRANCH1" ] || [ "" = "$BRANCH2" ];
then
echo "Usage: bash benchmark.sh BRANCH1 BRANCH2 ...BRANCHN"
exit 1;
fi
for BRANCH in "$@"
do
git checkout "$BRANCH" > /dev/null 2>&1 &&
git reset --hard > /dev/null 2>&1 &&
printf '%s' "$BRANCH"
composer update -q
(for _ in $(seq 1 10); do php php-cs-fixer fix --dry-run 2> /dev/null ; done) | grep -i seconds | awk '
{
total += $5;
++count;
}
END {
print " mean:" (total/count) " total:" total " rounds:" count
}'
done
PK qR#=
README.mdnu ٘
PHP Coding Standards Fixer
==========================
The PHP Coding Standards Fixer (PHP CS Fixer) tool fixes your code to follow standards;
whether you want to follow PHP coding standards as defined in the PSR-1, PSR-2, etc.,
or other community driven ones like the Symfony one.
You can **also** define your (team's) style through configuration.
It can modernize your code (like converting the ``pow`` function to the ``**`` operator on PHP 5.6)
and (micro) optimize it.
If you are already using a linter to identify coding standards problems in your
code, you know that fixing them by hand is tedious, especially on large
projects. This tool does not only detect them, but also fixes them for you.
## Documentation
### Installation
The recommended way to install PHP CS Fixer is to use [Composer](https://getcomposer.org/download/)
in a dedicated `composer.json` file in your project, for example in the
`tools/php-cs-fixer` directory:
```console
$ mkdir --parents tools/php-cs-fixer
$ composer require --working-dir=tools/php-cs-fixer friendsofphp/php-cs-fixer
```
For more details and other installation methods, see
[installation instructions](./doc/installation.rst).
### Usage
Assuming you installed PHP CS Fixer as instructed above, you can run the
following command to fix the files PHP files in the `src` directory:
```console
$ tools/php-cs-fixer/vendor/bin/php-cs-fixer fix src
```
See [usage](./doc/usage.rst), list of [built-in rules](./doc/rules/index.rst), list of [rule sets](./doc/ruleSets/index.rst)
and [configuration file](./doc/config.rst) documentation for more details.
If you need to apply code styles that are not supported by the tool, you can
[create custom rules](./doc/custom_rules.rst).
## Editor Integration
Dedicated plugins exist for:
* [Atom](https://github.com/Glavin001/atom-beautify)
* [NetBeans](http://plugins.netbeans.org/plugin/49042/php-cs-fixer)
* [PhpStorm](https://medium.com/@valeryan/how-to-configure-phpstorm-to-use-php-cs-fixer-1844991e521f)
* [Sublime Text](https://github.com/benmatselby/sublime-phpcs)
* [Vim](https://github.com/stephpy/vim-php-cs-fixer)
* [VS Code](https://github.com/junstyle/vscode-php-cs-fixer)
## Community
The PHP CS Fixer is maintained on GitHub at https://github.com/FriendsOfPHP/PHP-CS-Fixer.
Bug reports and ideas about new features are welcome there.
You can reach us at https://gitter.im/PHP-CS-Fixer/Lobby about the project,
configuration, possible improvements, ideas and questions, please visit us!
## Contribute
The tool comes with quite a few built-in fixers, but everyone is more than
welcome to [contribute](CONTRIBUTING.md) more of them.
PK qRc CONTRIBUTING.mdnu ٘ # 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
### Fixer
A *fixer* is a class that tries to fix one code style issue (a ``Fixer`` class
must implement ``FixerInterface``).
### Config
A *config* knows about the code style rules and the files and directories that
must be scanned by the tool when run in the directory of your project. It is
useful for projects that follow a well-known directory structures (like for
Symfony projects for instance).
### How-To
* [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 wrong file permissions in the repository: `./dev-tools/check_file_permissions.sh`.
* Make sure there is no trailing spaces in the code: `./dev-tools/check_trailing_spaces.sh`.
* Update documentation: `php dev-tools/doc.php`. This requires the highest version of PHP supported by PHP CS Fixer. If it is not installed on your system, you can run it in a Docker container instead: `docker run -it --rm --user="$(id -u):$(id -g)" -w="/app" --volume="$(pwd):/app" php:7.4-cli php dev-tools/doc.php`.
* Install dev tools: `dev-tools/install.sh`
* Run static analysis using PHPStan: `php -d memory_limit=256M dev-tools/vendor/bin/phpstan analyse`
* Check if tests pass: `vendor/bin/phpunit`.
* Fix project itself: `php php-cs-fixer fix`.
## Working With Docker
This project provides a Docker setup that allows working on it using any of the supported PHP versions.
To use it, you first need to install:
* [Docker](https://docs.docker.com/get-docker/)
* [Docker Compose](https://docs.docker.com/compose/install/)
Make sure the versions installed support [Compose file format 3.8](https://docs.docker.com/compose/compose-file/).
Next, copy [`docker-compose.override.yaml.dist`](./docker-compose.override.yaml.dist) to `docker-compose.override.yaml`
and edit it to your needs. The relevant parameters that might require some tweaking have comments to help you.
You can then build the images:
```console
$ docker-compose build --parallel
```
Now you can run commands needed to work on the project. For example, say you want to run PHPUnit tests on PHP 7.4:
```console
$ docker-compose run php-7.4 vendor/bin/phpunit
```
Sometimes it can be more convenient to have a shell inside the container:
```console
$ docker-compose run php-7.4 sh
/app $ vendor/bin/phpunit
```
The images come with an [`xdebug` script](github.com/julienfalque/xdebug/) that allows running any PHP command with
Xdebug enabled to help debug problems.
```console
docker-compose run php-7.4 xdebug vendor/bin/phpunit
```
If you're using PhpStorm, you need to create a [server](https://www.jetbrains.com/help/phpstorm/servers.html) with a
name that matches the `PHP_IDE_CONFIG` environment variable defined in the Docker Compose configuration files, which is
`php-cs-fixer` by default.
All images use port 9003 for debug connections.
## 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.rst) 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 qRKrJ73 73
UPGRADE.mdnu ٘ 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
no_short_echo_tag | echo_tag_syntax
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 qR$
.gitignorenu ٘ /.php-cs-fixer.php
/.phpunit.result.cache
/box.json
/composer.lock
/docker-compose.override.yaml
/php-cs-fixer.phar
/phpunit.xml
/vendor/
PK qRLً docker/php-5.6/xdebug.ininu ٘ ;zend_extension=xdebug.so
xdebug.remote_autostart=1
xdebug.remote_enable=1
xdebug.remote_host=host.docker.internal
xdebug.remote_port=9003
PK qR$h docker/php-5.6/Dockerfilenu ٘ FROM php:5.6-cli-alpine3.8
ARG DOCKER_USER_ID
ARG DOCKER_GROUP_ID
RUN if ! getent group "${DOCKER_GROUP_ID}" > /dev/null; \
then addgroup -S -g "${DOCKER_GROUP_ID}" devs; \
fi \
&& if ! getent passwd "${DOCKER_USER_ID}" > /dev/null; \
then adduser -S -u "${DOCKER_USER_ID}" -G "$(getent group "${DOCKER_GROUP_ID}" | awk -F: '{printf $1}')" dev; \
fi \
&& apk add --no-cache git libxml2-dev openssh-client \
&& apk add --no-cache --virtual .build-deps autoconf g++ make \
# xdebug
&& pecl install xdebug-2.5.5 \
&& docker-php-ext-enable xdebug \
# composer
&& curl --output composer-setup.php https://getcomposer.org/installer \
&& php composer-setup.php --install-dir=/usr/local/bin --filename=composer \
&& rm composer-setup.php \
# xdebug command
&& curl --location --output /usr/local/bin/xdebug https://github.com/julienfalque/xdebug/releases/download/v1.1.0/xdebug \
&& chmod +x /usr/local/bin/xdebug \
# clean up
&& apk del .build-deps
COPY xdebug.ini /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
PK qRLً docker/php-7.2/xdebug.ininu ٘ ;zend_extension=xdebug.so
xdebug.remote_autostart=1
xdebug.remote_enable=1
xdebug.remote_host=host.docker.internal
xdebug.remote_port=9003
PK qRt docker/php-7.2/Dockerfilenu ٘ FROM php:7.2-cli-alpine3.12
ARG DOCKER_USER_ID
ARG DOCKER_GROUP_ID
RUN if ! getent group "${DOCKER_GROUP_ID}" > /dev/null; \
then addgroup -S -g "${DOCKER_GROUP_ID}" devs; \
fi \
&& if ! getent passwd "${DOCKER_USER_ID}" > /dev/null; \
then adduser -S -u "${DOCKER_USER_ID}" -G "$(getent group "${DOCKER_GROUP_ID}" | awk -F: '{printf $1}')" dev; \
fi \
&& apk add --no-cache git libxml2-dev openssh-client \
&& apk add --no-cache --virtual .build-deps autoconf g++ make \
# xdebug
&& pecl install xdebug-2.9.8 \
&& docker-php-ext-enable xdebug \
# composer
&& curl --output composer-setup.php https://getcomposer.org/installer \
&& php composer-setup.php --install-dir=/usr/local/bin --filename=composer \
&& rm composer-setup.php \
# xdebug command
&& curl --location --output /usr/local/bin/xdebug https://github.com/julienfalque/xdebug/releases/download/v2.0.0/xdebug \
&& chmod +x /usr/local/bin/xdebug \
# clean up
&& apk del .build-deps
COPY xdebug.ini /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
PK qR`Jr r docker/php-8.0/xdebug.ininu ٘ ;zend_extension=xdebug.so
xdebug.client_host=host.docker.internal
xdebug.mode=debug
xdebug.start_with_request=yes
PK qR docker/php-8.0/Dockerfilenu ٘ FROM php:8.0-cli-alpine3.13
ARG DOCKER_USER_ID
ARG DOCKER_GROUP_ID
RUN if ! getent group "${DOCKER_GROUP_ID}" > /dev/null; \
then addgroup -S -g "${DOCKER_GROUP_ID}" devs; \
fi \
&& if ! getent passwd "${DOCKER_USER_ID}" > /dev/null; \
then adduser -S -u "${DOCKER_USER_ID}" -G "$(getent group "${DOCKER_GROUP_ID}" | awk -F: '{printf $1}')" dev; \
fi \
&& apk add --no-cache git libxml2-dev openssh-client \
&& apk add --no-cache --virtual .build-deps autoconf g++ make \
# xdebug
&& pecl install xdebug-3.0.1 \
&& docker-php-ext-enable xdebug \
# composer
&& curl --output composer-setup.php https://getcomposer.org/installer \
&& php composer-setup.php --install-dir=/usr/local/bin --filename=composer \
&& rm composer-setup.php \
# xdebug command
&& curl --location --output /usr/local/bin/xdebug https://github.com/julienfalque/xdebug/releases/download/v2.0.0/xdebug \
&& chmod +x /usr/local/bin/xdebug \
# clean up
&& apk del .build-deps
COPY xdebug.ini /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
PK qR`Jr r docker/php-7.3/xdebug.ininu ٘ ;zend_extension=xdebug.so
xdebug.client_host=host.docker.internal
xdebug.mode=debug
xdebug.start_with_request=yes
PK qR, docker/php-7.3/Dockerfilenu ٘ FROM php:7.3-cli-alpine3.13
ARG DOCKER_USER_ID
ARG DOCKER_GROUP_ID
RUN if ! getent group "${DOCKER_GROUP_ID}" > /dev/null; \
then addgroup -S -g "${DOCKER_GROUP_ID}" devs; \
fi \
&& if ! getent passwd "${DOCKER_USER_ID}" > /dev/null; \
then adduser -S -u "${DOCKER_USER_ID}" -G "$(getent group "${DOCKER_GROUP_ID}" | awk -F: '{printf $1}')" dev; \
fi \
&& apk add --no-cache git libxml2-dev openssh-client \
&& apk add --no-cache --virtual .build-deps autoconf g++ make \
# xdebug
&& pecl install xdebug-3.0.1 \
&& docker-php-ext-enable xdebug \
# composer
&& curl --output composer-setup.php https://getcomposer.org/installer \
&& php composer-setup.php --install-dir=/usr/local/bin --filename=composer \
&& rm composer-setup.php \
# xdebug command
&& curl --location --output /usr/local/bin/xdebug https://github.com/julienfalque/xdebug/releases/download/v2.0.0/xdebug \
&& chmod +x /usr/local/bin/xdebug \
# clean up
&& apk del .build-deps
COPY xdebug.ini /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
PK qRLً docker/php-7.1/xdebug.ininu ٘ ;zend_extension=xdebug.so
xdebug.remote_autostart=1
xdebug.remote_enable=1
xdebug.remote_host=host.docker.internal
xdebug.remote_port=9003
PK qR docker/php-7.1/Dockerfilenu ٘ FROM php:7.1-cli-alpine3.10
ARG DOCKER_USER_ID
ARG DOCKER_GROUP_ID
RUN if ! getent group "${DOCKER_GROUP_ID}" > /dev/null; \
then addgroup -S -g "${DOCKER_GROUP_ID}" devs; \
fi \
&& if ! getent passwd "${DOCKER_USER_ID}" > /dev/null; \
then adduser -S -u "${DOCKER_USER_ID}" -G "$(getent group "${DOCKER_GROUP_ID}" | awk -F: '{printf $1}')" dev; \
fi \
&& apk add --no-cache git libxml2-dev openssh-client \
&& apk add --no-cache --virtual .build-deps autoconf g++ make \
# xdebug
&& pecl install xdebug-2.9.8 \
&& docker-php-ext-enable xdebug \
# composer
&& curl --output composer-setup.php https://getcomposer.org/installer \
&& php composer-setup.php --install-dir=/usr/local/bin --filename=composer \
&& rm composer-setup.php \
# xdebug command
&& curl --location --output /usr/local/bin/xdebug https://github.com/julienfalque/xdebug/releases/download/v1.1.0/xdebug \
&& chmod +x /usr/local/bin/xdebug \
# clean up
&& apk del .build-deps
COPY xdebug.ini /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
PK qRLً docker/php-7.0/xdebug.ininu ٘ ;zend_extension=xdebug.so
xdebug.remote_autostart=1
xdebug.remote_enable=1
xdebug.remote_host=host.docker.internal
xdebug.remote_port=9003
PK qRك1 docker/php-7.0/Dockerfilenu ٘ FROM php:7.0-cli-alpine3.7
ARG DOCKER_USER_ID
ARG DOCKER_GROUP_ID
RUN if ! getent group "${DOCKER_GROUP_ID}" > /dev/null; \
then addgroup -S -g "${DOCKER_GROUP_ID}" devs; \
fi \
&& if ! getent passwd "${DOCKER_USER_ID}" > /dev/null; \
then adduser -S -u "${DOCKER_USER_ID}" -G "$(getent group "${DOCKER_GROUP_ID}" | awk -F: '{printf $1}')" dev; \
fi \
&& apk add --no-cache git libxml2-dev openssh-client \
&& apk add --no-cache --virtual .build-deps autoconf g++ make \
# xdebug
&& pecl install xdebug-2.7.2 \
&& docker-php-ext-enable xdebug \
# composer
&& curl --output composer-setup.php https://getcomposer.org/installer \
&& php composer-setup.php --install-dir=/usr/local/bin --filename=composer \
&& rm composer-setup.php \
# xdebug command
&& curl --location --output /usr/local/bin/xdebug https://github.com/julienfalque/xdebug/releases/download/v1.1.0/xdebug \
&& chmod +x /usr/local/bin/xdebug \
# clean up
&& apk del .build-deps
COPY xdebug.ini /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
PK qR`Jr r docker/php-7.4/xdebug.ininu ٘ ;zend_extension=xdebug.so
xdebug.client_host=host.docker.internal
xdebug.mode=debug
xdebug.start_with_request=yes
PK qR& docker/php-7.4/Dockerfilenu ٘ FROM php:7.4-cli-alpine3.13
ARG DOCKER_USER_ID
ARG DOCKER_GROUP_ID
RUN if ! getent group "${DOCKER_GROUP_ID}" > /dev/null; \
then addgroup -S -g "${DOCKER_GROUP_ID}" devs; \
fi \
&& if ! getent passwd "${DOCKER_USER_ID}" > /dev/null; \
then adduser -S -u "${DOCKER_USER_ID}" -G "$(getent group "${DOCKER_GROUP_ID}" | awk -F: '{printf $1}')" dev; \
fi \
&& apk add --no-cache git libxml2-dev openssh-client \
&& apk add --no-cache --virtual .build-deps autoconf g++ make \
# xdebug
&& pecl install xdebug-3.0.1 \
&& docker-php-ext-enable xdebug \
# composer
&& curl --output composer-setup.php https://getcomposer.org/installer \
&& php composer-setup.php --install-dir=/usr/local/bin --filename=composer \
&& rm composer-setup.php \
# xdebug command
&& curl --location --output /usr/local/bin/xdebug https://github.com/julienfalque/xdebug/releases/download/v2.0.0/xdebug \
&& chmod +x /usr/local/bin/xdebug \
# clean up
&& apk del .build-deps
COPY xdebug.ini /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
PK qRgo " src/AbstractNoUselessElseFixer.phpnu ٘
* 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 SpacePossum
*/
abstract class AbstractNoUselessElseFixer extends AbstractFixer
{
/**
* {@inheritdoc}
*/
public function getPriority()
{
// should be run before NoWhitespaceInBlankLineFixer, NoExtraBlankLinesFixer, BracesFixer and after NoEmptyStatementFixer.
return 39;
}
/**
* @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]])) {
return false;
}
if ($tokens[$candidateIndex]->equals([T_THROW])) {
$previousIndex = $tokens->getPrevMeaningfulToken($candidateIndex);
if (!$tokens[$previousIndex]->equalsAny([';', '{'])) {
return false;
}
}
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 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 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 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 qR+ src/AbstractAlignFixerHelper.phpnu ٘
* 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 int $startAt
* @param int $endAt
*/
abstract protected function injectAlignmentPlaceholders(Tokens $tokens, $startAt, $endAt);
/**
* Look for group of placeholders, and provide vertical alignment.
*
* @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 qR2i i src/ToolInfoInterface.phpnu ٘
* 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 qRBJtb b src/Tokenizer/TokensAnalyzer.phpnu ٘
* 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\Tokenizer\Analyzer\GotoLabelAnalyzer;
/**
* 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;
/**
* @var ?GotoLabelAnalyzer
*/
private $gotoLabelAnalyzer;
public function __construct(Tokens $tokens)
{
$this->tokens = $tokens;
}
/**
* Get indexes of methods and properties in classy code (classes, interfaces and traits).
*
* @param bool $returnTraitsImports TODO on v3 remove flag and return the imports
*
* @return array[]
*/
public function getClassyElements($returnTraitsImports = false)
{
$elements = [];
for ($index = 1, $count = \count($this->tokens) - 2; $index < $count; ++$index) {
if ($this->tokens[$index]->isClassy()) {
list($index, $newElements) = $this->findClassyElements($index, $index, $returnTraitsImports);
$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;
$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);
}
return $this->isBlockMultiline($tokens, $index);
}
/**
* @param int $index
*
* @return bool
*/
public function isBlockMultiline(Tokens $tokens, $index)
{
$blockType = Tokens::detectBlockType($tokens[$index]);
if (null === $blockType || !$blockType['isStart']) {
throw new \InvalidArgumentException(sprintf('Not an block start at given index %d.', $index));
}
$endIndex = $tokens->findBlockEnd($blockType['type'], $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)
{
if (!$this->tokens[$index]->isClassy()) {
throw new \LogicException(sprintf('No classy token at given index %d.', $index));
}
if (!$this->tokens[$index]->isGivenKind(T_CLASS)) {
return false;
}
return $this->tokens[$this->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)
&& (\PHP_VERSION_ID < 70400 || !$this->tokens[$index]->isGivenKind(T_FN))
) {
throw new \LogicException(sprintf('No T_FUNCTION or T_FN 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_TRAIT, CT::T_TYPE_COLON]) || $this->tokens[$prevIndex]->isObjectOperator()) {
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, CT::T_NULLABLE_TYPE, CT::T_TYPE_COLON, 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 `extends`/`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([T_EXTENDS, CT::T_GROUP_IMPORT_BRACE_OPEN, T_IMPLEMENTS, T_USE, 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 attribute: `#[Foo]`
if (
\defined('T_ATTRIBUTE') // @TODO: drop condition when PHP 8.0+ is required
&& $this->tokens[$prevIndex]->isGivenKind(T_ATTRIBUTE)
) {
return false;
}
// check for goto label
if ($this->tokens[$nextIndex]->equals(':')) {
if (null === $this->gotoLabelAnalyzer) {
$this->gotoLabelAnalyzer = new GotoLabelAnalyzer();
}
if ($this->gotoLabelAnalyzer->belongsToGoToLabel($this->tokens, $nextIndex)) {
return false;
}
}
// check for non-capturing catches
while ($this->tokens[$prevIndex]->isGivenKind([CT::T_TYPE_ALTERNATION, T_STRING])) {
$prevIndex = $this->tokens->getPrevMeaningfulToken($prevIndex);
}
if ($this->tokens[$prevIndex]->equals('(')) {
$prevPrevIndex = $this->tokens->getPrevMeaningfulToken($prevIndex);
if ($this->tokens[$prevPrevIndex]->isGivenKind(T_CATCH)) {
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_ARRAY_INDEX_CURLY_BRACE_CLOSE],
[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_ARRAY_INDEX_CURLY_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,
'.' => 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, // |
];
// @TODO: drop condition when PHP 7.0+ is required
if (\defined('T_SPACESHIP')) {
$arrayOperators[T_SPACESHIP] = true; // <=>
}
// @TODO: drop condition when PHP 7.0+ is required
if (\defined('T_COALESCE')) {
$arrayOperators[T_COALESCE] = true; // ??
}
// @TODO: drop condition when PHP 7.4+ is required
if (\defined('T_COALESCE_EQUAL')) {
$arrayOperators[T_COALESCE_EQUAL] = 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);
}
/**
* @param int $index
*
* @return bool
*/
public function isSuperGlobal($index)
{
static $superNames = [
'$_COOKIE' => true,
'$_ENV' => true,
'$_FILES' => true,
'$_GET' => true,
'$_POST' => true,
'$_REQUEST' => true,
'$_SERVER' => true,
'$_SESSION' => true,
'$GLOBALS' => true,
];
$token = $this->tokens[$index];
if (!$token->isGivenKind(T_VARIABLE)) {
return false;
}
return isset($superNames[strtoupper($token->getContent())]);
}
/**
* 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 $classIndex classy index
* @param int $index
* @param bool $returnTraitsImports
*
* @return array
*/
private function findClassyElements($classIndex, $index, $returnTraitsImports = false)
{
$elements = [];
$curlyBracesLevel = 0;
$bracesLevel = 0;
++$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
// check for nested anonymous classes inside the new call of an anonymous class,
// for example `new class(function (){new class(function (){new class(function (){}){};}){};}){};` etc.
// if class(XYZ) {} skip till `(` as XYZ might contain functions etc.
$nestedClassIndex = $index;
$index = $this->tokens->getNextMeaningfulToken($index);
if ($this->tokens[$index]->equals('(')) {
++$index; // move after `(`
for ($nestedBracesLevel = 1; $index < $count; ++$index) {
$token = $this->tokens[$index];
if ($token->equals('(')) {
++$nestedBracesLevel;
continue;
}
if ($token->equals(')')) {
--$nestedBracesLevel;
if (0 === $nestedBracesLevel) {
list($index, $newElements) = $this->findClassyElements($nestedClassIndex, $index, $returnTraitsImports);
$elements += $newElements;
break;
}
continue;
}
if ($token->isClassy()) { // anonymous class in class
list($index, $newElements) = $this->findClassyElements($index, $index, $returnTraitsImports);
$elements += $newElements;
}
}
} else {
list($index, $newElements) = $this->findClassyElements($nestedClassIndex, $nestedClassIndex, $returnTraitsImports);
$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,
];
} elseif ($returnTraitsImports && $token->isGivenKind(CT::T_USE_TRAIT)) {
$elements[$index] = [
'token' => $token,
'type' => 'trait_import',
'classIndex' => $classIndex,
];
}
}
return [$index, $elements];
}
}
PK qRޅ src/Tokenizer/CodeHasher.phpnu ٘
* 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 qRV src/Tokenizer/CT.phpnu ٘
* 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;
const T_CONSTRUCTOR_PROPERTY_PROMOTION_PUBLIC = 10028;
const T_CONSTRUCTOR_PROPERTY_PROMOTION_PROTECTED = 10029;
const T_CONSTRUCTOR_PROPERTY_PROMOTION_PRIVATE = 10030;
const T_ATTRIBUTE_CLOSE = 10031;
const T_NAMED_ARGUMENT_NAME = 10032;
const T_NAMED_ARGUMENT_COLON = 10033;
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 qRrO\ 2 src/Tokenizer/Transformer/AttributeTransformer.phpnu ٘
* 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;
/**
* Transforms attribute related Tokens.
*
* @internal
*/
final class AttributeTransformer extends AbstractTransformer
{
/**
* {@inheritdoc}
*/
public function getPriority()
{
// must run before all other transformers that might touch attributes
return 200;
}
/**
* {@inheritdoc}
*/
public function getRequiredPhpVersionId()
{
return 80000;
}
/**
* {@inheritdoc}
*/
public function process(Tokens $tokens, Token $token, $index)
{
if (!$tokens[$index]->isGivenKind(T_ATTRIBUTE)) {
return;
}
$level = 1;
do {
++$index;
if ($tokens[$index]->equals('[')) {
++$level;
} elseif ($tokens[$index]->equals(']')) {
--$level;
}
} while (0 < $level);
$tokens[$index] = new Token([CT::T_ATTRIBUTE_CLOSE, ']']);
}
/**
* {@inheritdoc}
*/
public function getCustomTokens()
{
return [
CT::T_ATTRIBUTE_CLOSE,
];
}
}
PK qR
, src/Tokenizer/Transformer/UseTransformer.phpnu ٘
* 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 getPriority()
{
// Should run after CurlyBraceTransformer and before TypeColonTransformer
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()]);
}
}
}
/**
* {@inheritdoc}
*/
public function getCustomTokens()
{
return [CT::T_USE_TRAIT, CT::T_USE_LAMBDA];
}
/**
* Check if token under given index is `use` statement for lambda function.
*
* @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 qRp4` 4 src/Tokenizer/Transformer/SquareBraceTransformer.phpnu ٘
* 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 getPriority()
{
// must run after CurlyBraceTransformer and AttributeTransformer
return -1;
}
/**
* {@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);
}
}
/**
* {@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,
];
}
/**
* @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 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 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 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 qR2,w w 5 src/Tokenizer/Transformer/NullableTypeTransformer.phpnu ٘
* 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 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],
[CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PUBLIC],
[CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PROTECTED],
[CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PRIVATE],
[CT::T_ATTRIBUTE_CLOSE],
[T_PRIVATE],
[T_PROTECTED],
[T_PUBLIC],
[T_VAR],
[T_STATIC],
])) {
$tokens[$index] = new Token([CT::T_NULLABLE_TYPE, '?']);
}
}
/**
* {@inheritdoc}
*/
public function getCustomTokens()
{
return [CT::T_NULLABLE_TYPE];
}
}
PK qRE / src/Tokenizer/Transformer/ImportTransformer.phpnu ٘
* 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 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(),
]);
}
}
/**
* {@inheritdoc}
*/
public function getCustomTokens()
{
return [CT::T_CONST_IMPORT, CT::T_FUNCTION_IMPORT];
}
}
PK qR#7\s 6 src/Tokenizer/Transformer/NamedArgumentTransformer.phpnu ٘
* 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 named argument tokens.
*
* @author SpacePossum
*
* @internal
*/
final class NamedArgumentTransformer extends AbstractTransformer
{
/**
* {@inheritdoc}
*/
public function getPriority()
{
// needs to run after TypeColonTransformer
return -15;
}
/**
* {@inheritdoc}
*/
public function getRequiredPhpVersionId()
{
return 80000;
}
/**
* {@inheritdoc}
*/
public function process(Tokens $tokens, Token $token, $index)
{
if (!$tokens[$index]->equals(':')) {
return;
}
$stringIndex = $tokens->getPrevMeaningfulToken($index);
if (!$tokens[$stringIndex]->isGivenKind(T_STRING)) {
return;
}
$preStringIndex = $tokens->getPrevMeaningfulToken($stringIndex);
// if equals any [';', '{', '}', [T_OPEN_TAG]] than it is a goto label
// if equals ')' than likely it is a type colon, but sure not a name argument
// if equals '?' than it is part of ternary statement
if (!$tokens[$preStringIndex]->equalsAny([',', '('])) {
return;
}
$tokens[$stringIndex] = new Token([CT::T_NAMED_ARGUMENT_NAME, $tokens[$stringIndex]->getContent()]);
$tokens[$index] = new Token([CT::T_NAMED_ARGUMENT_COLON, ':']);
}
/**
* {@inheritdoc}
*/
public function getCustomTokens()
{
return [
CT::T_NAMED_ARGUMENT_COLON,
CT::T_NAMED_ARGUMENT_NAME,
];
}
}
PK qRc}N N 6 src/Tokenizer/Transformer/ArrayTypehintTransformer.phpnu ٘
* 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 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()]);
}
}
/**
* {@inheritdoc}
*/
public function getCustomTokens()
{
return [CT::T_ARRAY_TYPEHINT];
}
}
PK qR
: src/Tokenizer/Transformer/WhitespacyCommentTransformer.phpnu ٘
* 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 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]));
}
}
/**
* {@inheritdoc}
*/
public function getCustomTokens()
{
return [];
}
}
PK qRH H : src/Tokenizer/Transformer/NamespaceOperatorTransformer.phpnu ٘
* 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 getRequiredPhpVersionId()
{
return 50300;
}
/**
* {@inheritdoc}
*/
public function process(Tokens $tokens, Token $token, $index)
{
if (!$token->isGivenKind(T_NAMESPACE)) {
return;
}
$nextIndex = $tokens->getNextMeaningfulToken($index);
if ($tokens[$nextIndex]->isGivenKind(T_NS_SEPARATOR)) {
$tokens[$index] = new Token([CT::T_NAMESPACE_OPERATOR, $token->getContent()]);
}
}
/**
* {@inheritdoc}
*/
public function getCustomTokens()
{
return [CT::T_NAMESPACE_OPERATOR];
}
}
PK qR 8 src/Tokenizer/Transformer/TypeAlternationTransformer.phpnu ٘
* 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 `function foo(Type1 | Type2 $x) {`
* or `} catch (ExceptionType1 | ExceptionType2 $e) {`.
*
* @author Dariusz Rumiński
*
* @internal
*/
final class TypeAlternationTransformer extends AbstractTransformer
{
/**
* {@inheritdoc}
*/
public function getPriority()
{
// needs to run after ArrayTypehintTransformer and TypeColonTransformer
return -15;
}
/**
* {@inheritdoc}
*/
public function getRequiredPhpVersionId()
{
return 70100;
}
/**
* {@inheritdoc}
*/
public function process(Tokens $tokens, Token $token, $index)
{
if (!$token->equals('|')) {
return;
}
$prevIndex = $tokens->getPrevMeaningfulToken($index);
if (!$tokens[$prevIndex]->isGivenKind([T_STRING, CT::T_ARRAY_TYPEHINT])) {
return;
}
do {
$prevIndex = $tokens->getPrevMeaningfulToken($prevIndex);
if (null === $prevIndex) {
return;
}
if (!$tokens[$prevIndex]->isGivenKind([T_NS_SEPARATOR, T_STRING])) {
break;
}
} while (true);
/** @var Token $prevToken */
$prevToken = $tokens[$prevIndex];
if ($prevToken->isGivenKind([
CT::T_TYPE_COLON, // `:` is part of a function return type `foo(): A`
CT::T_TYPE_ALTERNATION, // `|` is part of a union (chain) `X | Y`
T_STATIC, T_VAR, T_PUBLIC, T_PROTECTED, T_PRIVATE, // `var $a;`, `private $a` or `public static $a`
])) {
$this->replaceToken($tokens, $index);
return;
}
if (!$prevToken->equals('(')) {
return;
}
$prevPrevTokenIndex = $tokens->getPrevMeaningfulToken($prevIndex);
/** @var Token $prePrevToken */
$prePrevToken = $tokens[$prevPrevTokenIndex];
if ($prePrevToken->isGivenKind([
T_CATCH, // `|` is part of catch `catch(X |`
T_FUNCTION, // `|` is part of an anonymous function variable `static function (X|Y`
])) {
$this->replaceToken($tokens, $index);
return;
}
if (\PHP_VERSION_ID >= 70400 && $prePrevToken->isGivenKind(T_FN)) {
$this->replaceToken($tokens, $index); // `|` is part of an array function variable `fn(int|null`
return;
}
if (
$prePrevToken->isGivenKind(T_STRING)
&& $tokens[$tokens->getPrevMeaningfulToken($prevPrevTokenIndex)]->isGivenKind(T_FUNCTION)
) {
// `|` is part of function variable `function Foo (X|Y`
$this->replaceToken($tokens, $index);
return;
}
}
/**
* {@inheritdoc}
*/
public function getCustomTokens()
{
return [CT::T_TYPE_ALTERNATION];
}
private function replaceToken(Tokens $tokens, $index)
{
$tokens[$index] = new Token([CT::T_TYPE_ALTERNATION, '|']);
}
}
PK qRT% 3 src/Tokenizer/Transformer/CurlyBraceTransformer.phpnu ٘
* 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 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);
}
}
/**
* {@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,
];
}
/**
* Transform closing `}` for T_CURLY_OPEN into CT::T_CURLY_CLOSE.
*
* This should be done at very beginning of curly braces transformations.
*
* @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->isObjectOperator()) {
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],
']',
')',
])) {
return;
}
if (
$tokens[$prevIndex]->isGivenKind(T_STRING)
&& !$tokens[$tokens->getPrevMeaningfulToken($prevIndex)]->isObjectOperator()
) {
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 qRa a 2 src/Tokenizer/Transformer/ReturnRefTransformer.phpnu ٘
* 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 getRequiredPhpVersionId()
{
return 50000;
}
/**
* {@inheritdoc}
*/
public function process(Tokens $tokens, Token $token, $index)
{
$prevKinds = [T_FUNCTION];
if (\PHP_VERSION_ID >= 70400) {
$prevKinds[] = T_FN;
}
if (
$token->equals('&')
&& $tokens[$tokens->getPrevMeaningfulToken($index)]->isGivenKind($prevKinds)
) {
$tokens[$index] = new Token([CT::T_RETURN_REF, '&']);
}
}
/**
* {@inheritdoc}
*/
public function getCustomTokens()
{
return [CT::T_RETURN_REF];
}
}
PK qRd @ src/Tokenizer/Transformer/BraceClassInstantiationTransformer.phpnu ٘
* 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 getPriority()
{
// must run after CurlyBraceTransformer and SquareBraceTransformer
return -2;
}
/**
* {@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, ')']);
}
/**
* {@inheritdoc}
*/
public function getCustomTokens()
{
return [CT::T_BRACE_CLASS_INSTANTIATION_OPEN, CT::T_BRACE_CLASS_INSTANTIATION_CLOSE];
}
}
PK qRbH4 6 src/Tokenizer/Transformer/ClassConstantTransformer.phpnu ٘
* 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 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()]);
}
}
/**
* {@inheritdoc}
*/
public function getCustomTokens()
{
return [CT::T_CLASS_CONSTANT];
}
}
PK qR0 2 src/Tokenizer/Transformer/TypeColonTransformer.phpnu ٘
* 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 getPriority()
{
// needs to run after ReturnRefTransformer and UseTransformer
// and before TypeAlternationTransformer
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];
}
$prevKinds = [T_FUNCTION, CT::T_RETURN_REF, CT::T_USE_LAMBDA];
if (\PHP_VERSION_ID >= 70400) {
$prevKinds[] = T_FN;
}
if ($prevToken->isGivenKind($prevKinds)) {
$tokens[$index] = new Token([CT::T_TYPE_COLON, ':']);
}
}
/**
* {@inheritdoc}
*/
public function getCustomTokens()
{
return [CT::T_TYPE_COLON];
}
}
PK qRIө 6 src/Tokenizer/Transformer/NameQualifiedTransformer.phpnu ٘
* 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;
/**
* Transform NAME_QUALIFIED, T_NAME_FULLY_QUALIFIED and T_NAME_RELATIVE into T_NAMESPACE T_NS_SEPARATOR T_STRING.
*
* @author SpacePossum
*
* @internal
*/
final class NameQualifiedTransformer extends AbstractTransformer
{
/**
* {@inheritdoc}
*/
public function getPriority()
{
return 1; // must run before NamespaceOperatorTransformer
}
/**
* {@inheritdoc}
*/
public function getRequiredPhpVersionId()
{
return 80000;
}
/**
* {@inheritdoc}
*/
public function process(Tokens $tokens, Token $token, $index)
{
if ($token->isGivenKind([T_NAME_QUALIFIED, T_NAME_FULLY_QUALIFIED])) {
return $this->transformQualified($tokens, $token, $index);
}
if ($token->isGivenKind(T_NAME_RELATIVE)) {
return $this->transformRelative($tokens, $token, $index);
}
}
/**
* {@inheritdoc}
*/
public function getCustomTokens()
{
return [];
}
private function transformQualified(Tokens $tokens, Token $token, $index)
{
$parts = explode('\\', $token->getContent());
$newTokens = [];
if ('' === $parts[0]) {
$newTokens[] = new Token([T_NS_SEPARATOR, '\\']);
array_shift($parts);
}
foreach ($parts as $part) {
$newTokens[] = new Token([T_STRING, $part]);
$newTokens[] = new Token([T_NS_SEPARATOR, '\\']);
}
array_pop($newTokens);
$tokens->overrideRange($index, $index, $newTokens);
}
private function transformRelative(Tokens $tokens, Token $token, $index)
{
$parts = explode('\\', $token->getContent());
$newTokens = [
new Token([T_NAMESPACE, array_shift($parts)]),
new Token([T_NS_SEPARATOR, '\\']),
];
foreach ($parts as $part) {
$newTokens[] = new Token([T_STRING, $part]);
$newTokens[] = new Token([T_NS_SEPARATOR, '\\']);
}
array_pop($newTokens);
$tokens->overrideRange($index, $index, $newTokens);
}
}
PK qR&O` ` = src/Tokenizer/Transformer/ConstructorPromotionTransformer.phpnu ٘
* 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;
/**
* Transforms for Constructor Property Promotion.
*
* Transform T_PUBLIC, T_PROTECTED and T_PRIVATE of Constructor Property Promotion into custom tokens.
*
* @internal
*/
final class ConstructorPromotionTransformer extends AbstractTransformer
{
/**
* {@inheritdoc}
*/
public function getRequiredPhpVersionId()
{
return 80000;
}
/**
* {@inheritdoc}
*/
public function process(Tokens $tokens, Token $token, $index)
{
if (!$tokens[$index]->isGivenKind(T_FUNCTION)) {
return;
}
$index = $tokens->getNextMeaningfulToken($index);
if (!$tokens[$index]->isGivenKind(T_STRING) || '__construct' !== strtolower($tokens[$index]->getContent())) {
return;
}
/** @var int $openIndex */
$openIndex = $tokens->getNextMeaningfulToken($index); // we are @ '(' now
$closeIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openIndex);
for ($index = $openIndex; $index < $closeIndex; ++$index) {
if ($tokens[$index]->isGivenKind(T_PUBLIC)) {
$tokens[$index] = new Token([CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PUBLIC, $tokens[$index]->getContent()]);
} elseif ($tokens[$index]->isGivenKind(T_PROTECTED)) {
$tokens[$index] = new Token([CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PROTECTED, $tokens[$index]->getContent()]);
} elseif ($tokens[$index]->isGivenKind(T_PRIVATE)) {
$tokens[$index] = new Token([CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PRIVATE, $tokens[$index]->getContent()]);
}
}
}
/**
* {@inheritdoc}
*/
public function getCustomTokens()
{
return [
CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PUBLIC,
CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PROTECTED,
CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PRIVATE,
];
}
}
PK qR]&i 0 src/Tokenizer/Resolver/TypeShortNameResolver.phpnu ٘
* 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 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;
}
/**
* @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(static function (NamespaceAnalysis $info) {
return $info->getFullName();
}, (new NamespacesAnalyzer())->getDeclarations($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 qRG 0 src/Tokenizer/Analyzer/NamespaceUsesAnalyzer.phpnu ٘
* 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\Analyzer\Analysis\NamespaceUseAnalysis;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Tokens;
use PhpCsFixer\Tokenizer\TokensAnalyzer;
/**
* @internal
*/
final class NamespaceUsesAnalyzer
{
/**
* @return NamespaceUseAnalysis[]
*/
public function getDeclarationsFromTokens(Tokens $tokens)
{
$tokenAnalyzer = new TokensAnalyzer($tokens);
$useIndexes = $tokenAnalyzer->getImportUseIndexes();
return $this->getDeclarations($tokens, $useIndexes);
}
/**
* @return NamespaceUseAnalysis[]
*/
public function getDeclarationsInNamespace(Tokens $tokens, NamespaceAnalysis $namespace)
{
$namespaceUses = [];
foreach ($this->getDeclarationsFromTokens($tokens) as $namespaceUse) {
if ($namespaceUse->getStartIndex() >= $namespace->getScopeStartIndex() && $namespaceUse->getStartIndex() <= $namespace->getScopeEndIndex()) {
$namespaceUses[] = $namespaceUse;
}
}
return $namespaceUses;
}
/**
* @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 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 qRMD# D# , src/Tokenizer/Analyzer/FunctionsAnalyzer.phpnu ٘
* 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\NamespaceUseAnalysis;
use PhpCsFixer\Tokenizer\Analyzer\Analysis\TypeAnalysis;
use PhpCsFixer\Tokenizer\CT;
use PhpCsFixer\Tokenizer\Token;
use PhpCsFixer\Tokenizer\Tokens;
/**
* @internal
*/
final class FunctionsAnalyzer
{
/**
* @var array
*/
private $functionsAnalysis = ['tokens' => '', 'imports' => [], 'declarations' => []];
/**
* Important: risky because of the limited (file) scope of the tool.
*
* @param int $index
*
* @return bool
*/
public function isGlobalFunctionCall(Tokens $tokens, $index)
{
if (!$tokens[$index]->isGivenKind(T_STRING)) {
return false;
}
$nextIndex = $tokens->getNextMeaningfulToken($index);
if (!$tokens[$nextIndex]->equals('(')) {
return false;
}
$previousIsNamespaceSeparator = false;
$prevIndex = $tokens->getPrevMeaningfulToken($index);
if ($tokens[$prevIndex]->isGivenKind(T_NS_SEPARATOR)) {
$previousIsNamespaceSeparator = true;
$prevIndex = $tokens->getPrevMeaningfulToken($prevIndex);
}
$possibleKind = array_merge([T_DOUBLE_COLON, T_FUNCTION, CT::T_NAMESPACE_OPERATOR, T_NEW, CT::T_RETURN_REF, T_STRING], Token::getObjectOperatorKinds());
// @TODO: drop condition when PHP 8.0+ is required
if (\defined('T_ATTRIBUTE')) {
$possibleKind[] = T_ATTRIBUTE;
}
if ($tokens[$prevIndex]->isGivenKind($possibleKind)) {
return false;
}
if ($previousIsNamespaceSeparator) {
return true;
}
if ($tokens->isChanged() || $tokens->getCodeHash() !== $this->functionsAnalysis['tokens']) {
$this->buildFunctionsAnalysis($tokens);
}
// figure out in which namespace we are
$namespaceAnalyzer = new NamespacesAnalyzer();
$declarations = $namespaceAnalyzer->getDeclarations($tokens);
$scopeStartIndex = 0;
$scopeEndIndex = \count($tokens) - 1;
$inGlobalNamespace = false;
foreach ($declarations as $declaration) {
$scopeStartIndex = $declaration->getScopeStartIndex();
$scopeEndIndex = $declaration->getScopeEndIndex();
if ($index >= $scopeStartIndex && $index <= $scopeEndIndex) {
$inGlobalNamespace = '' === $declaration->getFullName();
break;
}
}
$call = strtolower($tokens[$index]->getContent());
// check if the call is to a function declared in the same namespace as the call is done,
// if the call is already in the global namespace than declared functions are in the same
// global namespace and don't need checking
if (!$inGlobalNamespace) {
/** @var int $functionNameIndex */
foreach ($this->functionsAnalysis['declarations'] as $functionNameIndex) {
if ($functionNameIndex < $scopeStartIndex || $functionNameIndex > $scopeEndIndex) {
continue;
}
if (strtolower($tokens[$functionNameIndex]->getContent()) === $call) {
return false;
}
}
}
/** @var NamespaceUseAnalysis $functionUse */
foreach ($this->functionsAnalysis['imports'] as $functionUse) {
if ($functionUse->getStartIndex() < $scopeStartIndex || $functionUse->getEndIndex() > $scopeEndIndex) {
continue;
}
if ($call !== strtolower($functionUse->getShortName())) {
continue;
}
// global import like `use function \str_repeat;`
return $functionUse->getShortName() === ltrim($functionUse->getFullName(), '\\');
}
return true;
}
/**
* @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 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]->isGivenKind(CT::T_TYPE_COLON)) {
return null;
}
$type = '';
$typeStartIndex = $tokens->getNextMeaningfulToken($typeColonIndex);
$typeEndIndex = $typeStartIndex;
$functionBodyStart = $tokens->getNextTokenOfKind($typeColonIndex, ['{', ';', [T_DOUBLE_ARROW]]);
for ($i = $typeStartIndex; $i < $functionBodyStart; ++$i) {
if ($tokens[$i]->isWhitespace() || $tokens[$i]->isComment()) {
continue;
}
$type .= $tokens[$i]->getContent();
$typeEndIndex = $i;
}
return new TypeAnalysis($type, $typeStartIndex, $typeEndIndex);
}
/**
* @param int $index
*
* @return bool
*/
public function isTheSameClassCall(Tokens $tokens, $index)
{
if (!$tokens->offsetExists($index)) {
return false;
}
$operatorIndex = $tokens->getPrevMeaningfulToken($index);
if (!$tokens->offsetExists($operatorIndex)) {
return false;
}
$referenceIndex = $tokens->getPrevMeaningfulToken($operatorIndex);
if (!$tokens->offsetExists($referenceIndex)) {
return false;
}
return $tokens[$operatorIndex]->isObjectOperator() && $tokens[$referenceIndex]->equals([T_VARIABLE, '$this'], false)
|| $tokens[$operatorIndex]->isGivenKind(T_DOUBLE_COLON) && $tokens[$referenceIndex]->equals([T_STRING, 'self'], false)
|| $tokens[$operatorIndex]->isGivenKind(T_DOUBLE_COLON) && $tokens[$referenceIndex]->equals([T_STATIC, 'static'], false);
}
private function buildFunctionsAnalysis(Tokens $tokens)
{
$this->functionsAnalysis = [
'tokens' => $tokens->getCodeHash(),
'imports' => [],
'declarations' => [],
];
// find declarations
if ($tokens->isTokenKindFound(T_FUNCTION)) {
$end = \count($tokens);
for ($i = 0; $i < $end; ++$i) {
// skip classy, we are looking for functions not methods
if ($tokens[$i]->isGivenKind(Token::getClassyTokenKinds())) {
$i = $tokens->getNextTokenOfKind($i, ['(', '{']);
if ($tokens[$i]->equals('(')) { // anonymous class
$i = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $i);
$i = $tokens->getNextTokenOfKind($i, ['{']);
}
$i = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $i);
continue;
}
if (!$tokens[$i]->isGivenKind(T_FUNCTION)) {
continue;
}
$i = $tokens->getNextMeaningfulToken($i);
if ($tokens[$i]->isGivenKind(CT::T_RETURN_REF)) {
$i = $tokens->getNextMeaningfulToken($i);
}
if (!$tokens[$i]->isGivenKind(T_STRING)) {
continue;
}
$this->functionsAnalysis['declarations'][] = $i;
}
}
// find imported functions
$namespaceUsesAnalyzer = new NamespaceUsesAnalyzer();
if ($tokens->isTokenKindFound(CT::T_FUNCTION_IMPORT)) {
$declarations = $namespaceUsesAnalyzer->getDeclarationsFromTokens($tokens);
foreach ($declarations as $declaration) {
if ($declaration->isFunction()) {
$this->functionsAnalysis['imports'][] = $declaration;
}
}
}
}
}
PK qRg9( , src/Tokenizer/Analyzer/ArgumentsAnalyzer.phpnu ٘
* 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;
/**
* @author Dariusz Rumiński
* @author Vladimir Reznichenko
*
* @internal
*/
final class ArgumentsAnalyzer
{
/**
* Count amount of parameters in a function/method reference.
*
* @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 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 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, CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PUBLIC, CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PROTECTED, CT::T_CONSTRUCTOR_PROPERTY_PROMOTION_PRIVATE])
|| $token->equals('&')
) {
continue;
}
if ($token->isGivenKind(T_VARIABLE)) {
$sawName = true;
$info['name_index'] = $index;
$info['name'] = $token->getContent();
continue;
}
if ($token->equals('=')) {
continue;
}
if (\defined('T_ATTRIBUTE') && $token->isGivenKind(T_ATTRIBUTE)) {
$index = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_ATTRIBUTE, $index);
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 qR . src/Tokenizer/Analyzer/WhitespacesAnalyzer.phpnu ٘
* 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\Tokens;
/**
* @internal
*/
final class WhitespacesAnalyzer
{
/**
* @param int $index
*
* @return string
*/
public static function detectIndent(Tokens $tokens, $index)
{
while (true) {
$whitespaceIndex = $tokens->getPrevTokenOfKind($index, [[T_WHITESPACE]]);
if (null === $whitespaceIndex) {
return '';
}
$whitespaceToken = $tokens[$whitespaceIndex];
if (false !== strpos($whitespaceToken->getContent(), "\n")) {
break;
}
$prevToken = $tokens[$whitespaceIndex - 1];
if ($prevToken->isGivenKind([T_OPEN_TAG, T_COMMENT]) && "\n" === substr($prevToken->getContent(), -1)) {
break;
}
$index = $whitespaceIndex;
}
$explodedContent = explode("\n", $whitespaceToken->getContent());
return end($explodedContent);
}
}
PK qROW?
?
) src/Tokenizer/Analyzer/ClassyAnalyzer.phpnu ٘
* 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\CT;
use PhpCsFixer\Tokenizer\Tokens;
/**
* @internal
*/
final class ClassyAnalyzer
{
/**
* @param int $index
*
* @return bool
*/
public function isClassyInvocation(Tokens $tokens, $index)
{
$token = $tokens[$index];
if (!$token->isGivenKind(T_STRING)) {
throw new \LogicException(sprintf('No T_STRING at given index %d, got "%s".', $index, $tokens[$index]->getName()));
}
if (\in_array(strtolower($token->getContent()), ['bool', 'float', 'int', 'iterable', 'object', 'parent', 'self', 'string', 'void', 'null', 'false'], true)) {
return false;
}
$next = $tokens->getNextMeaningfulToken($index);
$nextToken = $tokens[$next];
if ($nextToken->isGivenKind(T_NS_SEPARATOR)) {
return false;
}
if ($nextToken->isGivenKind([T_DOUBLE_COLON, T_ELLIPSIS, CT::T_TYPE_ALTERNATION, T_VARIABLE])) {
return true;
}
$prev = $tokens->getPrevMeaningfulToken($index);
while ($tokens[$prev]->isGivenKind([CT::T_NAMESPACE_OPERATOR, T_NS_SEPARATOR, T_STRING])) {
$prev = $tokens->getPrevMeaningfulToken($prev);
}
$prevToken = $tokens[$prev];
if ($prevToken->isGivenKind([T_EXTENDS, T_INSTANCEOF, T_INSTEADOF, T_IMPLEMENTS, T_NEW, CT::T_NULLABLE_TYPE, CT::T_TYPE_ALTERNATION, CT::T_TYPE_COLON, CT::T_USE_TRAIT])) {
return true;
}
// `Foo & $bar` could be:
// - function reference parameter: function baz(Foo & $bar) {}
// - bit operator: $x = Foo & $bar;
if ($nextToken->equals('&') && $tokens[$tokens->getNextMeaningfulToken($next)]->isGivenKind(T_VARIABLE)) {
$checkIndex = $tokens->getPrevTokenOfKind($prev + 1, [';', '{', '}', [T_FUNCTION], [T_OPEN_TAG], [T_OPEN_TAG_WITH_ECHO]]);
return $tokens[$checkIndex]->isGivenKind(T_FUNCTION);
}
if (!$prevToken->equals(',')) {
return false;
}
do {
$prev = $tokens->getPrevMeaningfulToken($prev);
} while ($tokens[$prev]->equalsAny([',', [T_NS_SEPARATOR], [T_STRING], [CT::T_NAMESPACE_OPERATOR]]));
return $tokens[$prev]->isGivenKind([T_IMPLEMENTS, CT::T_USE_TRAIT]);
}
}
PK qR" " + src/Tokenizer/Analyzer/CommentsAnalyzer.phpnu ٘
* 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 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.');
}
if (null === $tokens->getNextMeaningfulToken($index)) {
return false;
}
$prevIndex = $tokens->getPrevNonWhitespace($index);
if ($tokens[$prevIndex]->equals(';')) {
$braceCloseIndex = $tokens->getPrevMeaningfulToken($prevIndex);
if (!$tokens[$braceCloseIndex]->equals(')')) {
return false;
}
$braceOpenIndex = $tokens->findBlockStart(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $braceCloseIndex);
$declareIndex = $tokens->getPrevMeaningfulToken($braceOpenIndex);
if (!$tokens[$declareIndex]->isGivenKind(T_DECLARE)) {
return false;
}
$prevIndex = $tokens->getPrevNonWhitespace($declareIndex);
}
return $tokens[$prevIndex]->isGivenKind(T_OPEN_TAG);
}
/**
* 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 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);
// @TODO: drop condition when PHP 8.0+ is required
if (\defined('T_ATTRIBUTE')) {
while (null !== $nextIndex && $tokens[$nextIndex]->isGivenKind(T_ATTRIBUTE)) {
$nextIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_ATTRIBUTE, $nextIndex);
$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 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
*
* @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 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 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 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 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 qRg , src/Tokenizer/Analyzer/GotoLabelAnalyzer.phpnu ٘
* 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\Tokens;
/**
* @internal
*/
final class GotoLabelAnalyzer
{
/**
* @param int $index
*
* @return bool
*/
public function belongsToGoToLabel(Tokens $tokens, $index)
{
if (!$tokens[$index]->equals(':')) {
return false;
}
$prevMeaningfulTokenIndex = $tokens->getPrevMeaningfulToken($index);
if (!$tokens[$prevMeaningfulTokenIndex]->isGivenKind(T_STRING)) {
return false;
}
$prevMeaningfulTokenIndex = $tokens->getPrevMeaningfulToken($prevMeaningfulTokenIndex);
return $tokens[$prevMeaningfulTokenIndex]->equalsAny([';', '{', '}', [T_OPEN_TAG]]);
}
}
PK qRˋ , src/Tokenizer/Analyzer/ReferenceAnalyzer.phpnu ٘
* 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\CT;
use PhpCsFixer\Tokenizer\Tokens;
/**
* @author Kuba Werłos
*
* @internal
*/
final class ReferenceAnalyzer
{
/**
* @param int $index
*
* @return bool
*/
public function isReference(Tokens $tokens, $index)
{
if ($tokens[$index]->isGivenKind(CT::T_RETURN_REF)) {
return true;
}
if (!$tokens[$index]->equals('&')) {
return false;
}
/** @var int $index */
$index = $tokens->getPrevMeaningfulToken($index);
if ($tokens[$index]->equalsAny(['=', [T_AS], [T_CALLABLE], [T_DOUBLE_ARROW], [CT::T_ARRAY_TYPEHINT]])) {
return true;
}
if ($tokens[$index]->isGivenKind(T_STRING)) {
$index = $tokens->getPrevMeaningfulToken($index);
}
return $tokens[$index]->equalsAny(['(', ',', [T_NS_SEPARATOR], [CT::T_NULLABLE_TYPE]]);
}
}
PK qR7Ph h ) src/Tokenizer/Analyzer/SwitchAnalyzer.phpnu ٘
* 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\CaseAnalysis;
use PhpCsFixer\Tokenizer\Analyzer\Analysis\SwitchAnalysis;
use PhpCsFixer\Tokenizer\Tokens;
/**
* @author Kuba Werłos
*
* @internal
*/
final class SwitchAnalyzer
{
/**
* @param int $switchIndex
*
* @return SwitchAnalysis
*/
public function getSwitchAnalysis(Tokens $tokens, $switchIndex)
{
if (!$tokens[$switchIndex]->isGivenKind(T_SWITCH)) {
throw new \InvalidArgumentException(sprintf('Index %d is not "switch".', $switchIndex));
}
$casesStartIndex = $this->getCasesStart($tokens, $switchIndex);
$casesEndIndex = $this->getCasesEnd($tokens, $casesStartIndex);
$cases = [];
$index = $casesStartIndex;
while ($index < $casesEndIndex) {
$index = $this->getNextSameLevelToken($tokens, $index);
if (!$tokens[$index]->isGivenKind([T_CASE, T_DEFAULT])) {
continue;
}
$caseAnalysis = $this->getCaseAnalysis($tokens, $index);
$cases[] = $caseAnalysis;
}
return new SwitchAnalysis($casesStartIndex, $casesEndIndex, $cases);
}
/**
* @param int $switchIndex
*
* @return int
*/
private function getCasesStart(Tokens $tokens, $switchIndex)
{
/** @var int $parenthesisStartIndex */
$parenthesisStartIndex = $tokens->getNextMeaningfulToken($switchIndex);
$parenthesisEndIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $parenthesisStartIndex);
$casesStartIndex = $tokens->getNextMeaningfulToken($parenthesisEndIndex);
\assert(\is_int($casesStartIndex));
return $casesStartIndex;
}
/**
* @param int $casesStartIndex
*
* @return int
*/
private function getCasesEnd(Tokens $tokens, $casesStartIndex)
{
if ($tokens[$casesStartIndex]->equals('{')) {
return $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $casesStartIndex);
}
$index = $casesStartIndex;
while ($index < $tokens->count()) {
$index = $this->getNextSameLevelToken($tokens, $index);
if ($tokens[$index]->isGivenKind(T_ENDSWITCH)) {
break;
}
}
$afterEndswitchIndex = $tokens->getNextMeaningfulToken($index);
$afterEndswitchToken = $tokens[$afterEndswitchIndex];
return $afterEndswitchToken->equalsAny([';', [T_CLOSE_TAG]]) ? $afterEndswitchIndex : $index;
}
/**
* @param int $index
*
* @return CaseAnalysis
*/
private function getCaseAnalysis(Tokens $tokens, $index)
{
while ($index < $tokens->count()) {
$index = $this->getNextSameLevelToken($tokens, $index);
if ($tokens[$index]->equalsAny([':', ';'])) {
break;
}
}
return new CaseAnalysis($index);
}
/**
* @param int $index
*
* @return int
*/
private function getNextSameLevelToken(Tokens $tokens, $index)
{
$index = $tokens->getNextMeaningfulToken($index);
if ($tokens[$index]->isGivenKind(T_SWITCH)) {
return (new self())->getSwitchAnalysis($tokens, $index)->getCasesEnd();
}
/** @var null|array{isStart: bool, type: int} $blockType */
$blockType = Tokens::detectBlockType($tokens[$index]);
if (null !== $blockType && $blockType['isStart']) {
return $tokens->findBlockEnd($blockType['type'], $index) + 1;
}
return $index;
}
}
PK qRba 0 src/Tokenizer/Analyzer/Analysis/CaseAnalysis.phpnu ٘
* 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;
/**
* @author Kuba Werłos
*
* @internal
*/
final class CaseAnalysis
{
/**
* @var int
*/
private $colonIndex;
/**
* @param int $colonIndex
*/
public function __construct($colonIndex)
{
$this->colonIndex = $colonIndex;
}
/**
* @return int
*/
public function getColonIndex()
{
return $this->colonIndex;
}
}
PK qRPq > src/Tokenizer/Analyzer/Analysis/StartEndTokenAwareAnalysis.phpnu ٘
* 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 qR@K K 8 src/Tokenizer/Analyzer/Analysis/NamespaceUseAnalysis.phpnu ٘
* 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 int
*/
public function getType()
{
return $this->type;
}
/**
* @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 qR@) 4 src/Tokenizer/Analyzer/Analysis/ArgumentAnalysis.phpnu ٘
* 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
*/
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 qRQ\ \ 0 src/Tokenizer/Analyzer/Analysis/TypeAnalysis.phpnu ٘
* 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;
/**
* @var bool
*/
private $nullable;
/**
* @param string $name
* @param int $startIndex
* @param int $endIndex
*/
public function __construct($name, $startIndex, $endIndex)
{
$this->name = $name;
$this->nullable = false;
if (0 === strpos($name, '?')) {
$this->name = substr($name, 1);
$this->nullable = true;
}
$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);
}
/**
* @return bool
*/
public function isNullable()
{
return $this->nullable;
}
}
PK qR@)U 5 src/Tokenizer/Analyzer/Analysis/NamespaceAnalysis.phpnu ٘
* 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 qR& 2 src/Tokenizer/Analyzer/Analysis/SwitchAnalysis.phpnu ٘
* 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;
/**
* @author Kuba Werłos
*
* @internal
*/
final class SwitchAnalysis
{
/**
* @var int
*/
private $casesStart;
/**
* @var int
*/
private $casesEnd;
/**
* @var CaseAnalysis[]
*/
private $cases = [];
/**
* @param int $casesStart
* @param int $casesEnd
* @param CaseAnalysis[] $cases
*/
public function __construct($casesStart, $casesEnd, array $cases)
{
$this->casesStart = $casesStart;
$this->casesEnd = $casesEnd;
$this->cases = $cases;
}
/**
* @return int
*/
public function getCasesStart()
{
return $this->casesStart;
}
/**
* @return int
*/
public function getCasesEnd()
{
return $this->casesEnd;
}
/**
* @return CaseAnalysis[]
*/
public function getCases()
{
return $this->cases;
}
}
PK qRv7k
- src/Tokenizer/Analyzer/NamespacesAnalyzer.phpnu ٘
* 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
{
/**
* @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;
}
/**
* @param int $index
*
* @return NamespaceAnalysis
*/
public function getNamespaceAt(Tokens $tokens, $index)
{
if (!$tokens->offsetExists($index)) {
throw new \InvalidArgumentException("Token index {$index} does not exist.");
}
foreach ($this->getDeclarations($tokens) as $namespace) {
if ($namespace->getScopeStartIndex() <= $index && $namespace->getScopeEndIndex() >= $index) {
return $namespace;
}
}
throw new \LogicException("Unable to get the namespace at index {$index}.");
}
}
PK qR2 ) src/Tokenizer/Analyzer/BlocksAnalyzer.phpnu ٘
* 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\Token;
use PhpCsFixer\Tokenizer\Tokens;
/**
* @author Kuba Werłos
*
* @internal
*/
final class BlocksAnalyzer
{
/**
* @param null|int $openIndex
* @param null|int $closeIndex
*
* @return bool
*/
public function isBlock(Tokens $tokens, $openIndex, $closeIndex)
{
if (null === $openIndex || null === $closeIndex) {
return false;
}
if (!$tokens->offsetExists($openIndex)) {
return false;
}
if (!$tokens->offsetExists($closeIndex)) {
return false;
}
$blockType = $this->getBlockType($tokens[$openIndex]);
if (null === $blockType) {
return false;
}
return $closeIndex === $tokens->findBlockEnd($blockType, $openIndex);
}
/**
* @return null|int
*/
private function getBlockType(Token $token)
{
foreach (Tokens::getBlockEdgeDefinitions() as $blockType => $definition) {
if ($token->equals($definition['start'])) {
return $blockType;
}
}
return null;
}
}
PK qRp % src/Tokenizer/AbstractTransformer.phpnu ٘
* 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;
}
/**
* {@inheritdoc}
*/
abstract public function getCustomTokens();
}
PK qR> : src/Tokenizer/Generator/NamespacedStringTokenGenerator.phpnu ٘
* 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 qRmX
src/Tokenizer/Transformers.phpnu ٘
* 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;
use Symfony\Component\Finder\SplFileInfo;
/**
* 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()); // TODO: update to use spaceship operator (PHP 7.0 required)
});
}
/**
* @return Transformers
*/
public static function createSingleton()
{
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()
{
/** @var SplFileInfo $file */
foreach (Finder::create()->files()->in(__DIR__.'/Transformer') as $file) {
$relativeNamespace = $file->getRelativePath();
$class = __NAMESPACE__.'\\Transformer\\'.($relativeNamespace ? $relativeNamespace.'\\' : '').$file->getBasename('.php');
yield new $class();
}
}
}
PK qRHs &