From 7c476192c47671ec35855c271e84e9d1d77c7c44 Mon Sep 17 00:00:00 2001 From: SpacePossum Date: Mon, 4 Jan 2021 14:59:03 +0100 Subject: [PATCH 01/22] TypeAlternationTransformer - T_FN support --- src/Tokenizer/Analyzer/ArgumentsAnalyzer.php | 7 +++++++ src/Tokenizer/Analyzer/FunctionsAnalyzer.php | 4 +++- .../TypeAlternationTransformer.php | 6 ++++++ .../Analyzer/FunctionsAnalyzerTest.php | 1 + .../TypeAlternationTransformerTest.php | 19 ++++++++++++++++++- 5 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/Tokenizer/Analyzer/ArgumentsAnalyzer.php b/src/Tokenizer/Analyzer/ArgumentsAnalyzer.php index d463e8931e5..0904a04fda4 100644 --- a/src/Tokenizer/Analyzer/ArgumentsAnalyzer.php +++ b/src/Tokenizer/Analyzer/ArgumentsAnalyzer.php @@ -54,12 +54,14 @@ 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]; @@ -106,11 +108,14 @@ public function getArgumentInfo(Tokens $tokens, $argumentStart, $argumentEnd) ]; $sawName = false; + for ($index = $argumentStart; $index <= $argumentEnd; ++$index) { $token = $tokens[$index]; + if ($token->isComment() || $token->isWhitespace() || $token->isGivenKind(T_ELLIPSIS) || $token->equals('&')) { continue; } + if ($token->isGivenKind(T_VARIABLE)) { $sawName = true; $info['name_index'] = $index; @@ -118,9 +123,11 @@ public function getArgumentInfo(Tokens $tokens, $argumentStart, $argumentEnd) continue; } + if ($token->equals('=')) { continue; } + if ($sawName) { $info['default'] .= $token->getContent(); } else { diff --git a/src/Tokenizer/Analyzer/FunctionsAnalyzer.php b/src/Tokenizer/Analyzer/FunctionsAnalyzer.php index 463e452e895..8a5b74c6ea7 100644 --- a/src/Tokenizer/Analyzer/FunctionsAnalyzer.php +++ b/src/Tokenizer/Analyzer/FunctionsAnalyzer.php @@ -153,7 +153,8 @@ public function getFunctionReturnType(Tokens $tokens, $methodIndex) $argumentsStart = $tokens->getNextTokenOfKind($methodIndex, ['(']); $argumentsEnd = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $argumentsStart); $typeColonIndex = $tokens->getNextMeaningfulToken($argumentsEnd); - if (':' !== $tokens[$typeColonIndex]->getContent()) { + + if (!$tokens[$typeColonIndex]->isGivenKind(CT::T_TYPE_COLON)) { return null; } @@ -161,6 +162,7 @@ public function getFunctionReturnType(Tokens $tokens, $methodIndex) $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; diff --git a/src/Tokenizer/Transformer/TypeAlternationTransformer.php b/src/Tokenizer/Transformer/TypeAlternationTransformer.php index dde9b3c41c9..2a57f15b3da 100644 --- a/src/Tokenizer/Transformer/TypeAlternationTransformer.php +++ b/src/Tokenizer/Transformer/TypeAlternationTransformer.php @@ -101,6 +101,12 @@ public function process(Tokens $tokens, Token $token, $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) diff --git a/tests/Tokenizer/Analyzer/FunctionsAnalyzerTest.php b/tests/Tokenizer/Analyzer/FunctionsAnalyzerTest.php index b33a19dc629..f5f08469cfe 100644 --- a/tests/Tokenizer/Analyzer/FunctionsAnalyzerTest.php +++ b/tests/Tokenizer/Analyzer/FunctionsAnalyzerTest.php @@ -663,6 +663,7 @@ public function testFunctionReturnTypeInfoPhp74($code, $methodIndex, $expected) public function provideFunctionsWithReturnTypePhp74Cases() { yield [' null;', 1, null]; + yield [' null;', 1, null]; yield [' null;', 1, new TypeAnalysis('array', 7, 7)]; yield [' null;', 1, new TypeAnalysis('\Foo\Bar', 7, 10)]; yield [' null;', 1, new TypeAnalysis('array', 8, 8)]; diff --git a/tests/Tokenizer/Transformer/TypeAlternationTransformerTest.php b/tests/Tokenizer/Transformer/TypeAlternationTransformerTest.php index 5cb187d4572..7d35cb71f9c 100644 --- a/tests/Tokenizer/Transformer/TypeAlternationTransformerTest.php +++ b/tests/Tokenizer/Transformer/TypeAlternationTransformerTest.php @@ -96,12 +96,29 @@ public function testProcess80($source, array $expectedTokens) public function provideProcess80Cases() { + yield 'arrow function' => [ + ' $item * 2;', + [ + 8 => CT::T_TYPE_ALTERNATION, + 16 => CT::T_TYPE_ALTERNATION, + ], + ]; + yield 'static function' => [' CT::T_TYPE_ALTERNATION, 13 => CT::T_TYPE_ALTERNATION, + 20 => CT::T_TYPE_ALTERNATION, + ], + ]; + + yield 'static function with use' => [' CT::T_TYPE_ALTERNATION, ], ]; From 26657cb86de19efbec2396228d1cc149decc9dd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kuba=20Wer=C5=82os?= Date: Tue, 12 Jan 2021 22:39:20 +0100 Subject: [PATCH 02/22] PhpUnitTestCaseStaticMethodCallsFixer - fix for abstract static method --- .../PhpUnitTestCaseStaticMethodCallsFixer.php | 6 ++++-- .../PhpUnitTestCaseStaticMethodCallsFixerTest.php | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/Fixer/PhpUnit/PhpUnitTestCaseStaticMethodCallsFixer.php b/src/Fixer/PhpUnit/PhpUnitTestCaseStaticMethodCallsFixer.php index 343582c91be..605dceee33c 100644 --- a/src/Fixer/PhpUnit/PhpUnitTestCaseStaticMethodCallsFixer.php +++ b/src/Fixer/PhpUnit/PhpUnitTestCaseStaticMethodCallsFixer.php @@ -473,8 +473,10 @@ private function needsConversion(Tokens $tokens, $index, $referenceIndex, $callT */ private function findEndOfNextBlock(Tokens $tokens, $index) { - $index = $tokens->getNextTokenOfKind($index, ['{']); + $nextIndex = $tokens->getNextTokenOfKind($index, [';', '{']); - return $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $index); + return $tokens[$nextIndex]->equals('{') + ? $tokens->findBlockEnd(Tokens::BLOCK_TYPE_CURLY_BRACE, $nextIndex) + : $nextIndex; } } diff --git a/tests/Fixer/PhpUnit/PhpUnitTestCaseStaticMethodCallsFixerTest.php b/tests/Fixer/PhpUnit/PhpUnitTestCaseStaticMethodCallsFixerTest.php index 4b178326cbc..2a6ce25485f 100644 --- a/tests/Fixer/PhpUnit/PhpUnitTestCaseStaticMethodCallsFixerTest.php +++ b/tests/Fixer/PhpUnit/PhpUnitTestCaseStaticMethodCallsFixerTest.php @@ -466,6 +466,20 @@ public function foo() EOF , ], + 'do not crash on abstract static function' => [ + <<<'EOF' + PhpUnitTestCaseStaticMethodCallsFixer::CALL_TYPE_THIS, + ], + ], ]; } From 29ab72aa3fdff154d0e5626a09d6a952dc52aa3e Mon Sep 17 00:00:00 2001 From: Dariusz Ruminski Date: Tue, 5 Jan 2021 23:11:25 +0100 Subject: [PATCH 03/22] Forbid execution under PHP 8.0.0 --- .github/workflows/ci.yml | 1 + php-cs-fixer | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c63243e663e..6cc8dd6ea72 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -87,6 +87,7 @@ jobs: tools: flex env: fail-fast: false # disabled as old PHP version cannot run flex + update: ${{ matrix.php-version == '8.0' }} # force update to 8.0.1+, ref https://github.com/shivammathur/setup-php/issues/394#issuecomment-760461251 - name: Get Composer cache directory id: composer-cache diff --git a/php-cs-fixer b/php-cs-fixer index 25ee66da98f..426637bdcc5 100755 --- a/php-cs-fixer +++ b/php-cs-fixer @@ -25,6 +25,14 @@ if (defined('HHVM_VERSION_ID')) { } } elseif (!defined('PHP_VERSION_ID') || \PHP_VERSION_ID < 50600 || \PHP_VERSION_ID >= 70500) { fwrite(STDERR, "PHP needs to be a minimum version of PHP 5.6.0 and maximum version of PHP 7.4.*.\n"); + fwrite(STDERR, 'Current PHP version: '.PHP_VERSION.".\n"); + + if (defined('PHP_VERSION_ID') && \PHP_VERSION_ID === 80000) { + fwrite(STDERR, "PHP CS Fixer is not able run on PHP 8.0.0 due to bug in PHP tokenizer (https://bugs.php.net/bug.php?id=80462).\n"); + fwrite(STDERR, "Update PHP version to unblock execution.\n"); + + exit(1); + } if (getenv('PHP_CS_FIXER_IGNORE_ENV')) { fwrite(STDERR, "Ignoring environment requirements because `PHP_CS_FIXER_IGNORE_ENV` is set. Execution may be unstable.\n"); From 486254000b5d4cb9e770eb6d488e162fb885b295 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20Deruss=C3=A9?= Date: Tue, 15 Dec 2020 11:02:28 +0100 Subject: [PATCH 04/22] Don't allow unserializing classes with a destructor --- src/Cache/FileCacheManager.php | 20 ++++++++++++++++++++ src/Console/Output/ProcessOutput.php | 20 ++++++++++++++++++++ src/FileRemoval.php | 20 ++++++++++++++++++++ src/Linter/ProcessLinter.php | 20 ++++++++++++++++++++ 4 files changed, 80 insertions(+) diff --git a/src/Cache/FileCacheManager.php b/src/Cache/FileCacheManager.php index 6d76a888c38..e56a37138d6 100644 --- a/src/Cache/FileCacheManager.php +++ b/src/Cache/FileCacheManager.php @@ -77,6 +77,26 @@ public function __destruct() $this->writeCache(); } + /** + * This class is not intended to be serialized, + * and cannot be deserialized (see __wakeup method). + */ + public function __sleep() + { + throw new \BadMethodCallException('Cannot serialize '.__CLASS__); + } + + /** + * Disable the deserialization of the class to prevent attacker executing + * code by leveraging the __destruct method. + * + * @see https://owasp.org/www-community/vulnerabilities/PHP_Object_Injection + */ + public function __wakeup() + { + throw new \BadMethodCallException('Cannot unserialize '.__CLASS__); + } + public function needFixing($file, $fileContent) { $file = $this->cacheDirectory->getRelativePathTo($file); diff --git a/src/Console/Output/ProcessOutput.php b/src/Console/Output/ProcessOutput.php index 61b69b9a6af..6251c13abc1 100644 --- a/src/Console/Output/ProcessOutput.php +++ b/src/Console/Output/ProcessOutput.php @@ -91,6 +91,26 @@ public function __destruct() $this->eventDispatcher->removeListener(FixerFileProcessedEvent::NAME, [$this, 'onFixerFileProcessed']); } + /** + * This class is not intended to be serialized, + * and cannot be deserialized (see __wakeup method). + */ + public function __sleep() + { + throw new \BadMethodCallException('Cannot serialize '.__CLASS__); + } + + /** + * Disable the deserialization of the class to prevent attacker executing + * code by leveraging the __destruct method. + * + * @see https://owasp.org/www-community/vulnerabilities/PHP_Object_Injection + */ + public function __wakeup() + { + throw new \BadMethodCallException('Cannot unserialize '.__CLASS__); + } + public function onFixerFileProcessed(FixerFileProcessedEvent $event) { if ( diff --git a/src/FileRemoval.php b/src/FileRemoval.php index 2ba59884116..645c7a8380a 100644 --- a/src/FileRemoval.php +++ b/src/FileRemoval.php @@ -39,6 +39,26 @@ public function __destruct() $this->clean(); } + /** + * This class is not intended to be serialized, + * and cannot be deserialized (see __wakeup method). + */ + public function __sleep() + { + throw new \BadMethodCallException('Cannot serialize '.__CLASS__); + } + + /** + * Disable the deserialization of the class to prevent attacker executing + * code by leveraging the __destruct method. + * + * @see https://owasp.org/www-community/vulnerabilities/PHP_Object_Injection + */ + public function __wakeup() + { + throw new \BadMethodCallException('Cannot unserialize '.__CLASS__); + } + /** * Adds a file to be removed. * diff --git a/src/Linter/ProcessLinter.php b/src/Linter/ProcessLinter.php index 2d46ab9f491..c3cd9461ab3 100644 --- a/src/Linter/ProcessLinter.php +++ b/src/Linter/ProcessLinter.php @@ -83,6 +83,26 @@ public function __destruct() } } + /** + * This class is not intended to be serialized, + * and cannot be deserialized (see __wakeup method). + */ + public function __sleep() + { + throw new \BadMethodCallException('Cannot serialize '.__CLASS__); + } + + /** + * Disable the deserialization of the class to prevent attacker executing + * code by leveraging the __destruct method. + * + * @see https://owasp.org/www-community/vulnerabilities/PHP_Object_Injection + */ + public function __wakeup() + { + throw new \BadMethodCallException('Cannot unserialize '.__CLASS__); + } + /** * {@inheritdoc} */ From 3adc81b7b05408e84fa55b33fab995596fa554b4 Mon Sep 17 00:00:00 2001 From: Julien Falque Date: Mon, 2 Nov 2020 20:51:41 +0100 Subject: [PATCH 05/22] DX: Add Docker dev setup --- .gitattributes | 3 ++ .gitignore | 1 + CONTRIBUTING.md | 46 +++++++++++++++++++++++++++++++ docker-compose.override.yaml.dist | 25 +++++++++++++++++ docker-compose.yaml | 29 +++++++++++++++++++ docker/php-5.6/Dockerfile | 27 ++++++++++++++++++ docker/php-5.6/xdebug.ini | 5 ++++ docker/php-7.0/Dockerfile | 27 ++++++++++++++++++ docker/php-7.0/xdebug.ini | 5 ++++ docker/php-7.1/Dockerfile | 27 ++++++++++++++++++ docker/php-7.1/xdebug.ini | 5 ++++ docker/php-7.2/Dockerfile | 27 ++++++++++++++++++ docker/php-7.2/xdebug.ini | 5 ++++ docker/php-7.3/Dockerfile | 27 ++++++++++++++++++ docker/php-7.3/xdebug.ini | 4 +++ docker/php-7.4/Dockerfile | 27 ++++++++++++++++++ docker/php-7.4/xdebug.ini | 4 +++ docker/php-8.0/Dockerfile | 27 ++++++++++++++++++ docker/php-8.0/xdebug.ini | 4 +++ 19 files changed, 325 insertions(+) create mode 100644 docker-compose.override.yaml.dist create mode 100644 docker-compose.yaml create mode 100644 docker/php-5.6/Dockerfile create mode 100644 docker/php-5.6/xdebug.ini create mode 100644 docker/php-7.0/Dockerfile create mode 100644 docker/php-7.0/xdebug.ini create mode 100644 docker/php-7.1/Dockerfile create mode 100644 docker/php-7.1/xdebug.ini create mode 100644 docker/php-7.2/Dockerfile create mode 100644 docker/php-7.2/xdebug.ini create mode 100644 docker/php-7.3/Dockerfile create mode 100644 docker/php-7.3/xdebug.ini create mode 100644 docker/php-7.4/Dockerfile create mode 100644 docker/php-7.4/xdebug.ini create mode 100644 docker/php-8.0/Dockerfile create mode 100644 docker/php-8.0/xdebug.ini diff --git a/.gitattributes b/.gitattributes index e0e6b91774b..0e90da01c6a 100644 --- a/.gitattributes +++ b/.gitattributes @@ -13,6 +13,9 @@ /benchmark.sh export-ignore /box.json.dist export-ignore /dev-tools/ export-ignore +/docker/ export-ignore +/docker-compose.yaml export-ignore +/docker-compose.override.yaml.dist export-ignore /phpmd.xml export-ignore /phpstan.neon export-ignore /phpunit.xml.dist export-ignore diff --git a/.gitignore b/.gitignore index b66feada4ca..1acd5822c1e 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ /.phpunit.result.cache /box.json /composer.lock +/docker-compose.override.yaml /php-cs-fixer.phar /phpunit.xml /vendor/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 695d56112e3..a3eb381f275 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -34,6 +34,52 @@ Symfony projects for instance). * 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: diff --git a/docker-compose.override.yaml.dist b/docker-compose.override.yaml.dist new file mode 100644 index 00000000000..a593f888536 --- /dev/null +++ b/docker-compose.override.yaml.dist @@ -0,0 +1,25 @@ +version: '3.8' + +services: + php-5.6: &php + build: + args: + DOCKER_USER_ID: 1000 # replace with your user id (most likely 1000) + DOCKER_GROUP_ID: 1000 # replace with your group id (most likely 1000) + user: 1000:1000 # replace with your user and group ids (most likely 1000:1000) + extra_hosts: + # Required for Docker Linux until natively supported. + # See https://github.com/docker/for-linux/issues/264 + host.docker.internal: 172.17.0.1 + php-7.0: + <<: *php + php-7.1: + <<: *php + php-7.2: + <<: *php + php-7.3: + <<: *php + php-7.4: + <<: *php + php-8.0: + <<: *php diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 00000000000..b16e3ebf0d0 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,29 @@ +version: '3.8' + +services: + php-5.6: &php + build: docker/php-5.6 + working_dir: /app + volumes: + - .:/app + environment: + PHP_IDE_CONFIG: serverName=php-cs-fixer + PHP_CS_FIXER_ALLOW_XDEBUG: 1 + php-7.0: + <<: *php + build: docker/php-7.0 + php-7.1: + <<: *php + build: docker/php-7.1 + php-7.2: + <<: *php + build: docker/php-7.2 + php-7.3: + <<: *php + build: docker/php-7.3 + php-7.4: + <<: *php + build: docker/php-7.4 + php-8.0: + <<: *php + build: docker/php-8.0 diff --git a/docker/php-5.6/Dockerfile b/docker/php-5.6/Dockerfile new file mode 100644 index 00000000000..7c194f0173e --- /dev/null +++ b/docker/php-5.6/Dockerfile @@ -0,0 +1,27 @@ +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 diff --git a/docker/php-5.6/xdebug.ini b/docker/php-5.6/xdebug.ini new file mode 100644 index 00000000000..8ea67c8bebd --- /dev/null +++ b/docker/php-5.6/xdebug.ini @@ -0,0 +1,5 @@ +;zend_extension=xdebug.so +xdebug.remote_autostart=1 +xdebug.remote_enable=1 +xdebug.remote_host=host.docker.internal +xdebug.remote_port=9003 diff --git a/docker/php-7.0/Dockerfile b/docker/php-7.0/Dockerfile new file mode 100644 index 00000000000..b0b82ecd488 --- /dev/null +++ b/docker/php-7.0/Dockerfile @@ -0,0 +1,27 @@ +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 diff --git a/docker/php-7.0/xdebug.ini b/docker/php-7.0/xdebug.ini new file mode 100644 index 00000000000..8ea67c8bebd --- /dev/null +++ b/docker/php-7.0/xdebug.ini @@ -0,0 +1,5 @@ +;zend_extension=xdebug.so +xdebug.remote_autostart=1 +xdebug.remote_enable=1 +xdebug.remote_host=host.docker.internal +xdebug.remote_port=9003 diff --git a/docker/php-7.1/Dockerfile b/docker/php-7.1/Dockerfile new file mode 100644 index 00000000000..326738295ec --- /dev/null +++ b/docker/php-7.1/Dockerfile @@ -0,0 +1,27 @@ +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 diff --git a/docker/php-7.1/xdebug.ini b/docker/php-7.1/xdebug.ini new file mode 100644 index 00000000000..8ea67c8bebd --- /dev/null +++ b/docker/php-7.1/xdebug.ini @@ -0,0 +1,5 @@ +;zend_extension=xdebug.so +xdebug.remote_autostart=1 +xdebug.remote_enable=1 +xdebug.remote_host=host.docker.internal +xdebug.remote_port=9003 diff --git a/docker/php-7.2/Dockerfile b/docker/php-7.2/Dockerfile new file mode 100644 index 00000000000..8d3672c3cae --- /dev/null +++ b/docker/php-7.2/Dockerfile @@ -0,0 +1,27 @@ +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 diff --git a/docker/php-7.2/xdebug.ini b/docker/php-7.2/xdebug.ini new file mode 100644 index 00000000000..8ea67c8bebd --- /dev/null +++ b/docker/php-7.2/xdebug.ini @@ -0,0 +1,5 @@ +;zend_extension=xdebug.so +xdebug.remote_autostart=1 +xdebug.remote_enable=1 +xdebug.remote_host=host.docker.internal +xdebug.remote_port=9003 diff --git a/docker/php-7.3/Dockerfile b/docker/php-7.3/Dockerfile new file mode 100644 index 00000000000..512da58aa88 --- /dev/null +++ b/docker/php-7.3/Dockerfile @@ -0,0 +1,27 @@ +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 diff --git a/docker/php-7.3/xdebug.ini b/docker/php-7.3/xdebug.ini new file mode 100644 index 00000000000..cd4b802e88a --- /dev/null +++ b/docker/php-7.3/xdebug.ini @@ -0,0 +1,4 @@ +;zend_extension=xdebug.so +xdebug.client_host=host.docker.internal +xdebug.mode=debug +xdebug.start_with_request=yes diff --git a/docker/php-7.4/Dockerfile b/docker/php-7.4/Dockerfile new file mode 100644 index 00000000000..e6a4ab7aa62 --- /dev/null +++ b/docker/php-7.4/Dockerfile @@ -0,0 +1,27 @@ +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 diff --git a/docker/php-7.4/xdebug.ini b/docker/php-7.4/xdebug.ini new file mode 100644 index 00000000000..cd4b802e88a --- /dev/null +++ b/docker/php-7.4/xdebug.ini @@ -0,0 +1,4 @@ +;zend_extension=xdebug.so +xdebug.client_host=host.docker.internal +xdebug.mode=debug +xdebug.start_with_request=yes diff --git a/docker/php-8.0/Dockerfile b/docker/php-8.0/Dockerfile new file mode 100644 index 00000000000..e49c999a5dc --- /dev/null +++ b/docker/php-8.0/Dockerfile @@ -0,0 +1,27 @@ +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 diff --git a/docker/php-8.0/xdebug.ini b/docker/php-8.0/xdebug.ini new file mode 100644 index 00000000000..cd4b802e88a --- /dev/null +++ b/docker/php-8.0/xdebug.ini @@ -0,0 +1,4 @@ +;zend_extension=xdebug.so +xdebug.client_host=host.docker.internal +xdebug.mode=debug +xdebug.start_with_request=yes From 6081f933ffb3512ae5c2cbbf46f1f216aae76dcb Mon Sep 17 00:00:00 2001 From: Dariusz Ruminski Date: Mon, 18 Jan 2021 00:36:15 +0100 Subject: [PATCH 06/22] VisibilityRequiredFixer - support type alternation for properties --- .../ClassNotation/VisibilityRequiredFixer.php | 2 +- .../VisibilityRequiredFixerTest.php | 23 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/Fixer/ClassNotation/VisibilityRequiredFixer.php b/src/Fixer/ClassNotation/VisibilityRequiredFixer.php index 9ea690e7015..fe68614fa46 100644 --- a/src/Fixer/ClassNotation/VisibilityRequiredFixer.php +++ b/src/Fixer/ClassNotation/VisibilityRequiredFixer.php @@ -97,7 +97,7 @@ protected function createConfigurationDefinition() protected function applyFix(\SplFileInfo $file, Tokens $tokens) { $tokensAnalyzer = new TokensAnalyzer($tokens); - $propertyTypeDeclarationKinds = [T_STRING, T_NS_SEPARATOR, CT::T_NULLABLE_TYPE, CT::T_ARRAY_TYPEHINT]; + $propertyTypeDeclarationKinds = [T_STRING, T_NS_SEPARATOR, CT::T_NULLABLE_TYPE, CT::T_ARRAY_TYPEHINT, CT::T_TYPE_ALTERNATION]; foreach (array_reverse($tokensAnalyzer->getClassyElements(), true) as $index => $element) { if (!\in_array($element['type'], $this->configuration['elements'], true)) { diff --git a/tests/Fixer/ClassNotation/VisibilityRequiredFixerTest.php b/tests/Fixer/ClassNotation/VisibilityRequiredFixerTest.php index 298c4580153..ae9ca1defe2 100644 --- a/tests/Fixer/ClassNotation/VisibilityRequiredFixerTest.php +++ b/tests/Fixer/ClassNotation/VisibilityRequiredFixerTest.php @@ -827,4 +827,27 @@ public function provideFix74Cases() 'doTest($expected, $input); + } + + public function provideFix80Cases() + { + yield [ + ' Date: Mon, 28 Dec 2020 18:17:31 +0100 Subject: [PATCH 07/22] Add PHP8 integration test --- phpunit.xml.dist | 8 +- tests/Fixtures/Integration/misc/PHP8_0.test | 361 ++++++++++++++++++++ 2 files changed, 365 insertions(+), 4 deletions(-) create mode 100644 tests/Fixtures/Integration/misc/PHP8_0.test diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 4ee0a6a2d6f..7b829a2efcb 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -15,14 +15,14 @@ convertErrorsToExceptions="true" convertNoticesToExceptions="true" convertWarningsToExceptions="true" - defaultTimeLimit="3" + defaultTimeLimit="10" enforceTimeLimit="true" failOnWarning="true" processIsolation="false" stopOnFailure="false" - timeoutForSmallTests="3" - timeoutForMediumTests="6" - timeoutForLargeTests="9" + timeoutForSmallTests="10" + timeoutForMediumTests="20" + timeoutForLargeTests="30" verbose="false" > diff --git a/tests/Fixtures/Integration/misc/PHP8_0.test b/tests/Fixtures/Integration/misc/PHP8_0.test new file mode 100644 index 00000000000..7acc7fb43c3 --- /dev/null +++ b/tests/Fixtures/Integration/misc/PHP8_0.test @@ -0,0 +1,361 @@ +--TEST-- +PHP 8.0 test. +--RULESET-- +{ + "@PHP80Migration": true, + "@PHP80Migration:risky": true, + "@PhpCsFixer": true, + "@PhpCsFixer:risky": true, + "phpdoc_to_return_type": true +} +--REQUIREMENTS-- +{"php": 80000} +--EXPECT-- +user?->getAddress()?->country; + +// https://wiki.php.net/rfc/attributes_v2 +// https://wiki.php.net/rfc/attribute_amendments +// https://wiki.php.net/rfc/shorter_attribute_syntax +// https://wiki.php.net/rfc/shorter_attribute_syntax_change +#[MyAttribute] +#[\MyExample\MyAttribute] +#[MyAttribute(1234)] +#[MyAttribute(value: 1234)] +#[MyAttribute(MyAttribute::VALUE)] +#[MyAttribute(['key' => 'value'])] +#[MyAttribute(100 + 200)] +#[Attribute(Attribute::TARGET_METHOD | Attribute::TARGET_FUNCTION)] +#[MyAttribute(1234), MyAttribute(5678)] +class PostsController +{ + #[Route('/api/posts/{id}', methods: ['GET'])] + public function get($id): void + { + } +} + +$object = new #[ExampleAttribute] class() +{ + #[ExampleAttribute] + public function foo(#[ExampleAttribute] $bar): void + { + } +}; + +// https://wiki.php.net/rfc/union_types_v2 +class Number +{ + private int | float | null $number; + + public function setNumber(int | float $number): void + { + $this->number = $number; + } + + public function getNumber(): int | float | null + { + return $this->number; + } +} + +// https://wiki.php.net/rfc/class_name_literal_on_object +// nothing we can do + +// https://wiki.php.net/rfc/static_return_type +class T +{ + public function Foo(object $A): ?static + { + } + + /** @return static */ + public function something(): static + { + } +} + +// https://wiki.php.net/rfc/variable_syntax_tweaks +// nothing we can do + +// https://wiki.php.net/rfc/trailing_comma_in_parameter_list +function foo(string $param = null, ): void +{ +} +call_user_func('foo', 1, ); + +// https://wiki.php.net/rfc/trailing_comma_in_closure_use_list +$foo1a = function (): void {}; +$foo1b = function (): void {}; +$foo2 = function () use ($bar, $baz, ): void { echo $bar.$baz; }; + +// https://wiki.php.net/rfc/mixed_type_v2 +class T_Mixed +{ + public function Foo(mixed $a): mixed + { + } +} + +// https://wiki.php.net/rfc/throw_expression +if (1) { + foo() ?? throw new \Exception(); + $a = $condition && throw new Exception(); + $callable = fn () => throw new Exception(); + $value = $falsableValue ?: throw new InvalidArgumentException(); +} + +// https://wiki.php.net/rfc/non-capturing_catches +// TODO: https://github.com/FriendsOfPHP/PHP-CS-Fixer/pull/5175 - when implementing new rule for fixing such cases, add exception variable in --INPUT-- section +try { + foo(); +} catch (Exception) { + // ignore exception +} + +// https://wiki.php.net/rfc/constructor_promotion +class Point +{ + public function __construct( + public float $x = 0.0, + protected float $y = 0.0, + private float $z = 0.0, + private ?string $desc = null, + ) { + } +} + +// https://wiki.php.net/rfc/match_expression_v2 +echo match ($x) { + 1, 2 => 'Same for 1 and 2', +}; + +// https://wiki.php.net/rfc/add_str_starts_with_and_ends_with_functions +// no syntax changes + +// https://wiki.php.net/rfc/str_contains +// no syntax changes + +// https://wiki.php.net/rfc/deprecate_curly_braces_array_access +// syntax was deprecated, nothing we can do - a `normalize_index_brace` rule already exists and can be used on PHP <8 (or PHP 8 if errors are ignored) + +// https://wiki.php.net/rfc/concatenation_precedence +// nothing we can do + +// https://wiki.php.net/rfc/inheritance_private_methods +// nothing we can do + +// https://wiki.php.net/rfc/ternary_associativity +// nothing we can do about the warning - a rule can be implemented to run on PHP <8 (or PHP 8 if errors are ignored) + +// https://wiki.php.net/rfc/stringable +// nothing we can do + +// Date: mktime() and gmmktime() now require at least one argument. time() can be used to get the current timestamp. +time(); +time(); +mktime($a); +gmmktime(1, 2, 3, 4, 5, 6); + +// Exif: Removed read_exif_data(). exif_read_data() should be used instead. +exif_read_data($filename, $sections_needed, $sub_arrays, $read_thumbnail); + +// The imap_header() function which is an alias of imap_headerinfo() has been removed. +imap_headerinfo(); + +// parse_str() can no longer be used without specifying a result array. +// nothing we can do + +// Calling implode() with parameters in a reverse order ($pieces, $glue) is no longer supported. +implode('', $pieces); +implode('', $pieces); + +// Mbstring A number of deprecated mbregex aliases have been removed. +// nothing we can do + +// Namespaced names can no longer contain whitespace (nor comments) +// nothing we can do + +--INPUT-- +user?->getAddress()?->country; + +// https://wiki.php.net/rfc/attributes_v2 +// https://wiki.php.net/rfc/attribute_amendments +// https://wiki.php.net/rfc/shorter_attribute_syntax +// https://wiki.php.net/rfc/shorter_attribute_syntax_change +#[MyAttribute] +#[\MyExample\MyAttribute] +#[MyAttribute(1234)] +#[MyAttribute(value: 1234)] +#[MyAttribute(MyAttribute::VALUE)] +#[MyAttribute(array("key" => "value"))] +#[MyAttribute(100 + 200)] +#[Attribute(Attribute::TARGET_METHOD | Attribute::TARGET_FUNCTION)] +#[MyAttribute(1234), MyAttribute(5678)] +class PostsController +{ + #[Route("/api/posts/{id}", methods: ["GET"])] + public function get($id): void + { + } +} + +$object = new #[ExampleAttribute] class () +{ + #[ExampleAttribute] + public function foo(#[ExampleAttribute] $bar): void + { + } +}; + +// https://wiki.php.net/rfc/union_types_v2 +class Number +{ + private int|float|null $number; + + public function setNumber(int|float $number): void + { + $this->number = $number; + } + + public function getNumber(): int|float|null + { + return $this->number; + } +} + +// https://wiki.php.net/rfc/class_name_literal_on_object +// nothing we can do + +// https://wiki.php.net/rfc/static_return_type +class T +{ + public function Foo(object $A): ?StatiC + { + } + + /** @return static */ + public function something() + { + } +} + +// https://wiki.php.net/rfc/variable_syntax_tweaks +// nothing we can do + +// https://wiki.php.net/rfc/trailing_comma_in_parameter_list +function foo(string $param = null, ) {} +call_user_func('foo', 1, ); + +// https://wiki.php.net/rfc/trailing_comma_in_closure_use_list +$foo1a = function() use ($bar,) {}; +$foo1b = function() use ($bar, ) {}; +$foo2 = function() use ($bar,$baz,) { echo $bar.$baz; }; + +// https://wiki.php.net/rfc/mixed_type_v2 +class T_Mixed +{ + public function Foo(Mixed$a): MIXED + { + } +} + +// https://wiki.php.net/rfc/throw_expression +if (1) { + foo() ?? throw new \Exception(); + $a = $condition && throw new Exception(); + $callable = fn() => throw new Exception(); + $value = $falsableValue ?: throw new InvalidArgumentException(); +} + +// https://wiki.php.net/rfc/non-capturing_catches +// TODO: https://github.com/FriendsOfPHP/PHP-CS-Fixer/pull/5175 - when implementing new rule for fixing such cases, add exception variable in --INPUT-- section +try { + foo(); +} catch (Exception) { + // ignore exception +} + +// https://wiki.php.net/rfc/constructor_promotion +class Point +{ + public function __construct( + PUBLIC float $x = 0.0, + Protected float $y = 0.0, + privatE float $z = 0.0, + private ?string $desc = null, + ) { + } +} + +// https://wiki.php.net/rfc/match_expression_v2 +echo match($x) { + 1, 2 => 'Same for 1 and 2', +}; + +// https://wiki.php.net/rfc/add_str_starts_with_and_ends_with_functions +// no syntax changes + +// https://wiki.php.net/rfc/str_contains +// no syntax changes + +// https://wiki.php.net/rfc/deprecate_curly_braces_array_access +// syntax was deprecated, nothing we can do - a `normalize_index_brace` rule already exists and can be used on PHP <8 (or PHP 8 if errors are ignored) + +// https://wiki.php.net/rfc/concatenation_precedence +// nothing we can do + +// https://wiki.php.net/rfc/inheritance_private_methods +// nothing we can do + +// https://wiki.php.net/rfc/ternary_associativity +// nothing we can do about the warning - a rule can be implemented to run on PHP <8 (or PHP 8 if errors are ignored) + +// https://wiki.php.net/rfc/stringable +// nothing we can do + +// Date: mktime() and gmmktime() now require at least one argument. time() can be used to get the current timestamp. +mktime(); +gmmktime(); +mktime($a); +gmmktime(1, 2, 3, 4, 5, 6); + +// Exif: Removed read_exif_data(). exif_read_data() should be used instead. +read_exif_data($filename, $sections_needed, $sub_arrays, $read_thumbnail); + +// The imap_header() function which is an alias of imap_headerinfo() has been removed. +imap_header(); + +// parse_str() can no longer be used without specifying a result array. +// nothing we can do + +// Calling implode() with parameters in a reverse order ($pieces, $glue) is no longer supported. +implode($pieces, ''); +implode($pieces); + +// Mbstring A number of deprecated mbregex aliases have been removed. +// nothing we can do + +// Namespaced names can no longer contain whitespace (nor comments) +// nothing we can do From 6fd0101c214ecaaba4f8e1d82ff87c2f55cf40ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20M=C3=B6ller?= Date: Sat, 19 Dec 2020 08:56:41 +0100 Subject: [PATCH 08/22] PhpUnitMethodCasingFixer - Do not modify class name --- .../PhpUnit/PhpUnitMethodCasingFixer.php | 18 ++++++---- .../PhpUnit/PhpUnitMethodCasingFixerTest.php | 36 +++++++++++++++++++ 2 files changed, 48 insertions(+), 6 deletions(-) diff --git a/src/Fixer/PhpUnit/PhpUnitMethodCasingFixer.php b/src/Fixer/PhpUnit/PhpUnitMethodCasingFixer.php index c12e105a948..e4249180a1a 100644 --- a/src/Fixer/PhpUnit/PhpUnitMethodCasingFixer.php +++ b/src/Fixer/PhpUnit/PhpUnitMethodCasingFixer.php @@ -126,16 +126,22 @@ protected function applyPhpUnitClassFix(Tokens $tokens, $startIndex, $endIndex) */ private function updateMethodCasing($functionName) { + $parts = explode('::', $functionName); + + $functionNamePart = array_pop($parts); + if (self::CAMEL_CASE === $this->configuration['case']) { - $newFunctionName = $functionName; - $newFunctionName = ucwords($newFunctionName, '_'); - $newFunctionName = str_replace('_', '', $newFunctionName); - $newFunctionName = lcfirst($newFunctionName); + $newFunctionNamePart = $functionNamePart; + $newFunctionNamePart = ucwords($newFunctionNamePart, '_'); + $newFunctionNamePart = str_replace('_', '', $newFunctionNamePart); + $newFunctionNamePart = lcfirst($newFunctionNamePart); } else { - $newFunctionName = Utils::camelCaseToUnderscore($functionName); + $newFunctionNamePart = Utils::camelCaseToUnderscore($functionNamePart); } - return $newFunctionName; + $parts[] = $newFunctionNamePart; + + return implode('::', $parts); } /** diff --git a/tests/Fixer/PhpUnit/PhpUnitMethodCasingFixerTest.php b/tests/Fixer/PhpUnit/PhpUnitMethodCasingFixerTest.php index fb732eda8ff..ef4b36dcd53 100644 --- a/tests/Fixer/PhpUnit/PhpUnitMethodCasingFixerTest.php +++ b/tests/Fixer/PhpUnit/PhpUnitMethodCasingFixerTest.php @@ -99,6 +99,42 @@ public function test_my_app () {} public function test_my_app_too() {} }', ], + '@depends annotation with class name in PascalCase' => [ + ' [ + ' [ ' Date: Mon, 18 Jan 2021 03:58:25 +0100 Subject: [PATCH 09/22] DX: FunctionsAnalyzerTest - add missing 7.0 requirement --- .../Analyzer/FunctionsAnalyzerTest.php | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/tests/Tokenizer/Analyzer/FunctionsAnalyzerTest.php b/tests/Tokenizer/Analyzer/FunctionsAnalyzerTest.php index f5f08469cfe..594d3793525 100644 --- a/tests/Tokenizer/Analyzer/FunctionsAnalyzerTest.php +++ b/tests/Tokenizer/Analyzer/FunctionsAnalyzerTest.php @@ -404,7 +404,25 @@ public function testFunctionReturnTypeInfo($code, $methodIndex, $expected) $tokens = Tokens::fromCode($code); $analyzer = new FunctionsAnalyzer(); - static::assertSame(serialize($expected), serialize($analyzer->getFunctionReturnType($tokens, $methodIndex))); + $actual = $analyzer->getFunctionReturnType($tokens, $methodIndex); + static::assertSame(serialize($expected), serialize($actual)); + } + + /** + * @param string $code + * @param int $methodIndex + * @param array $expected + * + * @dataProvider provideFunctionsWithReturnTypePhp70Cases + * @requires PHP 7.0 + */ + public function testFunctionReturnTypeInfoPhp70($code, $methodIndex, $expected) + { + $tokens = Tokens::fromCode($code); + $analyzer = new FunctionsAnalyzer(); + + $actual = $analyzer->getFunctionReturnType($tokens, $methodIndex); + static::assertSame(serialize($expected), serialize($actual)); } public function provideFunctionsWithArgumentsCases() @@ -514,6 +532,10 @@ public function provideFunctionsWithArgumentsCases() public function provideFunctionsWithReturnTypeCases() { yield [' Date: Mon, 18 Jan 2021 04:06:22 +0100 Subject: [PATCH 10/22] prepared the 2.17.4 release --- CHANGELOG.md | 30 ++++++++++++++++++++++++++++++ src/Console/Application.php | 2 +- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d3d62dc709f..31e97e5bc0c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,36 @@ CHANGELOG for PHP CS Fixer This file contains changelogs for stable releases only. +Changelog for v2.17.4 +--------------------- + +* bug #5379 PhpUnitMethodCasingFixer - Do not modify class name (localheinz) +* bug #5404 NullableTypeTransformer - constructor property promotion support (Wirone) +* bug #5433 PhpUnitTestCaseStaticMethodCallsFixer - fix for abstract static method (kubawerlos) +* minor #5234 DX: Add Docker dev setup (julienfalque, keradus) +* minor #5391 PhpdocOrderByValueFixer - Add additional annotations to sort (localheinz) +* minor #5392 PhpdocScalarFixer - Fix description (localheinz) +* minor #5397 NoExtraBlankLinesFixer - PHP8 throw support (SpacePossum) +* minor #5399 Add PHP8 integration test (keradus) +* minor #5405 TypeAlternationTransformer - add support for PHP8 (SpacePossum) +* minor #5406 SingleSpaceAfterConstructFixer - Attributes, comments and PHPDoc support (SpacePossum) +* minor #5407 TokensAnalyzer::getClassyElements - return trait imports (SpacePossum) +* minor #5410 minors (SpacePossum) +* minor #5411 bump year in LICENSE file (SpacePossum) +* minor #5414 TypeAlternationTransformer - T_FN support (SpacePossum) +* minor #5415 Forbid execution under PHP 8.0.0 (keradus) +* minor #5416 Drop Travis CI (keradus) +* minor #5419 CI: separate SCA checks to dedicated jobs (keradus) +* minor #5420 DX: unblock PHPUnit 9.5 (keradus) +* minor #5423 DX: PHPUnit - disable verbose by default (keradus) +* minor #5425 Cleanup 3.0 todos (keradus) +* minor #5427 Plan changing defaults for array_syntax and list_syntax in 3.0 release (keradus) +* minor #5429 DX: Drop speedtrap PHPUnit listener (keradus) +* minor #5432 Don't allow unserializing classes with a destructor (jderusse) +* minor #5435 DX: PHPUnit - groom configuration of time limits (keradus) +* minor #5439 VisibilityRequiredFixer - support type alternation for properties (keradus) +* minor #5442 DX: FunctionsAnalyzerTest - add missing 7.0 requirement (keradus) + Changelog for v2.17.3 --------------------- diff --git a/src/Console/Application.php b/src/Console/Application.php index 63582b8f936..f8800ba2b0f 100644 --- a/src/Console/Application.php +++ b/src/Console/Application.php @@ -34,7 +34,7 @@ */ final class Application extends BaseApplication { - const VERSION = '2.17.4-DEV'; + const VERSION = '2.17.4'; const VERSION_CODENAME = 'Desert Beast'; /** From 51f38fe809da6af624e48318a2e40c6799829769 Mon Sep 17 00:00:00 2001 From: Dariusz Ruminski Date: Mon, 18 Jan 2021 04:08:14 +0100 Subject: [PATCH 11/22] bumped version --- src/Console/Application.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Console/Application.php b/src/Console/Application.php index f8800ba2b0f..29cd7d6de8e 100644 --- a/src/Console/Application.php +++ b/src/Console/Application.php @@ -34,7 +34,7 @@ */ final class Application extends BaseApplication { - const VERSION = '2.17.4'; + const VERSION = '2.17.5-DEV'; const VERSION_CODENAME = 'Desert Beast'; /** From c4c886958ae4dd8ae47be5cd17d09f695e22e02c Mon Sep 17 00:00:00 2001 From: "Alexander M. Turek" Date: Tue, 19 Jan 2021 00:32:55 +0100 Subject: [PATCH 12/22] switch_case_semicolon_to_colon should skip match/default statements --- .../SwitchCaseSemicolonToColonFixer.php | 21 +++++- .../SwitchCaseSemicolonToColonFixerTest.php | 66 +++++++++++++++++++ 2 files changed, 86 insertions(+), 1 deletion(-) diff --git a/src/Fixer/ControlStructure/SwitchCaseSemicolonToColonFixer.php b/src/Fixer/ControlStructure/SwitchCaseSemicolonToColonFixer.php index a7e9682fe0d..e27f97f5d78 100644 --- a/src/Fixer/ControlStructure/SwitchCaseSemicolonToColonFixer.php +++ b/src/Fixer/ControlStructure/SwitchCaseSemicolonToColonFixer.php @@ -71,9 +71,12 @@ public function isCandidate(Tokens $tokens) protected function applyFix(\SplFileInfo $file, Tokens $tokens) { foreach ($tokens as $index => $token) { - if ($token->isGivenKind([T_CASE, T_DEFAULT])) { + if ($token->isGivenKind(T_CASE)) { $this->fixSwitchCase($tokens, $index); } + if ($token->isGivenKind(T_DEFAULT)) { + $this->fixSwitchDefault($tokens, $index); + } } } @@ -110,4 +113,20 @@ protected function fixSwitchCase(Tokens $tokens, $index) $tokens[$index] = new Token(':'); } } + + /** + * @param int $index + */ + protected function fixSwitchDefault(Tokens $tokens, $index) + { + do { + if ($tokens[$index]->equalsAny([':', ';', [T_DOUBLE_ARROW]])) { + break; + } + } while (++$index); + + if ($tokens[$index]->equals(';')) { + $tokens[$index] = new Token(':'); + } + } } diff --git a/tests/Fixer/ControlStructure/SwitchCaseSemicolonToColonFixerTest.php b/tests/Fixer/ControlStructure/SwitchCaseSemicolonToColonFixerTest.php index 93763ed7b16..9fe44e62677 100644 --- a/tests/Fixer/ControlStructure/SwitchCaseSemicolonToColonFixerTest.php +++ b/tests/Fixer/ControlStructure/SwitchCaseSemicolonToColonFixerTest.php @@ -65,6 +65,20 @@ public function provideFixCases() } ', ], + [ + ' "bar"]: + break; + } + ', + ' "bar"]; + break; + } + ', + ], [ 'doTest($expected, $input); + } + + public function provideFix80Cases() + { + return [ + 'Simple match' => [ + ' "foo", + }; + ', + ], + 'Match in switch' => [ + ' "foo", + }; + break; + } + ', + ], + 'Match in case value' => [ + ' "foo", + }: echo "It works!"; + } + ', + ' "foo", + }; echo "It works!"; + } + ', + ], + ]; + } } From 33a07e247ca09ae7c466aaaaab077989bb2d9e79 Mon Sep 17 00:00:00 2001 From: Dariusz Ruminski Date: Tue, 19 Jan 2021 23:59:09 +0100 Subject: [PATCH 13/22] NullableTypeDeclarationForDefaultNullValueFixer - support property promotion via constructor --- src/Tokenizer/Analyzer/ArgumentsAnalyzer.php | 8 ++- ...eclarationForDefaultNullValueFixerTest.php | 18 ++++- .../Analyzer/ArgumentsAnalyzerTest.php | 67 +++++++++++++++++++ 3 files changed, 89 insertions(+), 4 deletions(-) diff --git a/src/Tokenizer/Analyzer/ArgumentsAnalyzer.php b/src/Tokenizer/Analyzer/ArgumentsAnalyzer.php index 0904a04fda4..67a68d35df8 100644 --- a/src/Tokenizer/Analyzer/ArgumentsAnalyzer.php +++ b/src/Tokenizer/Analyzer/ArgumentsAnalyzer.php @@ -14,6 +14,7 @@ use PhpCsFixer\Tokenizer\Analyzer\Analysis\ArgumentAnalysis; use PhpCsFixer\Tokenizer\Analyzer\Analysis\TypeAnalysis; +use PhpCsFixer\Tokenizer\CT; use PhpCsFixer\Tokenizer\Tokens; /** @@ -112,7 +113,12 @@ public function getArgumentInfo(Tokens $tokens, $argumentStart, $argumentEnd) for ($index = $argumentStart; $index <= $argumentEnd; ++$index) { $token = $tokens[$index]; - if ($token->isComment() || $token->isWhitespace() || $token->isGivenKind(T_ELLIPSIS) || $token->equals('&')) { + 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; } diff --git a/tests/Fixer/FunctionNotation/NullableTypeDeclarationForDefaultNullValueFixerTest.php b/tests/Fixer/FunctionNotation/NullableTypeDeclarationForDefaultNullValueFixerTest.php index 581effe8551..b94743d90cb 100644 --- a/tests/Fixer/FunctionNotation/NullableTypeDeclarationForDefaultNullValueFixerTest.php +++ b/tests/Fixer/FunctionNotation/NullableTypeDeclarationForDefaultNullValueFixerTest.php @@ -404,13 +404,13 @@ public function provideFixPhp74Cases() } /** - * @param string $expected - * @param string $input + * @param string $expected + * @param null|string $input * * @dataProvider provideFix80Cases * @requires PHP 8.0 */ - public function testFix80($expected, $input) + public function testFix80($expected, $input = null) { $this->doTest($expected, $input); } @@ -421,5 +421,17 @@ public function provideFix80Cases() 'getArgumentInfo($tokens, $openIndex, $closeIndex)) + ); + } + public function provideArgumentsCases() { return [ @@ -162,6 +182,27 @@ public function provideArgumentsInfoCases() ]; } + public function provideArgumentsInfo80Cases() + { + foreach (['public', 'protected', 'private'] as $visibility) { + yield [ + sprintf(' 3, 5 => 7]], ]; } + + /** + * @param string $code + * @param int $openIndex + * @param int $closeIndex + * + * @requires PHP 8.0 + * @dataProvider provideArguments80Cases + */ + public function testArguments80($code, $openIndex, $closeIndex, array $arguments) + { + $tokens = Tokens::fromCode($code); + $analyzer = new ArgumentsAnalyzer(); + + static::assertSame(\count($arguments), $analyzer->countArguments($tokens, $openIndex, $closeIndex)); + static::assertSame($arguments, $analyzer->getArguments($tokens, $openIndex, $closeIndex)); + } + + public function provideArguments80Cases() + { + return [ + [' 22]], + [' 22]], + [' 22]], + ]; + } } From 5c8d855a27dbd6a94c0b56c43bfa1ae5e3e396fe Mon Sep 17 00:00:00 2001 From: Dariusz Ruminski Date: Mon, 18 Jan 2021 11:48:50 +0100 Subject: [PATCH 14/22] DX: update usage of old TraversableContains in tests --- phpstan.neon | 1 - tests/AutoReview/CiConfigurationTest.php | 36 +++++++++--------------- 2 files changed, 13 insertions(+), 24 deletions(-) diff --git a/phpstan.neon b/phpstan.neon index 65c2ebf59e9..18dac5ccec8 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -14,7 +14,6 @@ parameters: - '/^Return typehint of method PhpCsFixer\\Tests\\Test\\.+::createIsIdenticalStringConstraint\(\) has invalid type PHPUnit_Framework_Constraint_IsIdentical\.$/' - '/^Class (Symfony\\Contracts\\EventDispatcher\\Event|Symfony\\Component\\EventDispatcher\\Event) not found.$/' - '/^Constant T_NAME_(RELATIVE|FULLY_QUALIFIED|QUALIFIED) not found\.$/' - - '/Instantiated class .*TraversableContains is abstract/' - '/assertInstanceOf\(\) expects class-string.*, string given/' - message: '/^Unsafe usage of new static\(\)\.$/' diff --git a/tests/AutoReview/CiConfigurationTest.php b/tests/AutoReview/CiConfigurationTest.php index 0d08a437bd7..1b377941e50 100644 --- a/tests/AutoReview/CiConfigurationTest.php +++ b/tests/AutoReview/CiConfigurationTest.php @@ -15,7 +15,7 @@ use PhpCsFixer\Preg; use PhpCsFixer\Tests\TestCase; use PhpCsFixer\Tokenizer\Tokens; -use PHPUnit\Framework\Constraint\TraversableContains; +use PHPUnit\Framework\Constraint\TraversableContainsIdentical; use Symfony\Component\Yaml\Yaml; /** @@ -74,36 +74,26 @@ public function testDeploymentJobsRunOnLatestStablePhpThatIsSupportedByTool() } } - private static function ensureTraversableContainsIsAvailable() + private static function ensureTraversableContainsIdenticalIsAvailable() { - if (!class_exists(TraversableContains::class)) { - static::markTestSkipped('TraversableContains not available.'); - } - - try { - new TraversableContains(''); - } catch (\Error $e) { - if (false === strpos($e->getMessage(), 'Cannot instantiate abstract class')) { - throw $e; - } - - static::markTestSkipped('TraversableContains not available.'); + if (!class_exists(TraversableContainsIdentical::class)) { + static::markTestSkipped('TraversableContainsIdentical not available.'); } } private static function assertUpcomingPhpVersionIsCoveredByCiJob($lastSupportedVersion, array $ciVersions) { - self::ensureTraversableContainsIsAvailable(); + self::ensureTraversableContainsIdenticalIsAvailable(); static::assertThat($ciVersions, static::logicalOr( // if `$lastsupportedVersion` is already a snapshot version - new TraversableContains(sprintf('%.1fsnapshot', $lastSupportedVersion)), + new TraversableContainsIdentical(sprintf('%.1fsnapshot', $lastSupportedVersion)), // if `$lastsupportedVersion` is not snapshot version, expect CI to run snapshot of next PHP version - new TraversableContains('nightly'), - new TraversableContains(sprintf('%.1fsnapshot', $lastSupportedVersion + 0.1)), + new TraversableContainsIdentical('nightly'), + new TraversableContainsIdentical(sprintf('%.1fsnapshot', $lastSupportedVersion + 0.1)), // GitHub CI uses just versions, without suffix, e.g. 8.1 for 8.1snapshot as of writing - new TraversableContains(sprintf('%.1f', $lastSupportedVersion + 0.1)), - new TraversableContains(sprintf('%.1f', round($lastSupportedVersion + 1))) + new TraversableContainsIdentical(sprintf('%.1f', $lastSupportedVersion + 0.1)), + new TraversableContainsIdentical(sprintf('%.1f', round($lastSupportedVersion + 1))) )); } @@ -115,11 +105,11 @@ private static function assertSupportedPhpVersionsAreCoveredByCiJobs(array $supp static::assertContains($expectedVersion, $ciVersions); } - self::ensureTraversableContainsIsAvailable(); + self::ensureTraversableContainsIdenticalIsAvailable(); static::assertThat($ciVersions, static::logicalOr( - new TraversableContains($lastSupportedVersion), - new TraversableContains(sprintf('%.1fsnapshot', $lastSupportedVersion)) + new TraversableContainsIdentical($lastSupportedVersion), + new TraversableContainsIdentical(sprintf('%.1fsnapshot', $lastSupportedVersion)) )); } From b76f42952fe6f8df9837330e009f4f32b4a94f7d Mon Sep 17 00:00:00 2001 From: Dariusz Ruminski Date: Tue, 19 Jan 2021 22:55:04 +0100 Subject: [PATCH 15/22] SingleSpaceAfterConstructFixer - better handling of closing parenthesis and brace --- .../SingleSpaceAfterConstructFixer.php | 2 +- .../SingleSpaceAfterConstructFixerTest.php | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/Fixer/LanguageConstruct/SingleSpaceAfterConstructFixer.php b/src/Fixer/LanguageConstruct/SingleSpaceAfterConstructFixer.php index b82e0223b2b..8c432d51dac 100644 --- a/src/Fixer/LanguageConstruct/SingleSpaceAfterConstructFixer.php +++ b/src/Fixer/LanguageConstruct/SingleSpaceAfterConstructFixer.php @@ -197,7 +197,7 @@ protected function applyFix(\SplFileInfo $file, Tokens $tokens) $whitespaceTokenIndex = $index + 1; - if (';' === $tokens[$whitespaceTokenIndex]->getContent()) { + if ($tokens[$whitespaceTokenIndex]->equalsAny([';', ')', [CT::T_ARRAY_SQUARE_BRACE_CLOSE]])) { continue; } diff --git a/tests/Fixer/LanguageConstruct/SingleSpaceAfterConstructFixerTest.php b/tests/Fixer/LanguageConstruct/SingleSpaceAfterConstructFixerTest.php index 5d7aacc1496..2e089998187 100644 --- a/tests/Fixer/LanguageConstruct/SingleSpaceAfterConstructFixerTest.php +++ b/tests/Fixer/LanguageConstruct/SingleSpaceAfterConstructFixerTest.php @@ -3137,10 +3137,10 @@ public function provideFixWithPhpOpenCases() /** * @dataProvider provideCommentsCases * - * @param string $expected - * @param string $input + * @param string $expected + * @param null|string $input */ - public function testComments($expected, $input) + public function testComments($expected, $input = null) { $this->fixer->configure([ 'constructs' => [ @@ -3170,6 +3170,15 @@ public function provideCommentsCases() $a = 3; # 3 $a = 4; /** 4 */ echo 1; +', + ]; + + yield 'exceptions' => [ + ' Date: Wed, 20 Jan 2021 00:52:26 +0100 Subject: [PATCH 16/22] DX: Fix CiIntegrationTest --- tests/Smoke/CiIntegrationTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/Smoke/CiIntegrationTest.php b/tests/Smoke/CiIntegrationTest.php index ba6c94382ae..79a94eee57c 100644 --- a/tests/Smoke/CiIntegrationTest.php +++ b/tests/Smoke/CiIntegrationTest.php @@ -141,6 +141,7 @@ public function testIntegration( ]); $optionalIncompatibilityWarning = 'PHP needs to be a minimum version of PHP 5.6.0 and maximum version of PHP 7.4.*. +Current PHP version: '.PHP_VERSION.'. Ignoring environment requirements because `PHP_CS_FIXER_IGNORE_ENV` is set. Execution may be unstable. '; From 44857f64bed3ee9f39684e5f19c2f6fba0163707 Mon Sep 17 00:00:00 2001 From: Dariusz Ruminski Date: Wed, 20 Jan 2021 00:44:43 +0100 Subject: [PATCH 17/22] PhpdocToCommentFixer - add support for attributes --- src/Tokenizer/Analyzer/CommentsAnalyzer.php | 7 ++ .../Fixer/Phpdoc/PhpdocToCommentFixerTest.php | 68 +++++++++++++++++++ .../Analyzer/CommentsAnalyzerTest.php | 27 ++++++++ 3 files changed, 102 insertions(+) diff --git a/src/Tokenizer/Analyzer/CommentsAnalyzer.php b/src/Tokenizer/Analyzer/CommentsAnalyzer.php index cd56fca1011..58d29bf06f4 100644 --- a/src/Tokenizer/Analyzer/CommentsAnalyzer.php +++ b/src/Tokenizer/Analyzer/CommentsAnalyzer.php @@ -84,6 +84,13 @@ public function isBeforeStructuralElement(Tokens $tokens, $index) $nextIndex = $index; do { $nextIndex = $tokens->getNextMeaningfulToken($nextIndex); + + 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('}')) { diff --git a/tests/Fixer/Phpdoc/PhpdocToCommentFixerTest.php b/tests/Fixer/Phpdoc/PhpdocToCommentFixerTest.php index a4cae3d9d15..81075adfb5a 100644 --- a/tests/Fixer/Phpdoc/PhpdocToCommentFixerTest.php +++ b/tests/Fixer/Phpdoc/PhpdocToCommentFixerTest.php @@ -713,4 +713,72 @@ class Foo { ], ]; } + + /** + * @param string $expected + * @param null|string $input + * + * @dataProvider provideFix80Cases + * @requires PHP 8.0 + */ + public function testFix80($expected, $input = null) + { + $this->doTest($expected, $input); + } + + public function provideFix80Cases() + { + return [ + [ + ' $x + 1;'], ]; } + + /** + * @param string $code + * + * @dataProvider providePhpdocCandidatePhp80Cases + * @requires PHP 8.0 + */ + public function testPhpdocCandidatePhp80($code) + { + $tokens = Tokens::fromCode($code); + $index = $tokens->getNextTokenOfKind(0, [[T_COMMENT], [T_DOC_COMMENT]]); + $analyzer = new CommentsAnalyzer(); + + static::assertTrue($analyzer->isBeforeStructuralElement($tokens, $index)); + } + + public function providePhpdocCandidatePhp80Cases() + { + return [ + [' Date: Wed, 20 Jan 2021 01:11:48 +0100 Subject: [PATCH 18/22] CI: fix params order --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6cc8dd6ea72..6a946fd0c6b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -145,7 +145,7 @@ jobs: env: PHP_CS_FIXER_IGNORE_ENV: ${{ matrix.PHP_CS_FIXER_IGNORE_ENV }} PHP_CS_FIXER_FUTURE_MODE: 1 - run: php php-cs-fixer --diff --dry-run -v fix + run: php php-cs-fixer fix --diff --dry-run -v - name: Execute deployment checks if: matrix.execute-deployment == 'yes' From 9e53d08d40ca6521852437978f908a87741c0088 Mon Sep 17 00:00:00 2001 From: Dariusz Ruminski Date: Wed, 20 Jan 2021 01:22:50 +0100 Subject: [PATCH 19/22] DX: cleanup PHP Migration rulesets --- .github/workflows/ci.yml | 2 +- doc/ruleSets/PHP80Migration.rst | 3 +-- doc/ruleSets/PHP80MigrationRisky.rst | 3 +-- doc/rules/cast_notation/short_scalar_cast.rst | 3 +++ doc/rules/function_notation/use_arrow_functions.rst | 5 ++++- src/RuleSet/Sets/PHP80MigrationRiskySet.php | 3 +-- src/RuleSet/Sets/PHP80MigrationSet.php | 3 +-- 7 files changed, 12 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6cc8dd6ea72..e2915e38443 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -120,7 +120,7 @@ jobs: - name: Execute migration rules if: matrix.execute-migration-rules == 'yes' - run: php php-cs-fixer fix --rules @PHP73Migration,@PHP71Migration:risky,blank_line_after_opening_tag -q + run: php php-cs-fixer fix --rules @PHP80Migration,@PHP80Migration:risky,blank_line_after_opening_tag -q - name: Disable time limit for tests when collecting coverage if: matrix.calculate-code-coverage == 'yes' diff --git a/doc/ruleSets/PHP80Migration.rst b/doc/ruleSets/PHP80Migration.rst index 289a66c1cfd..afd71b917d1 100644 --- a/doc/ruleSets/PHP80Migration.rst +++ b/doc/ruleSets/PHP80Migration.rst @@ -7,7 +7,6 @@ Rules to improve code for PHP 8.0 compatibility. Rules ----- -- `@PHP73Migration <./PHP73Migration.rst>`_ +- `@PHP74Migration <./PHP74Migration.rst>`_ - `clean_namespace <./../rules/namespace_notation/clean_namespace.rst>`_ - `no_unset_cast <./../rules/cast_notation/no_unset_cast.rst>`_ -- `normalize_index_brace <./../rules/array_notation/normalize_index_brace.rst>`_ diff --git a/doc/ruleSets/PHP80MigrationRisky.rst b/doc/ruleSets/PHP80MigrationRisky.rst index 7e0d47da5f1..6942647a890 100644 --- a/doc/ruleSets/PHP80MigrationRisky.rst +++ b/doc/ruleSets/PHP80MigrationRisky.rst @@ -7,8 +7,7 @@ Rules to improve code for PHP 8.0 compatibility. This set contains rules that ar Rules ----- -- `@PHP71Migration:risky <./PHP71MigrationRisky.rst>`_ -- `implode_call <./../rules/function_notation/implode_call.rst>`_ +- `@PHP74Migration:risky <./PHP74MigrationRisky.rst>`_ - `no_alias_functions <./../rules/alias/no_alias_functions.rst>`_ config: ``['sets' => ['@all']]`` diff --git a/doc/rules/cast_notation/short_scalar_cast.rst b/doc/rules/cast_notation/short_scalar_cast.rst index defeaaccb27..ce31dff42b9 100644 --- a/doc/rules/cast_notation/short_scalar_cast.rst +++ b/doc/rules/cast_notation/short_scalar_cast.rst @@ -57,6 +57,9 @@ The rule is part of the following rule sets: @PHP74Migration Using the `@PHP74Migration <./../../ruleSets/PHP74Migration.rst>`_ rule set will enable the ``short_scalar_cast`` rule. +@PHP80Migration + Using the `@PHP80Migration <./../../ruleSets/PHP80Migration.rst>`_ rule set will enable the ``short_scalar_cast`` rule. + @PhpCsFixer Using the `@PhpCsFixer <./../../ruleSets/PhpCsFixer.rst>`_ rule set will enable the ``short_scalar_cast`` rule. diff --git a/doc/rules/function_notation/use_arrow_functions.rst b/doc/rules/function_notation/use_arrow_functions.rst index 58dab632544..937336abad3 100644 --- a/doc/rules/function_notation/use_arrow_functions.rst +++ b/doc/rules/function_notation/use_arrow_functions.rst @@ -29,7 +29,10 @@ Example #1 Rule sets --------- -The rule is part of the following rule set: +The rule is part of the following rule sets: @PHP74Migration:risky Using the `@PHP74Migration:risky <./../../ruleSets/PHP74MigrationRisky.rst>`_ rule set will enable the ``use_arrow_functions`` rule. + +@PHP80Migration:risky + Using the `@PHP80Migration:risky <./../../ruleSets/PHP80MigrationRisky.rst>`_ rule set will enable the ``use_arrow_functions`` rule. diff --git a/src/RuleSet/Sets/PHP80MigrationRiskySet.php b/src/RuleSet/Sets/PHP80MigrationRiskySet.php index 637a0905b8e..5d35a3bdcc8 100644 --- a/src/RuleSet/Sets/PHP80MigrationRiskySet.php +++ b/src/RuleSet/Sets/PHP80MigrationRiskySet.php @@ -22,8 +22,7 @@ final class PHP80MigrationRiskySet extends AbstractRuleSetDescription public function getRules() { return [ - '@PHP71Migration:risky' => true, - 'implode_call' => true, + '@PHP74Migration:risky' => true, 'no_alias_functions' => [ 'sets' => [ '@all', diff --git a/src/RuleSet/Sets/PHP80MigrationSet.php b/src/RuleSet/Sets/PHP80MigrationSet.php index 334104c16f7..7e909eaac10 100644 --- a/src/RuleSet/Sets/PHP80MigrationSet.php +++ b/src/RuleSet/Sets/PHP80MigrationSet.php @@ -22,10 +22,9 @@ final class PHP80MigrationSet extends AbstractRuleSetDescription public function getRules() { return [ - '@PHP73Migration' => true, + '@PHP74Migration' => true, 'clean_namespace' => true, 'no_unset_cast' => true, - 'normalize_index_brace' => true, ]; } From 0f2098721b92f1fca16078355d033ec758de1c6f Mon Sep 17 00:00:00 2001 From: Dariusz Ruminski Date: Thu, 21 Jan 2021 19:23:37 +0100 Subject: [PATCH 20/22] NullableTypeDeclarationForDefaultNullValueFixer - support union types --- ...llableTypeDeclarationForDefaultNullValueFixer.php | 9 ++++++--- ...leTypeDeclarationForDefaultNullValueFixerTest.php | 12 ++++++++++-- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/Fixer/FunctionNotation/NullableTypeDeclarationForDefaultNullValueFixer.php b/src/Fixer/FunctionNotation/NullableTypeDeclarationForDefaultNullValueFixer.php index 4599c8a86f8..3ed4e0ba7f7 100644 --- a/src/Fixer/FunctionNotation/NullableTypeDeclarationForDefaultNullValueFixer.php +++ b/src/Fixer/FunctionNotation/NullableTypeDeclarationForDefaultNullValueFixer.php @@ -126,11 +126,14 @@ protected function applyFix(\SplFileInfo $file, Tokens $tokens) private function fixFunctionParameters(Tokens $tokens, array $arguments) { foreach (array_reverse($arguments) as $argumentInfo) { - // If the parameter doesn't have a type declaration or a default value null we can continue if ( + // Skip, if the parameter + // - doesn't have a type declaration !$argumentInfo->hasTypeAnalysis() - || !$argumentInfo->hasDefault() - || 'null' !== strtolower($argumentInfo->getDefault()) + // type is a union + || false !== strpos($argumentInfo->getTypeAnalysis()->getName(), '|') + // - a default value is not null we can continue + || !$argumentInfo->hasDefault() || 'null' !== strtolower($argumentInfo->getDefault()) ) { continue; } diff --git a/tests/Fixer/FunctionNotation/NullableTypeDeclarationForDefaultNullValueFixerTest.php b/tests/Fixer/FunctionNotation/NullableTypeDeclarationForDefaultNullValueFixerTest.php index b94743d90cb..d12833a06e5 100644 --- a/tests/Fixer/FunctionNotation/NullableTypeDeclarationForDefaultNullValueFixerTest.php +++ b/tests/Fixer/FunctionNotation/NullableTypeDeclarationForDefaultNullValueFixerTest.php @@ -417,12 +417,12 @@ public function testFix80($expected, $input = null) public function provideFix80Cases() { - yield [ + yield 'trailing comma' => [ ' [ ' [ + ' Date: Thu, 21 Jan 2021 19:44:47 +0100 Subject: [PATCH 21/22] prepared the 2.17.5 release --- CHANGELOG.md | 13 +++++++++++++ src/Console/Application.php | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 31e97e5bc0c..44f33f22846 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,19 @@ CHANGELOG for PHP CS Fixer This file contains changelogs for stable releases only. +Changelog for v2.17.5 +--------------------- + +* bug #5447 switch_case_semicolon_to_colon should skip match/default statements (derrabus) +* bug #5453 SingleSpaceAfterConstructFixer - better handling of closing parenthesis and brace (keradus) +* bug #5454 NullableTypeDeclarationForDefaultNullValueFixer - support property promotion via constructor (keradus) +* bug #5455 PhpdocToCommentFixer - add support for attributes (keradus) +* bug #5462 NullableTypeDeclarationForDefaultNullValueFixer - support union types (keradus) +* minor #5445 DX: update usage of old TraversableContains in tests (keradus) +* minor #5456 DX: Fix CiIntegrationTest (keradus) +* minor #5457 CI: fix params order (keradus) +* minor #5459 DX: cleanup PHP Migration rulesets (keradus) + Changelog for v2.17.4 --------------------- diff --git a/src/Console/Application.php b/src/Console/Application.php index 29cd7d6de8e..f7d142c1041 100644 --- a/src/Console/Application.php +++ b/src/Console/Application.php @@ -34,7 +34,7 @@ */ final class Application extends BaseApplication { - const VERSION = '2.17.5-DEV'; + const VERSION = '2.17.5'; const VERSION_CODENAME = 'Desert Beast'; /** From 27b5b5670a5127f7ef127eb2d1c8688fd8ae8db5 Mon Sep 17 00:00:00 2001 From: Dariusz Ruminski Date: Thu, 21 Jan 2021 19:45:32 +0100 Subject: [PATCH 22/22] bumped version --- src/Console/Application.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Console/Application.php b/src/Console/Application.php index f7d142c1041..84354b55611 100644 --- a/src/Console/Application.php +++ b/src/Console/Application.php @@ -34,7 +34,7 @@ */ final class Application extends BaseApplication { - const VERSION = '2.17.5'; + const VERSION = '2.17.6-DEV'; const VERSION_CODENAME = 'Desert Beast'; /**