diff --git a/.github/SECURITY.md b/.github/SECURITY.md deleted file mode 100644 index a4360b1e..00000000 --- a/.github/SECURITY.md +++ /dev/null @@ -1,22 +0,0 @@ -# Security Policy - -## Supported Versions - -The latest patch version of the `1.x` release series is supported for security updates. - -## Reporting a Vulnerability - -PHPCSExtra is a developer tool and should generally not be used in a production (web accessible) environment. - -Having said that, responsible disclosure of security issues is highly appreciated. - -**Please do not report or discuss security vulnerabilities through public GitHub issues, discussions, or pull requests.** - -Issues can be reported privately to the maintainers by opening a [Security vulnerability report](https://github.com/PHPCSStandards/PHPCSExtra/security/advisories/new). - -### Preferences - -* Please provide detailed reports with reproducible steps and a clearly defined impact. -* Include the version number of the vulnerable package in your report. -* Fixes are most welcome. - A private PR can be created from the security report to work on and discuss the patch. diff --git a/.github/workflows/basics.yml b/.github/workflows/basics.yml index 3ae47d5b..86acc3a9 100644 --- a/.github/workflows/basics.yml +++ b/.github/workflows/basics.yml @@ -48,7 +48,7 @@ jobs: composer require --no-update squizlabs/php_codesniffer:"dev-master" --no-interaction # Install dependencies and handle caching in one go. - # @link https://github.com/marketplace/actions/install-composer-dependencies + # @link https://github.com/marketplace/actions/install-php-dependencies-with-composer - name: Install Composer dependencies uses: "ramsey/composer-install@v2" with: @@ -62,7 +62,8 @@ jobs: # Show XML violations inline in the file diff. # @link https://github.com/marketplace/actions/xmllint-problem-matcher - - uses: korelstar/xmllint-problem-matcher@v1 + - name: Enable showing XML issues inline + uses: korelstar/xmllint-problem-matcher@v1 # Validate the Ruleset XML file. # @link http://xmlsoft.org/xmllint.html @@ -112,7 +113,7 @@ jobs: # Install dependencies and handle caching in one go. # Dependencies need to be installed to make sure the PHPCS and PHPUnit classes are recognized. - # @link https://github.com/marketplace/actions/install-composer-dependencies + # @link https://github.com/marketplace/actions/install-php-dependencies-with-composer - name: Install Composer dependencies uses: "ramsey/composer-install@v2" with: @@ -131,7 +132,7 @@ jobs: uses: actions/checkout@v4 - name: Set up node and enable caching of dependencies - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: '16' diff --git a/.github/workflows/label-remove-outdated.yml b/.github/workflows/label-remove-outdated.yml new file mode 100644 index 00000000..ce4bc4ce --- /dev/null +++ b/.github/workflows/label-remove-outdated.yml @@ -0,0 +1,53 @@ +name: Remove outdated labels + +on: + # https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target + issues: + types: + - closed + pull_request_target: + types: + - closed + +jobs: + on-issue-close: + runs-on: ubuntu-latest + if: github.repository_owner == 'PHPCSStandards' && github.event.issue.state == 'closed' + + name: Clean up labels on issue close + + steps: + - uses: mondeja/remove-labels-gh-action@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} + labels: | + Status: awaiting feedback + Status: wait for upstream + + on-pr-merge: + runs-on: ubuntu-latest + if: github.repository_owner == 'PHPCSStandards' && github.event.pull_request.merged == true + + name: Clean up labels on PR merge + + steps: + - uses: mondeja/remove-labels-gh-action@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} + labels: | + Status: awaiting feedback + Status: wait for upstream + + on-pr-close: + runs-on: ubuntu-latest + if: github.repository_owner == 'PHPCSStandards' && github.event_name == 'pull_request_target' && github.event.pull_request.merged == false + + name: Clean up labels on PR close + + steps: + - uses: mondeja/remove-labels-gh-action@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} + labels: | + Status: awaiting feedback + Status: wait for upstream diff --git a/.github/workflows/quicktest.yml b/.github/workflows/quicktest.yml index 2b90659f..1711cd4e 100644 --- a/.github/workflows/quicktest.yml +++ b/.github/workflows/quicktest.yml @@ -60,7 +60,7 @@ jobs: run: composer require squizlabs/php_codesniffer:"${{ matrix.phpcs_version }}" --no-update --no-scripts --no-interaction # Install dependencies and handle caching in one go. - # @link https://github.com/marketplace/actions/install-composer-dependencies + # @link https://github.com/marketplace/actions/install-php-dependencies-with-composer - name: Install Composer dependencies - normal if: matrix.php != 'latest' uses: "ramsey/composer-install@v2" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3b203cd3..2ed8aaf2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -32,12 +32,12 @@ jobs: # @link https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-syntax-for-github-actions#jobsjob_idstrategymatrix # # The matrix is set up so as not to duplicate the builds which are run for code coverage. - php: ['5.5', '5.6', '7.0', '7.1', '7.2', '7.3', '8.0', '8.1', '8.2', '8.3'] + php: ['5.5', '5.6', '7.0', '7.1', '7.2', '7.3', '8.0', '8.1', '8.2', '8.3', '8.4'] phpcs_version: ['lowest', 'dev-master'] name: "Test${{ matrix.phpcs_version == 'dev-master' && ' + Lint' || '' }}: PHP ${{ matrix.php }} - PHPCS ${{ matrix.phpcs_version }}" - continue-on-error: ${{ matrix.php == '8.3' }} + continue-on-error: ${{ matrix.php == '8.4' }} steps: - name: Checkout code @@ -66,7 +66,7 @@ jobs: run: composer require squizlabs/php_codesniffer:"${{ matrix.phpcs_version }}" --no-update --no-scripts --no-interaction # Install dependencies and handle caching in one go. - # @link https://github.com/marketplace/actions/install-composer-dependencies + # @link https://github.com/marketplace/actions/install-php-dependencies-with-composer - name: Install Composer dependencies - normal if: ${{ startsWith( matrix.php, '8' ) == false }} uses: "ramsey/composer-install@v2" @@ -148,7 +148,7 @@ jobs: run: composer require squizlabs/php_codesniffer:"${{ matrix.phpcs_version }}" --no-update --no-scripts --no-interaction # Install dependencies and handle caching in one go. - # @link https://github.com/marketplace/actions/install-composer-dependencies + # @link https://github.com/marketplace/actions/install-php-dependencies-with-composer - name: Install Composer dependencies uses: "ramsey/composer-install@v2" with: @@ -168,37 +168,14 @@ jobs: - name: Run the unit tests with code coverage run: composer coverage - # PHP Coveralls v2 (which supports GH Actions) has a PHP 5.5 minimum, so switch the PHP version. - - name: Switch to PHP latest - if: ${{ success() && matrix.php == '5.4' }} - uses: shivammathur/setup-php@v2 - with: - php-version: 'latest' - coverage: none - - # Global install is used to prevent a conflict with the local composer.lock. - - name: Install Coveralls + - name: Upload coverage results to Coveralls if: ${{ success() }} - run: composer global require php-coveralls/php-coveralls:"^2.6.0" --no-interaction - - - name: Upload coverage results to Coveralls (normal) - if: ${{ success() && github.actor != 'dependabot[bot]' }} - env: - COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_TOKEN }} - COVERALLS_PARALLEL: true - COVERALLS_FLAG_NAME: php-${{ matrix.php }}-phpcs-${{ matrix.phpcs_version }} - run: php-coveralls -v -x build/logs/clover.xml - - # Dependabot does not have access to secrets, other than the GH token. - # Ref: https://docs.github.com/en/code-security/dependabot/working-with-dependabot/automating-dependabot-with-github-actions - # Ref: https://github.com/lemurheavy/coveralls-public/issues/1721 - - name: Upload coverage results to Coveralls (Dependabot) - if: ${{ success() && github.actor == 'dependabot[bot]' }} - env: - COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} - COVERALLS_PARALLEL: true - COVERALLS_FLAG_NAME: php-${{ matrix.php }}-phpcs-${{ matrix.phpcs_version }} - run: php-coveralls -v -x build/logs/clover.xml + uses: coverallsapp/github-action@v2 + with: + format: clover + file: build/logs/clover.xml + flag-name: php-${{ matrix.php }}-phpcs-${{ matrix.phpcs_version }} + parallel: true coveralls-finish: needs: coverage @@ -207,19 +184,7 @@ jobs: runs-on: ubuntu-latest steps: - - name: Coveralls Finished (normal) - if: ${{ github.actor != 'dependabot[bot]' }} - uses: coverallsapp/github-action@v2 - with: - github-token: ${{ secrets.COVERALLS_TOKEN }} - parallel-finished: true - - # Dependabot does not have access to secrets, other than the GH token. - # Ref: https://docs.github.com/en/code-security/dependabot/working-with-dependabot/automating-dependabot-with-github-actions - # Ref: https://github.com/lemurheavy/coveralls-public/issues/1721 - - name: Coveralls Finished (Dependabot) - if: ${{ github.actor == 'dependabot[bot]' }} + - name: Coveralls Finished uses: coverallsapp/github-action@v2 with: - github-token: ${{ secrets.GITHUB_TOKEN }} parallel-finished: true diff --git a/.yamllint.yml b/.yamllint.yml index 8dc953ff..b5c4202d 100644 --- a/.yamllint.yml +++ b/.yamllint.yml @@ -6,6 +6,7 @@ yaml-files: - '*.yaml' - '*.yml' - '.yamllint' + - 'phpstan.neon*' # Rule documentation: https://yamllint.readthedocs.io/en/stable/rules.html rules: diff --git a/CHANGELOG.md b/CHANGELOG.md index d2fffeb6..5a619724 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,36 @@ This projects adheres to [Keep a CHANGELOG](http://keepachangelog.com/) and uses _Nothing yet._ +## [1.2.0] - 2023-12-02 + +### Added + +#### Universal + +* :wrench: :books: New `Universal.CodeAnalysis.NoDoubleNegative` sniff to detect double negatives (!!) and advise to use a boolean cast instead. Thanks [@diedexx] for reviewing. [#277] +* :wrench: :books: New `Universal.Operators.ConcatPosition` sniff to enforce that the concatenation operator for multi-line concatenations is in a preferred position, either always at the start of the next line or always at the end of the previous line. [#294] +* :wrench: :bar_chart: :books: New `Universal.PHP.LowercasePHPTag` sniff to enforce that the "PHP" in a PHP open tag is lowercase. Thanks [@fredden] for reviewing. [#276] + +### Changed + +#### NormalizedArrays + +* `NormalizedArrays.Arrays.CommaAfterLast`: the sniff now has two extra error codes to distinguish between multi-line arrays with the last array item on the _same line_ as the array closer vs the last array item being on a line _before_ the array closer. Thanks [@stronk7] for suggesting and patching this. [#283], [#284] + These new error codes allow for selectively excluding that specific situation from triggering the sniff. + The new error codes are `FoundMultiLineCloserSameLine` (for `multiLine="forbid"`) and `MissingMultiLineCloserSameLine` (for `multiLine="enforce"`). + The pre-existing `FoundMultiLine` and `FoundSingleLine` error codes continue to be used for multi-line arrays with the last array item on a different line than the array closer. + +#### Other + +* Various housekeeping. + +[#276]: https://github.com/PHPCSStandards/PHPCSExtra/pull/276 +[#277]: https://github.com/PHPCSStandards/PHPCSExtra/pull/277 +[#283]: https://github.com/PHPCSStandards/PHPCSExtra/issues/283 +[#284]: https://github.com/PHPCSStandards/PHPCSExtra/pull/284 +[#294]: https://github.com/PHPCSStandards/PHPCSExtra/pull/294 + + ## [1.1.2] - 2023-09-21 ### Changed @@ -523,6 +553,7 @@ This initial alpha release contains the following sniffs: [php_version-config]: https://github.com/squizlabs/PHP_CodeSniffer/wiki/Configuration-Options#setting-the-php-version [Unreleased]: https://github.com/PHPCSStandards/PHPCSExtra/compare/stable...HEAD +[1.2.0]: https://github.com/PHPCSStandards/PHPCSExtra/compare/1.1.2...1.2.0 [1.1.2]: https://github.com/PHPCSStandards/PHPCSExtra/compare/1.1.1...1.1.2 [1.1.1]: https://github.com/PHPCSStandards/PHPCSExtra/compare/1.1.0...1.1.1 [1.1.0]: https://github.com/PHPCSStandards/PHPCSExtra/compare/1.0.4...1.1.0 @@ -537,5 +568,8 @@ This initial alpha release contains the following sniffs: [@anomiex]: https://github.com/anomiex [@derickr]: https://github.com/derickr +[@diedexx]: https://github.com/diedexx +[@fredden]: https://github.com/fredden [@GaryJones]: https://github.com/GaryJones +[@stronk7]: https://github.com/stronk7 [@szepeviktor]: https://github.com/szepeviktor diff --git a/NormalizedArrays/Sniffs/Arrays/CommaAfterLastSniff.php b/NormalizedArrays/Sniffs/Arrays/CommaAfterLastSniff.php index d03d1ffc..7384421d 100644 --- a/NormalizedArrays/Sniffs/Arrays/CommaAfterLastSniff.php +++ b/NormalizedArrays/Sniffs/Arrays/CommaAfterLastSniff.php @@ -145,6 +145,13 @@ public function process(File $phpcsFile, $stackPtr) return; } + // If the closer is on the same line as the last element, change the error code for multi-line arrays. + if ($errorCode === 'MultiLine' + && $tokens[$lastNonEmpty]['line'] === $tokens[$closer]['line'] + ) { + $errorCode .= 'CloserSameLine'; + } + $isComma = ($tokens[$lastNonEmpty]['code'] === \T_COMMA); $phpcsFile->recordMetric( diff --git a/NormalizedArrays/Tests/Arrays/CommaAfterLastUnitTest.1.inc b/NormalizedArrays/Tests/Arrays/CommaAfterLastUnitTest.1.inc index 8615ee9e..46abe48b 100644 --- a/NormalizedArrays/Tests/Arrays/CommaAfterLastUnitTest.1.inc +++ b/NormalizedArrays/Tests/Arrays/CommaAfterLastUnitTest.1.inc @@ -166,6 +166,56 @@ EOD , /*comment*/ ) ); +/** + * Tests enforcing a comma after the last array item when the closer is in the same line. See #283. + */ +// phpcs:set NormalizedArrays.Arrays.CommaAfterLast multiLine enforce + +$missing = array( + 1, 2, + 3, 4); + +$missing = [ + '1', '2', + '3', '4']; + +$missing_but_good = [ + '1', '2', + '3', '4']; // phpcs:ignore NormalizedArrays.Arrays.CommaAfterLast.MissingMultiLineCloserSameLine + +$good = array( + 1, 2, + 3, 4,); + +$good = [ + '1', '2', + '3', '4',]; + +/** + * Tests forbidding a comma after the last array item when the closer is in the same line. See #283. + */ +// phpcs:set NormalizedArrays.Arrays.CommaAfterLast multiLine forbid + +$found = array( + 1, 2, + 3, 4,); + +$found = [ + '1', '2', + '3', '4',]; + +$found_but_good = [ + '1', '2', + '3', '4',]; // phpcs:ignore NormalizedArrays.Arrays.CommaAfterLast.FoundMultiLineCloserSameLine + +$good = array( + 1, 2, + 3, 4); + +$good = [ + '1', '2', + '3', '4']; + // Reset the properties to the defaults. // phpcs:set NormalizedArrays.Arrays.CommaAfterLast singleLine forbid // phpcs:set NormalizedArrays.Arrays.CommaAfterLast multiLine enforce diff --git a/NormalizedArrays/Tests/Arrays/CommaAfterLastUnitTest.1.inc.fixed b/NormalizedArrays/Tests/Arrays/CommaAfterLastUnitTest.1.inc.fixed index d06573c1..09ab321d 100644 --- a/NormalizedArrays/Tests/Arrays/CommaAfterLastUnitTest.1.inc.fixed +++ b/NormalizedArrays/Tests/Arrays/CommaAfterLastUnitTest.1.inc.fixed @@ -166,6 +166,56 @@ EOD /*comment*/ ) ); +/** + * Tests enforcing a comma after the last array item when the closer is in the same line. See #283. + */ +// phpcs:set NormalizedArrays.Arrays.CommaAfterLast multiLine enforce + +$missing = array( + 1, 2, + 3, 4,); + +$missing = [ + '1', '2', + '3', '4',]; + +$missing_but_good = [ + '1', '2', + '3', '4']; // phpcs:ignore NormalizedArrays.Arrays.CommaAfterLast.MissingMultiLineCloserSameLine + +$good = array( + 1, 2, + 3, 4,); + +$good = [ + '1', '2', + '3', '4',]; + +/** + * Tests forbidding a comma after the last array item when the closer is in the same line. See #283. + */ +// phpcs:set NormalizedArrays.Arrays.CommaAfterLast multiLine forbid + +$found = array( + 1, 2, + 3, 4); + +$found = [ + '1', '2', + '3', '4']; + +$found_but_good = [ + '1', '2', + '3', '4',]; // phpcs:ignore NormalizedArrays.Arrays.CommaAfterLast.FoundMultiLineCloserSameLine + +$good = array( + 1, 2, + 3, 4); + +$good = [ + '1', '2', + '3', '4']; + // Reset the properties to the defaults. // phpcs:set NormalizedArrays.Arrays.CommaAfterLast singleLine forbid // phpcs:set NormalizedArrays.Arrays.CommaAfterLast multiLine enforce diff --git a/NormalizedArrays/Tests/Arrays/CommaAfterLastUnitTest.php b/NormalizedArrays/Tests/Arrays/CommaAfterLastUnitTest.php index fbffdc12..f4463550 100644 --- a/NormalizedArrays/Tests/Arrays/CommaAfterLastUnitTest.php +++ b/NormalizedArrays/Tests/Arrays/CommaAfterLastUnitTest.php @@ -52,6 +52,10 @@ public function getErrorList($testFile = '') 152 => 1, 159 => 1, 166 => 1, + 176 => 1, + 180 => 1, + 201 => 1, + 205 => 1, ]; case 'CommaAfterLastUnitTest.2.inc': diff --git a/README.md b/README.md index 22ba5382..9e70026a 100644 --- a/README.md +++ b/README.md @@ -9,12 +9,13 @@ PHPCSExtra [![Latest Unstable Version](https://img.shields.io/badge/unstable-dev--develop-e68718.svg?maxAge=2419200)](https://packagist.org/packages/phpcsstandards/phpcsextra#dev-develop) [![Last Commit to Unstable](https://img.shields.io/github/last-commit/PHPCSStandards/PHPCSExtra/develop.svg)](https://github.com/PHPCSStandards/PHPCSExtra/commits/develop) -[![Minimum PHP Version](https://img.shields.io/packagist/php-v/phpcsstandards/phpcsextra.svg?maxAge=3600)][phpcsextra-packagist] [![CS Build Status](https://github.com/PHPCSStandards/PHPCSExtra/actions/workflows/basics.yml/badge.svg?branch=develop)][gha-qa-results] [![Test Build Status](https://github.com/PHPCSStandards/PHPCSExtra/actions/workflows/test.yml/badge.svg?branch=develop)][gha-test-results] -[![Tested on PHP 5.4 to 8.2](https://img.shields.io/badge/tested%20on-PHP%205.4%20|%205.5%20|%205.6%20|%207.0%20|%207.1%20|%207.2%20|%207.3%20|%207.4%20|%208.0%20|%208.1%20|%208.2-brightgreen.svg?maxAge=2419200)][gha-test-results] [![Coverage Status](https://coveralls.io/repos/github/PHPCSStandards/PHPCSExtra/badge.svg)](https://coveralls.io/github/PHPCSStandards/PHPCSExtra) +[![Minimum PHP Version](https://img.shields.io/packagist/php-v/phpcsstandards/phpcsextra.svg?maxAge=3600)][phpcsextra-packagist] +[![Tested on PHP 5.4 to 8.3](https://img.shields.io/badge/tested%20on-PHP%205.4%20|%205.5%20|%205.6%20|%207.0%20|%207.1%20|%207.2%20|%207.3%20|%207.4%20|%208.0%20|%208.1%20|%208.2%20|%208.3-brightgreen.svg?maxAge=2419200)][gha-test-results] + [![License: LGPLv3](https://poser.pugx.org/phpcsstandards/phpcsextra/license)](https://github.com/PHPCSStandards/PHPCSExtra/blob/stable/LICENSE) ![Awesome](https://img.shields.io/badge/awesome%3F-yes!-brightgreen.svg) @@ -61,7 +62,7 @@ Installing via Composer is highly recommended. Run the following from the root of your project: ```bash composer config allow-plugins.dealerdirect/phpcodesniffer-composer-installer true -composer require --dev phpcsstandards/phpcsextra:"^1.1.0" +composer require --dev phpcsstandards/phpcsextra:"^1.2.0" ``` ### Composer Global Installation @@ -69,7 +70,7 @@ composer require --dev phpcsstandards/phpcsextra:"^1.1.0" Alternatively, you may want to install this standard globally: ```bash composer global config allow-plugins.dealerdirect/phpcodesniffer-composer-installer true -composer global require --dev phpcsstandards/phpcsextra:"^1.1.0" +composer global require --dev phpcsstandards/phpcsextra:"^1.2.0" ``` ### Updating to a newer version @@ -229,6 +230,13 @@ Detects `foreach` control structures which use the same variable for both the ke Note: The fixer will maintain the existing behaviour of the code. This may not be the _intended_ behaviour. +#### `Universal.CodeAnalysis.NoDoubleNegative` :wrench: :books: + +Detects double negation `!!` in code, which is effectively the same as a boolean cast, but with a much higher cognitive load. +Also detects triple negation `!!!`, which is effectively the same as a single negation. + +The sniff has modular error codes to allow for disabling individual checks. The error codes are: `FoundDouble`, `FoundDoubleWithInstanceof` (not auto-fixable) and `FoundTriple`. + #### `Universal.CodeAnalysis.NoEchoSprintf` :wrench: :books: Detects use of the inefficient `echo [v]sprintf(...);` combi. Use `[v]printf()` instead. @@ -360,6 +368,14 @@ Enforce that the names used in a class/enum "implements" statement or an interfa The fixer will separate each name with a comma and one space. If alternative formatting is desired, a sniff which will check and fix the formatting should be added to the ruleset. +#### `Universal.Operators.ConcatPosition` :wrench: :bar_chart: :books: + +Enforce that the concatenation operator for multi-line concatenations is in a preferred position, either always at the start of the next line or always at the end of the previous line. + +* This sniff contains an `allowOnly` property to set the preferred position for the operator. + Accepted values: (string) `"start"` or `"end"`. Defaults to `"start"`. +* Note: mid-line concatenation is still allowed and will not be flagged by this sniff. + #### `Universal.Operators.DisallowLogicalAndOr` :bar_chart: :books: Enforce the use of the boolean `&&` and `||` operators instead of the logical `and`/`or` operators. @@ -391,6 +407,10 @@ Enforce no spaces around the union type and intersection type operators. The available error codes are: `UnionTypeSpacesBefore`, `UnionTypeSpacesAfter`, `IntersectionTypeSpacesBefore`, `IntersectionTypeSpacesAfter`. +#### `Universal.PHP.LowercasePHPTag` :wrench: :bar_chart: :books: + +Enforces that the "PHP" in a PHP open tag is lowercase. + #### `Universal.PHP.OneStatementInShortEchoTag` :wrench: :books: Disallow short open echo tags ` + + + + + + + ! $b; + +if((bool) callMe($a)) {} + ]]> + + + ! ! $b; + +if(! ! ! callMe($a)) {} + ]]> + + + diff --git a/Universal/Docs/Operators/ConcatPositionStandard.xml b/Universal/Docs/Operators/ConcatPositionStandard.xml new file mode 100644 index 00000000..4d2761af --- /dev/null +++ b/Universal/Docs/Operators/ConcatPositionStandard.xml @@ -0,0 +1,31 @@ + + + + + + + + . $b . 'text' + . $c; + ]]> + + + . + $b . 'text' + . $c; + ]]> + + + diff --git a/Universal/Docs/PHP/LowercasePHPTagStandard.xml b/Universal/Docs/PHP/LowercasePHPTagStandard.xml new file mode 100644 index 00000000..261f93cd --- /dev/null +++ b/Universal/Docs/PHP/LowercasePHPTagStandard.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + diff --git a/Universal/Sniffs/CodeAnalysis/NoDoubleNegativeSniff.php b/Universal/Sniffs/CodeAnalysis/NoDoubleNegativeSniff.php new file mode 100644 index 00000000..549c8cd3 --- /dev/null +++ b/Universal/Sniffs/CodeAnalysis/NoDoubleNegativeSniff.php @@ -0,0 +1,269 @@ + + */ + private $operatorsWithLowerPrecedence; + + /** + * Returns an array of tokens this test wants to listen for. + * + * @since 1.2.0 + * + * @return array + */ + public function register() + { + // Collect all the operators only once. + $this->operatorsWithLowerPrecedence = Tokens::$assignmentTokens; + $this->operatorsWithLowerPrecedence += Tokens::$booleanOperators; + $this->operatorsWithLowerPrecedence += Tokens::$comparisonTokens; + $this->operatorsWithLowerPrecedence += Tokens::$operators; + $this->operatorsWithLowerPrecedence[\T_INLINE_THEN] = \T_INLINE_THEN; + $this->operatorsWithLowerPrecedence[\T_INLINE_ELSE] = \T_INLINE_ELSE; + + return [\T_BOOLEAN_NOT]; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.2.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. + */ + public function process(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + + $notCount = 1; + $lastNot = $stackPtr; + for ($afterNot = ($stackPtr + 1); $afterNot < $phpcsFile->numTokens; $afterNot++) { + if (isset(Tokens::$emptyTokens[$tokens[$afterNot]['code']])) { + continue; + } + + if ($tokens[$afterNot]['code'] === \T_BOOLEAN_NOT) { + $lastNot = $afterNot; + ++$notCount; + continue; + } + + break; + } + + if ($notCount === 1) { + // Singular unary not-operator. Nothing to do. + return; + } + + $found = \trim(GetTokensAsString::compact($phpcsFile, $stackPtr, $lastNot)); + $data = [$found]; + + if (($notCount % 2) === 1) { + /* + * Oh dear... silly code time, found a triple negative (or other uneven number), + * this should just be a singular not-operator. + */ + $fix = $phpcsFile->addFixableError( + 'Triple negative (or more) detected. Use a singular not (!) operator instead. Found: %s', + $stackPtr, + 'FoundTriple', + $data + ); + + if ($fix === true) { + $phpcsFile->fixer->beginChangeset(); + + $this->removeNotAndTrailingSpaces($phpcsFile, $stackPtr, $lastNot); + + $phpcsFile->fixer->endChangeset(); + } + + // Only throw one error, even if there are more than two not-operators. + return $lastNot; + } + + /* + * Found a double negative, which should be a boolean cast. + */ + + $fixable = true; + + /* + * If whatever is being "cast" is within parentheses, we're good. + * If not, we need to prevent creating a change in behaviour + * when what follows is an `$x instanceof ...` expression, as + * the "instanceof" operator is right between a boolean cast + * and the ! operator precedence-wise. + * + * Note: this only applies to double negative, not triple negative. + * + * @link https://www.php.net/language.operators.precedence + */ + if ($tokens[$afterNot]['code'] !== \T_OPEN_PARENTHESIS) { + $end = Parentheses::getLastCloser($phpcsFile, $stackPtr); + if ($end === false) { + $end = BCFile::findEndOfStatement($phpcsFile, $stackPtr); + } + + for ($nextRelevant = $afterNot; $nextRelevant < $end; $nextRelevant++) { + if (isset(Tokens::$emptyTokens[$tokens[$nextRelevant]['code']])) { + continue; + } + + if ($tokens[$nextRelevant]['code'] === \T_INSTANCEOF) { + $fixable = false; + break; + } + + if (isset($this->operatorsWithLowerPrecedence[$tokens[$nextRelevant]['code']])) { + // The expression the `!` belongs to has ended. + break; + } + + // Skip over anything within some form of brackets. + if (isset($tokens[$nextRelevant]['scope_closer']) + && ($nextRelevant === $tokens[$nextRelevant]['scope_opener'] + || $nextRelevant === $tokens[$nextRelevant]['scope_condition']) + ) { + $nextRelevant = $tokens[$nextRelevant]['scope_closer']; + continue; + } + + if (isset($tokens[$nextRelevant]['bracket_opener'], $tokens[$nextRelevant]['bracket_closer']) + && $nextRelevant === $tokens[$nextRelevant]['bracket_opener'] + ) { + $nextRelevant = $tokens[$nextRelevant]['bracket_closer']; + continue; + } + + if ($tokens[$nextRelevant]['code'] === \T_OPEN_PARENTHESIS + && isset($tokens[$nextRelevant]['parenthesis_closer']) + ) { + $nextRelevant = $tokens[$nextRelevant]['parenthesis_closer']; + continue; + } + + // Skip over attributes (just in case). + if ($tokens[$nextRelevant]['code'] === \T_ATTRIBUTE + && isset($tokens[$nextRelevant]['attribute_closer']) + ) { + $nextRelevant = $tokens[$nextRelevant]['attribute_closer']; + continue; + } + } + } + + $error = 'Double negative detected. Use a (bool) cast %s instead. Found: %s'; + $code = 'FoundDouble'; + $data = [ + '', + $found, + ]; + + if ($fixable === false) { + $code = 'FoundDoubleWithInstanceof'; + $data[0] = 'and parentheses around the instanceof expression'; + + // Don't auto-fix in combination with instanceof. + $phpcsFile->addError($error, $stackPtr, $code, $data); + + // Only throw one error, even if there are more than two not-operators. + return $lastNot; + } + + $fix = $phpcsFile->addFixableError($error, $stackPtr, $code, $data); + + if ($fix === true) { + $phpcsFile->fixer->beginChangeset(); + + $this->removeNotAndTrailingSpaces($phpcsFile, $stackPtr, $lastNot); + + $phpcsFile->fixer->replaceToken($lastNot, '(bool)'); + + $phpcsFile->fixer->endChangeset(); + } + + // Only throw one error, even if there are more than two not-operators. + return $lastNot; + } + + /** + * Remove boolean not-operators and trailing whitespace after those, + * but don't remove comments or trailing whitespace after comments. + * + * @since 1.2.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * @param int $lastNot The position of the last boolean not token + * in the chain. + * + * @return void + */ + private function removeNotAndTrailingSpaces(File $phpcsFile, $stackPtr, $lastNot) + { + $tokens = $phpcsFile->getTokens(); + $ignore = false; + + for ($i = $stackPtr; $i < $lastNot; $i++) { + if (isset(Tokens::$commentTokens[$tokens[$i]['code']])) { + // Ignore comments and whitespace after comments. + $ignore = true; + continue; + } + + if ($tokens[$i]['code'] === \T_WHITESPACE && $ignore === false) { + $phpcsFile->fixer->replaceToken($i, ''); + continue; + } + + if ($tokens[$i]['code'] === \T_BOOLEAN_NOT) { + $ignore = false; + $phpcsFile->fixer->replaceToken($i, ''); + } + } + } +} diff --git a/Universal/Sniffs/Operators/ConcatPositionSniff.php b/Universal/Sniffs/Operators/ConcatPositionSniff.php new file mode 100644 index 00000000..093785ad --- /dev/null +++ b/Universal/Sniffs/Operators/ConcatPositionSniff.php @@ -0,0 +1,204 @@ + + */ + public function register() + { + return [\T_STRING_CONCAT]; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.2.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. + */ + public function process(File $phpcsFile, $stackPtr) + { + /* + * Validate the setting. + */ + if ($this->allowOnly !== self::POSITION_END) { + // Use the default. + $this->allowOnly = self::POSITION_START; + } + + $prevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true); + $nextNonEmpty = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true); + + if ($nextNonEmpty === false) { + // Parse error/live coding. + return; + } + + $tokens = $phpcsFile->getTokens(); + if ($tokens[$prevNonEmpty]['line'] === $tokens[$nextNonEmpty]['line']) { + // Not multi-line concatenation. Not our target. + return; + } + + $position = self::POSITION_STANDALONE; + if ($tokens[$prevNonEmpty]['line'] === $tokens[$stackPtr]['line']) { + $position = self::POSITION_END; + } elseif ($tokens[$nextNonEmpty]['line'] === $tokens[$stackPtr]['line']) { + $position = self::POSITION_START; + } + + // Record metric. + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, $position); + + if ($this->allowOnly === $position) { + // All okay. + return; + } + + $fix = $phpcsFile->addFixableError( + 'The concatenation operator for multi-line concatenations should always be at the %s of a line.', + $stackPtr, + 'Incorrect', + [$this->allowOnly] + ); + + if ($fix === true) { + if ($this->allowOnly === self::POSITION_END) { + $phpcsFile->fixer->beginChangeset(); + + // Move the concat operator. + $phpcsFile->fixer->replaceToken($stackPtr, ''); + $phpcsFile->fixer->addContent($prevNonEmpty, ' .'); + + if ($position === self::POSITION_START + && $tokens[($stackPtr + 1)]['code'] === \T_WHITESPACE + ) { + // Remove trailing space. + $phpcsFile->fixer->replaceToken(($stackPtr + 1), ''); + } elseif ($position === self::POSITION_STANDALONE) { + // Remove potential indentation space. + if ($tokens[($stackPtr - 1)]['code'] === \T_WHITESPACE) { + $phpcsFile->fixer->replaceToken(($stackPtr - 1), ''); + } + + // Remove new line. + if ($tokens[($stackPtr + 1)]['code'] === \T_WHITESPACE) { + $phpcsFile->fixer->replaceToken(($stackPtr + 1), ''); + } + } + + $phpcsFile->fixer->endChangeset(); + return; + } + + // Fixer for allowOnly === self::POSITION_START. + $phpcsFile->fixer->beginChangeset(); + + // Move the concat operator. + $phpcsFile->fixer->replaceToken($stackPtr, ''); + $phpcsFile->fixer->addContentBefore($nextNonEmpty, '. '); + + if ($position === self::POSITION_END + && $tokens[($stackPtr - 1)]['code'] === \T_WHITESPACE + ) { + // Remove trailing space. + $phpcsFile->fixer->replaceToken(($stackPtr - 1), ''); + } elseif ($position === self::POSITION_STANDALONE) { + // Remove potential indentation space. + if ($tokens[($stackPtr - 1)]['code'] === \T_WHITESPACE) { + $phpcsFile->fixer->replaceToken(($stackPtr - 1), ''); + } + + // Remove new line. + if ($tokens[($stackPtr + 1)]['code'] === \T_WHITESPACE) { + $phpcsFile->fixer->replaceToken(($stackPtr + 1), ''); + } + } + + $phpcsFile->fixer->endChangeset(); + } + } +} diff --git a/Universal/Sniffs/PHP/LowercasePHPTagSniff.php b/Universal/Sniffs/PHP/LowercasePHPTagSniff.php new file mode 100644 index 00000000..b231b225 --- /dev/null +++ b/Universal/Sniffs/PHP/LowercasePHPTagSniff.php @@ -0,0 +1,87 @@ + + */ + public function register() + { + return [\T_OPEN_TAG]; + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @since 1.2.0 + * + * @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned. + * @param int $stackPtr The position of the current token + * in the stack passed in $tokens. + * + * @return void + */ + public function process(File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + $content = $tokens[$stackPtr]['content']; + $contentLC = \strtolower($content); + + if ($contentLC === $content) { + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'lowercase'); + return; + } + + $errorCode = ''; + if (\strtoupper($content) === $content) { + $errorCode = 'Uppercase'; + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'uppercase'); + } else { + $errorCode = 'Mixedcase'; + $phpcsFile->recordMetric($stackPtr, self::METRIC_NAME, 'mixed case'); + } + + $fix = $phpcsFile->addFixableError( + 'The php open tag should be in lowercase. Found: %s', + $stackPtr, + $errorCode, + [\trim($content)] + ); + + if ($fix === true) { + $phpcsFile->fixer->replaceToken($stackPtr, $contentLC); + } + } +} diff --git a/Universal/Sniffs/UseStatements/NoUselessAliasesSniff.php b/Universal/Sniffs/UseStatements/NoUselessAliasesSniff.php index 6d699934..93de596c 100644 --- a/Universal/Sniffs/UseStatements/NoUselessAliasesSniff.php +++ b/Universal/Sniffs/UseStatements/NoUselessAliasesSniff.php @@ -28,15 +28,6 @@ final class NoUselessAliasesSniff implements Sniff { - /** - * Name of the "Use import source" metric. - * - * @since 1.1.0 - * - * @var string - */ - const METRIC_NAME = 'Import use statement type'; - /** * Returns an array of tokens this test wants to listen for. * diff --git a/Universal/Tests/CodeAnalysis/NoDoubleNegativeUnitTest.inc b/Universal/Tests/CodeAnalysis/NoDoubleNegativeUnitTest.inc new file mode 100644 index 00000000..dd8d8712 --- /dev/null +++ b/Universal/Tests/CodeAnalysis/NoDoubleNegativeUnitTest.inc @@ -0,0 +1,104 @@ +prop instanceof Something); +$var = !!!($something->method() instanceof Something); +$var = !!!!($something['key']->my['key']->chained instanceof Something); + +$var = !!(self::ENUM_SET_AS_CONSTANT instanceof \Something); +$var = !!(parent::ENUM_SET_AS_CONSTANT instanceof Something); +$var = !! (static::ENUM_SET_AS_CONSTANT instanceof \Something); + +if (!! + (function ($p) { + return new $p; +})('dummy') instanceof Foo) {} + +// If an instanceof is after another operator with lower precedence, we're good. +if (!!$something && $somethingElse instanceof \Something) {} +if (!!$something() || $somethingElse instanceof \Something) {} +if (!! $boolean !== $somethingElse instanceof Something) {} +while (!! $boolean = $somethingElse instanceof \Something) {} +if ( ! ! $boolean and $somethingElse instanceof \Something) {} +$var = !!$boolean ? $somethingElse instanceof Something : true; +$var = !!$boolean ?? $somethingElse instanceof \Something; + +// Instanceof where the not does not apply to. +if (!!$something($somethingElse instanceof \Something)) {} +if (!!['key' => $somethingElse instanceof \Something]['key']) {} +if (!!$array[$somethingElse instanceof \Something ? 'keyA' : 'keyB']) {} +if (!!match($a) { 'foo' => $a instanceof Foo, 'bar' => $a instanceof Bar } === true) {} + +// Note: this contains an intentional parse error, nothing to worry about. +if (!! + #[\Attr(self::ENUM_SET_AS_CONSTANT instanceof Foo)] + (function ($p) { + return $p instanceof Foo; +})('dummy')) {} + +// If it's an instanceof with triple not, we're good. +$var = !!!$something->method() instanceof \Something; + +// Make the error non-autofixable when used in combination with instanceof without parentheses to avoid issues with precedence. +if(!!$something instanceof \Something) {} + +$var = !!$something->prop instanceof \Something; + +$var = !!self::ENUM_SET_AS_CONSTANT instanceof \Something; +$var = (!!parent::ENUM_SET_AS_CONSTANT instanceof \Something); +$var = !! static::ENUM_SET_AS_CONSTANT instanceof \Something; + +$var = (!!!!$something['key']->my['key']->chained instanceof \Something); + +$var = !!$something[$a + 1] instanceof \Something; +$var = !!$something[++$a] instanceof \Something; + +$var = !!callMe($something[$a + 1]) instanceof \Something; +$var = !!new ClassA instanceof ('std' . 'Class'); + +if (!!match($a) { 'foo' => new Foo, 'bar' => new Bar } instanceof Bar) {} + +// Intentional parse error/live coding. +// This needs to be the last test in the file. +if(! diff --git a/Universal/Tests/CodeAnalysis/NoDoubleNegativeUnitTest.inc.fixed b/Universal/Tests/CodeAnalysis/NoDoubleNegativeUnitTest.inc.fixed new file mode 100644 index 00000000..185f9194 --- /dev/null +++ b/Universal/Tests/CodeAnalysis/NoDoubleNegativeUnitTest.inc.fixed @@ -0,0 +1,101 @@ +prop instanceof Something); +$var = !($something->method() instanceof Something); +$var = (bool)($something['key']->my['key']->chained instanceof Something); + +$var = (bool)(self::ENUM_SET_AS_CONSTANT instanceof \Something); +$var = (bool)(parent::ENUM_SET_AS_CONSTANT instanceof Something); +$var = (bool) (static::ENUM_SET_AS_CONSTANT instanceof \Something); + +if ((bool) + (function ($p) { + return new $p; +})('dummy') instanceof Foo) {} + +// If an instanceof is after another operator with lower precedence, we're good. +if ((bool)$something && $somethingElse instanceof \Something) {} +if ((bool)$something() || $somethingElse instanceof \Something) {} +if ((bool) $boolean !== $somethingElse instanceof Something) {} +while ((bool) $boolean = $somethingElse instanceof \Something) {} +if ( (bool) $boolean and $somethingElse instanceof \Something) {} +$var = (bool)$boolean ? $somethingElse instanceof Something : true; +$var = (bool)$boolean ?? $somethingElse instanceof \Something; + +// Instanceof where the not does not apply to. +if ((bool)$something($somethingElse instanceof \Something)) {} +if ((bool)['key' => $somethingElse instanceof \Something]['key']) {} +if ((bool)$array[$somethingElse instanceof \Something ? 'keyA' : 'keyB']) {} +if ((bool)match($a) { 'foo' => $a instanceof Foo, 'bar' => $a instanceof Bar } === true) {} + +// Note: this contains an intentional parse error, nothing to worry about. +if ((bool) + #[\Attr(self::ENUM_SET_AS_CONSTANT instanceof Foo)] + (function ($p) { + return $p instanceof Foo; +})('dummy')) {} + +// If it's an instanceof with triple not, we're good. +$var = !$something->method() instanceof \Something; + +// Make the error non-autofixable when used in combination with instanceof without parentheses to avoid issues with precedence. +if(!!$something instanceof \Something) {} + +$var = !!$something->prop instanceof \Something; + +$var = !!self::ENUM_SET_AS_CONSTANT instanceof \Something; +$var = (!!parent::ENUM_SET_AS_CONSTANT instanceof \Something); +$var = !! static::ENUM_SET_AS_CONSTANT instanceof \Something; + +$var = (!!!!$something['key']->my['key']->chained instanceof \Something); + +$var = !!$something[$a + 1] instanceof \Something; +$var = !!$something[++$a] instanceof \Something; + +$var = !!callMe($something[$a + 1]) instanceof \Something; +$var = !!new ClassA instanceof ('std' . 'Class'); + +if (!!match($a) { 'foo' => new Foo, 'bar' => new Bar } instanceof Bar) {} + +// Intentional parse error/live coding. +// This needs to be the last test in the file. +if(! diff --git a/Universal/Tests/CodeAnalysis/NoDoubleNegativeUnitTest.php b/Universal/Tests/CodeAnalysis/NoDoubleNegativeUnitTest.php new file mode 100644 index 00000000..ec377b1e --- /dev/null +++ b/Universal/Tests/CodeAnalysis/NoDoubleNegativeUnitTest.php @@ -0,0 +1,86 @@ + Key is the line number, value is the number of expected errors. + */ + public function getErrorList() + { + return [ + 16 => 1, + 18 => 1, + 19 => 1, + 20 => 1, + 21 => 1, + 22 => 1, + 24 => 1, + 30 => 1, + 34 => 1, + 43 => 1, + 45 => 1, + 46 => 1, + 47 => 1, + 49 => 1, + 50 => 1, + 51 => 1, + 53 => 1, + 59 => 1, + 60 => 1, + 61 => 1, + 62 => 1, + 63 => 1, + 64 => 1, + 65 => 1, + 68 => 1, + 69 => 1, + 70 => 1, + 71 => 1, + 74 => 1, + 81 => 1, + 84 => 1, + 86 => 1, + 88 => 1, + 89 => 1, + 90 => 1, + 92 => 1, + 94 => 1, + 95 => 1, + 97 => 1, + 98 => 1, + 100 => 1, + ]; + } + + /** + * Returns the lines where warnings should occur. + * + * @return array Key is the line number, value is the number of expected warnings. + */ + public function getWarningList() + { + return []; + } +} diff --git a/Universal/Tests/Operators/ConcatPositionUnitTest.inc b/Universal/Tests/Operators/ConcatPositionUnitTest.inc new file mode 100644 index 00000000..24569e7a --- /dev/null +++ b/Universal/Tests/Operators/ConcatPositionUnitTest.inc @@ -0,0 +1,122 @@ + Key is the line number, value is the number of expected errors. + */ + public function getErrorList() + { + return [ + 30 => 1, + 36 => 1, + 37 => 1, + 40 => 1, + 43 => 1, + 44 => 1, + 47 => 1, + 49 => 1, + 54 => 1, + 81 => 1, + 84 => 1, + 85 => 1, + 88 => 1, + 89 => 1, + 93 => 1, + 95 => 1, + 98 => 1, + 113 => 1, + ]; + } + + /** + * Returns the lines where warnings should occur. + * + * @return array Key is the line number, value is the number of expected warnings. + */ + public function getWarningList() + { + return []; + } +} diff --git a/Universal/Tests/PHP/LowercasePHPTagUnitTest.inc b/Universal/Tests/PHP/LowercasePHPTagUnitTest.inc new file mode 100644 index 00000000..c06d7659 --- /dev/null +++ b/Universal/Tests/PHP/LowercasePHPTagUnitTest.inc @@ -0,0 +1,8 @@ + + + + diff --git a/Universal/Tests/PHP/LowercasePHPTagUnitTest.inc.fixed b/Universal/Tests/PHP/LowercasePHPTagUnitTest.inc.fixed new file mode 100644 index 00000000..51cef43b --- /dev/null +++ b/Universal/Tests/PHP/LowercasePHPTagUnitTest.inc.fixed @@ -0,0 +1,8 @@ + + + + diff --git a/Universal/Tests/PHP/LowercasePHPTagUnitTest.php b/Universal/Tests/PHP/LowercasePHPTagUnitTest.php new file mode 100644 index 00000000..87a824b0 --- /dev/null +++ b/Universal/Tests/PHP/LowercasePHPTagUnitTest.php @@ -0,0 +1,48 @@ + Key is the line number, value is the number of expected errors. + */ + public function getErrorList() + { + return [ + 4 => 1, + 7 => 1, + 8 => 1, + ]; + } + + /** + * Returns the lines where warnings should occur. + * + * @return array Key is the line number, value is the number of expected warnings. + */ + public function getWarningList() + { + return []; + } +} diff --git a/composer.json b/composer.json index f4bbf2b9..b59f2d8e 100644 --- a/composer.json +++ b/composer.json @@ -17,7 +17,8 @@ ], "support" : { "issues" : "https://github.com/PHPCSStandards/PHPCSExtra/issues", - "source" : "https://github.com/PHPCSStandards/PHPCSExtra" + "source" : "https://github.com/PHPCSStandards/PHPCSExtra", + "security": "https://github.com/PHPCSStandards/PHPCSExtra/security/policy" }, "require" : { "php" : ">=5.4", diff --git a/phpstan.neon.dist b/phpstan.neon.dist index ba322bbb..060dda93 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -1,5 +1,5 @@ parameters: - #phpVersion: 50400 # Needs to be 70100 or higher... sigh... + # phpVersion: 50400 # Needs to be 70100 or higher... sigh... level: 6 paths: - Modernize @@ -10,6 +10,8 @@ parameters: treatPhpDocTypesAsCertain: false ignoreErrors: + # yamllint disable rule:line-length + # Level 1 # Keep to stay in line with parent class. - @@ -22,21 +24,21 @@ parameters: - message: '`^Property \S+Sniff::\$(phpVersion|tabWidth) \(int\) in isset\(\) is not nullable\.$`' paths: - - Modernize\Sniffs\FunctionCalls\DirnameSniff.php - - Universal\Sniffs\Arrays\DuplicateArrayKeySniff.php - - Universal\Sniffs\CodeAnalysis\ConstructorDestructorReturnSniff.php - - Universal\Sniffs\WhiteSpace\CommaSpacingSniff.php - - Universal\Sniffs\WhiteSpace\DisallowInlineTabsSniff.php - - Universal\Sniffs\WhiteSpace\PrecisionAlignmentSniff.php + - Modernize\Sniffs\FunctionCalls\DirnameSniff.php + - Universal\Sniffs\Arrays\DuplicateArrayKeySniff.php + - Universal\Sniffs\CodeAnalysis\ConstructorDestructorReturnSniff.php + - Universal\Sniffs\WhiteSpace\CommaSpacingSniff.php + - Universal\Sniffs\WhiteSpace\DisallowInlineTabsSniff.php + - Universal\Sniffs\WhiteSpace\PrecisionAlignmentSniff.php - message: '`^Strict comparison using === between true and false will always evaluate to false\.$`' paths: - - Modernize\Sniffs\FunctionCalls\DirnameSniff.php - - Universal\Sniffs\Arrays\DuplicateArrayKeySniff.php - - Universal\Sniffs\CodeAnalysis\ConstructorDestructorReturnSniff.php - - Universal\Sniffs\WhiteSpace\CommaSpacingSniff.php - - Universal\Sniffs\WhiteSpace\DisallowInlineTabsSniff.php - - Universal\Sniffs\WhiteSpace\PrecisionAlignmentSniff.php + - Modernize\Sniffs\FunctionCalls\DirnameSniff.php + - Universal\Sniffs\Arrays\DuplicateArrayKeySniff.php + - Universal\Sniffs\CodeAnalysis\ConstructorDestructorReturnSniff.php + - Universal\Sniffs\WhiteSpace\CommaSpacingSniff.php + - Universal\Sniffs\WhiteSpace\DisallowInlineTabsSniff.php + - Universal\Sniffs\WhiteSpace\PrecisionAlignmentSniff.php - message: '`^Property PHPCSExtra\\Universal\\Sniffs\\Arrays\\DuplicateArrayKeySniff\:\:\$currentMaxIntKey[GL]t8 \(int\) in isset\(\) is not nullable\.$`' path: Universal\Sniffs\Arrays\DuplicateArrayKeySniff.php @@ -50,3 +52,5 @@ parameters: # We're not using strict types, so this will be juggled without any issues. - '#^Parameter \#3 \$value of method \S+File::recordMetric\(\) expects string, \(?(float|int|bool)(<[^>]+>)?(\|(float|int|bool)(<[^>]+>)?)*\)? given\.$#' - '#^Parameter \#2 \$content of method \S+Fixer::replaceToken\(\) expects string, \(?(float|int|bool)(<[^>]+>)?(\|(float|int|bool)(<[^>]+>)?)*\)? given\.$#' + + # yamllint enable rule:line-length