diff --git a/.distignore b/.distignore index 124135930..fd46e5ff4 100644 --- a/.distignore +++ b/.distignore @@ -8,6 +8,8 @@ build-phpunit docs node_modules patches +# Distributed via vendor/plugin-check/phpcs-sniffs instead +/phpcs-sniffs/ tests *.DS_Store diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index a4ddf4467..7c6333e0a 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -17,7 +17,7 @@ jobs: - name: Install PHP dependencies uses: ramsey/composer-install@57532f8be5bda426838819c5ee9afb8af389d51a with: - composer-options: '--prefer-dist --no-progress --no-interaction --no-dev' + composer-options: '--prefer-dist --no-dev' - name: WordPress Plugin Deploy id: deploy diff --git a/.github/workflows/php-lint.yml b/.github/workflows/php-lint.yml index d318d019e..924378b15 100644 --- a/.github/workflows/php-lint.yml +++ b/.github/workflows/php-lint.yml @@ -51,7 +51,7 @@ jobs: - name: Install PHP dependencies uses: ramsey/composer-install@57532f8be5bda426838819c5ee9afb8af389d51a with: - composer-options: '--prefer-dist --no-progress --no-interaction' + composer-options: '--prefer-dist' - name: PHP Lint run: composer lint diff --git a/.github/workflows/php-test.yml b/.github/workflows/php-test.yml index 457f82d2d..75f4ecd8b 100644 --- a/.github/workflows/php-test.yml +++ b/.github/workflows/php-test.yml @@ -106,3 +106,51 @@ jobs: file: build/logs/*.xml flags: unit token: ${{ secrets.CODECOV_TOKEN }} + + phpcs-sniffs: + name: PHP (Sniffs) ${{ matrix.php }} ${{ matrix.coverage && ' (with coverage)' || '' }} + runs-on: ubuntu-latest + timeout-minutes: 20 + strategy: + fail-fast: true + matrix: + php: + - '7.2' + - '7.3' + - '7.4' + - '8.0' + - '8.1' + - '8.2' + wordpress: [ 'latest' ] + include: + - php: '8.3' + coverage: true + steps: + - uses: actions/checkout@v4 + + - uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + + - name: Install PHP dependencies + uses: ramsey/composer-install@57532f8be5bda426838819c5ee9afb8af389d51a + with: + working-directory: "phpcs-sniffs" + + - name: Run tests + working-directory: "phpcs-sniffs" + run: | + + if [[ ${{ matrix.coverage == true }} == true ]]; then + composer run-tests -- --coverage-clover ../build/logs/phpcs-coverage.xml + else + composer run-tests + fi + + - name: Upload code coverage report + if: ${{ matrix.coverage }} + uses: codecov/codecov-action@893cfea3da0870ceb77096be8b5fe014720f3c32 + with: + file: build/logs/*.xml + flags: phpcs-sniffs + token: ${{ secrets.CODECOV_TOKEN }} diff --git a/composer.json b/composer.json index ecdffd889..d6a11a3b7 100644 --- a/composer.json +++ b/composer.json @@ -1,16 +1,17 @@ { "name": "wordpress/plugin-check", "description": "Plugin Check is a WordPress.org tool which provides checks to help plugins meet the directory requirements and follow various best practices.", - "type": "wordpress-plugin", "license": "GPL-2.0-or-later", + "type": "wordpress-plugin", "require": { "php": ">=7.2.24", + "ext-json": "*", + "afragen/wordpress-plugin-readme-parser": "dev-master#67fba498d0b112acf84386b95e4905c539a33f0b", + "automattic/vipwpcs": "^3.0.0", "composer/installers": "^2.2", "dealerdirect/phpcodesniffer-composer-installer": "^1.0.0", - "wp-coding-standards/wpcs": "^3.1.0", - "automattic/vipwpcs": "^3.0.0", - "afragen/wordpress-plugin-readme-parser": "dev-master#67fba498d0b112acf84386b95e4905c539a33f0b", - "ext-json": "*" + "plugin-check/phpcs-sniffs": "@dev", + "wp-coding-standards/wpcs": "^3.1.0" }, "require-dev": { "phpcompatibility/php-compatibility": "^9.3", @@ -21,49 +22,58 @@ "szepeviktor/phpstan-wordpress": "^1.1", "wp-cli/extension-command": "^2.1", "wp-cli/wp-cli": "^2.8", - "wp-cli/wp-cli-tests": "^v4.2.9" + "wp-cli/wp-cli-tests": "^4.2.9" + }, + "repositories": [ + { + "type": "path", + "url": "./phpcs-sniffs", + "options": { + "symlink": false + } + } + ], + "autoload": { + "psr-4": { + "WordPress\\Plugin_Check\\": "includes/" + } + }, + "autoload-dev": { + "psr-4": { + "WordPress\\Plugin_Check\\Behat_Utils\\": "tests/behat/includes", + "WordPress\\Plugin_Check\\Test_Data\\": "tests/phpunit/testdata/Checks", + "WordPress\\Plugin_Check\\Test_Utils\\": "tests/phpunit/utils" + } + }, + "config": { + "allow-plugins": { + "composer/installers": true, + "cweagans/composer-patches": false, + "dealerdirect/phpcodesniffer-composer-installer": true, + "phpstan/extension-installer": true + }, + "platform": { + "php": "7.2.24" + } }, "scripts": { "behat": "BEHAT_FEATURES_FOLDER=tests/behat/features run-behat-tests", "behat-rerun": "BEHAT_FEATURES_FOLDER=tests/behat/features rerun-behat-tests", - "prepare-behat-tests": "install-package-tests", "format": "phpcbf --standard=phpcs.xml.dist", "lint": "phpcs --standard=phpcs.xml.dist", "phpmd": "phpmd . text phpmd.xml", "phpstan": "phpstan analyse --memory-limit=2048M", + "prepare-behat-tests": "install-package-tests", "test": "phpunit" }, "scripts-descriptions": { - "lint": "Detect coding standards issues", + "behat": "Run functional tests", + "behat-rerun": "Re-run failed functional tests", "format": "Detect and automatically fix most coding standards issues", - "test": "Run unit tests", + "lint": "Detect coding standards issues", "phpmd": "Run PHP mess detector", "phpstan": "Run static analysis", - "behat": "Run functional tests", - "behat-rerun": "Re-run failed functional tests", - "prepare-behat-tests": "Prepare functional tests" - }, - "config": { - "allow-plugins": { - "dealerdirect/phpcodesniffer-composer-installer": true, - "composer/installers": true, - "phpstan/extension-installer": true, - "cweagans/composer-patches": false - }, - "platform": { - "php": "7.2.24" - } - }, - "autoload": { - "psr-4": { - "WordPress\\Plugin_Check\\": "includes/" - } - }, - "autoload-dev": { - "psr-4": { - "WordPress\\Plugin_Check\\Test_Data\\": "tests/phpunit/testdata/Checks", - "WordPress\\Plugin_Check\\Test_Utils\\": "tests/phpunit/utils", - "WordPress\\Plugin_Check\\Behat_Utils\\": "tests/behat/includes" - } + "prepare-behat-tests": "Prepare functional tests", + "test": "Run unit tests" } } diff --git a/composer.lock b/composer.lock index e4f130017..69dac6d4f 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "8659a900d916eb86b5aab92af786710e", + "content-hash": "57475d38e66e7428a643b2275bdfcfba", "packages": [ { "name": "afragen/wordpress-plugin-readme-parser", @@ -547,6 +547,73 @@ ], "time": "2024-05-20T13:34:27+00:00" }, + { + "name": "plugin-check/phpcs-sniffs", + "version": "dev-486-check-calling-remote-files-js-css-images-etc-offloading-external", + "dist": { + "type": "path", + "url": "./phpcs-sniffs", + "reference": "03814379724f65217744c9d1e62c72804d4a843d" + }, + "require": { + "ext-libxml": "*", + "ext-tokenizer": "*", + "ext-xmlreader": "*", + "php": ">=7.2.24", + "phpcsstandards/phpcsutils": "^1.0.8", + "squizlabs/php_codesniffer": "^3.7.2" + }, + "require-dev": { + "php-parallel-lint/php-console-highlighter": "^1.0.0", + "php-parallel-lint/php-parallel-lint": "^1.3.2", + "phpcompatibility/php-compatibility": "^9.0", + "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0", + "wp-coding-standards/wpcs": "^3.0" + }, + "suggest": { + "ext-mbstring": "For improved results" + }, + "type": "phpcodesniffer-standard", + "scripts": { + "check-all": [ + "@lint", + "@check-cs", + "@run-tests" + ], + "check-cs": [ + "@php ./vendor/squizlabs/php_codesniffer/bin/phpcs" + ], + "fix-cs": [ + "@php ./vendor/squizlabs/php_codesniffer/bin/phpcbf" + ], + "lint": [ + "@php ./vendor/php-parallel-lint/php-parallel-lint/parallel-lint . -e php --show-deprecated --exclude vendor --exclude .git" + ], + "run-tests": [ + "@php ./vendor/phpunit/phpunit/phpunit --filter PluginCheck ./vendor/squizlabs/php_codesniffer/tests/AllTests.php --no-coverage" + ] + }, + "license": [ + "GPL-2.0-or-later" + ], + "authors": [ + { + "name": "Contributors", + "homepage": "https://github.com/WordPress/plugin-check/graphs/contributors" + } + ], + "description": "PHP_CodeSniffer rules (sniffs) for static checks in Plugin Check", + "keywords": [ + "phpcs", + "plugin-check", + "standards", + "static analysis" + ], + "transport-options": { + "symlink": false, + "relative": true + } + }, { "name": "sirbrillig/phpcs-variable-analysis", "version": "v2.11.19", @@ -954,26 +1021,26 @@ }, { "name": "composer/pcre", - "version": "2.2.0", + "version": "2.3.1", "source": { "type": "git", "url": "https://github.com/composer/pcre.git", - "reference": "0e455b78ac53637929b29d5ab5bf3c978329c1eb" + "reference": "26859a860a7f140fc08422c3cc14ad9c2a287d79" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/pcre/zipball/0e455b78ac53637929b29d5ab5bf3c978329c1eb", - "reference": "0e455b78ac53637929b29d5ab5bf3c978329c1eb", + "url": "https://api.github.com/repos/composer/pcre/zipball/26859a860a7f140fc08422c3cc14ad9c2a287d79", + "reference": "26859a860a7f140fc08422c3cc14ad9c2a287d79", "shasum": "" }, "require": { "php": "^7.2 || ^8.0" }, "conflict": { - "phpstan/phpstan": "<1.11.8" + "phpstan/phpstan": "<1.11.10" }, "require-dev": { - "phpstan/phpstan": "^1.11.8", + "phpstan/phpstan": "^1.11.10", "phpstan/phpstan-strict-rules": "^1.1", "phpunit/phpunit": "^8 || ^9" }, @@ -1013,7 +1080,7 @@ ], "support": { "issues": "https://github.com/composer/pcre/issues", - "source": "https://github.com/composer/pcre/tree/2.2.0" + "source": "https://github.com/composer/pcre/tree/2.3.1" }, "funding": [ { @@ -1029,7 +1096,7 @@ "type": "tidelift" } ], - "time": "2024-07-25T09:28:32+00:00" + "time": "2024-08-27T12:02:26+00:00" }, { "name": "composer/semver", @@ -1944,16 +2011,16 @@ }, { "name": "phpstan/phpdoc-parser", - "version": "1.29.1", + "version": "1.30.1", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "fcaefacf2d5c417e928405b71b400d4ce10daaf4" + "reference": "51b95ec8670af41009e2b2b56873bad96682413e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/fcaefacf2d5c417e928405b71b400d4ce10daaf4", - "reference": "fcaefacf2d5c417e928405b71b400d4ce10daaf4", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/51b95ec8670af41009e2b2b56873bad96682413e", + "reference": "51b95ec8670af41009e2b2b56873bad96682413e", "shasum": "" }, "require": { @@ -1985,9 +2052,9 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.29.1" + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.30.1" }, - "time": "2024-05-31T08:52:43+00:00" + "time": "2024-09-07T20:13:05+00:00" }, { "name": "phpstan/phpstan", @@ -3464,16 +3531,16 @@ }, { "name": "symfony/console", - "version": "v5.4.42", + "version": "v5.4.43", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "cef62396a0477e94fc52e87a17c6e5c32e226b7f" + "reference": "e86f8554de667c16dde8aeb89a3990cfde924df9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/cef62396a0477e94fc52e87a17c6e5c32e226b7f", - "reference": "cef62396a0477e94fc52e87a17c6e5c32e226b7f", + "url": "https://api.github.com/repos/symfony/console/zipball/e86f8554de667c16dde8aeb89a3990cfde924df9", + "reference": "e86f8554de667c16dde8aeb89a3990cfde924df9", "shasum": "" }, "require": { @@ -3543,7 +3610,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v5.4.42" + "source": "https://github.com/symfony/console/tree/v5.4.43" }, "funding": [ { @@ -3559,20 +3626,20 @@ "type": "tidelift" } ], - "time": "2024-07-26T12:21:55+00:00" + "time": "2024-08-13T16:31:56+00:00" }, { "name": "symfony/dependency-injection", - "version": "v5.4.42", + "version": "v5.4.43", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "c8409889fdbf48b99271930ea0ebcf3ee5e1ceae" + "reference": "8c946c5c1d1692d5378cb722060969730cebc96d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/c8409889fdbf48b99271930ea0ebcf3ee5e1ceae", - "reference": "c8409889fdbf48b99271930ea0ebcf3ee5e1ceae", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/8c946c5c1d1692d5378cb722060969730cebc96d", + "reference": "8c946c5c1d1692d5378cb722060969730cebc96d", "shasum": "" }, "require": { @@ -3632,7 +3699,7 @@ "description": "Allows you to standardize and centralize the way objects are constructed in your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dependency-injection/tree/v5.4.42" + "source": "https://github.com/symfony/dependency-injection/tree/v5.4.43" }, "funding": [ { @@ -3648,7 +3715,7 @@ "type": "tidelift" } ], - "time": "2024-07-25T13:57:40+00:00" + "time": "2024-08-27T00:56:45+00:00" }, { "name": "symfony/deprecation-contracts", @@ -3950,16 +4017,16 @@ }, { "name": "symfony/finder", - "version": "v5.4.42", + "version": "v5.4.43", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "0724c51fa067b198e36506d2864e09a52180998a" + "reference": "ae25a9145a900764158d439653d5630191155ca0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/0724c51fa067b198e36506d2864e09a52180998a", - "reference": "0724c51fa067b198e36506d2864e09a52180998a", + "url": "https://api.github.com/repos/symfony/finder/zipball/ae25a9145a900764158d439653d5630191155ca0", + "reference": "ae25a9145a900764158d439653d5630191155ca0", "shasum": "" }, "require": { @@ -3993,7 +4060,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v5.4.42" + "source": "https://github.com/symfony/finder/tree/v5.4.43" }, "funding": [ { @@ -4009,24 +4076,24 @@ "type": "tidelift" } ], - "time": "2024-07-22T08:53:29+00:00" + "time": "2024-08-13T14:03:51+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "0424dff1c58f028c451efff2045f5d92410bd540" + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/0424dff1c58f028c451efff2045f5d92410bd540", - "reference": "0424dff1c58f028c451efff2045f5d92410bd540", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "provide": { "ext-ctype": "*" @@ -4072,7 +4139,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0" }, "funding": [ { @@ -4088,24 +4155,24 @@ "type": "tidelift" } ], - "time": "2024-05-31T15:07:36+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "64647a7c30b2283f5d49b874d84a18fc22054b7a" + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/64647a7c30b2283f5d49b874d84a18fc22054b7a", - "reference": "64647a7c30b2283f5d49b874d84a18fc22054b7a", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "suggest": { "ext-intl": "For best performance" @@ -4150,7 +4217,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.31.0" }, "funding": [ { @@ -4166,24 +4233,24 @@ "type": "tidelift" } ], - "time": "2024-05-31T15:07:36+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "a95281b0be0d9ab48050ebd988b967875cdb9fdb" + "reference": "3833d7255cc303546435cb650316bff708a1c75c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/a95281b0be0d9ab48050ebd988b967875cdb9fdb", - "reference": "a95281b0be0d9ab48050ebd988b967875cdb9fdb", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "suggest": { "ext-intl": "For best performance" @@ -4231,7 +4298,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.31.0" }, "funding": [ { @@ -4247,24 +4314,24 @@ "type": "tidelift" } ], - "time": "2024-05-31T15:07:36+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c" + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fd22ab50000ef01661e2a31d850ebaa297f8e03c", - "reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341", + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "provide": { "ext-mbstring": "*" @@ -4311,7 +4378,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0" }, "funding": [ { @@ -4327,24 +4394,24 @@ "type": "tidelift" } ], - "time": "2024-06-19T12:30:46+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-php73", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php73.git", - "reference": "ec444d3f3f6505bb28d11afa41e75faadebc10a1" + "reference": "0f68c03565dcaaf25a890667542e8bd75fe7e5bb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/ec444d3f3f6505bb28d11afa41e75faadebc10a1", - "reference": "ec444d3f3f6505bb28d11afa41e75faadebc10a1", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/0f68c03565dcaaf25a890667542e8bd75fe7e5bb", + "reference": "0f68c03565dcaaf25a890667542e8bd75fe7e5bb", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "type": "library", "extra": { @@ -4387,7 +4454,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php73/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-php73/tree/v1.31.0" }, "funding": [ { @@ -4403,24 +4470,24 @@ "type": "tidelift" } ], - "time": "2024-05-31T15:07:36+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-php80", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "77fa7995ac1b21ab60769b7323d600a991a90433" + "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/77fa7995ac1b21ab60769b7323d600a991a90433", - "reference": "77fa7995ac1b21ab60769b7323d600a991a90433", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", + "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "type": "library", "extra": { @@ -4467,7 +4534,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.31.0" }, "funding": [ { @@ -4483,24 +4550,24 @@ "type": "tidelift" } ], - "time": "2024-05-31T15:07:36+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-php81", - "version": "v1.30.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php81.git", - "reference": "3fb075789fb91f9ad9af537c4012d523085bd5af" + "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/3fb075789fb91f9ad9af537c4012d523085bd5af", - "reference": "3fb075789fb91f9ad9af537c4012d523085bd5af", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", + "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "type": "library", "extra": { @@ -4543,7 +4610,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php81/tree/v1.30.0" + "source": "https://github.com/symfony/polyfill-php81/tree/v1.31.0" }, "funding": [ { @@ -4559,7 +4626,7 @@ "type": "tidelift" } ], - "time": "2024-06-19T12:30:46+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/service-contracts", @@ -4646,16 +4713,16 @@ }, { "name": "symfony/string", - "version": "v5.4.42", + "version": "v5.4.43", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "909cec913edea162a3b2836788228ad45fcab337" + "reference": "8be1d484951ff5ca995eaf8edcbcb8b9a5888450" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/909cec913edea162a3b2836788228ad45fcab337", - "reference": "909cec913edea162a3b2836788228ad45fcab337", + "url": "https://api.github.com/repos/symfony/string/zipball/8be1d484951ff5ca995eaf8edcbcb8b9a5888450", + "reference": "8be1d484951ff5ca995eaf8edcbcb8b9a5888450", "shasum": "" }, "require": { @@ -4712,7 +4779,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v5.4.42" + "source": "https://github.com/symfony/string/tree/v5.4.43" }, "funding": [ { @@ -4728,7 +4795,7 @@ "type": "tidelift" } ], - "time": "2024-07-20T18:38:32+00:00" + "time": "2024-08-01T10:24:28+00:00" }, { "name": "symfony/translation", @@ -4907,16 +4974,16 @@ }, { "name": "symfony/yaml", - "version": "v5.4.40", + "version": "v5.4.43", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "81cad0ceab3d61fe14fe941ff18a230ac9c80f83" + "reference": "62f96e1cfd4cf518882a36bfedcf1fe4093c1299" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/81cad0ceab3d61fe14fe941ff18a230ac9c80f83", - "reference": "81cad0ceab3d61fe14fe941ff18a230ac9c80f83", + "url": "https://api.github.com/repos/symfony/yaml/zipball/62f96e1cfd4cf518882a36bfedcf1fe4093c1299", + "reference": "62f96e1cfd4cf518882a36bfedcf1fe4093c1299", "shasum": "" }, "require": { @@ -4962,7 +5029,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v5.4.40" + "source": "https://github.com/symfony/yaml/tree/v5.4.43" }, "funding": [ { @@ -4978,7 +5045,7 @@ "type": "tidelift" } ], - "time": "2024-05-31T14:33:22+00:00" + "time": "2024-08-11T17:40:32+00:00" }, { "name": "szepeviktor/phpstan-wordpress", @@ -5095,16 +5162,16 @@ }, { "name": "wp-cli/config-command", - "version": "v2.3.5", + "version": "v2.3.6", "source": { "type": "git", "url": "https://github.com/wp-cli/config-command.git", - "reference": "a4ae2c73a03706b7f5b8c74426a44b4df198352c" + "reference": "82a64ae0dbd8bc91e2bf0446666ae24650223775" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/wp-cli/config-command/zipball/a4ae2c73a03706b7f5b8c74426a44b4df198352c", - "reference": "a4ae2c73a03706b7f5b8c74426a44b4df198352c", + "url": "https://api.github.com/repos/wp-cli/config-command/zipball/82a64ae0dbd8bc91e2bf0446666ae24650223775", + "reference": "82a64ae0dbd8bc91e2bf0446666ae24650223775", "shasum": "" }, "require": { @@ -5163,9 +5230,9 @@ "homepage": "https://github.com/wp-cli/config-command", "support": { "issues": "https://github.com/wp-cli/config-command/issues", - "source": "https://github.com/wp-cli/config-command/tree/v2.3.5" + "source": "https://github.com/wp-cli/config-command/tree/v2.3.6" }, - "time": "2024-07-22T10:31:46+00:00" + "time": "2024-08-05T13:34:06+00:00" }, { "name": "wp-cli/core-command", @@ -5703,16 +5770,16 @@ }, { "name": "yoast/phpunit-polyfills", - "version": "2.0.1", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/Yoast/PHPUnit-Polyfills.git", - "reference": "4a088f125c970d6d6ea52c927f96fe39b330d0f1" + "reference": "562f449a2ec8ab92fe7b30d94da9622c7b1345fe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Yoast/PHPUnit-Polyfills/zipball/4a088f125c970d6d6ea52c927f96fe39b330d0f1", - "reference": "4a088f125c970d6d6ea52c927f96fe39b330d0f1", + "url": "https://api.github.com/repos/Yoast/PHPUnit-Polyfills/zipball/562f449a2ec8ab92fe7b30d94da9622c7b1345fe", + "reference": "562f449a2ec8ab92fe7b30d94da9622c7b1345fe", "shasum": "" }, "require": { @@ -5727,7 +5794,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "2.x-dev" + "dev-main": "3.x-dev" } }, "autoload": { @@ -5762,13 +5829,14 @@ "security": "https://github.com/Yoast/PHPUnit-Polyfills/security/policy", "source": "https://github.com/Yoast/PHPUnit-Polyfills" }, - "time": "2024-04-05T16:36:44+00:00" + "time": "2024-09-06T22:38:28+00:00" } ], "aliases": [], "minimum-stability": "stable", "stability-flags": { - "afragen/wordpress-plugin-readme-parser": 20 + "afragen/wordpress-plugin-readme-parser": 20, + "plugin-check/phpcs-sniffs": 20 }, "prefer-stable": false, "prefer-lowest": false, diff --git a/includes/Checker/Checks/Plugin_Repo/Offloading_Files_Check.php b/includes/Checker/Checks/Plugin_Repo/Offloading_Files_Check.php new file mode 100644 index 000000000..599305ade --- /dev/null +++ b/includes/Checker/Checks/Plugin_Repo/Offloading_Files_Check.php @@ -0,0 +1,86 @@ + 'php', + 'standard' => 'PluginCheck', + 'sniffs' => 'PluginCheck.CodeAnalysis.EnqueuedResourceOffloading,PluginCheck.CodeAnalysis.Offloading', + ); + } + + /** + * Gets the description for the check. + * + * Every check must have a short description explaining what the check does. + * + * @since n.e.x.t. + * + * @return string Description. + */ + public function get_description(): string { + return __( 'Prevents using remote services that are not necessary.', 'plugin-check' ); + } + + /** + * Gets the documentation URL for the check. + * + * Every check must have a URL with further information about the check. + * + * @since n.e.x.t. + * + * @return string The documentation URL. + */ + public function get_documentation_url(): string { + return __( 'https://developer.wordpress.org/plugins/wordpress-org/common-issues/#calling-files-remotely', 'plugin-check' ); + } +} diff --git a/includes/Checker/Default_Check_Repository.php b/includes/Checker/Default_Check_Repository.php index 7edcb5ef6..7552ae948 100644 --- a/includes/Checker/Default_Check_Repository.php +++ b/includes/Checker/Default_Check_Repository.php @@ -59,6 +59,7 @@ private function register_default_checks() { 'no_unfiltered_uploads' => new Checks\Plugin_Repo\No_Unfiltered_Uploads_Check(), 'trademarks' => new Checks\Plugin_Repo\Trademarks_Check(), 'non_blocking_scripts' => new Checks\Performance\Non_Blocking_Scripts_Check(), + 'offloading_files' => new Checks\Plugin_Repo\Offloading_Files_Check(), ) ); diff --git a/phpcs-sniffs/.gitignore b/phpcs-sniffs/.gitignore new file mode 100644 index 000000000..bfec4c3c3 --- /dev/null +++ b/phpcs-sniffs/.gitignore @@ -0,0 +1,5 @@ +vendor +composer.lock +phpunit.xml +phpcs.xml +.phpcs.xml diff --git a/phpcs-sniffs/PluginCheck/Sniffs/CodeAnalysis/EnqueuedResourceOffloadingSniff.php b/phpcs-sniffs/PluginCheck/Sniffs/CodeAnalysis/EnqueuedResourceOffloadingSniff.php new file mode 100644 index 000000000..273c8469a --- /dev/null +++ b/phpcs-sniffs/PluginCheck/Sniffs/CodeAnalysis/EnqueuedResourceOffloadingSniff.php @@ -0,0 +1,181 @@ + Key is function name, value irrelevant. + */ + protected $target_functions = array( + 'wp_register_script' => true, + 'wp_enqueue_script' => true, + 'wp_register_style' => true, + 'wp_enqueue_style' => true, + ); + + /** + * False + the empty tokens array. + * + * This array is enriched with the $emptyTokens array in the register() method. + * + * @var array + */ + private $false_tokens = array( + \T_FALSE => \T_FALSE, + ); + + /** + * Token codes which are "safe" to accept to determine whether a version would evaluate to `false`. + * + * This array is enriched with the several of the PHPCS token arrays in the register() method. + * + * @var array + */ + private $safe_tokens = array( + \T_NULL => \T_NULL, + \T_FALSE => \T_FALSE, + \T_TRUE => \T_TRUE, + \T_LNUMBER => \T_LNUMBER, + \T_DNUMBER => \T_DNUMBER, + \T_CONSTANT_ENCAPSED_STRING => \T_CONSTANT_ENCAPSED_STRING, + \T_START_NOWDOC => \T_START_NOWDOC, + \T_NOWDOC => \T_NOWDOC, + \T_END_NOWDOC => \T_END_NOWDOC, + \T_OPEN_PARENTHESIS => \T_OPEN_PARENTHESIS, + \T_CLOSE_PARENTHESIS => \T_CLOSE_PARENTHESIS, + \T_STRING_CONCAT => \T_STRING_CONCAT, + ); + + /** + * Returns an array of tokens this test wants to listen for. + * + * Overloads and calls the parent method to allow for adding additional tokens to the $safe_tokens property. + * + * @return array + */ + public function register() { + $this->false_tokens += Tokens::$emptyTokens; + + $this->safe_tokens += Tokens::$emptyTokens; + $this->safe_tokens += Tokens::$assignmentTokens; + $this->safe_tokens += Tokens::$comparisonTokens; + $this->safe_tokens += Tokens::$operators; + $this->safe_tokens += Tokens::$booleanOperators; + $this->safe_tokens += Tokens::$castTokens; + + return parent::register(); + } + + /** + * Process the parameters of a matched function. + * + * @since 1.1.0 + * + * @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 + * in lowercase. + * @param array $parameters Array with information about the parameters. + * + * @return void + */ + public function process_parameters( $stackPtr, $group_name, $matched_content, $parameters ) { + $src_param = PassedParameters::getParameterFromStack( $parameters, 2, 'src' ); + + if ( false === $src_param ) { + return; + } + + // Known offloading services. + $look_known_offloading_services = array( + 'code\.jquery\.com', + '(?phpcsFile->findNext( Tokens::$emptyTokens, $src_param['start'], ( $src_param['end'] + 1 ), true ); + if ( false === $error_ptr ) { + $error_ptr = $src_param['start']; + } + + $type = 'script'; + if ( strpos( $matched_content, '_style' ) !== false ) { + $type = 'style'; + } + + $src_string = $src_param['clean']; + + $matches = array(); + if ( preg_match( $pattern, $src_string, $matches, PREG_OFFSET_CAPTURE ) > 0 ) { + $this->phpcsFile->addError( + 'Found call to %s() with external resource. Offloading %ss to your servers or any remote service is disallowed.', + $error_ptr, + 'OffloadedContent', + array( $matched_content, $type ) + ); + } + } +} diff --git a/phpcs-sniffs/PluginCheck/Sniffs/CodeAnalysis/OffloadingSniff.php b/phpcs-sniffs/PluginCheck/Sniffs/CodeAnalysis/OffloadingSniff.php new file mode 100644 index 000000000..b3a345542 --- /dev/null +++ b/phpcs-sniffs/PluginCheck/Sniffs/CodeAnalysis/OffloadingSniff.php @@ -0,0 +1,179 @@ +tokens[ $stackPtr ]['content']; + + if ( \T_INLINE_HTML !== $this->tokens[ $stackPtr ]['code'] ) { + try { + $end_ptr = TextStrings::getEndOfCompleteTextString( $this->phpcsFile, $stackPtr ); + $content = TextStrings::getCompleteTextString( $this->phpcsFile, $stackPtr ); + } catch ( RuntimeException $e ) { + // Parse error/live coding. + return; + } + } + + if ( empty( trim( $content ) ) ) { + return; + } + + // Only match HTML markup not arbitrary strings, as those could be covered by EnqueuedResourceOffloadingSniff already. + + if ( + false === strpos( $content, ' 0 ) { + foreach ( $matches[0] as $match ) { + $this->phpcsFile->addError( + 'Offloading images, js, css, and other scripts to your servers or any remote service is disallowed.', + $this->find_token_in_multiline_string( $stackPtr, $content, $match[1] ), + 'OffloadedContent' + ); + } + return ( $end_ptr + 1 ); + } + + // Known offloading extensions. + $look_known_offloading_ext = array( + 'css', + 'svg', + 'jpg', + 'jpeg', + 'gif', + 'png', + 'webm', + 'mp4', + 'mpg', + 'mpeg', + 'mp3', + 'json', + ); + + $offloading_ext = '\.' . implode( '|\.', $look_known_offloading_ext ); + $pattern = '/(https?:\/\/[www\.]?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b[-a-zA-Z0-9()@:%_\+.~#?&\/\/=]*(' . $offloading_ext . '){1})[\/]?([\?|#]{1}[-a-zA-Z0-9()@:%_\+.~#?&\/\/=]*)?[\s|\'|"]/'; + + $matches = array(); + if ( preg_match_all( $pattern, $content, $matches, PREG_OFFSET_CAPTURE ) > 0 ) { + foreach ( $matches[0] as $match ) { + $this->phpcsFile->addError( + 'Offloading images, js, css, and other scripts to your servers or any remote service is disallowed.', + $this->find_token_in_multiline_string( $stackPtr, $content, $match[1] ), + 'OffloadedContent' + ); + } + } + + return ( $end_ptr + 1 ); + } + + /** + * Find the exact token on which the error should be reported for multi-line strings. + * + * @param int $stackPtr The position of the current token in the stack. + * @param string $content The complete, potentially multi-line, text string. + * @param int $match_offset The offset within the content at which the match was found. + * + * @return int The stack pointer to the token containing the start of the match. + */ + private function find_token_in_multiline_string( $stackPtr, $content, $match_offset ) { + $newline_count = 0; + if ( $match_offset > 0 ) { + $newline_count = substr_count( $content, "\n", 0, $match_offset ); + } + + // Account for heredoc/nowdoc text starting at the token *after* the opener. + if ( isset( Tokens::$heredocTokens[ $this->tokens[ $stackPtr ]['code'] ] ) === true ) { + ++$newline_count; + } + + return ( $stackPtr + $newline_count ); + } +} diff --git a/phpcs-sniffs/PluginCheck/Tests/AbstractSniffUnitTest.php b/phpcs-sniffs/PluginCheck/Tests/AbstractSniffUnitTest.php new file mode 100644 index 000000000..7ebcd339b --- /dev/null +++ b/phpcs-sniffs/PluginCheck/Tests/AbstractSniffUnitTest.php @@ -0,0 +1,86 @@ +get_sniff_fqcn(); + if ( ! isset( $current_ruleset->sniffs[ $sniff_fqcn ] ) ) { + throw new \RuntimeException( $error_message ); // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- this is non-production code. + } + + $sniff = $current_ruleset->sniffs[ $sniff_fqcn ]; + $this->set_sniff_parameters( $sniff ); + } +} diff --git a/phpcs-sniffs/PluginCheck/Tests/CodeAnalysis/EnqueuedResourceOffloadingUnitTest.inc b/phpcs-sniffs/PluginCheck/Tests/CodeAnalysis/EnqueuedResourceOffloadingUnitTest.inc new file mode 100644 index 000000000..e052f9fa6 --- /dev/null +++ b/phpcs-sniffs/PluginCheck/Tests/CodeAnalysis/EnqueuedResourceOffloadingUnitTest.inc @@ -0,0 +1,16 @@ + => + */ + public function getErrorList() { + return array( + 5 => 1, + 13 => 1, + ); + } + + /** + * Returns the lines where warnings should occur. + * + * @return array => + */ + public function getWarningList() { + return array(); + } + + /** + * Returns the fully qualified class name (FQCN) of the sniff. + * + * @return string The fully qualified class name of the sniff. + */ + protected function get_sniff_fqcn() { + return EnqueuedResourceOffloadingSniff::class; + } + + /** + * Sets the parameters for the sniff. + * + * @throws \RuntimeException If unable to set the ruleset parameters required for the test. + * + * @param Sniff $sniff The sniff being tested. + */ + public function set_sniff_parameters( Sniff $sniff ) { + } +} diff --git a/phpcs-sniffs/PluginCheck/Tests/CodeAnalysis/OffloadingUnitTest.inc b/phpcs-sniffs/PluginCheck/Tests/CodeAnalysis/OffloadingUnitTest.inc new file mode 100644 index 000000000..9ebdaf84d --- /dev/null +++ b/phpcs-sniffs/PluginCheck/Tests/CodeAnalysis/OffloadingUnitTest.inc @@ -0,0 +1,5 @@ + + +