diff --git a/.gitattributes b/.gitattributes index 289761dafa2..2a4681cce94 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,5 +1,35 @@ # Improve diff output for PHP files. -*.php diff=php +*.php diff=php + +/.github export-ignore +/bin export-ignore +/tests export-ignore +/.browserslistrc export-ignore +/.devlib export-ignore +/.editorconfig export-ignore +/.eslintignore export-ignore +/.eslintrc export-ignore +/.gitattributes export-ignore +/.gitignore export-ignore +/.gitmodules export-ignore +/.npmrc export-ignore +/.nvmrc export-ignore +/.phpcs.xml.dist export-ignore +/.phpstorm.meta.php export-ignore +/.rtlcssrc export-ignore +/.stylelintignore export-ignore +/.stylelintrc.json export-ignore +/babel.config.js export-ignore +/codecov.yml export-ignore +/Gruntfile.js export-ignore +/package.json export-ignore +/package-lock.json export-ignore +/phpstan.neon.dist export-ignore +/phpunit.xml.dist export-ignore +/postcss.config.js export-ignore +/scoper.inc.php export-ignore +/sizereport.config.js export-ignore +/webpack.config.js export-ignore # Mark generated files so diffs are hidden by default. *.snap linguist-generated=true diff --git a/.github/workflows/build-test-measure.yml b/.github/workflows/build-test-measure.yml index 1ee847a32f5..b81030c5390 100644 --- a/.github/workflows/build-test-measure.yml +++ b/.github/workflows/build-test-measure.yml @@ -387,14 +387,6 @@ jobs: - name: Checkout uses: actions/checkout@v2 - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php }} - extensions: curl, date, dom, gd, iconv, json, libxml, mysql, spl - coverage: ${{ matrix.coverage && 'pcov' || 'none' }} - ini-values: pcov.directory=. - - name: Shutdown default MySQL service run: sudo service mysql stop @@ -425,8 +417,31 @@ jobs: restore-keys: | ${{ runner.os }}-composer- + # PHP-Scoper only works on PHP 7.3+ but is still a necessary tool to generate the scoped dependencies. To work + # around this, a compatible PHP version is temporarily installed and then used to install the dependencies. + # Once that is done, the PHP version is replaced with the one meant to be used and the vendor directory is + # removed so that the appropriate dependencies can then be later installed. + + - name: Setup PHP 7.4 (to run PHP Scoper) + uses: shivammathur/setup-php@v2 + with: + php-version: 7.4 + + - name: Install Composer dependencies (and scope necessary dependencies) + run: | + composer install --prefer-dist --no-suggest --no-progress --no-interaction + rm -rf vendor + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: curl, date, dom, gd, iconv, json, libxml, mysql, spl + coverage: ${{ matrix.coverage && 'pcov' || 'none' }} + ini-values: pcov.directory=. + - name: Install Composer dependencies - run: composer install --prefer-dist --ignore-platform-reqs --no-progress --no-interaction + run: composer install --prefer-dist --ignore-platform-reqs --no-progress --no-interaction --no-scripts - name: Get npm cache directory id: npm-cache @@ -670,7 +685,7 @@ jobs: git-sha-8: ${{ steps.retrieve-git-sha-8.outputs.sha8 }} strategy: matrix: - build: ['dev', 'prod'] + build: ['dev', 'prod', 'composer'] steps: - name: Check out source files @@ -755,18 +770,16 @@ jobs: runs-on: ubuntu-latest needs: - build-zip - steps: - - name: Download dev build - uses: actions/download-artifact@v2 - with: - name: amp-${{ needs.build-zip.outputs.branch-name }}-${{ needs.build-zip.outputs.git-sha-8 }}-dev - path: builds/dev + strategy: + matrix: + build: [ 'dev', 'prod', 'composer' ] - - name: Download prod build + steps: + - name: Download ${{ matrix.build }} build uses: actions/download-artifact@v2 with: - name: amp-${{ needs.build-zip.outputs.branch-name }}-${{ needs.build-zip.outputs.git-sha-8 }}-prod - path: builds/prod + name: amp-${{ needs.build-zip.outputs.branch-name }}-${{ needs.build-zip.outputs.git-sha-8 }}-${{ matrix.build }} + path: builds/${{ matrix.build }} - name: Setup Google Cloud SDK uses: google-github-actions/setup-gcloud@master @@ -774,11 +787,8 @@ jobs: project_id: ${{ secrets.GCS_PROJECT_ID }} service_account_key: ${{ secrets.GCS_APPLICATION_CREDENTIALS }} - - name: Upload dev build to bucket - run: gsutil cp -r builds/dev/amp.zip gs://ampwp_github_artifacts/${{ github.ref }}/dev/amp.zip - - - name: Upload prod build to bucket - run: gsutil cp -r builds/prod/amp.zip gs://ampwp_github_artifacts/${{ github.ref }}/prod/amp.zip + - name: Upload ${{ matrix.build }} build to bucket + run: gsutil cp -r builds/${{ matrix.build }}/amp.zip gs://ampwp_github_artifacts/${{ github.ref }}/${{ matrix.build }}/amp.zip #----------------------------------------------------------------------------------------------------------------------- @@ -807,7 +817,8 @@ jobs: run: | body="Plugin builds for ${{ github.event.pull_request.head.sha }} are ready :bellhop_bell:! - Download [development build](https://storage.googleapis.com/ampwp_github_artifacts/${{ github.ref }}/dev/amp.zip) - - Download [production build](https://storage.googleapis.com/ampwp_github_artifacts/${{ github.ref }}/prod/amp.zip)" + - Download [production build](https://storage.googleapis.com/ampwp_github_artifacts/${{ github.ref }}/prod/amp.zip) + - Download [composer build](https://storage.googleapis.com/ampwp_github_artifacts/${{ github.ref }}/composer/amp.zip)" body="${body//$'\n'/'%0A'}" echo "::set-output name=body::$body" diff --git a/.gitignore b/.gitignore index 1044d8cf962..a02048459e7 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ built /phpcs.xml /phpunit.xml /*.sql +/third-party # Generated via bin/transform-readme.php /readme.txt diff --git a/.phpcs.xml.dist b/.phpcs.xml.dist index 8bad0b81e96..90417f09b61 100644 --- a/.phpcs.xml.dist +++ b/.phpcs.xml.dist @@ -131,6 +131,10 @@ + + scoper.inc.php + + . @@ -146,4 +150,5 @@ ^build/* includes/sanitizers/class-amp-allowed-tags-generated.php assets/js/*.asset.php + third-party/* diff --git a/Gruntfile.js b/Gruntfile.js index 32385a415c1..1d1e71855af 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -3,7 +3,7 @@ module.exports = function( grunt ) { 'use strict'; - // Root paths to include in the plugin build ZIP when running `npm run build:prod`. + // Root paths to include in the plugin build ZIP. const productionIncludedRootFiles = [ 'LICENSE', 'amp.php', @@ -12,7 +12,6 @@ module.exports = function( grunt ) { 'includes', 'src', 'templates', - 'vendor', ]; // These patterns paths will be excluded from among the above directory. @@ -43,6 +42,17 @@ module.exports = function( grunt ) { 'vendor/ampproject/amp-toolbox/conceptual-diagram.svg', 'vendor/ampproject/amp-toolbox/phpstan.neon.dist', 'vendor/bin', + 'third-party/composer.json', + 'scoper.inc.php', + ]; + + // These will be removed from the Composer build of the plugin prior to creating a ZIP. + // ⚠️ Warning: These paths are passed straight to rm command in the shell, without any escaping. + const productionComposerExcludedFilePatterns = [ + 'vendor', + 'composer.lock', + 'third-party/composer.json', + 'scoper.inc.php', ]; grunt.initConfig( { @@ -81,10 +91,12 @@ module.exports = function( grunt ) { composer_install: { command: [ 'if [ ! -e build ]; then echo "Run grunt build first."; exit 1; fi', + 'mkdir -p build/vendor/bin', + 'cp vendor/bin/php-scoper build/vendor/bin/', 'cd build', 'composer install --no-dev -o', - 'composer remove cweagans/composer-patches --update-no-dev -o', - 'rm -rf ' + productionVendorExcludedFilePatterns.join( ' ' ), + 'COMPOSER_DISCARD_CHANGES=true composer remove --no-interaction --no-scripts --update-no-dev -o cweagans/composer-patches sabberworm/php-css-parser', + 'rm -rf ' + ( 'composer' === process.env.BUILD_TYPE ? productionComposerExcludedFilePatterns : productionVendorExcludedFilePatterns ).join( ' ' ), ].join( ' && ' ), }, create_build_zip: { @@ -155,6 +167,9 @@ module.exports = function( grunt ) { paths.push( 'readme.txt' ); paths.push( 'composer.*' ); // Copy in order to be able to do run composer_install. + paths.push( 'scoper.inc.php' ); // Copy in order generate scoped Composer dependencies. + + // Also copy recently built assets. paths.push( 'assets/js/**/*.js' ); paths.push( 'assets/js/**/*.asset.php' ); paths.push( 'assets/css/*.css' ); @@ -164,30 +179,44 @@ module.exports = function( grunt ) { paths.push( 'assets/css/*.css.map' ); } + // Get build version from amp.php. + const versionRegex = /(\*\s+Version:\s+)(?\d+(\.\d+)+)(?-\w+)?/; + const { groups: matches } = grunt.file.read( 'amp.php' ).match( versionRegex ); + + if ( ! matches || ! matches.version ) { + throw new Error( 'Plugin version could not be retrieved from amp.php' ); + } + + const version = matches.version; + grunt.config.set( 'copy', { build: { src: paths, dest: 'build', expand: true, options: { - noProcess: [ '*/**', 'LICENSE' ], // That is, only process amp.php and README.md. + noProcess: [ '**/*', '!amp.php', '!composer.json' ], process( content, srcpath ) { - let matches, version, versionRegex; - if ( /amp\.php$/.test( srcpath ) ) { - versionRegex = /(\*\s+Version:\s+)(\d+(\.\d+)+-\w+)/; - + if ( /^amp\.php$/.test( srcpath ) ) { // If not a stable build (e.g. 0.7.0-beta), amend the version with the git commit and current timestamp. - matches = content.match( versionRegex ); - if ( matches ) { - version = matches[ 2 ] + '-' + versionAppend; - console.log( 'Updating version in amp.php to ' + version ); // eslint-disable-line no-console - content = content.replace( versionRegex, '$1' + version ); - content = content.replace( /(define\(\s*'AMP__VERSION',\s*')(.+?)(?=')/, '$1' + version ); + if ( matches.identifier ) { + const pluginVersion = version + matches.identifier + '-' + versionAppend; + console.log( 'Updating version in amp.php to ' + pluginVersion ); // eslint-disable-line no-console + content = content.replace( versionRegex, '$1' + pluginVersion ); + content = content.replace( /(define\(\s*'AMP__VERSION',\s*')(.+?)(?=')/, '$1' + pluginVersion ); } // Remove dev mode code blocks. content = content.replace( /\n\/\/\s*DEV_CODE.+?\n}\n/s, '' ); + + if ( 'composer' === process.env.BUILD_TYPE ) { + content = content.replace( "require_once AMP__DIR__ . '/vendor/autoload.php';", '' ); + } + } else if ( /^composer\.json$/.test( srcpath ) && 'composer' === process.env.BUILD_TYPE ) { + console.log( 'Setting version in composer.json to ' + version ); // eslint-disable-line no-console + content = content.replace( /"name": "ampproject\/amp-wp",/, '$&\n "version": "' + version + '",' ); } + return content; }, }, diff --git a/amp.php b/amp.php index fba64bd65a9..49f61942fe6 100644 --- a/amp.php +++ b/amp.php @@ -157,7 +157,7 @@ unset( $_amp_required_extensions, $_amp_missing_extensions, $_amp_required_constructs, $_amp_missing_classes, $_amp_missing_functions, $_amp_required_extension, $_amp_construct_type, $_amp_construct, $_amp_constructs ); // DEV_CODE. This block of code is removed during the build process. -if ( ! file_exists( AMP__DIR__ . '/vendor/autoload.php' ) || ! file_exists( AMP__DIR__ . '/vendor/sabberworm/php-css-parser' ) || ! file_exists( AMP__DIR__ . '/assets/js/amp-block-editor.js' ) ) { +if ( ! file_exists( AMP__DIR__ . '/vendor/autoload.php' ) || ! file_exists( AMP__DIR__ . '/third-party/vendor/scoper-autoload.php' ) || ! file_exists( AMP__DIR__ . '/assets/js/amp-block-editor.js' ) ) { $_amp_load_errors->add( 'build_required', sprintf( @@ -251,6 +251,7 @@ function _amp_incorrect_plugin_slug_admin_notice() { } require_once AMP__DIR__ . '/vendor/autoload.php'; +require_once AMP__DIR__ . '/third-party/vendor/scoper-autoload.php'; register_activation_hook( __FILE__, 'amp_activate' ); diff --git a/codecov.yml b/codecov.yml index 34dc93a6d6f..7ba02126fb7 100644 --- a/codecov.yml +++ b/codecov.yml @@ -42,6 +42,8 @@ comment: ignore: - "/bin" - "/back-compat" + - "/third-party" - "/docs/**/*" - ".phpstorm.meta.php" - "/qa-tester/**" + - "scoper.inc.php" diff --git a/composer.json b/composer.json index 0eb084af1d1..9fde39137af 100644 --- a/composer.json +++ b/composer.json @@ -50,6 +50,11 @@ }, "extra": { "downloads": { + "php-scoper": { + "path": "vendor/bin/php-scoper", + "type": "phar", + "url": "https://github.com/humbug/php-scoper/releases/download/0.14.0/php-scoper.phar" + }, "phpstan": { "path": "vendor/bin/phpstan", "type": "phar", @@ -91,10 +96,22 @@ "minimum-stability": "dev", "prefer-stable": true, "scripts": { + "post-install-cmd": [ + "@scope-dependencies" + ], + "post-update-cmd": [ + "@scope-dependencies" + ], "analyze": "if [ -z $TEST_SKIP_PHPSTAN ]; then phpstan --version; phpstan analyze --ansi; fi", "pre-commit": [ "npm run lint:staged" ], - "prepare-tests": "install-package-tests" + "prepare-tests": "install-package-tests", + "scope-dependencies": [ + "php-scoper add-prefix --output-dir=third-party --force --quiet", + "echo '{ \"autoload\": { \"classmap\": [\"\"] } }' > third-party/composer.json", + "composer dump-autoload --working-dir third-party --no-dev --no-plugins --classmap-authoritative", + "sed -i'.bak' -e 's/Composer\\\\Autoload/AmpProject\\\\AmpWP\\\\Composer\\\\Autoload/' third-party/vendor/composer/*.php && rm -rf third-party/vendor/composer/*.php.bak" + ] } } diff --git a/composer.lock b/composer.lock index 548fc78c249..bd4d94a86da 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": "a6058fda10827da7ccfc7041bdd0c33c", + "content-hash": "f73a29684d97e6b2474f5d742dd8e6cc", "packages": [ { "name": "ampproject/amp-toolbox", @@ -12,12 +12,12 @@ "source": { "type": "git", "url": "https://github.com/ampproject/amp-toolbox-php.git", - "reference": "8e1dfacf4f4df8835c6a9ed1d3be8b4a7bf7b92e" + "reference": "3c52c66a3e0665d22111d4552e23d61d033ba1d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ampproject/amp-toolbox-php/zipball/8e1dfacf4f4df8835c6a9ed1d3be8b4a7bf7b92e", - "reference": "8e1dfacf4f4df8835c6a9ed1d3be8b4a7bf7b92e", + "url": "https://api.github.com/repos/ampproject/amp-toolbox-php/zipball/3c52c66a3e0665d22111d4552e23d61d033ba1d0", + "reference": "3c52c66a3e0665d22111d4552e23d61d033ba1d0", "shasum": "" }, "require": { @@ -72,7 +72,7 @@ "issues": "https://github.com/ampproject/amp-toolbox-php/issues", "source": "https://github.com/ampproject/amp-toolbox-php/tree/main" }, - "time": "2021-04-25T07:16:15+00:00" + "time": "2021-04-28T01:26:05+00:00" }, { "name": "cweagans/composer-patches", @@ -229,7 +229,7 @@ "issues": "https://github.com/sabberworm/PHP-CSS-Parser/issues", "source": "https://github.com/sabberworm/PHP-CSS-Parser/tree/master" }, - "time": "2021-04-23T05:58:34+00:00" + "time": "2021-04-26T15:40:51+00:00" }, { "name": "willwashburn/stream", @@ -286,16 +286,16 @@ "packages-dev": [ { "name": "automattic/vipwpcs", - "version": "2.3.1", + "version": "2.3.2", "source": { "type": "git", "url": "https://github.com/Automattic/VIP-Coding-Standards.git", - "reference": "90173cec6f4df2af9a31627f4880874d788459ab" + "reference": "efacebef421334d54b99afa92fb8fa645336a8a7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Automattic/VIP-Coding-Standards/zipball/90173cec6f4df2af9a31627f4880874d788459ab", - "reference": "90173cec6f4df2af9a31627f4880874d788459ab", + "url": "https://api.github.com/repos/Automattic/VIP-Coding-Standards/zipball/efacebef421334d54b99afa92fb8fa645336a8a7", + "reference": "efacebef421334d54b99afa92fb8fa645336a8a7", "shasum": "" }, "require": { @@ -334,7 +334,7 @@ "source": "https://github.com/Automattic/VIP-Coding-Standards", "wiki": "https://github.com/Automattic/VIP-Coding-Standards/wiki" }, - "time": "2021-04-23T14:52:17+00:00" + "time": "2021-04-28T16:41:50+00:00" }, { "name": "behat/behat", @@ -1199,16 +1199,16 @@ }, { "name": "guzzlehttp/psr7", - "version": "1.8.1", + "version": "1.8.2", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "35ea11d335fd638b5882ff1725228b3d35496ab1" + "reference": "dc960a912984efb74d0a90222870c72c87f10c91" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/35ea11d335fd638b5882ff1725228b3d35496ab1", - "reference": "35ea11d335fd638b5882ff1725228b3d35496ab1", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/dc960a912984efb74d0a90222870c72c87f10c91", + "reference": "dc960a912984efb74d0a90222870c72c87f10c91", "shasum": "" }, "require": { @@ -1268,9 +1268,9 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/1.8.1" + "source": "https://github.com/guzzle/psr7/tree/1.8.2" }, - "time": "2021-03-21T16:25:00+00:00" + "time": "2021-04-26T09:17:50+00:00" }, { "name": "mikey179/vfsstream", @@ -2931,23 +2931,30 @@ }, { "name": "rmccue/requests", - "version": "v1.7.0", + "version": "v1.8.0", "source": { "type": "git", - "url": "https://github.com/rmccue/Requests.git", - "reference": "87932f52ffad70504d93f04f15690cf16a089546" + "url": "https://github.com/WordPress/Requests.git", + "reference": "afbe4790e4def03581c4a0963a1e8aa01f6030f1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/rmccue/Requests/zipball/87932f52ffad70504d93f04f15690cf16a089546", - "reference": "87932f52ffad70504d93f04f15690cf16a089546", + "url": "https://api.github.com/repos/WordPress/Requests/zipball/afbe4790e4def03581c4a0963a1e8aa01f6030f1", + "reference": "afbe4790e4def03581c4a0963a1e8aa01f6030f1", "shasum": "" }, "require": { "php": ">=5.2" }, "require-dev": { - "requests/test-server": "dev-master" + "dealerdirect/phpcodesniffer-composer-installer": "^0.7", + "php-parallel-lint/php-console-highlighter": "^0.5.0", + "php-parallel-lint/php-parallel-lint": "^1.3", + "phpcompatibility/php-compatibility": "^9.0", + "phpunit/phpunit": "^4.8 || ^5.7 || ^6.5 || ^7.5", + "requests/test-server": "dev-master", + "squizlabs/php_codesniffer": "^3.5", + "wp-coding-standards/wpcs": "^2.0" }, "type": "library", "autoload": { @@ -2966,7 +2973,7 @@ } ], "description": "A HTTP library written in PHP, for human beings.", - "homepage": "http://github.com/rmccue/Requests", + "homepage": "http://github.com/WordPress/Requests", "keywords": [ "curl", "fsockopen", @@ -2977,10 +2984,10 @@ "sockets" ], "support": { - "issues": "https://github.com/rmccue/Requests/issues", - "source": "https://github.com/rmccue/Requests/tree/master" + "issues": "https://github.com/WordPress/Requests/issues", + "source": "https://github.com/WordPress/Requests/tree/v1.8.0" }, - "time": "2016-10-13T00:11:37+00:00" + "time": "2021-04-27T11:05:25+00:00" }, { "name": "roave/security-advisories", @@ -2988,12 +2995,12 @@ "source": { "type": "git", "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "3c97c13698c448fdbbda20acb871884a2d8f45b1" + "reference": "a9d356baedee32afbb2179a2a61a45b215f8c95e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/3c97c13698c448fdbbda20acb871884a2d8f45b1", - "reference": "3c97c13698c448fdbbda20acb871884a2d8f45b1", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/a9d356baedee32afbb2179a2a61a45b215f8c95e", + "reference": "a9d356baedee32afbb2179a2a61a45b215f8c95e", "shasum": "" }, "conflict": { @@ -3021,7 +3028,7 @@ "centreon/centreon": "<18.10.8|>=19,<19.4.5", "cesnet/simplesamlphp-module-proxystatistics": "<3.1", "codeigniter/framework": "<=3.0.6", - "composer/composer": "<=1-alpha.11", + "composer/composer": "<1.10.22|>=2-alpha.1,<2.0.13", "contao-components/mediaelement": ">=2.14.2,<2.21.1", "contao/core": ">=2,<3.5.39", "contao/core-bundle": ">=4,<4.4.52|>=4.5,<4.9.6|= 4.10.0", @@ -3133,7 +3140,7 @@ "paragonie/random_compat": "<2", "passbolt/passbolt_api": "<2.11", "paypal/merchant-sdk-php": "<3.12", - "pear/archive_tar": "<1.4.13", + "pear/archive_tar": "<1.4.12", "personnummer/personnummer": "<3.0.2", "phpfastcache/phpfastcache": ">=5,<5.0.13", "phpmailer/phpmailer": "<6.1.6", @@ -3161,6 +3168,7 @@ "pusher/pusher-php-server": "<2.2.1", "pwweb/laravel-core": "<=0.3.6-beta", "rainlab/debugbar-plugin": "<3.1", + "rmccue/requests": ">=1.6,<1.8", "robrichards/xmlseclibs": "<3.0.4", "sabberworm/php-css-parser": ">=1,<1.0.1|>=2,<2.0.1|>=3,<3.0.1|>=4,<4.0.1|>=5,<5.0.9|>=5.1,<5.1.3|>=5.2,<5.2.1|>=6,<6.0.2|>=7,<7.0.4|>=8,<8.0.1|>=8.1,<8.1.1|>=8.2,<8.2.1|>=8.3,<8.3.1", "sabre/dav": ">=1.6,<1.6.99|>=1.7,<1.7.11|>=1.8,<1.8.9", @@ -3326,7 +3334,7 @@ "type": "tidelift" } ], - "time": "2021-04-22T17:19:04+00:00" + "time": "2021-04-28T18:10:37+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", diff --git a/includes/options/class-amp-options-manager.php b/includes/options/class-amp-options-manager.php index 21bd8eb7a40..1698fb07dee 100644 --- a/includes/options/class-amp-options-manager.php +++ b/includes/options/class-amp-options-manager.php @@ -43,7 +43,6 @@ class AMP_Options_Manager { * Sets up hooks. */ public static function init() { - add_action( 'admin_notices', [ __CLASS__, 'render_php_css_parser_conflict_notice' ] ); add_action( 'admin_notices', [ __CLASS__, 'insecure_connection_notice' ] ); add_action( 'admin_notices', [ __CLASS__, 'reader_theme_fallback_notice' ] ); } @@ -421,48 +420,6 @@ public static function update_options( $options ) { return update_option( self::OPTION_NAME, $amp_options, false ); } - /** - * Render PHP-CSS-Parser conflict notice. - * - * @return void - */ - public static function render_php_css_parser_conflict_notice() { - $current_screen = get_current_screen(); - if ( ! ( $current_screen instanceof WP_Screen ) || 'toplevel_page_' . self::OPTION_NAME !== $current_screen->id ) { - return; - } - - if ( AMP_Style_Sanitizer::has_required_php_css_parser() ) { - return; - } - - try { - $reflection = new ReflectionClass( 'Sabberworm\CSS\CSSList\CSSList' ); - $source_dir = str_replace( - trailingslashit( WP_CONTENT_DIR ), - '', - preg_replace( '#/vendor/sabberworm/.+#', '', $reflection->getFileName() ) - ); - - printf( - '

%s

', - wp_kses( - sprintf( - /* translators: %s: path to the conflicting library */ - __( 'A conflicting version of PHP-CSS-Parser appears to be installed by another plugin or theme (located in %s). Because of this, CSS processing will be limited, and tree shaking will not be available.', 'amp' ), - '' . esc_html( $source_dir ) . '' - ), - [ 'code' => [] ] - ) - ); - } catch ( ReflectionException $e ) { - printf( - '

%s

', - esc_html__( 'PHP-CSS-Parser is not available so CSS processing will not be available.', 'amp' ) - ); - } - } - /** * Outputs an admin notice if the site is not served over HTTPS. * diff --git a/includes/sanitizers/class-amp-style-sanitizer.php b/includes/sanitizers/class-amp-style-sanitizer.php index 28658d89112..d005fbbced6 100644 --- a/includes/sanitizers/class-amp-style-sanitizer.php +++ b/includes/sanitizers/class-amp-style-sanitizer.php @@ -13,21 +13,24 @@ use AmpProject\Dom\Document; use AmpProject\Exception\FailedToGetFromRemoteUrl; use AmpProject\RemoteGetRequest; -use Sabberworm\CSS\RuleSet\DeclarationBlock; -use Sabberworm\CSS\CSSList\CSSList; -use Sabberworm\CSS\Property\Selector; -use Sabberworm\CSS\RuleSet\RuleSet; -use Sabberworm\CSS\Property\AtRule; -use Sabberworm\CSS\Rule\Rule; -use Sabberworm\CSS\CSSList\KeyFrame; -use Sabberworm\CSS\RuleSet\AtRuleSet; -use Sabberworm\CSS\OutputFormat; -use Sabberworm\CSS\Property\Import; -use Sabberworm\CSS\CSSList\AtRuleBlockList; -use Sabberworm\CSS\Value\RuleValueList; -use Sabberworm\CSS\Value\URL; -use Sabberworm\CSS\Value\Value; -use Sabberworm\CSS\CSSList\Document as CSSDocument; +use AmpProject\AmpWP\ScopedDependencies\Sabberworm\CSS\RuleSet\DeclarationBlock; +use AmpProject\AmpWP\ScopedDependencies\Sabberworm\CSS\CSSList\CSSList; +use AmpProject\AmpWP\ScopedDependencies\Sabberworm\CSS\Property\Selector; +use AmpProject\AmpWP\ScopedDependencies\Sabberworm\CSS\RuleSet\RuleSet; +use AmpProject\AmpWP\ScopedDependencies\Sabberworm\CSS\Property\AtRule; +use AmpProject\AmpWP\ScopedDependencies\Sabberworm\CSS\Rule\Rule; +use AmpProject\AmpWP\ScopedDependencies\Sabberworm\CSS\CSSList\KeyFrame; +use AmpProject\AmpWP\ScopedDependencies\Sabberworm\CSS\RuleSet\AtRuleSet; +use AmpProject\AmpWP\ScopedDependencies\Sabberworm\CSS\OutputFormat; +use AmpProject\AmpWP\ScopedDependencies\Sabberworm\CSS\Property\Import; +use AmpProject\AmpWP\ScopedDependencies\Sabberworm\CSS\CSSList\AtRuleBlockList; +use AmpProject\AmpWP\ScopedDependencies\Sabberworm\CSS\Value\RuleValueList; +use AmpProject\AmpWP\ScopedDependencies\Sabberworm\CSS\Value\URL; +use AmpProject\AmpWP\ScopedDependencies\Sabberworm\CSS\Value\Value; +use AmpProject\AmpWP\ScopedDependencies\Sabberworm\CSS\CSSList\Document as CSSDocument; +use AmpProject\AmpWP\ScopedDependencies\Sabberworm\CSS\Settings as CSSSettings; +use AmpProject\AmpWP\ScopedDependencies\Sabberworm\CSS\Parser as CSSParser; +use AmpProject\AmpWP\ScopedDependencies\Sabberworm\CSS\OutputFormat as CSSOutputFormat; /** * Class AMP_Style_Sanitizer @@ -376,6 +379,8 @@ public static function get_css_parser_validation_error_codes() { /** * Determine whether the version of PHP-CSS-Parser loaded has all required features for tree shaking and CSS processing. * + * @codeCoverageIgnore + * @deprecated As of v2.1 a patched version of PHP-CSS-Parser is now bundled with the plugin. * @since 1.0.2 * * @return bool Returns true if the plugin's forked version of PHP-CSS-Parser is loaded by Composer. @@ -1803,8 +1808,8 @@ private function create_validated_css_document( $stylesheet_string, $options ) { // Remove spaces from data URLs, which cause errors and PHP-CSS-Parser can't handle them. $stylesheet_string = $this->remove_spaces_from_url_values( $stylesheet_string ); - $parser_settings = Sabberworm\CSS\Settings::create(); - $css_parser = new Sabberworm\CSS\Parser( $stylesheet_string, $parser_settings ); + $parser_settings = CSSSettings::create(); + $css_parser = new CSSParser( $stylesheet_string, $parser_settings ); $css_document = $css_parser->parse(); // @todo If 'utf-8' is not $css_parser->getCharset() then issue warning? if ( ! empty( $options['stylesheet_url'] ) ) { @@ -1904,7 +1909,7 @@ private function parse_stylesheet( $stylesheet_string, $options = [] ) { if ( ! empty( $parsed_stylesheet['css_document'] ) ) { $css_document = $parsed_stylesheet['css_document']; - $output_format = Sabberworm\CSS\OutputFormat::createCompact(); + $output_format = CSSOutputFormat::createCompact(); $output_format->setSemicolonAfterLastRule( false ); $before_declaration_block = sprintf( '/*%s*/', chr( 1 ) ); @@ -1915,15 +1920,12 @@ private function parse_stylesheet( $stylesheet_string, $options = [] ) { $before_at_rule = sprintf( '/*%s*/', chr( 6 ) ); $after_at_rule = sprintf( '/*%s*/', chr( 7 ) ); - // Add comments to stylesheet if PHP-CSS-Parser has the required extensions for tree shaking. - if ( self::has_required_php_css_parser() ) { - $output_format->set( 'BeforeDeclarationBlock', $before_declaration_block ); - $output_format->set( 'SpaceBeforeSelectorSeparator', $between_selectors ); - $output_format->set( 'AfterDeclarationBlockSelectors', $after_declaration_block_selectors ); - $output_format->set( 'AfterDeclarationBlock', $after_declaration_block ); - $output_format->set( 'BeforeAtRuleBlock', $before_at_rule ); - $output_format->set( 'AfterAtRuleBlock', $after_at_rule ); - } + $output_format->set( 'BeforeDeclarationBlock', $before_declaration_block ); + $output_format->set( 'SpaceBeforeSelectorSeparator', $between_selectors ); + $output_format->set( 'AfterDeclarationBlockSelectors', $after_declaration_block_selectors ); + $output_format->set( 'AfterDeclarationBlock', $after_declaration_block ); + $output_format->set( 'BeforeAtRuleBlock', $before_at_rule ); + $output_format->set( 'AfterAtRuleBlock', $after_at_rule ); $output_format->set( 'SpaceBetweenRules', $between_properties ); $stylesheet_string = $css_document->render( $output_format ); diff --git a/includes/validation/class-amp-validated-url-post-type.php b/includes/validation/class-amp-validated-url-post-type.php index 9284334d7eb..fc076295564 100644 --- a/includes/validation/class-amp-validated-url-post-type.php +++ b/includes/validation/class-amp-validated-url-post-type.php @@ -2341,14 +2341,6 @@ static function ( $a, $b ) use ( $stylesheets ) { ?> - -
-

- -

-
- -
diff --git a/package.json b/package.json index 7b7d9a95f18..3c98ca8eb7d 100644 --- a/package.json +++ b/package.json @@ -94,8 +94,9 @@ "webpackbar": "4.0.0" }, "scripts": { - "build:dev": "cross-env NODE_ENV=development npm-run-all 'build:!(dev|prod)'", - "build:prod": "cross-env NODE_ENV=production npm-run-all 'build:!(dev|prod)'", + "build:dev": "cross-env NODE_ENV=development npm-run-all 'build:!(dev|prod|composer)'", + "build:prod": "cross-env NODE_ENV=production npm-run-all 'build:!(dev|prod|composer)'", + "build:composer": "cross-env NODE_ENV=production BUILD_TYPE=composer npm-run-all 'build:!(dev|prod|composer)'", "build:prepare": "grunt clean", "build:js": "wp-scripts build", "build:run": "grunt build", diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 210521e26d4..7977834706e 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -26,6 +26,7 @@ parameters: - %currentWorkingDirectory%/tests/php/static-analysis-stubs/twentyseventeen.php - %currentWorkingDirectory%/tests/php/static-analysis-stubs/legacy-i18n.php - %currentWorkingDirectory%/vendor/autoload.php + - %currentWorkingDirectory%/third-party/vendor/scoper-autoload.php - %currentWorkingDirectory%/amp.php - %currentWorkingDirectory%/includes/amp-frontend-actions.php - %currentWorkingDirectory%/includes/amp-post-template-functions.php diff --git a/scoper.inc.php b/scoper.inc.php new file mode 100644 index 00000000000..b49fc4dfd4c --- /dev/null +++ b/scoper.inc.php @@ -0,0 +1,73 @@ + 'AmpProject\\AmpWP\\ScopedDependencies', + + // By default when running php-scoper add-prefix, it will prefix all relevant code found in the current working + // directory. You can however define which files should be scoped by defining a collection of Finders in the + // following configuration key. + // + // For more see: https://github.com/humbug/php-scoper#finders-and-paths. + 'finders' => [ + Finder::create() + ->files() + ->ignoreVCS( true ) + ->ignoreDotFiles( true ) + ->name( '*.php' ) + ->exclude( [ 'tests' ] ) + ->in( 'vendor/sabberworm/php-css-parser' ) + ->append( [ 'vendor/sabberworm/php-css-parser/composer.json' ] ), + + // Main composer.json file so that we can build a classmap. + Finder::create() + ->append( [ 'composer.json' ] ), + ], + + // Whitelists a list of files. Unlike the other whitelist related features, this one is about completely leaving + // a file untouched. + // Paths are relative to the configuration file unless if they are already absolute. + 'files-whitelist' => [], + + // When scoping PHP files, there will be scenarios where some of the code being scoped indirectly references the + // original namespace. These will include, for example, strings or string manipulations. PHP-Scoper has limited + // support for prefixing such strings. To circumvent that, you can define patchers to manipulate the file to your + // heart contents. + // + // For more see: https://github.com/humbug/php-scoper#patchers. + 'patchers' => [ + function ( $filePath, $prefix, $contents ) { //phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase + // Change the contents here. + + return $contents; + }, + ], + + // PHP-Scoper's goal is to make sure that all code for a project lies in a distinct PHP namespace. However, you + // may want to share a common API between the bundled code of your PHAR and the consumer code. For example if + // you have a PHPUnit PHAR with isolated code, you still want the PHAR to be able to understand the + // PHPUnit\Framework\TestCase class. + // + // A way to achieve this is by specifying a list of classes to not prefix with the following configuration key. Note + // that this does not work with functions or constants neither with classes belonging to the global namespace. + // + // Fore more see https://github.com/humbug/php-scoper#whitelist. + 'whitelist' => [], + + // If `true` then the user defined constants belonging to the global namespace will not be prefixed. + // + // For more see https://github.com/humbug/php-scoper#constants--constants--functions-from-the-global-namespace. + 'whitelist-global-constants' => false, + + // If `true` then the user defined classes belonging to the global namespace will not be prefixed. + // + // For more see https://github.com/humbug/php-scoper#constants--constants--functions-from-the-global-namespace. + 'whitelist-global-classes' => false, + + // If `true` then the user defined functions belonging to the global namespace will not be prefixed. + // + // For more see https://github.com/humbug/php-scoper#constants--constants--functions-from-the-global-namespace. + 'whitelist-global-functions' => false, +]; diff --git a/tests/php/test-class-amp-options-manager.php b/tests/php/test-class-amp-options-manager.php index 49ec10fe91c..7a46e2f7fbf 100644 --- a/tests/php/test-class-amp-options-manager.php +++ b/tests/php/test-class-amp-options-manager.php @@ -73,7 +73,6 @@ public function test_constants() { */ public function test_init() { AMP_Options_Manager::init(); - $this->assertEquals( 10, has_action( 'admin_notices', [ AMP_Options_Manager::class, 'render_php_css_parser_conflict_notice' ] ) ); $this->assertEquals( 10, has_action( 'admin_notices', [ AMP_Options_Manager::class, 'insecure_connection_notice' ] ) ); $this->assertEquals( 10, has_action( 'admin_notices', [ AMP_Options_Manager::class, 'reader_theme_fallback_notice' ] ) ); } @@ -711,17 +710,6 @@ public function test_get_options_theme_support_defaults( $args, $expected_mode, $this->assertEquals( $expected_mode, AMP_Options_Manager::get_option( Option::THEME_SUPPORT ) ); } - /** @covers AMP_Options_Manager::render_php_css_parser_conflict_notice() */ - public function test_render_php_css_parser_conflict_notice() { - $this->assertEmpty( get_echo( [ 'AMP_Options_Manager', 'render_php_css_parser_conflict_notice' ] ) ); - - set_current_screen( 'themes' ); - $this->assertEmpty( get_echo( [ 'AMP_Options_Manager', 'render_php_css_parser_conflict_notice' ] ) ); - - set_current_screen( 'toplevel_page_' . AMP_Options_Manager::OPTION_NAME ); - $this->assertEmpty( get_echo( [ 'AMP_Options_Manager', 'render_php_css_parser_conflict_notice' ] ) ); - } - /** @covers AMP_Options_Manager::insecure_connection_notice() */ public function test_insecure_connection_notice() { $_SERVER['HTTPS'] = false;