From b0bd854b5c2518ea69a2b4d8ece4f992a3b2c82f Mon Sep 17 00:00:00 2001 From: jrfnl Date: Wed, 26 Dec 2018 20:39:02 +0100 Subject: [PATCH 01/42] Composer: sync with other config files I've done a compare between the `composer.json` config files in (nearly) all plugin/library repos. This commit adds some additional properties to the config file to improve consistency with the other repos. Additionally, it adds a `fix-cs` script. Note: The `fix-cs` script does not have `--runtime-set testVersion 5.4-` in the command as that is only relevant for PHPCompatibility and the PHPCompatibility standard does not contain any fixers, so setting the `testVersion` is not needed when running `phpcbf`. --- composer.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 836cdad1..5f2fff8f 100644 --- a/composer.json +++ b/composer.json @@ -7,6 +7,7 @@ "wordpress", "yoast" ], + "homepage": "https://github.com/Yoast/yoastcs", "license": "MIT", "authors": [ { @@ -17,7 +18,8 @@ ], "type" : "phpcodesniffer-standard", "support": { - "issues": "https://github.com/Yoast/yoastcs/issues" + "issues": "https://github.com/Yoast/yoastcs/issues", + "source": "https://github.com/Yoast/yoastcs" }, "require": { "php": ">=5.4", @@ -32,6 +34,8 @@ "roave/security-advisories": "dev-master", "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" }, + "minimum-stability": "dev", + "prefer-stable": true, "scripts": { "config-yoastcs" : [ "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin::run", @@ -40,6 +44,9 @@ "check-cs": [ "@php ./vendor/squizlabs/php_codesniffer/bin/phpcs --runtime-set testVersion 5.4-" ], + "fix-cs": [ + "@php ./vendor/squizlabs/php_codesniffer/bin/phpcbf" + ], "test": [ "@php ./vendor/phpunit/phpunit/phpunit --filter Yoast --bootstrap=\"./vendor/squizlabs/php_codesniffer/tests/bootstrap.php\" ./vendor/squizlabs/php_codesniffer/tests/AllTests.php" ] From 788965630e76106b0b0c69813366c9c0091405a4 Mon Sep 17 00:00:00 2001 From: jrfnl Date: Wed, 13 Feb 2019 00:14:20 +0100 Subject: [PATCH 02/42] Travis: test builds against PHP 7.4 Nightly has become PHP 8.0 since PHP 7.4 has been branched, so to continue to also test against PHP 7.4, it needs to be added separately. Refs: * https://twitter.com/nikita_ppv/status/1089839541828112384 * https://twitter.com/nikita_ppv/status/1094897743594770433 As PHP 8.x - current `nightly` - is not expected to be released until December 2020, with PHP 7.4 expected in December 2019, I've elected to replace the build against `nightly` with a build against `7.4snapshot`. Once PHP 7.4 is released, the "high PHP" build - currently PHP 7.3 - should be replaced with a build against PHP 7.4 (stable) and the "unstable" build - now `7.4snapshot` - should be reverted to `nightly`. --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index c0d00b14..67d02671 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,7 +21,7 @@ php: - 7.1 - 7.2 - 7.3 - - nightly + - "7.4snapshot" env: # Test against the highest supported PHPCS version. @@ -42,7 +42,7 @@ matrix: allow_failures: # Allow failures for unstable builds. - - php: nightly + - php: "7.4snapshot" before_install: # Speed up build time by disabling Xdebug. From 02b441d012fd20018777ee5734e2bd9eef6a55de Mon Sep 17 00:00:00 2001 From: jrfnl Date: Wed, 13 Feb 2019 00:20:28 +0100 Subject: [PATCH 03/42] Travis: work around PHPUnit 8.x on PHP >= 7.2 images As of recently, the Travis images for PHP 7.2+ ship with PHPUnit 8.x. The PHPCS native test framework is not compatible with PHPUnit 8.x and won't be able to be made compatible with it until the minimum PHP version would be raised to PHP 7.1. So for the unit tests to be able to run on PHP 7.2+, we need to explicitly require PHPUnit 7.x for those builds. As for nightly: there is no PHPUnit version which is currently compatible with PHP 8. --- Technical explanation: PHPUnit 8 adds `void` return type declarations to the PHPUnit methods used by PHPCS, making it neigh impossible to make the unit test suite cross-version compatible for the PHP versions supported by PHPCS as the `void` return type was only added in PHP 7.1 and the minimum PHP version supported by PHPCS is 5.4. Officially, PHPUnit 7 is compatible with PHP 7.1, 7.2 and 7.3. However for the functionality used by the PHPCS test suite, it looks to be compatible with PHP 7.4 as well (for now). Ref: https://phpunit.de/supported-versions.html For this reason, for Travis images which come natively with PHPUnit 8 (PHP >= 7.2), PHPUnit 7 needs to be installed via Composer. This is not necessary for older PHP versions as the Travis native PHPUnit version works fine for those. As for nightly/PHP 8: * The Travis native PHPUnit version for nightly is PHPUnit 8, which will not work. * Running composer install for PHPUnit on nightly currently installs PHPUnit 4.1.6. Most likely because the PHPUnit 4.x composer.json file did not yet contain a PHP requirement, while any higher PHPUnit versions do. Anyways, that will most definitely not work. * Running composer install for PHPUnit with an explicit requirement of PHPUnit 7.x will also not work as PHPUnit 7 will not install on PHP 8 based on the PHPUnit composer.json file. All in all, the unit tests can not currently be run on PHP 8. --- .travis.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 67d02671..79d0a585 100644 --- a/.travis.yml +++ b/.travis.yml @@ -61,6 +61,8 @@ before_install: else # For testing the YoastCS native sniffs, the rest of the packages aren't needed. composer remove wp-coding-standards/wpcs phpcompatibility/phpcompatibility-wp phpmd/phpmd --no-update + # The Travis images for PHP >= 7.2 ship with PHPUnit 8, but the unit test suite is not compatible with that. + if [[ ${TRAVIS_PHP_VERSION:0:3} > "7.1" ]]; then composer require phpunit/phpunit:^7.0 --no-update --no-suggest --no-scripts;fi # This will now only install the version of PHPCS to test against. composer install --no-dev --no-suggest --no-scripts # Set the installed_paths for the test environment. @@ -69,7 +71,12 @@ before_install: script: - if [[ "$PHPLINT" == "1" ]]; then if find . -path ./vendor -prune -o -name "*.php" -exec php -l {} \; | grep "^[Parse error|Fatal error]"; then exit 1; fi; fi - - phpunit --filter Yoast --bootstrap="$PHPCS_DIR/tests/bootstrap.php" $PHPCS_DIR/tests/AllTests.php + - | + if [[ ${TRAVIS_PHP_VERSION:0:3} > "7.1" ]]; then + vendor/bin/phpunit --filter Yoast --bootstrap="$PHPCS_DIR/tests/bootstrap.php" $PHPCS_DIR/tests/AllTests.php + else + phpunit --filter Yoast --bootstrap="$PHPCS_DIR/tests/bootstrap.php" $PHPCS_DIR/tests/AllTests.php + fi # Check the codestyle of the files within YoastCS. - if [[ "$SNIFF" == "1" ]]; then composer check-cs; fi # Validate the xml files. From cf4504b0b565c14210fcf5603a3290bc8831f095 Mon Sep 17 00:00:00 2001 From: jrfnl Date: Thu, 28 Mar 2019 23:12:28 +0100 Subject: [PATCH 04/42] Add new Yoast.Yoast.AlternativeFunctions sniff This introduces a new sniff to YoastCS: `Yoast.Yoast.AlternativeFunctions` which will initially sniff for function calls to `json_encode()` and `wp_json_encode()` and throw an `error` when these are detected, suggesting to use `WPSEO_Utils::format_json_encode()` instead. The sniff has been set up to easily allow for additional function groups to be added where the use of PHP/WP native functions should be discouraged in favour of using Yoast native functions. Includes auto-fixer. Includes unit tests. Includes disabling the WPCS native check for `json_encode()` which recommends using `wp_json_encode()`. Additional notes: * The sniff will not trigger on calls to non-global functions of the same name. * As it currently is, the sniff will throw false positives for namespaced functions imported via a `use` statement, like in the below code sample, but this is not an issue for the current Yoast code bases. ```php use My\Namespace\some_function as json_encode; json_encode( $data ); ``` * As it currently is, the sniff will throw a false positive for code using the `namespace` keyword as an operator. This will be fixed in WPCS 2.1.0 and the unit test can be uncommented once WPCS 2.1.0 has been released and YoastCS ups its minimum WPCS requirement. * The auto-fixer will only be allowed to run when the function call detected only passes one parameter. Both the `json_encode()`, as well as the `wp_json_encode()` functions allow for passing three parameters `$data, $options, $depth`. The new Yoast native `WPSEO_Utils:format_json_encode()` method only allows for the first parameter and auto-fixing would break any function call passing more than one parameter. An (non-fixable) error with a separate error code will still be thrown in that case. * The auto-fixer takes into account that code may be namespaced and will prefix the replacement with a `\` to indicate that the class name is fully qualified if it is detected that the file is namespaced. It will *not* add a `use` statement for the class, that is outside the scope of this sniff. * While the duplication within the name may appear a little redundant, I've created this to mirror WPCS, where the `WP` category has sniffs regarding WP features being sniffed for, just like this sniff is sniffing for a `Yoast` specific feature (not) being used. Alternative category name suggestions welcome. * Once the sniff has been merged and WPSEO has adopted the latest version of YoastCS, the call to `wp_json_encode()` from within the `WPSEO_Utils::format_json_encode()` function will need to be whitelisted for this sniff. Fixes 124 --- .../Yoast/AlternativeFunctionsSniff.php | 97 +++++++++++++++++++ .../Yoast/AlternativeFunctionsUnitTest.inc | 22 +++++ .../AlternativeFunctionsUnitTest.inc.fixed | 22 +++++ .../Yoast/AlternativeFunctionsUnitTest.php | 41 ++++++++ Yoast/ruleset.xml | 9 ++ 5 files changed, 191 insertions(+) create mode 100644 Yoast/Sniffs/Yoast/AlternativeFunctionsSniff.php create mode 100644 Yoast/Tests/Yoast/AlternativeFunctionsUnitTest.inc create mode 100644 Yoast/Tests/Yoast/AlternativeFunctionsUnitTest.inc.fixed create mode 100644 Yoast/Tests/Yoast/AlternativeFunctionsUnitTest.php diff --git a/Yoast/Sniffs/Yoast/AlternativeFunctionsSniff.php b/Yoast/Sniffs/Yoast/AlternativeFunctionsSniff.php new file mode 100644 index 00000000..fbe97e8d --- /dev/null +++ b/Yoast/Sniffs/Yoast/AlternativeFunctionsSniff.php @@ -0,0 +1,97 @@ + array( + 'type' => 'error', + 'message' => 'Detected a call to %s(). Use %s() instead.', + 'functions' => array( + 'json_encode', + 'wp_json_encode', + ), + 'replacement' => 'WPSEO_Utils::format_json_encode', + ), + ); + } + + /** + * Process a matched token. + * + * @param int $stackPtr The position of the current token in the stack. + * @param string $group_name The name of the group which was matched. + * @param string $matched_content The token content (function name) which was matched. + * + * @return int|void Integer stack pointer to skip forward or void to continue + * normal file processing. + */ + public function process_matched_token( $stackPtr, $group_name, $matched_content ) { + + $replacement = ''; + if ( isset( $this->groups[ $group_name ]['replacement'] ) ) { + $replacement = $this->groups[ $group_name ]['replacement']; + } + + $fixable = true; + $error = $this->groups[ $group_name ]['message']; + $type = ( $this->groups[ $group_name ]['type'] === 'error' ); + $code = $this->string_to_errorcode( $group_name . '_' . $matched_content ); + $data = array( + $matched_content, + $replacement, + ); + + /* + * Deal with specific situations. + */ + switch ( $matched_content ) { + case 'json_encode': + case 'wp_json_encode': + /* + * The function `WPSEO_Utils:format_json_encode()` is only a valid alternative + * when only the first parameter is passed. + */ + if ( $this->get_function_call_parameter_count( $stackPtr ) !== 1 ) { + $fixable = false; + $code .= 'WithAdditionalParams'; + } + + break; + } + + if ( $fixable === false ) { + $this->addMessage( $error, $stackPtr, $type, $code, $data ); + return; + } + + $fix = $this->addFixableMessage( $error, $stackPtr, $type, $code, $data ); + if ( $fix === true ) { + $namespaced = $this->determine_namespace( $stackPtr ); + + if ( empty( $namespaced ) || empty( $replacement ) ) { + $this->phpcsFile->fixer->replaceToken( $stackPtr, $replacement ); + } + else { + $this->phpcsFile->fixer->replaceToken( $stackPtr, '\\' . $replacement ); + } + } + } +} diff --git a/Yoast/Tests/Yoast/AlternativeFunctionsUnitTest.inc b/Yoast/Tests/Yoast/AlternativeFunctionsUnitTest.inc new file mode 100644 index 00000000..ee9d2e6b --- /dev/null +++ b/Yoast/Tests/Yoast/AlternativeFunctionsUnitTest.inc @@ -0,0 +1,22 @@ +wp_json_encode( $thing ); // OK. +$json = MyNamespace\json_encode( $thing ); // OK. +//$json = namespace\wp_json_encode( $thing ); // OK - this will not (yet) be correctly recognized, but will with WPCS 2.1.0. + +// The sniff should trigger on these. +$json = json_encode( $thing ); // Error. +$json = wp_json_encode( $thing ); // Error. +return function_call(nested_call(json_encode( $thing ))); // Error. + +$json = json_encode ($value, $options); // Error, non-fixable. +$json = wp_json_encode( $data, $options, $depth ); // Error, non-fixable. + +namespace Yoast\CMS\Plugin\Dir; + +$json = json_encode( $thing ); // Error. +$json = wp_json_encode( $thing ); // Error. diff --git a/Yoast/Tests/Yoast/AlternativeFunctionsUnitTest.inc.fixed b/Yoast/Tests/Yoast/AlternativeFunctionsUnitTest.inc.fixed new file mode 100644 index 00000000..3a06a083 --- /dev/null +++ b/Yoast/Tests/Yoast/AlternativeFunctionsUnitTest.inc.fixed @@ -0,0 +1,22 @@ +wp_json_encode( $thing ); // OK. +$json = MyNamespace\json_encode( $thing ); // OK. +//$json = namespace\wp_json_encode( $thing ); // OK - this will not (yet) be correctly recognized, but will with WPCS 2.1.0. + +// The sniff should trigger on these. +$json = WPSEO_Utils::format_json_encode( $thing ); // Error. +$json = WPSEO_Utils::format_json_encode( $thing ); // Error. +return function_call(nested_call(WPSEO_Utils::format_json_encode( $thing ))); // Error. + +$json = json_encode ($value, $options); // Error, non-fixable. +$json = wp_json_encode( $data, $options, $depth ); // Error, non-fixable. + +namespace Yoast\CMS\Plugin\Dir; + +$json = \WPSEO_Utils::format_json_encode( $thing ); // Error. +$json = \WPSEO_Utils::format_json_encode( $thing ); // Error. diff --git a/Yoast/Tests/Yoast/AlternativeFunctionsUnitTest.php b/Yoast/Tests/Yoast/AlternativeFunctionsUnitTest.php new file mode 100644 index 00000000..dc4fd96c --- /dev/null +++ b/Yoast/Tests/Yoast/AlternativeFunctionsUnitTest.php @@ -0,0 +1,41 @@ + => + */ + public function getErrorList() { + return array( + 12 => 1, + 13 => 1, + 14 => 1, + 16 => 1, + 17 => 1, + 21 => 1, + 22 => 1, + ); + } + + /** + * Returns the lines where warnings should occur. + * + * @return array => + */ + public function getWarningList() { + return array(); + } +} diff --git a/Yoast/ruleset.xml b/Yoast/ruleset.xml index 3e170fd3..cbb3cb77 100644 --- a/Yoast/ruleset.xml +++ b/Yoast/ruleset.xml @@ -51,6 +51,15 @@ + + + + + + + + + From 57749624325cb3c094d015ca5da9e589513f67c9 Mon Sep 17 00:00:00 2001 From: jrfnl Date: Fri, 29 Mar 2019 00:39:35 +0100 Subject: [PATCH 05/42] Travis: test against high/low WPCS versions As the new `Yoast.Yoast.AlternativeFunctions` uses one of the WPCS native abstract classes, WPCS now as become a dependency we need for the unit tests to be able to run. These changes to the Travis file take care of that and will ensure that the sniffs are tested against the highest and lowest supported versions of WPCS in combination with the highest/lowest supported versions of PHPCS. --- .travis.yml | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/.travis.yml b/.travis.yml index 79d0a585..f19ea232 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,17 +24,18 @@ php: - "7.4snapshot" env: - # Test against the highest supported PHPCS version. - - PHPCS_BRANCH="dev-master" PHPLINT=1 - # Test against the lowest supported PHPCS version. - - PHPCS_BRANCH="3.4.0" + # Test against the highest/lowest supported PHPCS and WPCS versions. + - PHPCS_BRANCH="dev-master" WPCS="dev-develop" PHPLINT=1 + - PHPCS_BRANCH="dev-master" WPCS="2.0.0" + - PHPCS_BRANCH="3.4.0" WPCS="dev-develop" + - PHPCS_BRANCH="3.4.0" WPCS="2.0.0" matrix: fast_finish: true include: # Extra build to check for XML codestyle. - php: 7.1 - env: PHPCS_BRANCH="dev-master" SNIFF=1 + env: PHPCS_BRANCH="dev-master" WPCS="^2.0.0" SNIFF=1 addons: apt: packages: @@ -54,20 +55,22 @@ before_install: - export PHPCS_BIN=$(pwd)/vendor/bin/phpcs # Set the PHPCS version to test against. - composer require squizlabs/php_codesniffer:${PHPCS_BRANCH} --no-update --no-suggest --no-scripts + # Set the WPCS version to test against. + - composer require wp-coding-standards/wpcs:${WPCS} --no-update --no-suggest --no-scripts - | if [[ "$SNIFF" == "1" ]]; then composer install --dev --no-suggest # The DealerDirect Composer plugin script takes care of the installed_paths. else # For testing the YoastCS native sniffs, the rest of the packages aren't needed. - composer remove wp-coding-standards/wpcs phpcompatibility/phpcompatibility-wp phpmd/phpmd --no-update + composer remove phpcompatibility/phpcompatibility-wp phpmd/phpmd --no-update # The Travis images for PHP >= 7.2 ship with PHPUnit 8, but the unit test suite is not compatible with that. if [[ ${TRAVIS_PHP_VERSION:0:3} > "7.1" ]]; then composer require phpunit/phpunit:^7.0 --no-update --no-suggest --no-scripts;fi - # This will now only install the version of PHPCS to test against. - composer install --no-dev --no-suggest --no-scripts - # Set the installed_paths for the test environment. - $PHPCS_BIN --config-set installed_paths $(pwd) + # This will now only install the version of PHPCS/WPCS to test against. + composer install --no-dev --no-suggest + # The DealerDirect Composer plugincompos script takes care of the installed_paths. fi + - $PHPCS_BIN -i script: - if [[ "$PHPLINT" == "1" ]]; then if find . -path ./vendor -prune -o -name "*.php" -exec php -l {} \; | grep "^[Parse error|Fatal error]"; then exit 1; fi; fi From 75bc9843e78b5251ed05a85ceaa2e26d816fb87d Mon Sep 17 00:00:00 2001 From: jrfnl Date: Fri, 29 Mar 2019 11:36:18 +0100 Subject: [PATCH 06/42] AlternativeFunctions: improve variable names used --- .../Sniffs/Yoast/AlternativeFunctionsSniff.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Yoast/Sniffs/Yoast/AlternativeFunctionsSniff.php b/Yoast/Sniffs/Yoast/AlternativeFunctionsSniff.php index fbe97e8d..2e595151 100644 --- a/Yoast/Sniffs/Yoast/AlternativeFunctionsSniff.php +++ b/Yoast/Sniffs/Yoast/AlternativeFunctionsSniff.php @@ -50,11 +50,11 @@ public function process_matched_token( $stackPtr, $group_name, $matched_content $replacement = $this->groups[ $group_name ]['replacement']; } - $fixable = true; - $error = $this->groups[ $group_name ]['message']; - $type = ( $this->groups[ $group_name ]['type'] === 'error' ); - $code = $this->string_to_errorcode( $group_name . '_' . $matched_content ); - $data = array( + $fixable = true; + $message = $this->groups[ $group_name ]['message']; + $is_error = ( $this->groups[ $group_name ]['type'] === 'error' ); + $error_code = $this->string_to_errorcode( $group_name . '_' . $matched_content ); + $data = array( $matched_content, $replacement, ); @@ -70,19 +70,19 @@ public function process_matched_token( $stackPtr, $group_name, $matched_content * when only the first parameter is passed. */ if ( $this->get_function_call_parameter_count( $stackPtr ) !== 1 ) { - $fixable = false; - $code .= 'WithAdditionalParams'; + $fixable = false; + $error_code .= 'WithAdditionalParams'; } break; } if ( $fixable === false ) { - $this->addMessage( $error, $stackPtr, $type, $code, $data ); + $this->addMessage( $message, $stackPtr, $is_error, $error_code, $data ); return; } - $fix = $this->addFixableMessage( $error, $stackPtr, $type, $code, $data ); + $fix = $this->addFixableMessage( $message, $stackPtr, $is_error, $error_code, $data ); if ( $fix === true ) { $namespaced = $this->determine_namespace( $stackPtr ); From 86bf5a5347653bece96da95a5b28ad9ba9d22d27 Mon Sep 17 00:00:00 2001 From: jrfnl Date: Thu, 18 Apr 2019 01:15:15 +0200 Subject: [PATCH 07/42] Composer: update minimum CS versions * PHPCS has released version 3.4.2. * WPCS has released version 2.1.0. Refs: * https://github.com/squizlabs/PHP_CodeSniffer/releases/tag/3.4.1 * https://github.com/squizlabs/PHP_CodeSniffer/releases/tag/3.4.2 * https://github.com/WordPress-Coding-Standards/WordPress-Coding-Standards/releases/tag/2.1.0 --- composer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 5f2fff8f..9c9ebb07 100644 --- a/composer.json +++ b/composer.json @@ -23,8 +23,8 @@ }, "require": { "php": ">=5.4", - "squizlabs/php_codesniffer": "^3.4.0", - "wp-coding-standards/wpcs": "^2.0.0", + "squizlabs/php_codesniffer": "^3.4.2", + "wp-coding-standards/wpcs": "^2.1.0", "phpcompatibility/phpcompatibility-wp": "^2.0.0", "phpmd/phpmd": "^2.2.3", "dealerdirect/phpcodesniffer-composer-installer": "^0.5.0" From 11b1b2c753bd88b9acb1c1b477bcd694575756e8 Mon Sep 17 00:00:00 2001 From: jrfnl Date: Wed, 24 Apr 2019 13:01:25 +0200 Subject: [PATCH 08/42] Remove PHP Mess Detector PHPMD is currently not used by any of the Yoast plugins, nor is the package at this time being actively maintained. With this in mind, PHPMD will be removed from YoastCS. It can always be brought back in the future if needed. This PR: * Removes the dependency from the `composer.json` file. * Removes the PHPMD example configuration file. * Removes any and all references to PHPMD in other places, except for the changelog. --- .travis.yml | 6 ++---- README.md | 21 --------------------- composer.json | 1 - phpmd.xml | 37 ------------------------------------- 4 files changed, 2 insertions(+), 63 deletions(-) delete mode 100644 phpmd.xml diff --git a/.travis.yml b/.travis.yml index f19ea232..db211b83 100644 --- a/.travis.yml +++ b/.travis.yml @@ -63,12 +63,12 @@ before_install: # The DealerDirect Composer plugin script takes care of the installed_paths. else # For testing the YoastCS native sniffs, the rest of the packages aren't needed. - composer remove phpcompatibility/phpcompatibility-wp phpmd/phpmd --no-update + composer remove phpcompatibility/phpcompatibility-wp --no-update # The Travis images for PHP >= 7.2 ship with PHPUnit 8, but the unit test suite is not compatible with that. if [[ ${TRAVIS_PHP_VERSION:0:3} > "7.1" ]]; then composer require phpunit/phpunit:^7.0 --no-update --no-suggest --no-scripts;fi # This will now only install the version of PHPCS/WPCS to test against. composer install --no-dev --no-suggest - # The DealerDirect Composer plugincompos script takes care of the installed_paths. + # The DealerDirect PHPCS Composer plugin takes care of the installed_paths. fi - $PHPCS_BIN -i @@ -85,10 +85,8 @@ script: # Validate the xml files. # @link http://xmlsoft.org/xmllint.html - if [[ "$SNIFF" == "1" ]]; then xmllint --noout --schema ./vendor/squizlabs/php_codesniffer/phpcs.xsd ./Yoast/ruleset.xml; fi - - if [[ "$SNIFF" == "1" ]]; then xmllint --noout ./phpmd.xml; fi # Check the code-style consistency of the xml files. - if [[ "$SNIFF" == "1" ]]; then diff -B --tabsize=4 ./Yoast/ruleset.xml <(xmllint --format "./Yoast/ruleset.xml"); fi - - if [[ "$SNIFF" == "1" ]]; then diff -B --tabsize=4 ./phpmd.xml <(xmllint --format "./phpmd.xml"); fi # Validate the composer.json file. # @link https://getcomposer.org/doc/03-cli.md#validate - if [[ "$PHPLINT" == "1" ]]; then composer validate --no-check-all --strict; fi diff --git a/README.md b/README.md index 84f61838..6df01eb0 100644 --- a/README.md +++ b/README.md @@ -47,27 +47,6 @@ Refer to [Using PHP Code Sniffer Tool](https://www.jetbrains.com/phpstorm/help/u After installation `Yoast` standard will be available as a choice in PHP Code Sniffer Validation inspection. -## PHP Mess Detector - -Set of [PHP Mess Detector](http://phpmd.org/) rules. - -Original ruleset, produced for Yoast projects, taking under consideration typical WordPress practices and current state of code base. - -All issues are considered informational for code development and maintenance. - -### Command line - -```bash -"vendor/bin/phpmd" /path/to/folder/ text phpmd.xml -``` - -### PhpStorm - -Refer to [Using PHP Mess Detector](https://www.jetbrains.com/phpstorm/help/using-php-mess-detector.html) in PhpStorm documentation. - -After installation add `phpmd.xml` file from project as custom ruleset in PHP Mess Detector Validation inspection settings. - - ## Changelog The changelog for this package can be found in the [CHANGELOG.md](https://github.com/Yoast/yoastcs/blob/develop/CHANGELOG.md) file. diff --git a/composer.json b/composer.json index 9c9ebb07..ff703fb9 100644 --- a/composer.json +++ b/composer.json @@ -26,7 +26,6 @@ "squizlabs/php_codesniffer": "^3.4.2", "wp-coding-standards/wpcs": "^2.1.0", "phpcompatibility/phpcompatibility-wp": "^2.0.0", - "phpmd/phpmd": "^2.2.3", "dealerdirect/phpcodesniffer-composer-installer": "^0.5.0" }, "require-dev": { diff --git a/phpmd.xml b/phpmd.xml deleted file mode 100644 index 9bfa123e..00000000 --- a/phpmd.xml +++ /dev/null @@ -1,37 +0,0 @@ - - - - Yoast Standards - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 8f4b904a0a9dd78b863ca0882881a8320a3f90cd Mon Sep 17 00:00:00 2001 From: jrfnl Date: Wed, 8 May 2019 04:12:52 +0200 Subject: [PATCH 09/42] YoastCS: :sparkles: add new `Yoast.Commenting.TestsHaveCoversTag` sniff This new sniff checks that each test method/class has either at least one `@covers` tag or a `@coversNothing` tag. Notes: * As per the PHPUnit conventions, test classes are expected to have a class name ending in `Test`. * As per the PHPUnit conventions, test functions are expected to have a function name starting with `test`. * Functions within a test class where the function name doesn't start with test _will_ still be recognized as test methods if they have a `@test` annotation in the method docblock. * Having a `@covers` tag at the class level is allowed and will be recognized. In that case, the individual methods do not need the tag anymore. Includes unit tests. Note: potential enhancement for the future: check if when a class contains a `@covers` tag, methods do not contain the same `@covers` tag (as those would be redundant). --- .../Commenting/TestsHaveCoversTagSniff.php | 188 ++++++++++++++++++ .../Commenting/TestsHaveCoversTagUnitTest.inc | 105 ++++++++++ .../Commenting/TestsHaveCoversTagUnitTest.php | 36 ++++ 3 files changed, 329 insertions(+) create mode 100644 Yoast/Sniffs/Commenting/TestsHaveCoversTagSniff.php create mode 100644 Yoast/Tests/Commenting/TestsHaveCoversTagUnitTest.inc create mode 100644 Yoast/Tests/Commenting/TestsHaveCoversTagUnitTest.php diff --git a/Yoast/Sniffs/Commenting/TestsHaveCoversTagSniff.php b/Yoast/Sniffs/Commenting/TestsHaveCoversTagSniff.php new file mode 100644 index 00000000..4f002d9a --- /dev/null +++ b/Yoast/Sniffs/Commenting/TestsHaveCoversTagSniff.php @@ -0,0 +1,188 @@ +getTokens(); + + if ( $tokens[ $stackPtr ]['code'] === T_CLASS ) { + return $this->process_class( $phpcsFile, $stackPtr ); + } + + if ( $tokens[ $stackPtr ]['code'] === T_FUNCTION ) { + return $this->process_function( $phpcsFile, $stackPtr ); + } + } + + /** + * Processes the docblock for a class token. + * + * @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|int If covers annotations were found (or this is not a test class), + * will returns the stack pointer to the end of the class. + */ + protected function process_class( File $phpcsFile, $stackPtr ) { + $tokens = $phpcsFile->getTokens(); + $name = $phpcsFile->getDeclarationName( $stackPtr ); + + if ( substr( $name, -4 ) !== 'Test' ) { + // Not a test class. + if ( isset( $tokens[ $stackPtr ]['scope_closer'] ) ) { + // No need to examine the methods in this class. + return $tokens[ $stackPtr ]['scope_closer']; + } + + return; + } + + // @todo: Once PHPCS 3.5.0 is out, replace with call to new findCommentAboveOOStructure() method. + $find = [ + T_WHITESPACE, + T_ABSTRACT, + T_FINAL, + ]; + + $commentEnd = $stackPtr; + do { + $commentEnd = $phpcsFile->findPrevious( $find, ( $commentEnd - 1 ), null, true ); + } while ( $tokens[ $commentEnd ]['line'] === $tokens[ $stackPtr ]['line'] ); + + if ( $tokens[ $commentEnd ]['code'] !== T_DOC_COMMENT_CLOSE_TAG + || $tokens[ $commentEnd ]['line'] !== ( $tokens[ $stackPtr ]['line'] - 1 ) + ) { + // Class without (proper) docblock. Not our concern. + return; + } + + $commentStart = $tokens[ $commentEnd ]['comment_opener']; + + $foundCovers = false; + $foundCoversNothing = false; + foreach ( $tokens[ $commentStart ]['comment_tags'] as $tag ) { + if ( $tokens[ $tag ]['content'] === '@covers' ) { + $foundCovers = true; + } + + if ( $tokens[ $tag ]['content'] === '@coversNothing' ) { + $foundCoversNothing = true; + } + } + + if ( $foundCovers === true || $foundCoversNothing === true ) { + // Class level tags found, valid for all methods. No need to examine the individual methods. + if ( isset( $tokens[ $stackPtr ]['scope_closer'] ) ) { + return $tokens[ $stackPtr ]['scope_closer']; + } + } + } + + /** + * Processes the docblock for a function token. + * + * @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 + */ + protected function process_function( File $phpcsFile, $stackPtr ) { + $tokens = $phpcsFile->getTokens(); + + // @todo: Once PHPCS 3.5.0 is out, replace with call to new findCommentAboveOOStructure() method. + $find = Tokens::$methodPrefixes; + $find[] = T_WHITESPACE; + + $commentEnd = $stackPtr; + do { + $commentEnd = $phpcsFile->findPrevious( $find, ( $commentEnd - 1 ), null, true ); + } while ( $tokens[ $commentEnd ]['line'] === $tokens[ $stackPtr ]['line'] ); + + if ( $tokens[ $commentEnd ]['code'] !== T_DOC_COMMENT_CLOSE_TAG + || $tokens[ $commentEnd ]['line'] !== ( $tokens[ $stackPtr ]['line'] - 1 ) + ) { + // Function without (proper) docblock. Not our concern. + return; + } + + $commentStart = $tokens[ $commentEnd ]['comment_opener']; + + $foundTest = false; + $foundCovers = false; + $foundCoversNothing = false; + foreach ( $tokens[ $commentStart ]['comment_tags'] as $tag ) { + if ( $tokens[ $tag ]['content'] === '@test' ) { + $foundTest = true; + continue; + } + + if ( $tokens[ $tag ]['content'] === '@covers' ) { + $foundCovers = true; + continue; + } + + if ( $tokens[ $tag ]['content'] === '@coversNothing' ) { + $foundCoversNothing = true; + continue; + } + } + + $name = $phpcsFile->getDeclarationName( $stackPtr ); + if ( stripos( $name, 'test' ) !== 0 && $foundTest === false ) { + // Not a test method. + return; + } + + if ( $foundCovers === true || $foundCoversNothing === true ) { + // Docblock contains one or more @covers tags. + return; + } + + $phpcsFile->addError( + 'Each test function should have at least one @covers tag annotating which class/method/function is being tested. Tag missing for function %s()', + $stackPtr, + 'Missing', + array( $name ) + ); + } +} diff --git a/Yoast/Tests/Commenting/TestsHaveCoversTagUnitTest.inc b/Yoast/Tests/Commenting/TestsHaveCoversTagUnitTest.inc new file mode 100644 index 00000000..555aec50 --- /dev/null +++ b/Yoast/Tests/Commenting/TestsHaveCoversTagUnitTest.inc @@ -0,0 +1,105 @@ + => + */ + public function getErrorList() { + return array( + 59 => 1, + 88 => 1, + ); + } + + /** + * Returns the lines where warnings should occur. + * + * @return array => + */ + public function getWarningList() { + return array(); + } +} From d5b47ec836b8746b781bdb3a17bbee70376d48ac Mon Sep 17 00:00:00 2001 From: jrfnl Date: Wed, 8 May 2019 04:13:23 +0200 Subject: [PATCH 10/42] YoastCS/unit tests: add @covers tags --- Yoast/Tests/Commenting/CodeCoverageIgnoreDeprecatedUnitTest.php | 2 ++ Yoast/Tests/Commenting/FileCommentUnitTest.php | 2 ++ Yoast/Tests/Commenting/TestsHaveCoversTagUnitTest.php | 2 ++ Yoast/Tests/ControlStructures/IfElseDeclarationUnitTest.php | 2 ++ Yoast/Tests/Files/FileNameUnitTest.php | 2 ++ Yoast/Tests/Files/TestDoublesUnitTest.php | 2 ++ Yoast/Tests/Namespaces/NamespaceDeclarationUnitTest.php | 2 ++ Yoast/Tests/WhiteSpace/FunctionSpacingUnitTest.php | 2 ++ 8 files changed, 16 insertions(+) diff --git a/Yoast/Tests/Commenting/CodeCoverageIgnoreDeprecatedUnitTest.php b/Yoast/Tests/Commenting/CodeCoverageIgnoreDeprecatedUnitTest.php index a925dd27..b935d4a8 100644 --- a/Yoast/Tests/Commenting/CodeCoverageIgnoreDeprecatedUnitTest.php +++ b/Yoast/Tests/Commenting/CodeCoverageIgnoreDeprecatedUnitTest.php @@ -10,6 +10,8 @@ * @package Yoast\YoastCS * * @since 1.1.0 + * + * @covers YoastCS\Yoast\Sniffs\Commenting\CodeCoverageIgnoreDeprecatedSniff */ class CodeCoverageIgnoreDeprecatedUnitTest extends AbstractSniffUnitTest { diff --git a/Yoast/Tests/Commenting/FileCommentUnitTest.php b/Yoast/Tests/Commenting/FileCommentUnitTest.php index c54ba1b5..7fc68063 100644 --- a/Yoast/Tests/Commenting/FileCommentUnitTest.php +++ b/Yoast/Tests/Commenting/FileCommentUnitTest.php @@ -10,6 +10,8 @@ * @package Yoast\YoastCS * * @since 1.2.0 + * + * @covers YoastCS\Yoast\Sniffs\Commenting\FileCommentSniff */ class FileCommentUnitTest extends AbstractSniffUnitTest { diff --git a/Yoast/Tests/Commenting/TestsHaveCoversTagUnitTest.php b/Yoast/Tests/Commenting/TestsHaveCoversTagUnitTest.php index d385552f..93a39312 100644 --- a/Yoast/Tests/Commenting/TestsHaveCoversTagUnitTest.php +++ b/Yoast/Tests/Commenting/TestsHaveCoversTagUnitTest.php @@ -10,6 +10,8 @@ * @package Yoast\YoastCS * * @since 1.3.0 + * + * @covers YoastCS\Yoast\Sniffs\Commenting\TestsHaveCoversTagSniff */ class TestsHaveCoversTagUnitTest extends AbstractSniffUnitTest { diff --git a/Yoast/Tests/ControlStructures/IfElseDeclarationUnitTest.php b/Yoast/Tests/ControlStructures/IfElseDeclarationUnitTest.php index fea04ad7..327794b6 100644 --- a/Yoast/Tests/ControlStructures/IfElseDeclarationUnitTest.php +++ b/Yoast/Tests/ControlStructures/IfElseDeclarationUnitTest.php @@ -11,6 +11,8 @@ * * @since 0.4.1 * @since 0.5 This class now uses namespaces and is no longer compatible with PHPCS 2.x. + * + * @covers YoastCS\Yoast\Sniffs\ControlStructures\IfElseDeclarationSniff */ class IfElseDeclarationUnitTest extends AbstractSniffUnitTest { diff --git a/Yoast/Tests/Files/FileNameUnitTest.php b/Yoast/Tests/Files/FileNameUnitTest.php index 3cda5a3d..ebaa5df5 100644 --- a/Yoast/Tests/Files/FileNameUnitTest.php +++ b/Yoast/Tests/Files/FileNameUnitTest.php @@ -10,6 +10,8 @@ * @package Yoast\YoastCS * * @since 0.5 + * + * @covers YoastCS\Yoast\Sniffs\Files\FileNameSniff */ class FileNameUnitTest extends AbstractSniffUnitTest { diff --git a/Yoast/Tests/Files/TestDoublesUnitTest.php b/Yoast/Tests/Files/TestDoublesUnitTest.php index 597a1c9d..66116131 100644 --- a/Yoast/Tests/Files/TestDoublesUnitTest.php +++ b/Yoast/Tests/Files/TestDoublesUnitTest.php @@ -10,6 +10,8 @@ * @package Yoast\YoastCS * * @since 1.0.0 + * + * @covers YoastCS\Yoast\Sniffs\Files\TestDoublesSniff */ class TestDoublesUnitTest extends AbstractSniffUnitTest { diff --git a/Yoast/Tests/Namespaces/NamespaceDeclarationUnitTest.php b/Yoast/Tests/Namespaces/NamespaceDeclarationUnitTest.php index 1af45833..a6572f14 100644 --- a/Yoast/Tests/Namespaces/NamespaceDeclarationUnitTest.php +++ b/Yoast/Tests/Namespaces/NamespaceDeclarationUnitTest.php @@ -10,6 +10,8 @@ * @package Yoast\YoastCS * * @since 1.2.0 + * + * @covers YoastCS\Yoast\Sniffs\Namespaces\NamespaceDeclarationSniff */ class NamespaceDeclarationUnitTest extends AbstractSniffUnitTest { diff --git a/Yoast/Tests/WhiteSpace/FunctionSpacingUnitTest.php b/Yoast/Tests/WhiteSpace/FunctionSpacingUnitTest.php index c52c1e8e..d4061549 100644 --- a/Yoast/Tests/WhiteSpace/FunctionSpacingUnitTest.php +++ b/Yoast/Tests/WhiteSpace/FunctionSpacingUnitTest.php @@ -10,6 +10,8 @@ * @package Yoast\YoastCS * * @since 1.0.0 + * + * @covers YoastCS\Yoast\Sniffs\WhiteSpace\FunctionSpacingSniff */ class FunctionSpacingUnitTest extends AbstractSniffUnitTest { From 25ed1bdf10ddeb39da4dec553ef98f1eddd19144 Mon Sep 17 00:00:00 2001 From: jrfnl Date: Wed, 8 May 2019 04:12:05 +0200 Subject: [PATCH 11/42] YoastCS: :sparkles: add new `Yoast.Commenting.CoversTag` sniff This new sniff checks the following: * That the contents of PHPUnit `@covers` contains a supported annotation. Includes a fixer for some common errors. * That union/intersect annotations aren't used. If found, the annotation will be split in separate `@covers` tags. * That a docblock doesn't contain both a `@covers` tag as well as a `@coversNothing` tag. * That a docblock doesn't contain multiple `@coversNothing` tags. If found and none or only one of the annotations contains a comment, the superfluous tags will be removed. * That a docblock doesn't contain duplicate `@covers` tags. If found, the superfluous tags will be removed. Includes unit tests. Note: a future enhancement to this sniff could be to add support for detecting class-level `@coversDefaultClass` annotations and verifying that the default class name is not used in method-level `@covers` tags, including auto-fixing those. --- Yoast/Sniffs/Commenting/CoversTagSniff.php | 355 ++++++++++++++++++ Yoast/Tests/Commenting/CoversTagUnitTest.inc | 156 ++++++++ .../Commenting/CoversTagUnitTest.inc.fixed | 155 ++++++++ Yoast/Tests/Commenting/CoversTagUnitTest.php | 64 ++++ 4 files changed, 730 insertions(+) create mode 100644 Yoast/Sniffs/Commenting/CoversTagSniff.php create mode 100644 Yoast/Tests/Commenting/CoversTagUnitTest.inc create mode 100644 Yoast/Tests/Commenting/CoversTagUnitTest.inc.fixed create mode 100644 Yoast/Tests/Commenting/CoversTagUnitTest.php diff --git a/Yoast/Sniffs/Commenting/CoversTagSniff.php b/Yoast/Sniffs/Commenting/CoversTagSniff.php new file mode 100644 index 00000000..3ff0fd55 --- /dev/null +++ b/Yoast/Sniffs/Commenting/CoversTagSniff.php @@ -0,0 +1,355 @@ +[A-Z][a-zA-Z0-9_\x7f-\xff]*)\\\\)*(?P>OOName)(?:|::<[!]?(?:public|protected|private)>|::(?(?!public$|protected$|private$)[a-z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*))?|::(?P>functionName)|\\\\?(?:(?P>OOName)\\\\)+(?P>functionName))'; + + /** + * Base error message. + * + * Will be enhanced during the run. + * + * @var string + */ + const ERROR_MSG = 'Invalid @covers annotation found.'; + + /** + * Returns an array of tokens this test wants to listen for. + * + * @return array + */ + public function register() { + return array( + T_DOC_COMMENT_OPEN_TAG, + ); + } + + /** + * Processes this test, when one of its tokens is encountered. + * + * @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(); + + $firstCoversTag = false; + $coversTags = []; + $coversNothingTags = []; + foreach ( $tokens[ $stackPtr ]['comment_tags'] as $tag ) { + if ( $tokens[ $tag ]['content'] === '@coversNothing' ) { + $coversNothingTags[] = $tag; + continue; + } + + if ( $tokens[ $tag ]['content'] !== '@covers' ) { + continue; + } + + if ( $firstCoversTag === false ) { + $firstCoversTag = $tag; + } + + // Found a @covers tag. + $next = $phpcsFile->findNext( T_DOC_COMMENT_WHITESPACE, ( $tag + 1 ), null, true ); + if ( $tokens[ $next ]['code'] !== T_DOC_COMMENT_STRING + || $tokens[ $next ]['line'] !== $tokens[ $tag ]['line'] + ) { + $phpcsFile->addError( + 'A @covers tag must indicate which class/function/method is being covered by the test', + $tag, + 'Empty' + ); + + continue; + } + + $annotation = $tokens[ $next ]['content']; + $coversTags[ "$tag-$next" ] = $annotation; + + if ( preg_match( '`^' . self::VALID_CONTENT_REGEX . '$`', $annotation ) === 1 ) { + continue; + } + + /* + * Account for a number of common "mistakes". + */ + + $errorThrown = false; + + // Check for Union/Intersect types. + if ( strpos( $annotation, '&' ) !== false ) { + if ( $this->fixAnnotationToSplit( $phpcsFile, $next, 'IntersectFound', '&' ) === true ) { + continue; + } + + $errorThrown = true; + } + + if ( strpos( $annotation, '|' ) !== false ) { + if ( $this->fixAnnotationToSplit( $phpcsFile, $next, 'UnionFound', '|' ) === true ) { + continue; + } + + $errorThrown = true; + } + + // Parentheses/Braces at the end of the annotation. + $expected = rtrim( $annotation, '(){} ' ); + if ( $this->fixSimpleError( $phpcsFile, $next, $expected, 'InvalidBraces' ) === true ) { + $errorThrown = true; + + } + + // Incorrect `` annotation. + if ( preg_match( '`::[{(\[]?(!)?(public|protected|private)[})\]]?`', $annotation, $matches ) === 1 ) { + $replacement = '::<' . $matches[1] . $matches[2] . '>'; + $expected = str_replace( $matches[0], $replacement, $annotation ); + + if ( $this->fixSimpleError( $phpcsFile, $next, $expected, 'InvalidFunctionGroup' ) === true ) { + $errorThrown = true; + } + } + + if ( $errorThrown === true ) { + // We've already thrown an error. No need for duplicates. + continue; + } + + // Throw a generic error for all other invalid annotations. + $error = self::ERROR_MSG; + $error .= ' Check the PHPUnit documentation to see which annotations are supported. Found: %s'; + $data = array( $annotation ); + $phpcsFile->addError( $error, $next, 'Invalid', $data ); + } + + $coversNothingCount = count( $coversNothingTags ); + if ( $firstCoversTag !== false && $coversNothingCount > 0 ) { + $error = 'A test can\'t both cover something as well as cover nothing. First @coversNothing tag encountered on line %d; first @covers tag encountered on line %d'; + $data = array( + $tokens[ $coversNothingTags[0] ]['line'], + $tokens[ $firstCoversTag ]['line'], + ); + + $phpcsFile->addError( $error, $tokens[ $stackPtr ]['comment_closer'], 'Contradictory', $data ); + } + + if ( $coversNothingCount > 1 ) { + $error = 'Only one @coversNothing tag allowed per test'; + $code = 'DuplicateCoversNothing'; + $fixable = true; + $removeTags = array(); + foreach ( $coversNothingTags as $position => $ptr ) { + $next = ( $ptr + 1 ); + if ( $tokens[ $next ]['code'] === T_DOC_COMMENT_WHITESPACE + && $tokens[ $next ]['content'] === $phpcsFile->eolChar + ) { + // No comment, ok to remove. + $removeTags[] = $ptr; + } + } + + $removalCount = count( $removeTags ); + if ( ( $coversNothingCount - $removalCount ) > 1 ) { + // More than one tag had a comment. + $phpcsFile->addError( $error, $tokens[ $stackPtr ]['comment_closer'], $code ); + } + else { + + $fix = $phpcsFile->addFixableError( $error, $tokens[ $stackPtr ]['comment_closer'], $code ); + if ( $fix === true ) { + $skipFirst = ( $coversNothingCount === $removalCount ); + + $phpcsFile->fixer->beginChangeset(); + + foreach ( $removeTags as $key => $ptr ) { + if ( $skipFirst === true && $key === 0 ) { + // Let the first one remain if none of the tags has a comment. + continue; + } + + // Remove the whole line. + for ( $i = ( $ptr + 1 ); $i >= 0; $i-- ) { + if ( $tokens[ $i ]['line'] !== $tokens[ $ptr ]['line'] ) { + break; + } + + $phpcsFile->fixer->replaceToken( $i, '' ); + } + } + + $phpcsFile->fixer->endChangeset(); + } + } + } + + $coversCount = count( $coversTags ); + if ( $coversCount > 1 ) { + $unique = array_unique( $coversTags ); + if ( count( $unique ) !== $coversCount ) { + $value_count = array_count_values( $coversTags ); + $error = 'Duplicate @covers tag found. First tag with the same annotation encountered on line %d'; + $code = 'DuplicateCovers'; + foreach ( $value_count as $annotation => $count ) { + if ( $count < 2 ) { + continue; + } + + $first = null; + foreach ( $coversTags as $ptrs => $annot ) { + if ( $annotation !== $annot ) { + continue; + } + + if ( ! isset( $first ) ) { + $first = explode( '-', $ptrs ); + $data = array( $tokens[ $first[0] ]['line'] ); + continue; + } + + $ptrs = explode( '-', $ptrs ); + + $fix = $phpcsFile->addFixableError( $error, $ptrs[0], $code, $data ); + if ( $fix === true ) { + + $phpcsFile->fixer->beginChangeset(); + + // Remove the whole line. + for ( $i = ( $ptrs[1] ); $i >= 0; $i-- ) { + if ( $tokens[ $i ]['line'] !== $tokens[ $ptrs[1] ]['line'] ) { + if ( $tokens[ $i ]['code'] === T_DOC_COMMENT_WHITESPACE + && $tokens[ $i ]['content'] === $phpcsFile->eolChar + ) { + $phpcsFile->fixer->replaceToken( $i, '' ); + } + break; + } + + $phpcsFile->fixer->replaceToken( $i, '' ); + } + + $phpcsFile->fixer->endChangeset(); + } + } + } + } + } + } + + /** + * Add a fixable error if a suitable alternative is available. + * + * @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 string $expected The expected alternative annotation. + * This annotation might not be valid itself. + * @param string $errorCode The error code. + * + * @return bool Whether an error has been thrown or not. + */ + protected function fixSimpleError( File $phpcsFile, $stackPtr, $expected, $errorCode ) { + $tokens = $phpcsFile->getTokens(); + $annotation = $tokens[ $stackPtr ]['content']; + + if ( $expected === $annotation + || preg_match( '`^' . self::VALID_CONTENT_REGEX . '$`', $expected ) !== 1 + ) { + return false; + } + + $error = self::ERROR_MSG . "\nExpected: `%s`\nFound: `%s`"; + $data = array( + $expected, + $annotation, + ); + + $fix = $phpcsFile->addFixableError( $error, $stackPtr, $errorCode, $data ); + if ( $fix === true ) { + $phpcsFile->fixer->replaceToken( $stackPtr, $expected ); + } + + return true; + } + + /** + * Add a fixable error for a union/intersect @covers annotation. + * + * @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 string $errorCode The error code. + * @param string $separator The separator to split the + * annotation on. + * + * @return bool Whether to skip the rest of the annotation examination or not. + */ + protected function fixAnnotationToSplit( File $phpcsFile, $stackPtr, $errorCode, $separator ) { + $fix = $phpcsFile->addFixableError( + 'Each @covers annotation should reference only one covered structure', + $stackPtr, + $errorCode + ); + + if ( $fix === true ) { + $tokens = $phpcsFile->getTokens(); + $annotation = $tokens[ $stackPtr ]['content']; + $annotations = explode( $separator, $annotation ); + $annotations = array_map( 'trim', $annotations ); + $annotations = array_filter( $annotations ); // Remove empties. + + $phpcsFile->fixer->beginChangeset(); + $phpcsFile->fixer->replaceToken( $stackPtr, '' ); + + for ( $i = ( $stackPtr - 1 ); $i >= 0; $i-- ) { + if ( $tokens[ $i ]['line'] !== $tokens[ $stackPtr ]['line'] ) { + break; + } + + $phpcsFile->fixer->replaceToken( $i, '' ); + } + + $stub = $phpcsFile->getTokensAsString( $i, ( $stackPtr - $i ), true ); + $replacement = ''; + foreach ( $annotations as $annotation ) { + $replacement .= $stub . $annotation; + } + + $phpcsFile->fixer->replaceToken( $i, $replacement ); + $phpcsFile->fixer->endChangeset(); + + return true; + } + + return false; + } +} diff --git a/Yoast/Tests/Commenting/CoversTagUnitTest.inc b/Yoast/Tests/Commenting/CoversTagUnitTest.inc new file mode 100644 index 00000000..419d2c6b --- /dev/null +++ b/Yoast/Tests/Commenting/CoversTagUnitTest.inc @@ -0,0 +1,156 @@ + + * @covers Class_Name:: + * @covers Class_Name:: + * @covers Class_Name:: + * @covers Class_Name:: + * @covers Class_Name:: + * @covers Class_Name:: + * @covers Name\Space\Class_Name + * @covers \Name\Space\Class_Name + * @covers Name\Space\Class_Name:: + * @covers Name\Space\Class_Name:: + * @covers Name\Space\Class_Name:: + * @covers Name\Space\Class_Name:: + * @covers Name\Space\Class_Name:: + * @covers Name\Space\Class_Name:: + * @covers Class_Name::method_name + * @covers \Class_name::method_name + * @covers Name\Space\Class_Name::method_name + * @covers \Name\Space\Class_name::method_name + * + * Incorrect: + * @covers global_function + * @covers ::global_func() + * @covers Name\Space\func_name() + * @covers My_Class {} + * @covers self::method_name + * @covers My_Class::another_method_name() + * @covers Name\Space\My_Class::another_method_name() + */ + public function testCoversTag() {} + + /** + * Docblock. + * + * @covers ::global_functionA && ::other_functionA + * @covers ::global_functionB & ::other_functionB + * @covers ::global_functionC|::other_functionC + * @covers ::global_functionD||::other_functionD & Some_Class_Name + */ + public function testCoversTagSplitUnionIntersect() {} + + /** + * Docblock. + * + * @covers Class_Name::public + * @covers Class_Name::protected + * @covers Class_Name::private + */ + public function testCoversTagFixGroupsTypeA() {} + + /** + * Docblock. + * + * @covers Class_Name::!public + * @covers Class_Name::!protected + * @covers Class_Name::!private + */ + public function testCoversTagFixGroupsTypeB() {} + + /** + * Docblock. + * + * @covers Class_Name::{public} + * @covers Class_Name::(!protected) + * @covers Class_Name::[private] + */ + public function testCoversTagFixGroupsTypeC() {} + + /** + * Docblock. + * + * @covers + */ + public function testHasCoversTagNoContent() {} + + /** + * Docblock. + * + * @covers + * Some unrelated comment. + */ + public function testHasCoversTagNoContentOnSameLine() {} + + /** + * Docblock. + * + * @coversNothing + * @covers ::globalFunction + */ + public function testCoversNothingAndCoversTag() {} + + /** + * Docblock. + * + * @coversNothing + * @since x.x.x + * + * @coversNothing + * @param int $int Description. + * + * @coversNothing + */ + public function testDuplicateCoversNothingTagFixable($int) {} + + /** + * Docblock. + * + * @coversNothing + * @since x.x.x + * + * @coversNothing Some comment. + * @param int $int Description. + * + * @coversNothing + */ + public function testDuplicateCoversNothingTagWithCommentFixable($int) {} + + /** + * Docblock. + * + * @coversNothing + * @since x.x.x + * + * @coversNothing Some comment. + * @param int $int Description. + * + * @coversNothing Another comment. + */ + public function testDuplicateCoversNothingTagUnfixable($int) {} + + /** + * Docblock. + * + * @since x.x.x + * + * @covers Name\Space\function_name + * @covers Class_Name + * @covers Name\Space\function_name + * @covers Name\Space\function_name + * + * @param int $int Description. + */ + public function testDuplicateCoversTagFixable($int) {} +} diff --git a/Yoast/Tests/Commenting/CoversTagUnitTest.inc.fixed b/Yoast/Tests/Commenting/CoversTagUnitTest.inc.fixed new file mode 100644 index 00000000..f355236f --- /dev/null +++ b/Yoast/Tests/Commenting/CoversTagUnitTest.inc.fixed @@ -0,0 +1,155 @@ + + * @covers Class_Name:: + * @covers Class_Name:: + * @covers Class_Name:: + * @covers Class_Name:: + * @covers Class_Name:: + * @covers Class_Name:: + * @covers Name\Space\Class_Name + * @covers \Name\Space\Class_Name + * @covers Name\Space\Class_Name:: + * @covers Name\Space\Class_Name:: + * @covers Name\Space\Class_Name:: + * @covers Name\Space\Class_Name:: + * @covers Name\Space\Class_Name:: + * @covers Name\Space\Class_Name:: + * @covers Class_Name::method_name + * @covers \Class_name::method_name + * @covers Name\Space\Class_Name::method_name + * @covers \Name\Space\Class_name::method_name + * + * Incorrect: + * @covers global_function + * @covers ::global_func + * @covers Name\Space\func_name + * @covers My_Class + * @covers self::method_name + * @covers My_Class::another_method_name + * @covers Name\Space\My_Class::another_method_name + */ + public function testCoversTag() {} + + /** + * Docblock. + * + * @covers ::global_functionA + * @covers ::other_functionA + * @covers ::global_functionB + * @covers ::other_functionB + * @covers ::global_functionC + * @covers ::other_functionC + * @covers ::global_functionD + * @covers ::other_functionD + * @covers Some_Class_Name + */ + public function testCoversTagSplitUnionIntersect() {} + + /** + * Docblock. + * + * @covers Class_Name:: + * @covers Class_Name:: + * @covers Class_Name:: + */ + public function testCoversTagFixGroupsTypeA() {} + + /** + * Docblock. + * + * @covers Class_Name:: + * @covers Class_Name:: + * @covers Class_Name:: + */ + public function testCoversTagFixGroupsTypeB() {} + + /** + * Docblock. + * + * @covers Class_Name:: + * @covers Class_Name:: + * @covers Class_Name:: + */ + public function testCoversTagFixGroupsTypeC() {} + + /** + * Docblock. + * + * @covers + */ + public function testHasCoversTagNoContent() {} + + /** + * Docblock. + * + * @covers + * Some unrelated comment. + */ + public function testHasCoversTagNoContentOnSameLine() {} + + /** + * Docblock. + * + * @coversNothing + * @covers ::globalFunction + */ + public function testCoversNothingAndCoversTag() {} + + /** + * Docblock. + * + * @coversNothing + * @since x.x.x + * + * @param int $int Description. + * + */ + public function testDuplicateCoversNothingTagFixable($int) {} + + /** + * Docblock. + * + * @since x.x.x + * + * @coversNothing Some comment. + * @param int $int Description. + * + */ + public function testDuplicateCoversNothingTagWithCommentFixable($int) {} + + /** + * Docblock. + * + * @coversNothing + * @since x.x.x + * + * @coversNothing Some comment. + * @param int $int Description. + * + * @coversNothing Another comment. + */ + public function testDuplicateCoversNothingTagUnfixable($int) {} + + /** + * Docblock. + * + * @since x.x.x + * + * @covers Name\Space\function_name + * @covers Class_Name + * + * @param int $int Description. + */ + public function testDuplicateCoversTagFixable($int) {} +} diff --git a/Yoast/Tests/Commenting/CoversTagUnitTest.php b/Yoast/Tests/Commenting/CoversTagUnitTest.php new file mode 100644 index 00000000..9734933f --- /dev/null +++ b/Yoast/Tests/Commenting/CoversTagUnitTest.php @@ -0,0 +1,64 @@ + => + */ + public function getErrorList() { + return array( + 34 => 1, + 35 => 1, + 36 => 1, + 37 => 1, + 38 => 1, + 39 => 1, + 40 => 1, + 47 => 1, + 48 => 1, + 49 => 1, + 50 => 2, + 57 => 1, + 58 => 1, + 59 => 1, + 66 => 1, + 67 => 1, + 68 => 1, + 75 => 1, + 76 => 1, + 77 => 1, + 84 => 1, + 91 => 1, + 101 => 1, + 114 => 1, + 127 => 1, + 140 => 1, + 150 => 1, + 151 => 1, + ); + } + + /** + * Returns the lines where warnings should occur. + * + * @return array => + */ + public function getWarningList() { + return array(); + } +} From 6d3d30a8914d7a64bd324499ae912cbe28ad32ad Mon Sep 17 00:00:00 2001 From: jrfnl Date: Thu, 16 May 2019 17:50:52 +0200 Subject: [PATCH 12/42] PHPCS: add common exclude patterns to the ruleset As these exclude patterns are a special kind of regex which will be applied to the _absolute_ path of the file being scanned, setting these in the `YoastCS` ruleset instead of in the repo specific custom configs will work fine and will allow us to simplify the repo specific rulesets a little. --- .phpcs.xml.dist | 3 --- Yoast/ruleset.xml | 16 ++++++++++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/.phpcs.xml.dist b/.phpcs.xml.dist index 3c267253..612a0033 100644 --- a/.phpcs.xml.dist +++ b/.phpcs.xml.dist @@ -8,9 +8,6 @@ . - - */vendor/* - diff --git a/Yoast/ruleset.xml b/Yoast/ruleset.xml index cbb3cb77..64ec9888 100644 --- a/Yoast/ruleset.xml +++ b/Yoast/ruleset.xml @@ -3,6 +3,22 @@ Yoast Coding Standards + + + + */.git/* + */.wordpress-svn/* + + + */node_modules/* + */vendor(_prefixed)?/* + + + + + + + From 2fe7e767a3a26a9de25e7c6c96dac23a0dacfa02 Mon Sep 17 00:00:00 2001 From: jrfnl Date: Thu, 16 May 2019 18:11:28 +0200 Subject: [PATCH 14/42] PHPCS: upgrade variable alignment issues from warning to error This is a simple whitespace issue which should be easy to solve in all cases. However, as the sniffs throw warnings instead of errors and warnings are ignored by some of the repos, these are typically issues which creep back into the repo quickly if builds don't break on them, necessitating a regular clean up again. By changing the message type for these sniffs from `warning` to `error`, we can safeguard that these issues will be kept at bay. --- Yoast/ruleset.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Yoast/ruleset.xml b/Yoast/ruleset.xml index cbb3cb77..ac08cb29 100644 --- a/Yoast/ruleset.xml +++ b/Yoast/ruleset.xml @@ -51,6 +51,10 @@ + + error + + @@ -61,6 +65,7 @@ + error From fe046f4e7a69754b54bdfc2497b40d69bd8fa4e1 Mon Sep 17 00:00:00 2001 From: jrfnl Date: Wed, 22 May 2019 02:38:21 +0200 Subject: [PATCH 15/42] Composer: require WPCS ^2.1.1 WPCS 2.1.1 has been released and contains a bug fix relevant to the Yoast repos. Previously test classes imported via `use` statements were not correctly recognized. While, the `use` statements still won't be examined, WPCS will now regard every class which extends or implements a class called `TestCase` as a test class. This allows us to remove some temporary exclusions in relation to this bug from select repository custom rulesets. Ref: https://github.com/WordPress-Coding-Standards/WordPress-Coding-Standards/releases/tag/2.1.1 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index ff703fb9..3a3f8f5f 100644 --- a/composer.json +++ b/composer.json @@ -24,7 +24,7 @@ "require": { "php": ">=5.4", "squizlabs/php_codesniffer": "^3.4.2", - "wp-coding-standards/wpcs": "^2.1.0", + "wp-coding-standards/wpcs": "^2.1.1", "phpcompatibility/phpcompatibility-wp": "^2.0.0", "dealerdirect/phpcodesniffer-composer-installer": "^0.5.0" }, From 204243e5110e13f6a89f8dd5de650a7ebeddac5d Mon Sep 17 00:00:00 2001 From: jrfnl Date: Wed, 22 May 2019 02:54:14 +0200 Subject: [PATCH 16/42] PHPCS ruleset: add sniff to check spacing between property declarations The `Yoast.WhiteSpace.FunctionSpacing` sniff which was added in 86 checks that each function within an OO structure has one blank line before the function declaration and no blank line(s) after the last function in a class. This sniff was added as there was quite some inconsistency regarding this across classes and repos and this way we could enforce consistency. Similar to this, we should also enforce consistency regarding the spacing out of property declarations in OO structures., which I've noticed becoming inconsistent in some repos as well. The sniff I'm proposing to add is a PHPCS native sniff which checks that there is one blank line before the first property declaration in an OO structure, as well as one blank line between property declarations in OO structures. The sniff include an auto-fixer. Ref: https://github.com/squizlabs/PHP_CodeSniffer/wiki/Customisable-Sniff-Properties#squizwhitespacemembervarspacing --- Yoast/ruleset.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Yoast/ruleset.xml b/Yoast/ruleset.xml index cbb3cb77..e6a93791 100644 --- a/Yoast/ruleset.xml +++ b/Yoast/ruleset.xml @@ -106,6 +106,9 @@ + + + From 2a480524a958e61b4a8d38034ba650b467012f47 Mon Sep 17 00:00:00 2001 From: jrfnl Date: Fri, 17 May 2019 06:10:52 +0200 Subject: [PATCH 17/42] YoastCS/FileName: remove support for passing properties as strings Both the public `exclude`, as well as the `prefixes` property for this sniff are by nature array properties. Setting array properties within unit tests was not yet properly supported by PHPCS until version 3.3.0. For that reason, the unit tests for this sniff still used the old-style property setting annotation and the sniff itself contained code to work around the issue. As this problem has been solved now in PHPCS, the work-arounds in the sniff can be removed and the new-style property setting can now be used in the unit tests. --- Yoast/Sniffs/Files/FileNameSniff.php | 14 ++------------ .../Tests/Files/FileNameUnitTests/ExcludedFile.inc | 5 +++-- .../classes/class-wpseo-some-class.inc | 5 +++-- .../classes/excluded-CLASS-file.inc | 5 +++-- .../Files/FileNameUnitTests/classes/some-class.inc | 5 +++-- .../FileNameUnitTests/classes/wpseo-some-class.inc | 5 +++-- .../Files/FileNameUnitTests/excluded-file.inc | 5 +++-- .../Files/FileNameUnitTests/excluded_file.inc | 5 +++-- .../functions/excluded-functions-file.inc | 5 +++-- .../interfaces/different-interface.inc | 5 +++-- .../interfaces/excluded-interface-file.inc | 5 +++-- .../interfaces/no-duplicate-interface.inc | 5 +++-- .../interfaces/outline-something-interface.inc | 5 +++-- .../interfaces/outline-something.inc | 5 +++-- .../interfaces/yoast-outline-something.inc | 5 +++-- .../Tests/Files/FileNameUnitTests/no-basepath.inc | 5 +++-- .../FileNameUnitTests/traits/different-trait.inc | 5 +++-- .../traits/excluded-trait-file.inc | 5 +++-- .../traits/no-duplicate-trait.inc | 5 +++-- .../traits/outline-something-trait.inc | 5 +++-- .../FileNameUnitTests/traits/outline-something.inc | 5 +++-- .../traits/yoast-outline-something.inc | 5 +++-- 22 files changed, 65 insertions(+), 54 deletions(-) diff --git a/Yoast/Sniffs/Files/FileNameSniff.php b/Yoast/Sniffs/Files/FileNameSniff.php index 857323de..b4bc0a80 100644 --- a/Yoast/Sniffs/Files/FileNameSniff.php +++ b/Yoast/Sniffs/Files/FileNameSniff.php @@ -32,7 +32,7 @@ class FileNameSniff implements Sniff { * * These prefixes do not need to be reflected in the file name. * - * @var string[]|string + * @var string[] */ public $prefixes = array(); @@ -53,7 +53,7 @@ class FileNameSniff implements Sniff { * from the root of the repository - , the PHPCS `--basepath` config variable * needs to be set. If it is not, a warning will be issued. * - * @var string[]|string + * @var string[] */ public $exclude = array(); @@ -238,7 +238,6 @@ protected function is_file_excluded( File $phpcsFile, $path_to_file ) { * Clean a custom array property received from a ruleset. * * Deals with incorrectly passed custom array properties. - * - If the property was passed as a string, change it to an array. * - Remove whitespace surrounding values. * - Remove empty array entries. * @@ -251,15 +250,6 @@ protected function is_file_excluded( File $phpcsFile, $path_to_file ) { * @return array */ protected function clean_custom_array_property( $property, $flip = false, $to_lower = false ) { - if ( is_bool( $property ) ) { - // Allow for resetting in the unit tests. - return array(); - } - - if ( is_string( $property ) ) { - $property = explode( ',', $property ); - } - $property = array_filter( array_map( 'trim', $property ) ); if ( true === $to_lower ) { diff --git a/Yoast/Tests/Files/FileNameUnitTests/ExcludedFile.inc b/Yoast/Tests/Files/FileNameUnitTests/ExcludedFile.inc index e65d364f..ad132138 100644 --- a/Yoast/Tests/Files/FileNameUnitTests/ExcludedFile.inc +++ b/Yoast/Tests/Files/FileNameUnitTests/ExcludedFile.inc @@ -1,5 +1,6 @@ -@codingStandardsChangeSetting Yoast.Files.FileName exclude ExcludedFile.inc + +phpcs:set Yoast.Files.FileName exclude[] ExcludedFile.inc +phpcs:set Yoast.Files.FileName prefixes[] wpseo,yoast +phpcs:set Yoast.Files.FileName exclude[] classes/excluded-CLASS-file.inc +phpcs:set Yoast.Files.FileName prefixes[] wpseo,yoast +phpcs:set Yoast.Files.FileName prefixes[] wpseo,yoast +phpcs:set Yoast.Files.FileName exclude[] excluded-file.inc +phpcs:set Yoast.Files.FileName exclude[] excluded_file.inc +phpcs:set Yoast.Files.FileName exclude[] functions/excluded-functions-file.inc +phpcs:set Yoast.Files.FileName prefixes[] wpseo,yoast +phpcs:set Yoast.Files.FileName exclude[] interfaces/excluded-interface-file.inc +phpcs:set Yoast.Files.FileName prefixes[] wpseo,yoast +phpcs:set Yoast.Files.FileName prefixes[] wpseo,yoast +phpcs:set Yoast.Files.FileName prefixes[] wpseo,yoast +phpcs:set Yoast.Files.FileName prefixes[] wpseo,yoast +phpcs:set Yoast.Files.FileName exclude[] no-basepath.inc +phpcs:set Yoast.Files.FileName prefixes[] wpseo,yoast +phpcs:set Yoast.Files.FileName exclude[] traits/excluded-trait-file.inc +phpcs:set Yoast.Files.FileName prefixes[] wpseo,yoast +phpcs:set Yoast.Files.FileName prefixes[] wpseo,yoast +phpcs:set Yoast.Files.FileName prefixes[] wpseo,yoast +phpcs:set Yoast.Files.FileName prefixes[] wpseo,yoast Date: Fri, 17 May 2019 06:11:34 +0200 Subject: [PATCH 18/42] YoastCS/FileName: remove outdated comment --- Yoast/Sniffs/Files/FileNameSniff.php | 1 - 1 file changed, 1 deletion(-) diff --git a/Yoast/Sniffs/Files/FileNameSniff.php b/Yoast/Sniffs/Files/FileNameSniff.php index b4bc0a80..f73a7a78 100644 --- a/Yoast/Sniffs/Files/FileNameSniff.php +++ b/Yoast/Sniffs/Files/FileNameSniff.php @@ -46,7 +46,6 @@ class FileNameSniff implements Sniff { * File names should be provided including the path to the file relative * to the "basepath" known to PHPCS. * File names should not be prefixed with a directory separator. - * File names are compared in a case-sensitive manner! * The list should be provided as a PHPCS array list. * * For this functionality to work with relative file paths - i.e. file paths From 0839d4645d576dcdc0b3088081ea0b85790116cc Mon Sep 17 00:00:00 2001 From: jrfnl Date: Fri, 17 May 2019 06:27:00 +0200 Subject: [PATCH 19/42] YoastCS/FileName: always remove the longest matching prefix when prefixes overlap The sniff allows for passing a set of `prefixes` which will be removed from an OO name to get to the expected file name. As it was, when two `prefixes` passed would overlap - think `yoast` and `yoast_news` -, the order in which these were provided in the configuration file would determine the order in which the sniff would attempt to match these. I.e. if the prefixes were provided in the above order and the class name was `Yoast_News_Something`, the sniff would suggest `news-something.php` as the filename, not `something.php`. The change in this commit, changes that behaviour by ordering the `prefixes` array before using it which ensures that the _longest_ matching prefix will be used when two prefixes overlap. Includes unit test. Unfortunately, as the tests are more similar to integration tests rather than unit tests, the actual message is not tested, even though that's where the change lies. --- Yoast/Sniffs/Files/FileNameSniff.php | 7 +++++++ Yoast/Tests/Files/FileNameUnitTest.php | 1 + .../FileNameUnitTests/classes/yoast-plugin-some-class.inc | 8 ++++++++ 3 files changed, 16 insertions(+) create mode 100644 Yoast/Tests/Files/FileNameUnitTests/classes/yoast-plugin-some-class.inc diff --git a/Yoast/Sniffs/Files/FileNameSniff.php b/Yoast/Sniffs/Files/FileNameSniff.php index f73a7a78..0333b5b7 100644 --- a/Yoast/Sniffs/Files/FileNameSniff.php +++ b/Yoast/Sniffs/Files/FileNameSniff.php @@ -32,6 +32,11 @@ class FileNameSniff implements Sniff { * * These prefixes do not need to be reflected in the file name. * + * Note: + * - Prefixes are matched in a case-insensitive manner. + * - When several overlapping prefixes match, the longest matching prefix + * will be removed. + * * @var string[] */ public $prefixes = array(); @@ -125,6 +130,8 @@ public function process( File $phpcsFile, $stackPtr ) { $prefixes = $this->clean_custom_array_property( $this->prefixes ); if ( ! empty( $prefixes ) ) { + // Use reverse natural sorting to get the longest of overlapping prefixes first. + rsort( $prefixes, ( SORT_NATURAL | SORT_FLAG_CASE ) ); foreach ( $prefixes as $prefix ) { if ( $name !== $prefix && stripos( $name, $prefix ) === 0 ) { $name = substr( $name, strlen( $prefix ) ); diff --git a/Yoast/Tests/Files/FileNameUnitTest.php b/Yoast/Tests/Files/FileNameUnitTest.php index ebaa5df5..52c37108 100644 --- a/Yoast/Tests/Files/FileNameUnitTest.php +++ b/Yoast/Tests/Files/FileNameUnitTest.php @@ -43,6 +43,7 @@ class FileNameUnitTest extends AbstractSniffUnitTest { 'class-my-class.inc' => 1, // Prefix 'class' not needed. 'some-class.inc' => 0, 'wpseo-some-class.inc' => 1, // Prefix 'wpseo' not necessary. + 'yoast-plugin-some-class.inc' => 1, // Prefix 'yoast-plugin' not necessary. 'class-wpseo-some-class.inc' => 1, // Prefixes 'class' and 'wpseo' not necessary. 'excluded-CLASS-file.inc' => 1, // Lowercase expected. diff --git a/Yoast/Tests/Files/FileNameUnitTests/classes/yoast-plugin-some-class.inc b/Yoast/Tests/Files/FileNameUnitTests/classes/yoast-plugin-some-class.inc new file mode 100644 index 00000000..5913aa72 --- /dev/null +++ b/Yoast/Tests/Files/FileNameUnitTests/classes/yoast-plugin-some-class.inc @@ -0,0 +1,8 @@ + +phpcs:set Yoast.Files.FileName prefixes[] wpseo,yoast,yoast-plugin + + Date: Wed, 29 May 2019 02:09:56 +0200 Subject: [PATCH 20/42] CS: don't use yoda conditions While WPCS still demands Yoda conditions, YoastCS never has. This fixes the few instances in the codebase were Yoda conditions had creeped in as code as based off code taken from WPCS. --- Yoast/Sniffs/Files/FileNameSniff.php | 10 +++++----- Yoast/Sniffs/Files/TestDoublesSniff.php | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Yoast/Sniffs/Files/FileNameSniff.php b/Yoast/Sniffs/Files/FileNameSniff.php index 857323de..d22225fc 100644 --- a/Yoast/Sniffs/Files/FileNameSniff.php +++ b/Yoast/Sniffs/Files/FileNameSniff.php @@ -91,7 +91,7 @@ public function process( File $phpcsFile, $stackPtr ) { // Stripping potential quotes to ensure `stdin_path` passed by IDEs does not include quotes. $file = preg_replace( '`^([\'"])(.*)\1$`Ds', '$2', $phpcsFile->getFileName() ); - if ( 'STDIN' === $file ) { + if ( $file === 'STDIN' ) { return; } @@ -119,7 +119,7 @@ public function process( File $phpcsFile, $stackPtr ) { if ( $this->is_file_excluded( $phpcsFile, $file ) === false ) { $oo_structure = $phpcsFile->findNext( $this->oo_tokens, $stackPtr ); - if ( false !== $oo_structure ) { + if ( $oo_structure !== false ) { $tokens = $phpcsFile->getTokens(); $name = $phpcsFile->getDeclarationName( $oo_structure ); @@ -166,7 +166,7 @@ public function process( File $phpcsFile, $stackPtr ) { } else { $has_function = $phpcsFile->findNext( T_FUNCTION, $stackPtr ); - if ( false !== $has_function ) { + if ( $has_function !== false ) { $error = 'Files containing function declarations should have "-functions" as a suffix. Expected %s, but found %s.'; $error_code = 'InvalidFunctionsFileName'; @@ -262,11 +262,11 @@ protected function clean_custom_array_property( $property, $flip = false, $to_lo $property = array_filter( array_map( 'trim', $property ) ); - if ( true === $to_lower ) { + if ( $to_lower === true ) { $property = array_map( 'strtolower', $property ); } - if ( true === $flip ) { + if ( $flip === true ) { $property = array_fill_keys( $property, false ); } diff --git a/Yoast/Sniffs/Files/TestDoublesSniff.php b/Yoast/Sniffs/Files/TestDoublesSniff.php index d2cfe017..e909f39b 100644 --- a/Yoast/Sniffs/Files/TestDoublesSniff.php +++ b/Yoast/Sniffs/Files/TestDoublesSniff.php @@ -72,7 +72,7 @@ public function process( File $phpcsFile, $stackPtr ) { // Stripping potential quotes to ensure `stdin_path` passed by IDEs does not include quotes. $file = preg_replace( '`^([\'"])(.*)\1$`Ds', '$2', $phpcsFile->getFileName() ); - if ( 'STDIN' === $file ) { + if ( $file === 'STDIN' ) { return; } From b3250ebc649f21d64e965601815da188e97a541d Mon Sep 17 00:00:00 2001 From: jrfnl Date: Wed, 29 May 2019 02:25:44 +0200 Subject: [PATCH 21/42] Travis: update script for updated minimum requirements of dependencies PR 126 updated the PHPCS minimum requirement to `3.4.2`. PR 134 updated the WPCS minimum requirement to `2.1.1`. The Travis script should have been updated at the same time to reflect these new minimum requirements. --- .travis.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index db211b83..85ab8f48 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,16 +26,16 @@ php: env: # Test against the highest/lowest supported PHPCS and WPCS versions. - PHPCS_BRANCH="dev-master" WPCS="dev-develop" PHPLINT=1 - - PHPCS_BRANCH="dev-master" WPCS="2.0.0" - - PHPCS_BRANCH="3.4.0" WPCS="dev-develop" - - PHPCS_BRANCH="3.4.0" WPCS="2.0.0" + - PHPCS_BRANCH="dev-master" WPCS="2.1.1" + - PHPCS_BRANCH="3.4.2" WPCS="dev-develop" + - PHPCS_BRANCH="3.4.2" WPCS="2.1.1" matrix: fast_finish: true include: # Extra build to check for XML codestyle. - php: 7.1 - env: PHPCS_BRANCH="dev-master" WPCS="^2.0.0" SNIFF=1 + env: PHPCS_BRANCH="dev-master" WPCS="^2.1.1" SNIFF=1 addons: apt: packages: From 8eb1e9d37e232401caba42a442eb679a0d03f6c8 Mon Sep 17 00:00:00 2001 From: jrfnl Date: Wed, 29 May 2019 01:59:58 +0200 Subject: [PATCH 22/42] YoastCS/AlternativeFunctions: activate disabled test See 125 This unit tests was disabled as the parent sniff from WPCS did not handle this correctly yet in WPCS < 2.1.0. As the minimum WPCS requirement for YoastCS has gone up to WPCS 2.1.1, this test can now be enabled. --- Yoast/Tests/Yoast/AlternativeFunctionsUnitTest.inc | 2 +- Yoast/Tests/Yoast/AlternativeFunctionsUnitTest.inc.fixed | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Yoast/Tests/Yoast/AlternativeFunctionsUnitTest.inc b/Yoast/Tests/Yoast/AlternativeFunctionsUnitTest.inc index ee9d2e6b..631f4d40 100644 --- a/Yoast/Tests/Yoast/AlternativeFunctionsUnitTest.inc +++ b/Yoast/Tests/Yoast/AlternativeFunctionsUnitTest.inc @@ -6,7 +6,7 @@ $json = WPSEO_Utils:format_json_encode( $thing ); // OK. $json = Class_Other_Plugin::json_encode( $thing ); // OK. $json = $obj->wp_json_encode( $thing ); // OK. $json = MyNamespace\json_encode( $thing ); // OK. -//$json = namespace\wp_json_encode( $thing ); // OK - this will not (yet) be correctly recognized, but will with WPCS 2.1.0. +$json = namespace\wp_json_encode( $thing ); // OK. // The sniff should trigger on these. $json = json_encode( $thing ); // Error. diff --git a/Yoast/Tests/Yoast/AlternativeFunctionsUnitTest.inc.fixed b/Yoast/Tests/Yoast/AlternativeFunctionsUnitTest.inc.fixed index 3a06a083..72387ca9 100644 --- a/Yoast/Tests/Yoast/AlternativeFunctionsUnitTest.inc.fixed +++ b/Yoast/Tests/Yoast/AlternativeFunctionsUnitTest.inc.fixed @@ -6,7 +6,7 @@ $json = WPSEO_Utils:format_json_encode( $thing ); // OK. $json = Class_Other_Plugin::json_encode( $thing ); // OK. $json = $obj->wp_json_encode( $thing ); // OK. $json = MyNamespace\json_encode( $thing ); // OK. -//$json = namespace\wp_json_encode( $thing ); // OK - this will not (yet) be correctly recognized, but will with WPCS 2.1.0. +$json = namespace\wp_json_encode( $thing ); // OK. // The sniff should trigger on these. $json = WPSEO_Utils::format_json_encode( $thing ); // Error. From b7cfce0bfff891935b7d4dd47b93a64042f3dd52 Mon Sep 17 00:00:00 2001 From: jrfnl Date: Tue, 2 Jul 2019 01:52:13 +0200 Subject: [PATCH 23/42] Composer: update PHPCompatibility requirement PHPCompatibility 9.2.0 has been released which contains a lot of checks for PHP 7.4. Ref: https://github.com/PHPCompatibility/PHPCompatibility/releases/tag/9.2.0 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 3a3f8f5f..e1ca36b2 100644 --- a/composer.json +++ b/composer.json @@ -29,7 +29,7 @@ "dealerdirect/phpcodesniffer-composer-installer": "^0.5.0" }, "require-dev": { - "phpcompatibility/php-compatibility": "^9.0.0", + "phpcompatibility/php-compatibility": "^9.2.0", "roave/security-advisories": "dev-master", "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" }, From 0826bba4ae269e4011c711d3b3e92e8d6130469e Mon Sep 17 00:00:00 2001 From: jrfnl Date: Mon, 22 Jul 2019 01:08:36 +0200 Subject: [PATCH 24/42] Travis: ignore PHP deprecation notices for stable PHPCS releases The unit tests will fail when a PHP warning/notice/deprecation notice is encountered. Deprecation notices thrown by already released PHPCS versions won't get fixed anymore (in that version), so failing the unit tests on those is moot and will skew the reliability of the Travis results. --- .travis.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.travis.yml b/.travis.yml index 85ab8f48..ca198576 100644 --- a/.travis.yml +++ b/.travis.yml @@ -50,6 +50,16 @@ before_install: # https://johnblackbourn.com/reducing-travis-ci-build-times-for-wordpress-projects/ # https://twitter.com/kelunik/status/954242454676475904 - phpenv config-rm xdebug.ini || echo 'No xdebug config.' + + # On stable PHPCS versions, allow for PHP deprecation notices. + # Unit tests don't need to fail on those for stable releases where those issues won't get fixed anymore. + - | + if [[ $PHPCS_BRANCH != "dev-master" && $WPCS != "dev-develop" ]]; then + echo 'error_reporting = E_ALL & ~E_DEPRECATED' >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini + fi + - php -r "echo ini_get('error_reporting');" + + - export XMLLINT_INDENT=" " - export PHPCS_DIR=$(pwd)/vendor/squizlabs/php_codesniffer - export PHPCS_BIN=$(pwd)/vendor/bin/phpcs From 7d58ec841d56320fe47b6830ed167f778d8090cc Mon Sep 17 00:00:00 2001 From: jrfnl Date: Tue, 23 Jul 2019 02:27:00 +0200 Subject: [PATCH 25/42] PHPCS unit tests: minor fixes to the test case files Fix unintentional parse errors and annotate intentional parse errors which are there to test edge cases. --- .../CodeCoverageIgnoreDeprecatedUnitTest.inc | 4 ++-- .../CodeCoverageIgnoreDeprecatedUnitTest.inc.fixed | 4 ++-- .../ControlStructures/IfElseDeclarationUnitTest.inc | 10 +++++----- .../tests/multiple-objects-in-file-reverse.inc | 2 +- .../tests/multiple-objects-in-file.inc | 2 +- .../Namespaces/NamespaceDeclarationUnitTest.2.inc | 4 ++-- Yoast/Tests/Yoast/AlternativeFunctionsUnitTest.inc | 4 ++-- .../Tests/Yoast/AlternativeFunctionsUnitTest.inc.fixed | 4 ++-- 8 files changed, 17 insertions(+), 17 deletions(-) diff --git a/Yoast/Tests/Commenting/CodeCoverageIgnoreDeprecatedUnitTest.inc b/Yoast/Tests/Commenting/CodeCoverageIgnoreDeprecatedUnitTest.inc index 1a4735d4..b686e7a0 100644 --- a/Yoast/Tests/Commenting/CodeCoverageIgnoreDeprecatedUnitTest.inc +++ b/Yoast/Tests/Commenting/CodeCoverageIgnoreDeprecatedUnitTest.inc @@ -15,7 +15,7 @@ class SomeThing { // Code } // end function - public function functionWithoutDocblock {} + public function functionWithoutDocblock() {} /** * @codeCoverageIgnore @@ -47,7 +47,7 @@ class SomeThing { * * This is a sentence containing the phrase codeCoverageIgnore, but should not be regarded as the tag. */ - static protected function missingCodeCoverageIgnore() {} + static protected function missingStaticCodeCoverageIgnore() {} /** * @deprecated x.x diff --git a/Yoast/Tests/Commenting/CodeCoverageIgnoreDeprecatedUnitTest.inc.fixed b/Yoast/Tests/Commenting/CodeCoverageIgnoreDeprecatedUnitTest.inc.fixed index 0563e445..a8a80c8d 100644 --- a/Yoast/Tests/Commenting/CodeCoverageIgnoreDeprecatedUnitTest.inc.fixed +++ b/Yoast/Tests/Commenting/CodeCoverageIgnoreDeprecatedUnitTest.inc.fixed @@ -15,7 +15,7 @@ class SomeThing { // Code } // end function - public function functionWithoutDocblock {} + public function functionWithoutDocblock() {} /** * @codeCoverageIgnore @@ -47,7 +47,7 @@ class SomeThing { * * This is a sentence containing the phrase codeCoverageIgnore, but should not be regarded as the tag. */ - static protected function missingCodeCoverageIgnore() {} + static protected function missingStaticCodeCoverageIgnore() {} /** * @deprecated x.x diff --git a/Yoast/Tests/ControlStructures/IfElseDeclarationUnitTest.inc b/Yoast/Tests/ControlStructures/IfElseDeclarationUnitTest.inc index f6f2694b..fa42db12 100644 --- a/Yoast/Tests/ControlStructures/IfElseDeclarationUnitTest.inc +++ b/Yoast/Tests/ControlStructures/IfElseDeclarationUnitTest.inc @@ -10,11 +10,11 @@ else { // Ok. if ( false ): -elseif: -else if: +elseif: // Intentional parse error. +else if: // Intentional parse error. else /* comment */ -if: -else: +if: // Intentional parse error. +else: // Intentional parse error. endif; if ( true ) { @@ -34,7 +34,7 @@ if ( true ) { } else // Bad - else if should be on the next line. if (2) { // ... -} else ( 3 ) { // Bad - else should be on the next line. +} else { // Bad - else should be on the next line. // ... } diff --git a/Yoast/Tests/Files/TestDoublesUnitTests/tests/multiple-objects-in-file-reverse.inc b/Yoast/Tests/Files/TestDoublesUnitTests/tests/multiple-objects-in-file-reverse.inc index c0cdf7e7..2543cb50 100644 --- a/Yoast/Tests/Files/TestDoublesUnitTests/tests/multiple-objects-in-file-reverse.inc +++ b/Yoast/Tests/Files/TestDoublesUnitTests/tests/multiple-objects-in-file-reverse.inc @@ -2,6 +2,6 @@ use PHPUnit\Framework\TestCase; -class Test_ClassName extends extends TestCase {} +class Test_ClassName extends TestCase {} class Prefix_ClassName_Double {} diff --git a/Yoast/Tests/Files/TestDoublesUnitTests/tests/multiple-objects-in-file.inc b/Yoast/Tests/Files/TestDoublesUnitTests/tests/multiple-objects-in-file.inc index d1f6b8e0..31997686 100644 --- a/Yoast/Tests/Files/TestDoublesUnitTests/tests/multiple-objects-in-file.inc +++ b/Yoast/Tests/Files/TestDoublesUnitTests/tests/multiple-objects-in-file.inc @@ -4,4 +4,4 @@ use PHPUnit\Framework\TestCase; class Prefix_ClassName_Double {} -class Test_ClassName extends extends TestCase {} +class Test_ClassName extends TestCase {} diff --git a/Yoast/Tests/Namespaces/NamespaceDeclarationUnitTest.2.inc b/Yoast/Tests/Namespaces/NamespaceDeclarationUnitTest.2.inc index 6284d049..7c5180d7 100644 --- a/Yoast/Tests/Namespaces/NamespaceDeclarationUnitTest.2.inc +++ b/Yoast/Tests/Namespaces/NamespaceDeclarationUnitTest.2.inc @@ -1,6 +1,6 @@ Date: Wed, 29 May 2019 18:51:08 +0200 Subject: [PATCH 26/42] Readme: update the text The text in the readme was a bit dated. --- README.md | 51 +++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 6df01eb0..0d685e06 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Yoast Coding Standards (YoastCS) is a project with rulesets for code style and q ### Standalone -Standards are provided as [Composer](https://getcomposer.org/) package and can be installed with: +Standards are provided as a [Composer](https://getcomposer.org/) package and can be installed with: ```bash composer create-project yoast/yoastcs:dev-master @@ -19,33 +19,68 @@ Composer will automatically install dependencies, register standards paths, and To include standards as part of a project require them as development dependencies: ```bash -composer require yoast/yoastcs:dev-master --dev +composer require --dev yoast/yoastcs:^1.0 ``` -Note that Composer won't run configuration scripts in this scenario and the root project needs to take care of it. +Composer will automatically install dependencies and register the YoastCS and other external standards with PHP_CodeSniffer. ## PHP Code Sniffer Set of [PHP Code Sniffer](https://github.com/squizlabs/PHP_CodeSniffer) rules. -Based on [WordPress Coding Standards](https://github.com/WordPress-Coding-Standards/WordPress-Coding-Standards) project, implementing official [WordPress PHP Coding Standards](https://make.wordpress.org/core/handbook/coding-standards/php/). - Severity levels: - error level issues are considered mandatory to fix in Yoast projects and enforced in continuous integration - warning level issues are considered recommended to fix -### Command line +### The YoastCS Standard + +The `Yoast` standard for PHP_CodeSniffer is comprised of the following: +* The `WordPress` ruleset from the [WordPress Coding Standards](https://github.com/WordPress-Coding-Standards/WordPress-Coding-Standards) implementing the official [WordPress PHP Coding Standards](https://make.wordpress.org/core/handbook/coding-standards/php/), with some select exclusions. +* The [`PHPCompatibilityWP`](https://github.com/PHPCompatibility/PHPCompatibilityWP) ruleset which checks code for PHP cross-version compatibility while preventing false positives for functionality polyfilled within WordPress. +* A number of custom Yoast specific sniffs. +* A number of additional sniffs taken from `PHP_CodeSniffer`. + +Files within version management and dependency related directories, such as for instance the Composer `vendor` directory, are not scanned by default. + +#### Sniffs + +To obtain a list of all sniffs used within YoastCS: +```bash +"vendor/bin/phpcs" -e --standard=Yoast +``` + +#### Sniff Documentation + +Not all sniffs have documentation available about what they sniff for, but for those which do, this documentation can be viewed from the command-line: +```bash +"vendor/bin/phpcs" --standard=Yoast --generator=text +``` + +### Running the sniffs + +#### Command line ```bash "vendor/bin/phpcs" --extensions=php /path/to/folder/ ``` -### PhpStorm +For more command-line options, please have a read through the [PHP_CodeSniffer documentation](https://github.com/squizlabs/PHP_CodeSniffer/wiki/Usage). + +#### Yoast plugin repositories + +All Yoast plugin repositories contain a `[.]phpcs.xml.dist` file contain the repository specific configuration. + +From the root of these repositories, you can run PHPCS by using: +```bash +composer check-cs +``` + +#### PhpStorm Refer to [Using PHP Code Sniffer Tool](https://www.jetbrains.com/phpstorm/help/using-php-code-sniffer-tool.html) in PhpStorm documentation. -After installation `Yoast` standard will be available as a choice in PHP Code Sniffer Validation inspection. +After installation, the `Yoast` standard will be available as a choice in PHP Code Sniffer Validation inspection. ## Changelog From 8cd6cc02c7c12c999c15137c2aa7466427461d45 Mon Sep 17 00:00:00 2001 From: jrfnl Date: Thu, 30 May 2019 03:24:47 +0200 Subject: [PATCH 27/42] Travis: verify XML sniff documentation is well-formed Check that the XML sniff documentation files are well-formed and will not cause parse errors, i.e. have matching open/close tags. --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 85ab8f48..41fc5309 100644 --- a/.travis.yml +++ b/.travis.yml @@ -85,6 +85,7 @@ script: # Validate the xml files. # @link http://xmlsoft.org/xmllint.html - if [[ "$SNIFF" == "1" ]]; then xmllint --noout --schema ./vendor/squizlabs/php_codesniffer/phpcs.xsd ./Yoast/ruleset.xml; fi + - if [[ "$SNIFF" == "1" ]]; then xmllint --noout ./Yoast/Docs/*/*Standard.xml; fi # Check the code-style consistency of the xml files. - if [[ "$SNIFF" == "1" ]]; then diff -B --tabsize=4 ./Yoast/ruleset.xml <(xmllint --format "./Yoast/ruleset.xml"); fi # Validate the composer.json file. From d87dd9bf2f619c8a000c0159eae01952ef7bf635 Mon Sep 17 00:00:00 2001 From: jrfnl Date: Thu, 30 May 2019 09:18:16 +0200 Subject: [PATCH 28/42] IfElseDeclaration: add sniff documentation --- .../IfElseDeclarationStandard.xml | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 Yoast/Docs/ControlStructures/IfElseDeclarationStandard.xml diff --git a/Yoast/Docs/ControlStructures/IfElseDeclarationStandard.xml b/Yoast/Docs/ControlStructures/IfElseDeclarationStandard.xml new file mode 100644 index 00000000..505eb32f --- /dev/null +++ b/Yoast/Docs/ControlStructures/IfElseDeclarationStandard.xml @@ -0,0 +1,56 @@ + + + + + + + + +elseif ($bar) { + $var = 2; +} + ]]> + + + elseif ($bar) { + $var = 2; +} + ]]> + + + + + + + + elseif ($bar) { + $var = 2; +} + ]]> + + + else { + $var = 2; + } + ]]> + + + From 9dc7e8f57fb091590e961fe8ae78762ec374dcae Mon Sep 17 00:00:00 2001 From: jrfnl Date: Thu, 30 May 2019 09:18:24 +0200 Subject: [PATCH 29/42] FileComment: add sniff documentation Includes documentation on the error codes inherited from the upstream `Squiz.Commenting.FileComment` sniff. --- Yoast/Docs/Commenting/FileCommentStandard.xml | 204 ++++++++++++++++++ 1 file changed, 204 insertions(+) create mode 100644 Yoast/Docs/Commenting/FileCommentStandard.xml diff --git a/Yoast/Docs/Commenting/FileCommentStandard.xml b/Yoast/Docs/Commenting/FileCommentStandard.xml new file mode 100644 index 00000000..b4d2a193 --- /dev/null +++ b/Yoast/Docs/Commenting/FileCommentStandard.xml @@ -0,0 +1,204 @@ + + + + + + + + namespace Yoast\A\B; + +/** + * Class docblock. + */ +class { + ... + ]]> + + + /** + * File comment. + */ + +namespace Yoast\A\B; + +/** + * Class docblock. + */ +class { + ... + ]]> + + + + + + + + + /** + * File comment. + */ + +/** + * Class docblock. + */ +class { + ... + ]]> + + + +/** + * Class docblock. + */ +class { + ... + ]]> + + + + + + + + + /** + * File comment. + */ + ]]> + + + /* + * File comment. + */ + ]]> + + + + + + + + + +/** + * File comment. + */ + ]]> + + + + + +/** + * File comment. + */ + ]]> + + + + + + + + + + + echo $something; + ]]> + + + + echo $something; + ]]> + + + + + + + + + @package Yoast\Package + */ + ]]> + + + + + + + + + + + + @package Yoast\Package + */ + ]]> + + + @package + */ + ]]> + + + + From fe7e8fdabd4355b167edc4f8f0d681aa4d7bb49a Mon Sep 17 00:00:00 2001 From: jrfnl Date: Thu, 30 May 2019 03:38:15 +0200 Subject: [PATCH 30/42] CodeCoverageIgnoreDeprecated: add sniff documentation --- .../CodeCoverageIgnoreDeprecatedStandard.xml | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 Yoast/Docs/Commenting/CodeCoverageIgnoreDeprecatedStandard.xml diff --git a/Yoast/Docs/Commenting/CodeCoverageIgnoreDeprecatedStandard.xml b/Yoast/Docs/Commenting/CodeCoverageIgnoreDeprecatedStandard.xml new file mode 100644 index 00000000..3a5ee941 --- /dev/null +++ b/Yoast/Docs/Commenting/CodeCoverageIgnoreDeprecatedStandard.xml @@ -0,0 +1,27 @@ + + + + + + + + @deprecated x.x + * @codeCoverageIgnore + */ +function deprecated_function() {} + ]]> + + + @deprecated x.x + */ +function deprecated_function() {} + ]]> + + + From 3017348e48f0e8343f93ec62298189ea43ff328a Mon Sep 17 00:00:00 2001 From: jrfnl Date: Thu, 30 May 2019 03:48:14 +0200 Subject: [PATCH 31/42] NamespaceDeclaration: add sniff documentation --- .../NamespaceDeclarationStandard.xml | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 Yoast/Docs/Namespaces/NamespaceDeclarationStandard.xml diff --git a/Yoast/Docs/Namespaces/NamespaceDeclarationStandard.xml b/Yoast/Docs/Namespaces/NamespaceDeclarationStandard.xml new file mode 100644 index 00000000..d5a548f4 --- /dev/null +++ b/Yoast/Docs/Namespaces/NamespaceDeclarationStandard.xml @@ -0,0 +1,62 @@ + + + + + + + + ; + ]]> + + + { + // Code. +} + ]]> + + + + + + + + + Yoast\Sub; + ]]> + + + ; + ]]> + + + + + + + + + + + + namespace Yoast\Sub\B { +} + ]]> + + + From 841671db9ef0e7daa2624722c5d0fc67eb182b15 Mon Sep 17 00:00:00 2001 From: jrfnl Date: Thu, 30 May 2019 04:04:06 +0200 Subject: [PATCH 32/42] FunctionSpacing: add sniff documentation Documents the error codes inherited from the upstream `Squiz.WhiteSpace.FunctionSpacing` sniff: * with the property values as set in the Yoast version; * with the limitation to OO structures as set in the Yoast version. --- .../WhiteSpace/FunctionSpacingStandard.xml | 116 ++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 Yoast/Docs/WhiteSpace/FunctionSpacingStandard.xml diff --git a/Yoast/Docs/WhiteSpace/FunctionSpacingStandard.xml b/Yoast/Docs/WhiteSpace/FunctionSpacingStandard.xml new file mode 100644 index 00000000..763901ad --- /dev/null +++ b/Yoast/Docs/WhiteSpace/FunctionSpacingStandard.xml @@ -0,0 +1,116 @@ + + + + + + + + + + /** + * Function docblock. + */ + function func1() { + } +} + ]]> + + + + /** + * Function docblock. + */ + function func1() { + } +} + ]]> + + + + + + + + + + + /** + * Function docblock. + */ + function func2() { + } +} + ]]> + + + + + + /** + * Function docblock. + */ + function func2() { + } + /** + * Function docblock. + */ + function func3() { + } +} + ]]> + + + + + + + + + +} + ]]> + + + + +} + ]]> + + + From 86765fdde726876cfb3ca981f72d454dd5329e09 Mon Sep 17 00:00:00 2001 From: jrfnl Date: Thu, 30 May 2019 16:31:51 +0200 Subject: [PATCH 33/42] AlternativeFunctions: add sniff documentation --- .../Yoast/AlternativeFunctionsStandard.xml | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 Yoast/Docs/Yoast/AlternativeFunctionsStandard.xml diff --git a/Yoast/Docs/Yoast/AlternativeFunctionsStandard.xml b/Yoast/Docs/Yoast/AlternativeFunctionsStandard.xml new file mode 100644 index 00000000..97742446 --- /dev/null +++ b/Yoast/Docs/Yoast/AlternativeFunctionsStandard.xml @@ -0,0 +1,20 @@ + + + + + + + + WPSEO_Utils:format_json_encode( $in ); + ]]> + + + wp_json_encode( $in ); + ]]> + + + From 9f896ff7b68e6d82af86cc4a0f33f1a27aaacc38 Mon Sep 17 00:00:00 2001 From: jrfnl Date: Fri, 31 May 2019 00:52:53 +0200 Subject: [PATCH 34/42] TestDoubles: add sniff documentation --- Yoast/Docs/Files/TestDoublesStandard.xml | 40 ++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 Yoast/Docs/Files/TestDoublesStandard.xml diff --git a/Yoast/Docs/Files/TestDoublesStandard.xml b/Yoast/Docs/Files/TestDoublesStandard.xml new file mode 100644 index 00000000..3b0d1ecb --- /dev/null +++ b/Yoast/Docs/Files/TestDoublesStandard.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + From 839c5a4b47bdf74163a4cce8c70c6351c3f98ad9 Mon Sep 17 00:00:00 2001 From: jrfnl Date: Fri, 31 May 2019 17:03:36 +0200 Subject: [PATCH 35/42] FileName: add sniff documentation --- Yoast/Docs/Files/FileNameStandard.xml | 86 +++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 Yoast/Docs/Files/FileNameStandard.xml diff --git a/Yoast/Docs/Files/FileNameStandard.xml b/Yoast/Docs/Files/FileNameStandard.xml new file mode 100644 index 00000000..a43819e8 --- /dev/null +++ b/Yoast/Docs/Files/FileNameStandard.xml @@ -0,0 +1,86 @@ + + + + + + + + file-name.php + ]]> + + + File_Name.php + ]]> + + + + + + + + utils.php --> +Utils {} + ]]> + + + class-wpseo-utils.php --> + + + + + + + + + output-thing-interface.php --> +interface Yoast_Output_Thing {} + ]]> + + + yoast-outline-something.php --> +trait Yoast_Outline_Something {} + ]]> + + + + + + + + functions.php --> + + + + + + + + From bdacd199d6cab4315dc2ab7a2f2a8db3c32e3467 Mon Sep 17 00:00:00 2001 From: jrfnl Date: Fri, 31 May 2019 17:50:36 +0200 Subject: [PATCH 36/42] TestsHaveCoversTag: add sniff documentation --- .../Commenting/TestsHaveCoversTagStandard.xml | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 Yoast/Docs/Commenting/TestsHaveCoversTagStandard.xml diff --git a/Yoast/Docs/Commenting/TestsHaveCoversTagStandard.xml b/Yoast/Docs/Commenting/TestsHaveCoversTagStandard.xml new file mode 100644 index 00000000..aceea4f2 --- /dev/null +++ b/Yoast/Docs/Commenting/TestsHaveCoversTagStandard.xml @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 57229680f28f49338554cc46ca1ddb4d09df66bb Mon Sep 17 00:00:00 2001 From: jrfnl <663378+jrfnl@users.noreply.github.com> Date: Thu, 25 Jul 2019 14:34:07 +0200 Subject: [PATCH 37/42] CoversTag: add sniff documentation --- Yoast/Docs/Commenting/CoversTagStandard.xml | 110 ++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 Yoast/Docs/Commenting/CoversTagStandard.xml diff --git a/Yoast/Docs/Commenting/CoversTagStandard.xml b/Yoast/Docs/Commenting/CoversTagStandard.xml new file mode 100644 index 00000000..5895b588 --- /dev/null +++ b/Yoast/Docs/Commenting/CoversTagStandard.xml @@ -0,0 +1,110 @@ + + + + + + + + Class_Name::method_name + */ + function test_something() {} +} + ]]> + + + () + */ + function test_something() {} +} + ]]> + + + + + + + + + + + Name\Space\function_name + */ + function test_something() {} +} + ]]> + + + + + + + + @covers ::globalFunction + */ + function test_something() {} + + /** + * Testing... + * + * @coversNothing + */ + function test_something_else() {} +} + ]]> + + + @coversNothing + * @covers ::globalFunction + */ + function test_something() {} +} + ]]> + + + From 73251bef41057aeb28f13a9f6445cccd279e4fd5 Mon Sep 17 00:00:00 2001 From: jrfnl <663378+jrfnl@users.noreply.github.com> Date: Wed, 31 Jul 2019 11:18:37 +0200 Subject: [PATCH 38/42] Readme: further text improvements ... based on PR review. --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 0d685e06..28c71a04 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ Composer will automatically install dependencies and register the YoastCS and ot ## PHP Code Sniffer -Set of [PHP Code Sniffer](https://github.com/squizlabs/PHP_CodeSniffer) rules. +Set of [PHP_CodeSniffer](https://github.com/squizlabs/PHP_CodeSniffer) rules. Severity levels: @@ -38,10 +38,10 @@ Severity levels: The `Yoast` standard for PHP_CodeSniffer is comprised of the following: * The `WordPress` ruleset from the [WordPress Coding Standards](https://github.com/WordPress-Coding-Standards/WordPress-Coding-Standards) implementing the official [WordPress PHP Coding Standards](https://make.wordpress.org/core/handbook/coding-standards/php/), with some select exclusions. * The [`PHPCompatibilityWP`](https://github.com/PHPCompatibility/PHPCompatibilityWP) ruleset which checks code for PHP cross-version compatibility while preventing false positives for functionality polyfilled within WordPress. +* Select additional sniffs taken from [`PHP_CodeSniffer`](https://github.com/squizlabs/PHP_CodeSniffer). * A number of custom Yoast specific sniffs. -* A number of additional sniffs taken from `PHP_CodeSniffer`. -Files within version management and dependency related directories, such as for instance the Composer `vendor` directory, are not scanned by default. +Files within version management and dependency related directories, such as the Composer `vendor` directory, are excluded from the scans by default. #### Sniffs @@ -78,7 +78,7 @@ composer check-cs #### PhpStorm -Refer to [Using PHP Code Sniffer Tool](https://www.jetbrains.com/phpstorm/help/using-php-code-sniffer-tool.html) in PhpStorm documentation. +Refer to [Using PHP Code Sniffer Tool](https://www.jetbrains.com/phpstorm/help/using-php-code-sniffer-tool.html) in the PhpStorm documentation. After installation, the `Yoast` standard will be available as a choice in PHP Code Sniffer Validation inspection. From 931b9b65817403b25eff615bef2eb8506b9d8a38 Mon Sep 17 00:00:00 2001 From: jrfnl <663378+jrfnl@users.noreply.github.com> Date: Wed, 31 Jul 2019 11:24:03 +0200 Subject: [PATCH 39/42] Readme: add link to the select exclusions --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 28c71a04..525f1a84 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ Severity levels: ### The YoastCS Standard The `Yoast` standard for PHP_CodeSniffer is comprised of the following: -* The `WordPress` ruleset from the [WordPress Coding Standards](https://github.com/WordPress-Coding-Standards/WordPress-Coding-Standards) implementing the official [WordPress PHP Coding Standards](https://make.wordpress.org/core/handbook/coding-standards/php/), with some select exclusions. +* The `WordPress` ruleset from the [WordPress Coding Standards](https://github.com/WordPress-Coding-Standards/WordPress-Coding-Standards) implementing the official [WordPress PHP Coding Standards](https://make.wordpress.org/core/handbook/coding-standards/php/), with some [select exclusions](https://github.com/Yoast/yoastcs/blob/develop/Yoast/ruleset.xml#L29-L75). * The [`PHPCompatibilityWP`](https://github.com/PHPCompatibility/PHPCompatibilityWP) ruleset which checks code for PHP cross-version compatibility while preventing false positives for functionality polyfilled within WordPress. * Select additional sniffs taken from [`PHP_CodeSniffer`](https://github.com/squizlabs/PHP_CodeSniffer). * A number of custom Yoast specific sniffs. From bf7365e38ffd1c55a8890c7e57fb8bc75a0009d9 Mon Sep 17 00:00:00 2001 From: jrfnl Date: Wed, 29 May 2019 18:52:59 +0200 Subject: [PATCH 40/42] GH: add pull request templates This adds three different pull request templates: 1. A dependency change template. 2. A sniff change template. 3. A generic template for anything that doesn't fit in the above two categories. --- .../dependency_change.md | 19 +++++++++ .github/PULL_REQUEST_TEMPLATE/generic.md | 6 +++ .github/PULL_REQUEST_TEMPLATE/sniff_change.md | 41 +++++++++++++++++++ 3 files changed, 66 insertions(+) create mode 100644 .github/PULL_REQUEST_TEMPLATE/dependency_change.md create mode 100644 .github/PULL_REQUEST_TEMPLATE/generic.md create mode 100644 .github/PULL_REQUEST_TEMPLATE/sniff_change.md diff --git a/.github/PULL_REQUEST_TEMPLATE/dependency_change.md b/.github/PULL_REQUEST_TEMPLATE/dependency_change.md new file mode 100644 index 00000000..d7848654 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/dependency_change.md @@ -0,0 +1,19 @@ +--- +name: Dependency change +about: Update one or more of the YoastCS dependencies +labels: "yoast cs/qa" +--- + +## Description + +* + +Refs: + +* + + diff --git a/.github/PULL_REQUEST_TEMPLATE/generic.md b/.github/PULL_REQUEST_TEMPLATE/generic.md new file mode 100644 index 00000000..ab92da69 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/generic.md @@ -0,0 +1,6 @@ +--- +name: General +about: Change which doesn't fit any of the other categories +labels: "yoast cs/qa" +--- + diff --git a/.github/PULL_REQUEST_TEMPLATE/sniff_change.md b/.github/PULL_REQUEST_TEMPLATE/sniff_change.md new file mode 100644 index 00000000..add6a4e6 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/sniff_change.md @@ -0,0 +1,41 @@ +--- +name: Sniff change +about: Introduce a new sniff or update an existing sniff +labels: "yoast cs/qa" +--- + +## Description + +* + + +Refs: + +* + + +## Test instructions + +This PR can be tested by following these steps: + +* + + +## Sniff feature completeness + +* [ ] **Documentation**: I have added/updated the sniff `Standard.xml` documentation to match this change. + * [ ] Not applicable. +* [ ] **Functionality**: This change adds auto-fixer(s). + * [ ] Not applicable. +* [ ] **Unit tests**: I have added unit tests to verify the code works as intended. +* [ ] **End-to-end tests**: I have run the new/updated sniff against one or more of the Yoast plugin repositories to find false positives/negatives. + +Fixes # From 82f5799f03a279ab6b6beff4fb4f99a5de36806d Mon Sep 17 00:00:00 2001 From: jrfnl Date: Wed, 29 May 2019 01:50:02 +0200 Subject: [PATCH 41/42] Changelog for the 1.3.0 release * Preliminary release date set at Wednesday July 31. * Includes entries for all currently merged changes as well as for the open PRs marked with the `1.3.0` milestone. --- CHANGELOG.md | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 744b3158..ded39219 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,55 @@ All notable changes to this project will be documented in this file. This projects adheres to [Semantic Versioning](https://semver.org/) and [Keep a CHANGELOG](https://keepachangelog.com/). +### [1.3.0] - 2019-07-31 + +#### Added +* PHPCS: New `Yoast.Commenting.CoversTag` sniff. + This sniff verifies that: + - the contents of a `@covers` annotation is valid based on what's supported by PHPUnit; + - there are no duplicate `@covers` or `@coversNothing` tags in a docblock; + - a docblock doesn't contain both a `@covers` tag as well as a `@coversNothing` tag; + Includes a fixer for common errors. +* PHPCS: New `Yoast.Commenting.TestsHaveCoversTag` sniff. + This sniff verifies that all unit test functions have at least one `@covers` tag - or a `@coversNothing` tag - in the function docblock or in the class docblock. +* PHPCS: New `Yoast.Yoast.AlternativeFunctions` sniff. + This sniff allows for discouraging/forbidding the use of PHP/WP native functions in favor of using Yoast native functions. + In this initial version, the sniff checks for the use of the `json_encode()` and `wp_json_encode()` functions and suggests using `WPSEO_Utils::format_json_encode()` instead. + Note: this sniff contains an auto-fixer. If for any of the repos, the auto-fixer should not be used, the auto-fixer can be disabled from within the repo specific ruleset using ``. +* PHPCS: The `Squiz.WhiteSpace.MemberVarSpacing` sniff. + This sniff verifies and auto-fixes the number of blank lines between property declarations within OO-structures. +* PHPCS: A default value for the `minimum_supported_wp_version` property which is used by various WPCS sniffs. The current default is WP `4.9`. + Previously this value would have to be set via a `config` directive in custom repo specific rulesets. + For those rulesets which use the Yoast default, this `config` directive can now be removed. + For more details, see [#131](https://github.com/Yoast/yoastcs/pull/131). +* PHPCS: All YoastCS native sniffs are now accompanied by documentation which can be viewed from the command-line using `phpcs --generator=Text --standard=Yoast`. +* Repo/QA: Various templates for typical pull requests to this repo. +* Composer: `fix-cs` script. +* Travis: Testing of the code against PHP 7.4 (unstable). + +#### Changed +* PHPCS: Files in the following directories will now be excluded from all scans by default: + - `/.git/` + - `/.wordpress-svn/` + - `/node-modules/` + - `/vendor/` + - `/vendor_prefixed/` + Custom repo specific rulesets which contain excludes to this effect, can now remove them safely. +* PHPCS: The message type for issues reported by the `Generic.Formatting.MultipleStatementAlignment` and `WordPress.Arrays.MultipleStatementAlignment` sniffs, has been upgraded from `warning` to `error`. +* PHPCS: The WPCS native check for the `json_encode()` function in the `WordPress.WP.AlternativeFunctions` has been disabled in favor of the new YoastCS native `Yoast.Yoast.AlternativeFunctions` sniff. +* Composer: Supported version of [PHP_CodeSniffer] has been changed from `^3.4.0` to `^3.4.2`. +* Composer: Supported version of [WordPressCS] has been changed from `^2.0.0` to `^2.1.1`. +* Travis: As there is now a sniff which extends a WPCS sniff, the unit tests will now run against various combinations of PHPCS and WPCS combined. +* Minor housekeeping. + +#### Removed +* PHPMD is no longer part of the YoastCS repo. + PHPMD was not used as a stand-alone tool by any of the repos, only in combination with CodeClimate. +* Travis: Testing of the repo against PHP `nightly` (PHP 8, unstable) as no viable PHPUnit version is currently available. + +#### Fixed +* PHPCS: The `Yoast.Files.FileName` sniff will now always suggest removing the longest prefix of the prefixes passed in the configuration. + ### [1.2.2] - 2019-01-21 #### Changed @@ -21,7 +70,7 @@ This projects adheres to [Semantic Versioning](https://semver.org/) and [Keep a #### Added * PHPCS: New `Yoast.Commenting.FileComment` sniff. This sniff is a wrapper around the `FileComment` sniff used in WordPressCS and manages the slightly different requirements for file comments set for the Yoast organisation, in particular: - - If a file is namespaced, no file comment is needed (and having one is discouraged). + - If a file is namespaced, no file comment is needed (and having one is discouraged). * PHPCS: New `Yoast.Namespaces.NamespaceDeclaration` sniff. This sniff forbids the use of: - Namespace declarations without a namespace name, i.e. `namespace;` which in effect means "global namespace". @@ -242,6 +291,7 @@ Initial public release as a stand-alone package. [PHP Mess Detector]: https://github.com/phpmd/phpmd/blob/master/CHANGELOG [DealerDirect Composer PHPCS plugin]: https://github.com/Dealerdirect/phpcodesniffer-composer-installer/releases +[1.3.0]: https://github.com/Yoast/yoastcs/compare/1.2.2...1.3.0 [1.2.2]: https://github.com/Yoast/yoastcs/compare/1.2.1...1.2.2 [1.2.1]: https://github.com/Yoast/yoastcs/compare/1.2.0...1.2.1 [1.2.0]: https://github.com/Yoast/yoastcs/compare/1.1.0...1.2.0 From bfeb640e25e4ca07e715dd68898f22eac65b08c7 Mon Sep 17 00:00:00 2001 From: jrfnl <663378+jrfnl@users.noreply.github.com> Date: Wed, 31 Jul 2019 13:50:20 +0200 Subject: [PATCH 42/42] Changelog: minor other grammar fixes --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ded39219..4e7b4254 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,8 @@ -# Change Log for YoastCS +# Changelog for YoastCS All notable changes to this project will be documented in this file. -This projects adheres to [Semantic Versioning](https://semver.org/) and [Keep a CHANGELOG](https://keepachangelog.com/). +This project adheres to [Semantic Versioning](https://semver.org/) and [Keep a CHANGELOG](https://keepachangelog.com/). ### [1.3.0] - 2019-07-31