diff --git a/.travis.yml b/.travis.yml index bbd5417579..4369ad0926 100644 --- a/.travis.yml +++ b/.travis.yml @@ -45,7 +45,7 @@ jobs: include: #### SNIFF STAGE #### - stage: sniff - php: 7.3 + php: 7.4 env: PHPCS_BRANCH="dev-master" addons: apt: @@ -70,12 +70,21 @@ jobs: - diff -B --tabsize=4 ./WordPress-Extra/ruleset.xml <(xmllint --format "./WordPress-Extra/ruleset.xml") - diff -B --tabsize=4 ./phpcs.xml.dist.sample <(xmllint --format "./phpcs.xml.dist.sample") + # Validate the composer.json file. + # @link https://getcomposer.org/doc/03-cli.md#validate + - composer validate --no-check-all --strict + + # Check that the sniffs available are feature complete. + # For now, just check that all sniffs have unit tests. + # At a later stage the documentation check can be activated. + - composer check-complete + #### RULESET STAGE #### # Make sure the rulesets don't throw unexpected errors or warnings. # This check needs to be run against a high PHP version to prevent triggering the syntax error check. # It also needs to be run against all PHPCS versions WPCS is tested against. - stage: rulesets - php: 7.3 + php: 7.4 env: PHPCS_BRANCH="dev-master" script: - $(pwd)/vendor/bin/phpcs -ps ./bin/class-ruleset-test.php --standard=WordPress-Core @@ -91,7 +100,7 @@ jobs: - travis_retry $(pwd)/vendor/bin/phpcbf -pq ./WordPress/Tests/ --standard=WordPress --extensions=inc --exclude=Generic.PHP.Syntax --report=summary - stage: rulesets - php: 7.3 + php: 7.4 env: PHPCS_BRANCH="3.3.1" script: - $(pwd)/vendor/bin/phpcs -ps ./bin/class-ruleset-test.php --standard=WordPress-Core @@ -103,7 +112,7 @@ jobs: # This is a much quicker test which only runs the unit tests and linting against the low/high # supported PHP/PHPCS combinations. - stage: quicktest - php: 7.3 + php: 7.4 env: PHPCS_BRANCH="dev-master" LINT=1 - php: 7.3 env: PHPCS_BRANCH="3.3.1" @@ -112,9 +121,16 @@ jobs: - php: 5.4 env: PHPCS_BRANCH="3.3.1" + #### TEST STAGE #### + # Add extra build to test against PHPCS 4. + - stage: test + php: 7.4 + env: PHPCS_BRANCH="4.0.x-dev" + allow_failures: # Allow failures for unstable builds. - php: "nightly" + - env: PHPCS_BRANCH="4.0.x-dev" before_install: # Speed up build time by disabling Xdebug. @@ -125,15 +141,22 @@ before_install: # 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 [[ "$TRAVIS_BUILD_STAGE_NAME" != "Sniff" && $PHPCS_BRANCH != "dev-master" ]]; then + if [[ "${TRAVIS_BUILD_STAGE_NAME^}" != "Sniff" && $PHPCS_BRANCH != "dev-master" ]]; then echo 'error_reporting = E_ALL & ~E_DEPRECATED' >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini fi - export XMLLINT_INDENT=" " - export PHPUNIT_DIR=/tmp/phpunit + - | + if [[ $TRAVIS_PHP_VERSION == "nightly" || $PHPCS_BRANCH == "4.0.x-dev" ]]; then + # Even though we're not doing a dev install, dev requirements are still checked. + # Neither the PHPCS Composer plugin nor PHPCompatibility allows yet for PHPCS 4.x. + # The Composer plugin also doesn't allow for installation on PHP 8.x/nightly. + composer remove --dev dealerdirect/phpcodesniffer-composer-installer phpcompatibility/php-compatibility --no-update --no-scripts + fi - composer require squizlabs/php_codesniffer:${PHPCS_BRANCH} --update-no-dev --no-suggest --no-scripts - | - if [[ "$TRAVIS_BUILD_STAGE_NAME" == "Sniff" ]]; then + if [[ "${TRAVIS_BUILD_STAGE_NAME^}" == "Sniff" ]]; then composer install --dev --no-suggest # The `dev` required DealerDirect Composer plugin takes care of the installed_paths. else @@ -148,10 +171,6 @@ script: # Lint the PHP files against parse errors. - if [[ "$LINT" == "1" ]]; then if find . -path ./vendor -prune -o -path ./bin -prune -o -name "*.php" -exec php -l {} \; | grep "^[Parse error|Fatal error]"; then exit 1; fi; fi - # Validate the composer.json file. - # @link https://getcomposer.org/doc/03-cli.md#validate - - if [[ "$LINT" == "1" ]]; then composer validate --no-check-all --strict; fi - # Run the unit tests. - | if [[ ${TRAVIS_PHP_VERSION:0:3} > "7.1" ]]; then diff --git a/CHANGELOG.md b/CHANGELOG.md index 072f0ac71d..f8c8c98784 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,24 @@ This projects adheres to [Semantic Versioning](https://semver.org/) and [Keep a _No documentation available about unreleased changes as of yet._ +## [2.3.0] - 2020-05-14 + +### Added +- The `WordPress.WP.I18n` sniff contains a new check for translatable text strings which are wrapped in HTML tags, like `

Translate me

`. Those tags should be moved out of the translatable string. + Note: Translatable strings wrapped in `` tags where the URL is intended to be localized will not trigger this check. + +### Changed +- The default value for `minimum_supported_wp_version`, as used by a [number of sniffs detecting usage of deprecated WP features](https://github.com/WordPress/WordPress-Coding-Standards/wiki/Customizable-sniff-properties#minimum-wp-version-to-check-for-usage-of-deprecated-functions-classes-and-function-parameters), has been updated to `5.1`. +- The `WordPress.WP.DeprecatedFunctions` sniff will now detect functions deprecated in WP 5.4. +- Improved grammar of an error message in the `WordPress.WP.DiscouragedFunctions` sniff. +- CI: The codebase is now - preliminary - being tested against the PHPCS 4.x development branch. + +### Fixed +- All function call detection sniffs: fixed a bug where constants with the same name as one of the targeted functions could inadvertently be recognized as if they were a called function. +- `WordPress.DB.PreparedSQL`: fixed a bug where the sniff would trigger on the namespace separator character `\\`. +- `WordPress.Security.EscapeOutput`: fixed a bug with the variable replacement in one of the error messages. + + ## [2.2.1] - 2020-02-04 ### Added diff --git a/WordPress/AbstractFunctionRestrictionsSniff.php b/WordPress/AbstractFunctionRestrictionsSniff.php index e208c90ac1..53ea9a255e 100644 --- a/WordPress/AbstractFunctionRestrictionsSniff.php +++ b/WordPress/AbstractFunctionRestrictionsSniff.php @@ -57,7 +57,7 @@ abstract class AbstractFunctionRestrictionsSniff extends Sniff { * * @var string */ - protected $regex_pattern = '`\b(?:%s)\b`i'; + protected $regex_pattern = '`^(?:%s)$`i'; /** * Cache for the group information. @@ -212,35 +212,52 @@ public function process_token( $stackPtr ) { */ public function is_targetted_token( $stackPtr ) { - // Exclude function definitions, class methods, and namespaced calls. - if ( \T_STRING === $this->tokens[ $stackPtr ]['code'] ) { - if ( $this->is_class_object_call( $stackPtr ) === true ) { - return false; - } + if ( \T_STRING !== $this->tokens[ $stackPtr ]['code'] ) { + return false; + } - if ( $this->is_token_namespaced( $stackPtr ) === true ) { - return false; - } + // Exclude function definitions, class methods, and namespaced calls. + if ( $this->is_class_object_call( $stackPtr ) === true ) { + return false; + } - $prev = $this->phpcsFile->findPrevious( Tokens::$emptyTokens, ( $stackPtr - 1 ), null, true ); + if ( $this->is_token_namespaced( $stackPtr ) === true ) { + return false; + } - if ( false !== $prev ) { - // Skip sniffing if calling a same-named method, or on function definitions. - $skipped = array( - \T_FUNCTION => \T_FUNCTION, - \T_CLASS => \T_CLASS, - \T_AS => \T_AS, // Use declaration alias. - ); + $prev = $this->phpcsFile->findPrevious( Tokens::$emptyTokens, ( $stackPtr - 1 ), null, true ); + if ( false !== $prev ) { + // Skip sniffing on function, class definitions or for function aliases in use statements. + $skipped = array( + \T_FUNCTION => \T_FUNCTION, + \T_CLASS => \T_CLASS, + \T_AS => \T_AS, // Use declaration alias. + ); - if ( isset( $skipped[ $this->tokens[ $prev ]['code'] ] ) ) { - return false; - } + if ( isset( $skipped[ $this->tokens[ $prev ]['code'] ] ) ) { + return false; } + } + + // Check if this could even be a function call. + $next = $this->phpcsFile->findNext( Tokens::$emptyTokens, ( $stackPtr + 1 ), null, true ); + if ( false === $next ) { + return false; + } + // Check for `use function ... (as|;)`. + if ( ( \T_STRING === $this->tokens[ $prev ]['code'] && 'function' === $this->tokens[ $prev ]['content'] ) + && ( \T_AS === $this->tokens[ $next ]['code'] || \T_SEMICOLON === $this->tokens[ $next ]['code'] ) + ) { return true; } - return false; + // If it's not a `use` statement, there should be parenthesis. + if ( \T_OPEN_PARENTHESIS !== $this->tokens[ $next ]['code'] ) { + return false; + } + + return true; } /** diff --git a/WordPress/Sniff.php b/WordPress/Sniff.php index 8c3528b2fa..55ca6e7419 100644 --- a/WordPress/Sniff.php +++ b/WordPress/Sniff.php @@ -82,7 +82,7 @@ abstract class Sniff implements PHPCS_Sniff { * * @var string WordPress version. */ - public $minimum_supported_version = '5.0'; + public $minimum_supported_version = '5.1'; /** * Custom list of classes which test classes can extend. diff --git a/WordPress/Sniffs/DB/PreparedSQLSniff.php b/WordPress/Sniffs/DB/PreparedSQLSniff.php index a3ed993542..1fe2ad96da 100644 --- a/WordPress/Sniffs/DB/PreparedSQLSniff.php +++ b/WordPress/Sniffs/DB/PreparedSQLSniff.php @@ -69,6 +69,7 @@ class PreparedSQLSniff extends Sniff { \T_INT_CAST => true, \T_DOUBLE_CAST => true, \T_BOOL_CAST => true, + \T_NS_SEPARATOR => true, ); /** diff --git a/WordPress/Sniffs/Security/EscapeOutputSniff.php b/WordPress/Sniffs/Security/EscapeOutputSniff.php index aef649468f..48a4766bad 100644 --- a/WordPress/Sniffs/Security/EscapeOutputSniff.php +++ b/WordPress/Sniffs/Security/EscapeOutputSniff.php @@ -456,7 +456,7 @@ public function process_token( $stackPtr ) { "All output should be run through an escaping function (see the Security sections in the WordPress Developer Handbooks), found '%s'.", $ptr, 'OutputNotEscaped', - $content + array( $content ) ); } diff --git a/WordPress/Sniffs/WP/AlternativeFunctionsSniff.php b/WordPress/Sniffs/WP/AlternativeFunctionsSniff.php index 43ad575cd3..fc6e8fbbb3 100644 --- a/WordPress/Sniffs/WP/AlternativeFunctionsSniff.php +++ b/WordPress/Sniffs/WP/AlternativeFunctionsSniff.php @@ -89,6 +89,9 @@ public function getGroups() { 'functions' => array( 'curl_*', ), + 'whitelist' => array( + 'curl_version' => true, + ), ), 'parse_url' => array( @@ -273,10 +276,6 @@ public function process_matched_token( $stackPtr, $group_name, $matched_content unset( $first_param ); break; - - case 'curl_version': - // Curl version doesn't actually create a connection. - return; } if ( ! isset( $this->groups[ $group_name ]['since'] ) ) { diff --git a/WordPress/Sniffs/WP/DeprecatedFunctionsSniff.php b/WordPress/Sniffs/WP/DeprecatedFunctionsSniff.php index 7793917096..df7a4b5525 100644 --- a/WordPress/Sniffs/WP/DeprecatedFunctionsSniff.php +++ b/WordPress/Sniffs/WP/DeprecatedFunctionsSniff.php @@ -1359,6 +1359,12 @@ class DeprecatedFunctionsSniff extends AbstractFunctionRestrictionsSniff { 'alt' => 'wp_update_user()', 'version' => '5.3.0', ), + + // WP 5.4.0. + 'wp_get_user_request_data' => array( + 'alt' => 'wp_get_user_request()', + 'version' => '5.4.0', + ), ); /** diff --git a/WordPress/Sniffs/WP/DiscouragedFunctionsSniff.php b/WordPress/Sniffs/WP/DiscouragedFunctionsSniff.php index 89d8e09c47..d51504a3d3 100644 --- a/WordPress/Sniffs/WP/DiscouragedFunctionsSniff.php +++ b/WordPress/Sniffs/WP/DiscouragedFunctionsSniff.php @@ -46,7 +46,7 @@ public function getGroups() { 'wp_reset_query' => array( 'type' => 'warning', - 'message' => '%s() is discouraged. Use the wp_reset_postdata() instead.', + 'message' => '%s() is discouraged. Use wp_reset_postdata() instead.', 'functions' => array( 'wp_reset_query', ), diff --git a/WordPress/Sniffs/WP/I18nSniff.php b/WordPress/Sniffs/WP/I18nSniff.php index d481955368..d229cb5e9a 100644 --- a/WordPress/Sniffs/WP/I18nSniff.php +++ b/WordPress/Sniffs/WP/I18nSniff.php @@ -630,11 +630,46 @@ protected function check_text( $context ) { * * Strip placeholders and surrounding quotes. */ - $non_placeholder_content = trim( $this->strip_quotes( $content ) ); - $non_placeholder_content = preg_replace( self::SPRINTF_PLACEHOLDER_REGEX, '', $non_placeholder_content ); + $content_without_quotes = trim( $this->strip_quotes( $content ) ); + $non_placeholder_content = preg_replace( self::SPRINTF_PLACEHOLDER_REGEX, '', $content_without_quotes ); if ( '' === $non_placeholder_content ) { $this->phpcsFile->addError( 'Strings should have translatable content', $stack_ptr, 'NoEmptyStrings' ); + return; + } + + /* + * NoHtmlWrappedStrings + * + * Strip surrounding quotes. + */ + $reader = new \XMLReader(); + $reader->XML( $content_without_quotes, 'UTF-8', LIBXML_NOERROR | LIBXML_ERR_NONE | LIBXML_NOWARNING ); + + // Is the first node an HTML element? + if ( ! $reader->read() || \XMLReader::ELEMENT !== $reader->nodeType ) { + return; + } + + // If the opening HTML element includes placeholders in its attributes, we don't warn. + // E.g. ''. + $i = 0; + while ( $attr = $reader->getAttributeNo( $i ) ) { + if ( preg_match( self::SPRINTF_PLACEHOLDER_REGEX, $attr ) === 1 ) { + return; + } + + ++$i; + } + + // We don't flag strings wrapped in `...`, as the link target might actually need localization. + if ( 'a' === $reader->name && $reader->getAttribute( 'href' ) ) { + return; + } + + // Does the entire string only consist of this HTML node? + if ( $reader->readOuterXml() === $content_without_quotes ) { + $this->phpcsFile->addWarning( 'Strings should not be wrapped in HTML', $stack_ptr, 'NoHtmlWrappedStrings' ); } } diff --git a/WordPress/Tests/DB/PreparedSQLUnitTest.inc b/WordPress/Tests/DB/PreparedSQLUnitTest.inc index 57fc2c5918..52ef3a0e31 100644 --- a/WordPress/Tests/DB/PreparedSQLUnitTest.inc +++ b/WordPress/Tests/DB/PreparedSQLUnitTest.inc @@ -39,7 +39,7 @@ $all_post_meta = $wpdb->get_results( $wpdb->prepare( sprintf( WHERE `meta_key` = "sort_order" AND `post_id` IN (%s)', $wpdb->postmeta, - implode( ',', array_fill( 0, count( $post_ids ), '%d' ) ) + \implode( ',', \array_fill( 0, \count( $post_ids ), '%d' ) ) ), $post_ids ) ); // Ok. $wpdb->query( " diff --git a/WordPress/Tests/WP/AlternativeFunctionsUnitTest.inc b/WordPress/Tests/WP/AlternativeFunctionsUnitTest.inc index 7bfaf9b14d..d819f25a11 100644 --- a/WordPress/Tests/WP/AlternativeFunctionsUnitTest.inc +++ b/WordPress/Tests/WP/AlternativeFunctionsUnitTest.inc @@ -71,3 +71,10 @@ curl_version(); // OK. // Safeguard that additional logic uses case-insensitive function name check. Strip_Tags( $something ); // Warning. + +if ( ! $curl['features'] && CURL_VERSION_SSL ) {} // OK. +my_parse_url_function(); // OK. +function curl_version_ssl() {} // OK. +use function curl_version; // OK. +use function something as curl_version; // OK. +use function curl_init as curl_version; // Bad. diff --git a/WordPress/Tests/WP/AlternativeFunctionsUnitTest.php b/WordPress/Tests/WP/AlternativeFunctionsUnitTest.php index f25dec8cd3..ca95672572 100644 --- a/WordPress/Tests/WP/AlternativeFunctionsUnitTest.php +++ b/WordPress/Tests/WP/AlternativeFunctionsUnitTest.php @@ -64,6 +64,7 @@ public function getWarningList() { 67 => 1, 68 => 1, 73 => 1, + 80 => 1, ); } diff --git a/WordPress/Tests/WP/DeprecatedFunctionsUnitTest.inc b/WordPress/Tests/WP/DeprecatedFunctionsUnitTest.inc index b7095678b5..3990ad6c4d 100644 --- a/WordPress/Tests/WP/DeprecatedFunctionsUnitTest.inc +++ b/WordPress/Tests/WP/DeprecatedFunctionsUnitTest.inc @@ -345,3 +345,5 @@ install_blog(); _wp_json_prepare_data(); _wp_privacy_requests_screen_options(); update_user_status(); +/* ============ WP 5.4 ============ */ +wp_get_user_request_data(); diff --git a/WordPress/Tests/WP/DeprecatedFunctionsUnitTest.php b/WordPress/Tests/WP/DeprecatedFunctionsUnitTest.php index 6f20c09dca..979807e3b0 100644 --- a/WordPress/Tests/WP/DeprecatedFunctionsUnitTest.php +++ b/WordPress/Tests/WP/DeprecatedFunctionsUnitTest.php @@ -76,10 +76,13 @@ public function getErrorList() { */ public function getWarningList() { - $warnings = array_fill( 342, 6, 1 ); + $warnings = array_fill( 342, 8, 1 ); // Unset the lines related to version comments. - unset( $warnings[344] ); + unset( + $warnings[344], + $warnings[348] + ); return $warnings; } diff --git a/WordPress/Tests/WP/I18nUnitTest.1.inc b/WordPress/Tests/WP/I18nUnitTest.1.inc index ac9dde8eec..9dee14b160 100644 --- a/WordPress/Tests/WP/I18nUnitTest.1.inc +++ b/WordPress/Tests/WP/I18nUnitTest.1.inc @@ -183,5 +183,37 @@ _n_noop($singular); // Bad x 3. // This test is needed to verify that the missing plural argument above does not cause an internal error, stopping the run. _n_noop( 'I have %d cat.', 'I have %d cats.' ); // Bad. +// HTML wrapped strings. +__( '
123 Fake Street
', 'my-slug' ); // Bad, string shouldn't be wrapped in HTML. +__( 'I live at
123 Fake Street
', 'my-slug' ); // Okay, no consensus on HTML tags within strings. +__( '
123 Fake Street
is my home', 'my-slug' ); // Okay, no consensus on HTML tags within strings. +__( 'Text More text Text', 'my-slug' ); // Good, we're not wrapping +__( '
Translatable content
', 'my-slug' ); // Bad +__( '', 'my-slug' ); // Wrapping is okay, since there are placeholders +__( 'Foo', 'my-slug' ); // Bad +__( 'Foo', 'my-slug' ); // Good + +// Strings wrapped in `...` aren't flagged, since the link target might require localization. +__( 'WordPress', 'my-slug' ); // Good +__( 'translatable text', 'my-slug' ) // Bad, since no href +__( 'translatable text', 'my-slug' ) // Bad, since no href +__( 'translatable text', 'my-slug' ) // Good + +// Strings containing malformed XML, to make sure we're able to parse them. No warnings should be thrown. +__( '
text to translate', 'my-slug' ); +__( 'text to
translate', 'my-slug' ); +__( 'text < to translate', 'my-slug' ); +__( 'text to > translate', 'my-slug' ); +__( '
my address
', 'my-slug' ); +__( '
text to translate
', 'my-slug' ); +__( '<>text to translate', 'my-slug' ); +__( '<>text to translate<>', 'my-slug' ); +__( '
', 'my-slug' ); +__( '<<>>text to translate<<>', 'my-slug' ); +__( '<<>>123', 'my-slug' ); +__( 'Foo', 'my-slug' ); +__( 'Foo', 'my-slug' ); +__( 'Foo', 'my-slug' ); + // phpcs:set WordPress.WP.I18n text_domain[] // phpcs:set WordPress.WP.I18n check_translator_comments true diff --git a/WordPress/Tests/WP/I18nUnitTest.1.inc.fixed b/WordPress/Tests/WP/I18nUnitTest.1.inc.fixed index 6e2cf60622..f143370ec0 100644 --- a/WordPress/Tests/WP/I18nUnitTest.1.inc.fixed +++ b/WordPress/Tests/WP/I18nUnitTest.1.inc.fixed @@ -183,5 +183,37 @@ _n_noop($singular); // Bad x 3. // This test is needed to verify that the missing plural argument above does not cause an internal error, stopping the run. _n_noop( 'I have %d cat.', 'I have %d cats.' ); // Bad. +// HTML wrapped strings. +__( '
123 Fake Street
', 'my-slug' ); // Bad, string shouldn't be wrapped in HTML. +__( 'I live at
123 Fake Street
', 'my-slug' ); // Okay, no consensus on HTML tags within strings. +__( '
123 Fake Street
is my home', 'my-slug' ); // Okay, no consensus on HTML tags within strings. +__( 'Text More text Text', 'my-slug' ); // Good, we're not wrapping +__( '
Translatable content
', 'my-slug' ); // Bad +__( '', 'my-slug' ); // Wrapping is okay, since there are placeholders +__( 'Foo', 'my-slug' ); // Bad +__( 'Foo', 'my-slug' ); // Good + +// Strings wrapped in `...` aren't flagged, since the link target might require localization. +__( 'WordPress', 'my-slug' ); // Good +__( 'translatable text', 'my-slug' ) // Bad, since no href +__( 'translatable text', 'my-slug' ) // Bad, since no href +__( 'translatable text', 'my-slug' ) // Good + +// Strings containing malformed XML, to make sure we're able to parse them. No warnings should be thrown. +__( '
text to translate', 'my-slug' ); +__( 'text to
translate', 'my-slug' ); +__( 'text < to translate', 'my-slug' ); +__( 'text to > translate', 'my-slug' ); +__( '
my address
', 'my-slug' ); +__( '
text to translate
', 'my-slug' ); +__( '<>text to translate', 'my-slug' ); +__( '<>text to translate<>', 'my-slug' ); +__( '
', 'my-slug' ); +__( '<<>>text to translate<<>', 'my-slug' ); +__( '<<>>123', 'my-slug' ); +__( 'Foo', 'my-slug' ); +__( 'Foo', 'my-slug' ); +__( 'Foo', 'my-slug' ); + // phpcs:set WordPress.WP.I18n text_domain[] // phpcs:set WordPress.WP.I18n check_translator_comments true diff --git a/WordPress/Tests/WP/I18nUnitTest.php b/WordPress/Tests/WP/I18nUnitTest.php index 8a462ef915..0d81d06e30 100644 --- a/WordPress/Tests/WP/I18nUnitTest.php +++ b/WordPress/Tests/WP/I18nUnitTest.php @@ -33,6 +33,9 @@ public function setCliValues( $testFile, $config ) { // Test overruling the text domain from the command line for one test file. if ( 'I18nUnitTest.3.inc' === $testFile ) { $config->setConfigData( 'text_domain', 'something', true ); + } else { + // Delete the text domain option so it doesn't persist for subsequent test files. + $config->setConfigData( 'text_domain', null, true ); } } @@ -161,6 +164,12 @@ public function getWarningList( $testFile = '' ) { 154 => 1, 158 => 1, 159 => 1, + 187 => 1, + 191 => 1, + 193 => 1, + 194 => 1, + 198 => 1, + 199 => 1, ); case 'I18nUnitTest.2.inc': diff --git a/composer.json b/composer.json index 13d4afbc3d..7e3c558fb3 100644 --- a/composer.json +++ b/composer.json @@ -21,7 +21,8 @@ "require-dev": { "dealerdirect/phpcodesniffer-composer-installer": "^0.5 || ^0.6", "phpcompatibility/php-compatibility": "^9.0", - "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0", + "phpcsstandards/phpcsdevtools": "^1.0" }, "suggest": { "dealerdirect/phpcodesniffer-composer-installer": "^0.6 || This Composer plugin will sort out the PHPCS 'installed_paths' automatically." @@ -39,6 +40,12 @@ ], "run-tests": [ "@php ./vendor/phpunit/phpunit/phpunit --filter WordPress --bootstrap=\"./vendor/squizlabs/php_codesniffer/tests/bootstrap.php\" ./vendor/squizlabs/php_codesniffer/tests/AllTests.php" + ], + "check-complete": [ + "@php ./vendor/phpcsstandards/phpcsdevtools/bin/phpcs-check-feature-completeness -q ./WordPress" + ], + "check-complete-strict": [ + "@php ./vendor/phpcsstandards/phpcsdevtools/bin/phpcs-check-feature-completeness ./WordPress" ] }, "support": {