diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 00000000..7a7cd872 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,6 @@ +# Our standard eslint config reads .gitignore and .eslintignore to determine what to ignore. + +# @todo: Files below ignored as part of Automattic/jetpack/pull/25449 +# They can be removed here as lint errors are addressed. +/inc/delete-cache-button.js +/inc/preload-notification.js diff --git a/.gitattributes b/.gitattributes index a336949f..33422e5d 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,24 +1,13 @@ -# Automatically generated rules. -/.git* export-ignore - -# Package attributes file. -# Files not needed to be distributed in the package. -.gitattributes export-ignore -.github/ export-ignore -package.json export-ignore - -# Files to include in the mirror repo, but excluded via gitignore -# Remember to end all directories with `/**` to properly tag every file. -# /src/js/example.min.js production-include - -/jetpack_vendor/** production-include -/vendor/autoload.php production-include -/vendor/composer/** production-include -/vendor/automattic/jetpack-device-detection/** production-include - -# Files to exclude from the mirror repo, but included in the monorepo. -# Remember to end all directories with `/**` to properly tag every file. -.gitignore production-exclude -.phpcs.dir.xml production-exclude -changelog/** production-exclude -tests/** production-exclude +# Files not needed in the release zip. +/.git* export-ignore +.gitattributes export-ignore +.github/ export-ignore +package.json export-ignore +tests/ export-ignore +.phan/ export-ignore +phpunit.*.xml.dist export-ignore +.phpcs.* export-ignore +.eslintignore export-ignore +eslint.config.mjs export-ignore +changelog/ export-ignore +.w.org-assets/ export-ignore diff --git a/.github/workflows/changelogger.yml b/.github/workflows/changelogger.yml new file mode 100644 index 00000000..f295ce99 --- /dev/null +++ b/.github/workflows/changelogger.yml @@ -0,0 +1,27 @@ +name: Changelogger + +on: + pull_request: + +jobs: + validate: + name: Validate changelog + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: shivammathur/setup-php@v2 + with: + php-version: '8.4' + tools: composer + coverage: none + - run: composer install --no-progress + - name: Check for changelog entry + run: | + BASE_SHA=${{ github.event.pull_request.base.sha }} + HEAD_SHA=${{ github.event.pull_request.head.sha }} + CHANGELOG_FILES=$(git diff --name-only "$BASE_SHA" "$HEAD_SHA" -- 'changelog/*') + if [ -z "$CHANGELOG_FILES" ]; then + echo "::warning::No changelog entry found. If this change is user-facing, please add one with: vendor/bin/changelogger add" + fi diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml deleted file mode 100644 index aac439b4..00000000 --- a/.github/workflows/e2e-tests.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: End to end tests - -on: - push: - -# We use secrets.REPO_DISPATCH_TOKEN for everything here, so no need for permissions. -permissions: {} - -jobs: - run-tests: - name: "Trigger e2e tests in Jetpack monorepo" - runs-on: ubuntu-latest - - steps: - - name: Create a repository dispatch - uses: peter-evans/repository-dispatch@v4 - with: - token: ${{ secrets.REPO_DISPATCH_TOKEN }} - repository: automattic/jetpack - event-type: e2e tests at ${{github.event_name}} on ${{github.repository}} ${{github.ref_name}} ${{github.ref_type}} - client-payload: '{"repository": "${{github.repository}}", "ref_name": "${{github.ref_name}}", "ref_type": "${{github.ref_type}}"}' diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml new file mode 100644 index 00000000..597b563a --- /dev/null +++ b/.github/workflows/linting.yml @@ -0,0 +1,20 @@ +name: Linting + +on: + pull_request: + push: + branches: [trunk] + +jobs: + phpcs: + name: PHPCS + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: shivammathur/setup-php@v2 + with: + php-version: '8.4' + tools: composer + coverage: none + - run: composer install --no-progress + - run: vendor/bin/phpcs diff --git a/.github/workflows/php-tests.yml b/.github/workflows/php-tests.yml new file mode 100644 index 00000000..e867f532 --- /dev/null +++ b/.github/workflows/php-tests.yml @@ -0,0 +1,24 @@ +name: PHP Tests + +on: + pull_request: + push: + branches: [trunk] + +jobs: + phpunit: + name: PHPUnit (PHP ${{ matrix.php }}) + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + php: ['8.2', '8.3', '8.4', '8.5'] + steps: + - uses: actions/checkout@v4 + - uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + tools: composer + coverage: none + - run: composer install --no-progress + - run: composer test-php diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..140fd587 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +vendor/ +node_modules/ diff --git a/.phan/baseline.php b/.phan/baseline.php new file mode 100644 index 00000000..249d59df --- /dev/null +++ b/.phan/baseline.php @@ -0,0 +1,81 @@ + [ + 'advanced-cache.php' => ['PhanPluginSimplifyExpressionBool'], + 'inc/delete-cache-button.php' => ['PhanPluginNeverReturnFunction', 'PhanTypeMismatchArgument'], + 'ossdl-cdn.php' => ['PhanUndeclaredClassMethod'], + 'partials/advanced.php' => ['PhanPluginSimplifyExpressionBool', 'PhanPossiblyUndeclaredGlobalVariable', 'PhanTypeMismatchArgument', 'PhanTypeNonVarPassByRef', 'PhanUndeclaredGlobalVariable'], + 'partials/debug.php' => ['PhanTypeNonVarPassByRef', 'PhanUndeclaredGlobalVariable'], + 'partials/easy.php' => ['PhanPluginSimplifyExpressionBool', 'PhanTypeArraySuspiciousNull', 'PhanTypeInvalidDimOffset', 'PhanTypeMismatchArgumentInternalReal', 'PhanTypeMismatchArgumentProbablyReal', 'PhanUndeclaredConstant', 'PhanUndeclaredGlobalVariable'], + 'partials/lockdown.php' => ['PhanUndeclaredGlobalVariable'], + 'partials/preload.php' => ['PhanPluginDuplicateAdjacentStatement', 'PhanPluginSimplifyExpressionBool', 'PhanPossiblyUndeclaredGlobalVariable', 'PhanTypeMismatchDimAssignment', 'PhanUndeclaredGlobalVariable'], + 'partials/tracking_parameters.php' => ['PhanUndeclaredGlobalVariable'], + 'plugins/domain-mapping.php' => ['PhanUndeclaredFunction'], + 'plugins/jetpack.php' => ['PhanPluginSimplifyExpressionBool'], + 'plugins/wptouch.php' => ['PhanPluginSimplifyExpressionBool', 'PhanUndeclaredFunction'], + 'rest/class.wp-super-cache-rest-get-cache.php' => ['PhanPluginSimplifyExpressionBool'], + 'rest/class.wp-super-cache-rest-get-settings.php' => ['PhanPluginSimplifyExpressionBool', 'PhanSuspiciousValueComparison', 'PhanTypeMismatchReturn', 'PhanUndeclaredFunctionInCallable', 'PhanUndeclaredVariable'], + 'rest/class.wp-super-cache-rest-get-status.php' => ['PhanPluginSimplifyExpressionBool', 'PhanSuspiciousValueComparison', 'PhanTypeNonVarPassByRef', 'PhanUndeclaredVariable'], + 'rest/class.wp-super-cache-rest-test-cache.php' => ['PhanPluginSimplifyExpressionBool', 'PhanTypeConversionFromArray', 'PhanTypePossiblyInvalidDimOffset', 'PhanUndeclaredVariableDim'], + 'rest/class.wp-super-cache-rest-update-settings.php' => ['PhanCommentParamWithoutRealParam', 'PhanPluginSimplifyExpressionBool', 'PhanTypeMissingReturn'], + 'src/device-detection/class-user-agent-info.php' => ['PhanPluginSimplifyExpressionBool', 'PhanTypeMismatchProperty', 'PhanTypeMismatchReturn'], + 'tests/e2e/tools/mu-test-helpers.php' => ['PhanTypeMismatchArgument'], + 'wp-cache-base.php' => ['PhanTypeMismatchArgumentNullableInternal'], + 'wp-cache-phase1.php' => ['PhanTypeNonVarPassByRef'], + 'wp-cache-phase2.php' => ['PhanPluginDuplicateConditionalNullCoalescing', 'PhanPluginSimplifyExpressionBool', 'PhanPluginUnreachableCode', 'PhanPossiblyUndeclaredVariable', 'PhanSuspiciousValueComparison', 'PhanTypeArraySuspiciousNullable', 'PhanTypeMismatchArgument', 'PhanTypeMismatchArgumentInternalProbablyReal', 'PhanTypeMismatchArgumentNullable', 'PhanTypeMismatchArgumentNullableInternal', 'PhanTypeNonVarPassByRef', 'PhanTypePossiblyInvalidDimOffset', 'PhanTypeSuspiciousNonTraversableForeach', 'PhanTypeSuspiciousStringExpression', 'PhanUndeclaredVariableDim'], + 'wp-cache.php' => ['PhanPluginDuplicateAdjacentStatement', 'PhanPluginDuplicateExpressionAssignmentOperation', 'PhanPluginNeverReturnFunction', 'PhanPluginSimplifyExpressionBool', 'PhanPossiblyUndeclaredVariable', 'PhanSuspiciousValueComparison', 'PhanTypeArraySuspiciousNullable', 'PhanTypeInvalidDimOffset', 'PhanTypeInvalidLeftOperandOfBitwiseOp', 'PhanTypeInvalidLeftOperandOfNumericOp', 'PhanTypeInvalidRightOperandOfAdd', 'PhanTypeInvalidRightOperandOfBitwiseOp', 'PhanTypeMismatchArgument', 'PhanTypeMismatchArgumentInternal', 'PhanTypeMismatchArgumentInternalProbablyReal', 'PhanTypeMismatchArgumentInternalReal', 'PhanTypeMismatchArgumentNullable', 'PhanTypeMismatchArgumentNullableInternal', 'PhanTypeMismatchArgumentProbablyReal', 'PhanTypeNonVarPassByRef', 'PhanTypePossiblyInvalidDimOffset', 'PhanTypeSuspiciousNonTraversableForeach', 'PhanUndeclaredFunction', 'PhanUndeclaredVariable', 'PhanUndeclaredVariableDim'], + ], + // 'directory_suppressions' => ['src/directory_name' => ['PhanIssueName1', 'PhanIssueName2']] can be manually added if needed. + // (directory_suppressions will currently be ignored by subsequent calls to --save-baseline, but may be preserved in future Phan releases) +]; diff --git a/.phan/config.php b/.phan/config.php new file mode 100644 index 00000000..ed85a106 --- /dev/null +++ b/.phan/config.php @@ -0,0 +1,49 @@ + '7.2', + 'target_php_version' => '8.5', + + 'backward_compatibility_checks' => false, + 'enable_class_alias_support' => false, + 'redundant_condition_detection' => true, + + 'directory_list' => [ + '.', + ], + + 'file_list' => [ + __DIR__ . '/stubs/amp-stubs.php', + ], + + 'exclude_analysis_directory_list' => [ + 'vendor/', + 'node_modules/', + 'tests/e2e/node_modules/', + 'jetpack_vendor/', + ], + + 'autoload_internal_extension_signatures' => [], + + 'stubs' => [ + 'vendor/php-stubs/wordpress-stubs/wordpress-stubs.php', + 'vendor/php-stubs/wp-cli-stubs/wp-cli-stubs.php', + ], + + 'plugins' => [ + 'AddNeverReturnTypePlugin', + 'DuplicateArrayKeyPlugin', + 'DuplicateExpressionPlugin', + 'LoopVariableReusePlugin', + 'PHPUnitNotDeadCodePlugin', + 'PregRegexCheckerPlugin', + 'RedundantAssignmentPlugin', + 'SimplifyExpressionPlugin', + 'UnreachableCodePlugin', + 'UseReturnValuePlugin', + 'UnusedSuppressionPlugin', + ], +]; diff --git a/.phan/stubs/amp-stubs.php b/.phan/stubs/amp-stubs.php new file mode 100644 index 00000000..0705f4ab --- /dev/null +++ b/.phan/stubs/amp-stubs.php @@ -0,0 +1,152 @@ + true, + * ) ); + * ``` + * + * Transitional mode is also implied if you define a `template_dir`: + * + * ```php + * add_theme_support( AMP_Theme_Support::SLUG, array( + * 'template_dir' => 'amp', + * ) ); + * ``` + * + * If you want to have AMP-specific templates in addition to serving AMP-first, do: + * + * ```php + * add_theme_support( AMP_Theme_Support::SLUG, array( + * 'paired' => false, + * 'template_dir' => 'amp', + * ) ); + * ``` + * + * @see AMP_Theme_Support::read_theme_support() + * @return boolean Whether this is in AMP 'canonical' mode, that is whether it is AMP-first and there is not a separate (paired) AMP URL. + */ +function amp_is_canonical() +{ +} +/** + * Determines whether the legacy AMP post templates are being used. + * + * @since 2.0 + * @return bool + */ +function amp_is_legacy() +{ +} +/** + * Determine whether AMP is available for the current URL. + * + * @since 2.0 + * + * @return bool Whether there is an AMP version for the provided URL. + * @global string $pagenow + * @global WP_Query $wp_query + */ +function amp_is_available() +{ +} +/** + * Retrieves the full AMP-specific permalink for the given post ID. + * + * On a site in Standard mode, this is the same as `get_permalink()`. + * + * @since 0.1 + * + * @param int $post_id Post ID. + * @return string AMP permalink. + */ +function amp_get_permalink($post_id) +{ +} +/** + * Determine whether the current request is for an AMP page. + * + * This function cannot be called before the parse_query action because it needs to be able + * to determine the queried object is able to be served as AMP. If 'amp' theme support is not + * present, this function returns true just if the query var is present. If theme support is + * present, then it returns true in transitional mode if an AMP template is available and the query + * var is present, or else in standard mode if just the template is available. + * + * @since 2.0 Formerly known as is_amp_endpoint(). + * + * @return bool Whether it is the AMP endpoint. + * @global WP_Query $wp_query + */ +function amp_is_request() +{ +} +/** + * Determine whether the current response being served as AMP. + * + * This function cannot be called before the parse_query action because it needs to be able + * to determine the queried object is able to be served as AMP. If 'amp' theme support is not + * present, this function returns true just if the query var is present. If theme support is + * present, then it returns true in transitional mode if an AMP template is available and the query + * var is present, or else in standard mode if just the template is available. + * + * @since 0.1 + * @since 2.0 Renamed to AMP-prefixed version, amp_is_request(). + * @deprecated Use amp_is_request() instead. + * + * @return bool Whether it is the AMP endpoint. + */ +function is_amp_endpoint() +{ +} +/** + * Class AMP_Options_Manager + * + * @internal + */ +class AMP_Options_Manager +{ + /** + * Get plugin option. + * + * @param string $option Plugin option name. + * @param bool $default Default value. + * + * @return mixed Option value. + */ + public static function get_option($option, $default = \false) + { + } +} +/** + * Registers a submenu page to access the AMP template editor panel in the Customizer. + * + * @internal + */ +function amp_add_customizer_link() +{ +} +// AMP endpoint Verifier +/** + * @phan-return mixed Dummy doc for stub. + */ +function ampforwp_is_amp_endpoint() +{ +} diff --git a/.phpcs.xml.dist b/.phpcs.xml.dist new file mode 100644 index 00000000..cf4d6e8c --- /dev/null +++ b/.phpcs.xml.dist @@ -0,0 +1,59 @@ + + + PHPCS ruleset for WP Super Cache + + . + + /vendor/* + /node_modules/* + /tests/e2e/* + /jetpack_vendor/* + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.w.org-assets/README.md b/.w.org-assets/README.md new file mode 100644 index 00000000..edae01c6 --- /dev/null +++ b/.w.org-assets/README.md @@ -0,0 +1,5 @@ +# WordPress.org Assets + +This directory is intended to hold assets meant for w.org plugins SVN. + +There is no auto-deployment of these assets, so once you make changes, please ping the plugin's team or ask the Monorepo team to deploy them. diff --git a/.w.org-assets/banner-1544x500.png b/.w.org-assets/banner-1544x500.png new file mode 100644 index 00000000..f70052f3 Binary files /dev/null and b/.w.org-assets/banner-1544x500.png differ diff --git a/.w.org-assets/banner-772x250.png b/.w.org-assets/banner-772x250.png new file mode 100644 index 00000000..87927d0f Binary files /dev/null and b/.w.org-assets/banner-772x250.png differ diff --git a/.w.org-assets/blueprints/blueprint.json b/.w.org-assets/blueprints/blueprint.json new file mode 100644 index 00000000..b99ab62c --- /dev/null +++ b/.w.org-assets/blueprints/blueprint.json @@ -0,0 +1,19 @@ +{ + "landingPage": "/wp-admin/options-general.php?page=wpsupercache", + "steps": [ + { + "step": "installPlugin", + "pluginData": { + "resource": "wordpress.org/plugins", + "slug": "wp-super-cache" + }, + "options": { + "activate": true + } + }, + { + "step": "runPHP", + "code": "=7.4", + "phpcompatibility/phpcompatibility-wp": "^2.1", + "sirbrillig/phpcs-variable-analysis": "^2.10", + "wp-coding-standards/wpcs": "^3.0" + }, + "require-dev": { + "automattic/jetpack-changelogger": "^4.2.6", + "yoast/phpunit-polyfills": "^1.1.1" + }, + "type": "phpcodesniffer-standard", + "extra": { + "autotagger": true, + "mirror-repo": "Automattic/jetpack-codesniffer", + "branch-alias": { + "dev-trunk": "4.0.x-dev" + }, + "changelogger": { + "link-template": "https://github.com/Automattic/jetpack-codesniffer/compare/v${old}...v${new}" + } + }, + "autoload": { + "psr-4": { + "Automattic\\Jetpack\\Sniffs\\": "Jetpack/Sniffs" + }, + "classmap": [ + "hacks/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-or-later" + ], + "description": "Jetpack Coding Standards. Based on the WordPress Coding Standards, with some additions.", + "keywords": [ + "codesniffer", + "dev", + "jetpack", + "phpcs", + "standards", + "testing" + ], + "support": { + "source": "https://github.com/Automattic/jetpack-codesniffer/tree/v4.0.0" + }, + "time": "2024-08-29T19:01:59+00:00" }, { "name": "automattic/phpunit-select-config", - "version": "1.0.4", + "version": "v1.0.4", + "source": { + "type": "git", + "url": "https://github.com/Automattic/phpunit-select-config.git", + "reference": "698bb97becda13dda0c5516bce42c3c9852349b2" + }, "dist": { - "type": "path", - "url": "/tmp/jetpack-build/Automattic/phpunit-select-config", - "reference": "228436b8cf848e2714976d7ab9b8027cadf77fde" + "type": "zip", + "url": "https://api.github.com/repos/Automattic/phpunit-select-config/zipball/698bb97becda13dda0c5516bce42c3c9852349b2", + "reference": "698bb97becda13dda0c5516bce42c3c9852349b2", + "shasum": "" }, "require": { "composer-runtime-api": "^2.2.2", @@ -114,31 +169,15 @@ "type": "library", "extra": { "autotagger": true, + "mirror-repo": "Automattic/phpunit-select-config", "branch-alias": { "dev-trunk": "1.0.x-dev" }, "changelogger": { "link-template": "https://github.com/Automattic/phpunit-select-config/compare/v${old}...v${new}" - }, - "mirror-repo": "Automattic/phpunit-select-config" - }, - "scripts": { - "phpunit": [ - "./vendor/bin/phpunit-select-config phpunit.#.xml.dist --colors=always" - ], - "test-coverage": [ - "php -dpcov.directory=. ./vendor/bin/phpunit-select-config phpunit.#.xml.dist --coverage-php \"$COVERAGE_DIR/php.cov\"" - ], - "test-php": [ - "@composer phpunit" - ], - "post-install-cmd": [ - "rm -f vendor/bin/phpunit-select-config && ln -s ../../bin/local-phpunit-select-config vendor/bin/phpunit-select-config" - ], - "post-update-cmd": [ - "rm -f vendor/bin/phpunit-select-config && ln -s ../../bin/local-phpunit-select-config vendor/bin/phpunit-select-config" - ] + } }, + "notification-url": "https://packagist.org/downloads/", "license": [ "GPL-2.0-or-later" ], @@ -148,245 +187,1706 @@ "dev", "phpunit" ], - "transport-options": { - "relative": false - } + "support": { + "source": "https://github.com/Automattic/phpunit-select-config/tree/v1.0.4" + }, + "time": "2026-03-30T16:01:55+00:00" }, { - "name": "myclabs/deep-copy", - "version": "1.13.4", + "name": "automattic/vipwpcs", + "version": "3.0.0", "source": { "type": "git", - "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" + "url": "https://github.com/Automattic/VIP-Coding-Standards.git", + "reference": "1b8960ebff9ea3eb482258a906ece4d1ee1e25fd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", - "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "url": "https://api.github.com/repos/Automattic/VIP-Coding-Standards/zipball/1b8960ebff9ea3eb482258a906ece4d1ee1e25fd", + "reference": "1b8960ebff9ea3eb482258a906ece4d1ee1e25fd", "shasum": "" }, "require": { - "php": "^7.1 || ^8.0" + "php": ">=5.4", + "phpcsstandards/phpcsextra": "^1.1.0", + "phpcsstandards/phpcsutils": "^1.0.8", + "sirbrillig/phpcs-variable-analysis": "^2.11.17", + "squizlabs/php_codesniffer": "^3.7.2", + "wp-coding-standards/wpcs": "^3.0" + }, + "require-dev": { + "php-parallel-lint/php-console-highlighter": "^1.0.0", + "php-parallel-lint/php-parallel-lint": "^1.3.2", + "phpcompatibility/php-compatibility": "^9", + "phpcsstandards/phpcsdevtools": "^1.0", + "phpunit/phpunit": "^4 || ^5 || ^6 || ^7" + }, + "type": "phpcodesniffer-standard", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Contributors", + "homepage": "https://github.com/Automattic/VIP-Coding-Standards/graphs/contributors" + } + ], + "description": "PHP_CodeSniffer rules (sniffs) to enforce WordPress VIP minimum coding conventions", + "keywords": [ + "phpcs", + "standards", + "static analysis", + "wordpress" + ], + "support": { + "issues": "https://github.com/Automattic/VIP-Coding-Standards/issues", + "source": "https://github.com/Automattic/VIP-Coding-Standards", + "wiki": "https://github.com/Automattic/VIP-Coding-Standards/wiki" + }, + "time": "2023-09-05T11:01:05+00:00" + }, + { + "name": "composer/pcre", + "version": "3.3.2", + "source": { + "type": "git", + "url": "https://github.com/composer/pcre.git", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/pcre/zipball/b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" }, "conflict": { - "doctrine/collections": "<1.6.8", - "doctrine/common": "<2.13.3 || >=3 <3.2.2" + "phpstan/phpstan": "<1.11.10" }, "require-dev": { - "doctrine/collections": "^1.6.8", - "doctrine/common": "^2.13.3 || ^3.2.2", - "phpspec/prophecy": "^1.10", - "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + "phpstan/phpstan": "^1.12 || ^2", + "phpstan/phpstan-strict-rules": "^1 || ^2", + "phpunit/phpunit": "^8 || ^9" }, "type": "library", + "extra": { + "phpstan": { + "includes": [ + "extension.neon" + ] + }, + "branch-alias": { + "dev-main": "3.x-dev" + } + }, "autoload": { - "files": [ - "src/DeepCopy/deep_copy.php" - ], "psr-4": { - "DeepCopy\\": "src/DeepCopy/" + "Composer\\Pcre\\": "src" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "description": "Create deep copies (clones) of your objects", + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", "keywords": [ - "clone", - "copy", - "duplicate", - "object", - "object graph" + "PCRE", + "preg", + "regex", + "regular expression" ], "support": { - "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/3.3.2" }, "funding": [ { - "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", "type": "tidelift" } ], - "time": "2025-08-01T08:46:24+00:00" + "time": "2024-11-12T16:29:46+00:00" }, { - "name": "nikic/php-parser", - "version": "v5.7.0", + "name": "composer/semver", + "version": "3.4.0", "source": { "type": "git", - "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82" + "url": "https://github.com/composer/semver.git", + "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82", - "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82", + "url": "https://api.github.com/repos/composer/semver/zipball/35e8d0af4486141bc745f23a29cc2091eb624a32", + "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32", "shasum": "" }, "require": { - "ext-ctype": "*", - "ext-json": "*", - "ext-tokenizer": "*", - "php": ">=7.4" + "php": "^5.3.2 || ^7.0 || ^8.0" }, "require-dev": { - "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^9.0" + "phpstan/phpstan": "^1.4", + "symfony/phpunit-bridge": "^4.2 || ^5" }, - "bin": [ - "bin/php-parse" + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Semver\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "description": "Semver library that offers utilities, version constraint parsing and validation.", + "keywords": [ + "semantic", + "semver", + "validation", + "versioning" + ], + "support": { + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.4.0" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } ], + "time": "2023-08-31T09:50:34+00:00" + }, + { + "name": "composer/spdx-licenses", + "version": "1.5.9", + "source": { + "type": "git", + "url": "https://github.com/composer/spdx-licenses.git", + "reference": "edf364cefe8c43501e21e88110aac10b284c3c9f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/edf364cefe8c43501e21e88110aac10b284c3c9f", + "reference": "edf364cefe8c43501e21e88110aac10b284c3c9f", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.11", + "symfony/phpunit-bridge": "^3 || ^7" + }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.x-dev" + "dev-main": "1.x-dev" } }, "autoload": { "psr-4": { - "PhpParser\\": "lib/PhpParser" + "Composer\\Spdx\\": "src" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Nikita Popov" + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" } ], - "description": "A PHP parser written in PHP", + "description": "SPDX licenses list and validation library.", "keywords": [ - "parser", - "php" + "license", + "spdx", + "validator" ], "support": { - "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.7.0" + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/spdx-licenses/issues", + "source": "https://github.com/composer/spdx-licenses/tree/1.5.9" }, - "time": "2025-12-06T11:56:16+00:00" + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2025-05-12T21:07:07+00:00" }, { - "name": "phar-io/manifest", - "version": "2.0.4", + "name": "composer/xdebug-handler", + "version": "3.0.5", "source": { "type": "git", - "url": "https://github.com/phar-io/manifest.git", - "reference": "54750ef60c58e43759730615a392c31c80e23176" + "url": "https://github.com/composer/xdebug-handler.git", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/6c1925561632e83d60a44492e0b344cf48ab85ef", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef", + "shasum": "" + }, + "require": { + "composer/pcre": "^1 || ^2 || ^3", + "php": "^7.2.5 || ^8.0", + "psr/log": "^1 || ^2 || ^3" + }, + "require-dev": { + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-strict-rules": "^1.1", + "phpunit/phpunit": "^8.5 || ^9.6 || ^10.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Composer\\XdebugHandler\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "John Stevenson", + "email": "john-stevenson@blueyonder.co.uk" + } + ], + "description": "Restarts a process without Xdebug.", + "keywords": [ + "Xdebug", + "performance" + ], + "support": { + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/xdebug-handler/issues", + "source": "https://github.com/composer/xdebug-handler/tree/3.0.5" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-05-06T16:37:16+00:00" + }, + { + "name": "dealerdirect/phpcodesniffer-composer-installer", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/PHPCSStandards/composer-installer.git", + "reference": "845eb62303d2ca9b289ef216356568ccc075ffd1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/845eb62303d2ca9b289ef216356568ccc075ffd1", + "reference": "845eb62303d2ca9b289ef216356568ccc075ffd1", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^2.2", + "php": ">=5.4", + "squizlabs/php_codesniffer": "^3.1.0 || ^4.0" + }, + "require-dev": { + "composer/composer": "^2.2", + "ext-json": "*", + "ext-zip": "*", + "php-parallel-lint/php-parallel-lint": "^1.4.0", + "phpcompatibility/php-compatibility": "^9.0 || ^10.0.0@dev", + "yoast/phpunit-polyfills": "^1.0" + }, + "type": "composer-plugin", + "extra": { + "class": "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin" + }, + "autoload": { + "psr-4": { + "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Franck Nijhof", + "email": "opensource@frenck.dev", + "homepage": "https://frenck.dev", + "role": "Open source developer" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCSStandards/composer-installer/graphs/contributors" + } + ], + "description": "PHP_CodeSniffer Standards Composer Installer Plugin", + "keywords": [ + "PHPCodeSniffer", + "PHP_CodeSniffer", + "code quality", + "codesniffer", + "composer", + "installer", + "phpcbf", + "phpcs", + "plugin", + "qa", + "quality", + "standard", + "standards", + "style guide", + "stylecheck", + "tests" + ], + "support": { + "issues": "https://github.com/PHPCSStandards/composer-installer/issues", + "security": "https://github.com/PHPCSStandards/composer-installer/security/policy", + "source": "https://github.com/PHPCSStandards/composer-installer" + }, + "funding": [ + { + "url": "https://github.com/PHPCSStandards", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcsstandards", + "type": "thanks_dev" + } + ], + "time": "2025-11-11T04:32:07+00:00" + }, + { + "name": "doctrine/deprecations", + "version": "1.1.6", + "source": { + "type": "git", + "url": "https://github.com/doctrine/deprecations.git", + "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca", + "reference": "d4fe3e6fd9bb9e72557a19674f44d8ac7db4c6ca", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "phpunit/phpunit": "<=7.5 || >=14" + }, + "require-dev": { + "doctrine/coding-standard": "^9 || ^12 || ^14", + "phpstan/phpstan": "1.4.10 || 2.1.30", + "phpstan/phpstan-phpunit": "^1.0 || ^2", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12.4 || ^13.0", + "psr/log": "^1 || ^2 || ^3" + }, + "suggest": { + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Deprecations\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", + "support": { + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/1.1.6" + }, + "time": "2026-02-07T07:09:04+00:00" + }, + { + "name": "felixfbecker/advanced-json-rpc", + "version": "v3.2.1", + "source": { + "type": "git", + "url": "https://github.com/felixfbecker/php-advanced-json-rpc.git", + "reference": "b5f37dbff9a8ad360ca341f3240dc1c168b45447" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/felixfbecker/php-advanced-json-rpc/zipball/b5f37dbff9a8ad360ca341f3240dc1c168b45447", + "reference": "b5f37dbff9a8ad360ca341f3240dc1c168b45447", + "shasum": "" + }, + "require": { + "netresearch/jsonmapper": "^1.0 || ^2.0 || ^3.0 || ^4.0", + "php": "^7.1 || ^8.0", + "phpdocumentor/reflection-docblock": "^4.3.4 || ^5.0.0" + }, + "require-dev": { + "phpunit/phpunit": "^7.0 || ^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "AdvancedJsonRpc\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "ISC" + ], + "authors": [ + { + "name": "Felix Becker", + "email": "felix.b@outlook.com" + } + ], + "description": "A more advanced JSONRPC implementation", + "support": { + "issues": "https://github.com/felixfbecker/php-advanced-json-rpc/issues", + "source": "https://github.com/felixfbecker/php-advanced-json-rpc/tree/v3.2.1" + }, + "time": "2021-06-11T22:34:44+00:00" + }, + { + "name": "mediawiki/mediawiki-codesniffer", + "version": "v43.0.0", + "source": { + "type": "git", + "url": "https://github.com/wikimedia/mediawiki-tools-codesniffer.git", + "reference": "c559bc02e87b0a969b6ed7380d7fa1d02738158b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/wikimedia/mediawiki-tools-codesniffer/zipball/c559bc02e87b0a969b6ed7380d7fa1d02738158b", + "reference": "c559bc02e87b0a969b6ed7380d7fa1d02738158b", + "shasum": "" + }, + "require": { + "composer/semver": "3.3.2 || 3.4.0", + "composer/spdx-licenses": "~1.5.2", + "ext-json": "*", + "ext-mbstring": "*", + "php": ">=7.4.0", + "phpcsstandards/phpcsextra": "1.1.2", + "squizlabs/php_codesniffer": "3.8.1", + "symfony/polyfill-php80": "^1.26.0" + }, + "require-dev": { + "mediawiki/mediawiki-phan-config": "0.12.1", + "mediawiki/minus-x": "1.1.1", + "php-parallel-lint/php-console-highlighter": "1.0.0", + "php-parallel-lint/php-parallel-lint": "1.3.2", + "phpunit/phpunit": "9.5.28" + }, + "type": "phpcodesniffer-standard", + "autoload": { + "psr-4": { + "MediaWiki\\Sniffs\\": "MediaWiki/Sniffs/", + "MediaWiki\\Sniffs\\Tests\\": "MediaWiki/Tests/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0-or-later" + ], + "description": "MediaWiki CodeSniffer Standards", + "homepage": "https://www.mediawiki.org/wiki/Manual:Coding_conventions/PHP", + "keywords": [ + "codesniffer", + "mediawiki" + ], + "support": { + "source": "https://github.com/wikimedia/mediawiki-tools-codesniffer/tree/v43.0.0" + }, + "time": "2024-01-29T16:06:37+00:00" + }, + { + "name": "microsoft/tolerant-php-parser", + "version": "v0.1.2", + "source": { + "type": "git", + "url": "https://github.com/microsoft/tolerant-php-parser.git", + "reference": "3eccfd273323aaf69513e2f1c888393f5947804b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/microsoft/tolerant-php-parser/zipball/3eccfd273323aaf69513e2f1c888393f5947804b", + "reference": "3eccfd273323aaf69513e2f1c888393f5947804b", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.15" + }, + "type": "library", + "autoload": { + "psr-4": { + "Microsoft\\PhpParser\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Rob Lourens", + "email": "roblou@microsoft.com" + } + ], + "description": "Tolerant PHP-to-AST parser designed for IDE usage scenarios", + "support": { + "issues": "https://github.com/microsoft/tolerant-php-parser/issues", + "source": "https://github.com/microsoft/tolerant-php-parser/tree/v0.1.2" + }, + "time": "2022-10-05T17:30:19+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.13.4", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3 <3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2025-08-01T08:46:24+00:00" + }, + { + "name": "netresearch/jsonmapper", + "version": "v4.5.0", + "source": { + "type": "git", + "url": "https://github.com/cweiske/jsonmapper.git", + "reference": "8e76efb98ee8b6afc54687045e1b8dba55ac76e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/8e76efb98ee8b6afc54687045e1b8dba55ac76e5", + "reference": "8e76efb98ee8b6afc54687045e1b8dba55ac76e5", + "shasum": "" + }, + "require": { + "ext-json": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-spl": "*", + "php": ">=7.1" + }, + "require-dev": { + "phpunit/phpunit": "~7.5 || ~8.0 || ~9.0 || ~10.0", + "squizlabs/php_codesniffer": "~3.5" + }, + "type": "library", + "autoload": { + "psr-0": { + "JsonMapper": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "OSL-3.0" + ], + "authors": [ + { + "name": "Christian Weiske", + "email": "cweiske@cweiske.de", + "homepage": "http://github.com/cweiske/jsonmapper/", + "role": "Developer" + } + ], + "description": "Map nested JSON structures onto PHP classes", + "support": { + "email": "cweiske@cweiske.de", + "issues": "https://github.com/cweiske/jsonmapper/issues", + "source": "https://github.com/cweiske/jsonmapper/tree/v4.5.0" + }, + "time": "2024-09-08T10:13:13+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v5.7.0", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82", + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-json": "*", + "ext-tokenizer": "*", + "php": ">=7.4" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v5.7.0" + }, + "time": "2025-12-06T11:56:16+00:00" + }, + { + "name": "phan/phan", + "version": "5.5.2", + "source": { + "type": "git", + "url": "https://github.com/phan/phan.git", + "reference": "25d7e8d185a4c78e7423c188fb28bba5dbde20c1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phan/phan/zipball/25d7e8d185a4c78e7423c188fb28bba5dbde20c1", + "reference": "25d7e8d185a4c78e7423c188fb28bba5dbde20c1", + "shasum": "" + }, + "require": { + "composer/semver": "^1.4|^2.0|^3.0", + "composer/xdebug-handler": "^2.0|^3.0", + "ext-filter": "*", + "ext-json": "*", + "ext-tokenizer": "*", + "felixfbecker/advanced-json-rpc": "^3.0.4", + "microsoft/tolerant-php-parser": "0.1.2", + "netresearch/jsonmapper": "^1.6.0|^2.0|^3.0|^4.0|^5.0", + "php": "^7.2.0|^8.0.0", + "sabre/event": "^5.1.3", + "symfony/console": "^3.2|^4.0|^5.0|^6.0|^7.0", + "symfony/polyfill-mbstring": "^1.11.0", + "symfony/polyfill-php80": "^1.20.0", + "tysonandre/var_representation_polyfill": "^0.0.2|^0.1.0" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.0" + }, + "suggest": { + "ext-ast": "Needed for parsing ASTs (unless --use-fallback-parser is used). 1.0.1+ is needed, 1.0.16+ is recommended.", + "ext-iconv": "Either iconv or mbstring is needed to ensure issue messages are valid utf-8", + "ext-igbinary": "Improves performance of polyfill when ext-ast is unavailable", + "ext-mbstring": "Either iconv or mbstring is needed to ensure issue messages are valid utf-8", + "ext-tokenizer": "Needed for fallback/polyfill parser support and file/line-based suppressions.", + "ext-var_representation": "Suggested for converting values to strings in issue messages" + }, + "bin": [ + "phan", + "phan_client", + "tocheckstyle" + ], + "type": "project", + "autoload": { + "psr-4": { + "Phan\\": "src/Phan" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Tyson Andre" + }, + { + "name": "Rasmus Lerdorf" + }, + { + "name": "Andrew S. Morrison" + } + ], + "description": "A static analyzer for PHP", + "keywords": [ + "analyzer", + "php", + "static", + "static analysis" + ], + "support": { + "issues": "https://github.com/phan/phan/issues", + "source": "https://github.com/phan/phan/tree/5.5.2" + }, + "time": "2025-10-04T18:04:38+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "54750ef60c58e43759730615a392c31c80e23176" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "php-stubs/wordpress-stubs", + "version": "v6.9.1", + "source": { + "type": "git", + "url": "https://github.com/php-stubs/wordpress-stubs.git", + "reference": "f12220f303e0d7c0844c0e5e957b0c3cee48d2f7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-stubs/wordpress-stubs/zipball/f12220f303e0d7c0844c0e5e957b0c3cee48d2f7", + "reference": "f12220f303e0d7c0844c0e5e957b0c3cee48d2f7", + "shasum": "" + }, + "conflict": { + "phpdocumentor/reflection-docblock": "5.6.1" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "^1.0", + "nikic/php-parser": "^5.5", + "php": "^7.4 || ^8.0", + "php-stubs/generator": "^0.8.3", + "phpdocumentor/reflection-docblock": "^6.0", + "phpstan/phpstan": "^2.1", + "phpunit/phpunit": "^9.5", + "symfony/polyfill-php80": "*", + "szepeviktor/phpcs-psr-12-neutron-hybrid-ruleset": "^1.1.1", + "wp-coding-standards/wpcs": "3.1.0 as 2.3.0" + }, + "suggest": { + "paragonie/sodium_compat": "Pure PHP implementation of libsodium", + "symfony/polyfill-php80": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "szepeviktor/phpstan-wordpress": "WordPress extensions for PHPStan" + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "WordPress function and class declaration stubs for static analysis.", + "homepage": "https://github.com/php-stubs/wordpress-stubs", + "keywords": [ + "PHPStan", + "static analysis", + "wordpress" + ], + "support": { + "issues": "https://github.com/php-stubs/wordpress-stubs/issues", + "source": "https://github.com/php-stubs/wordpress-stubs/tree/v6.9.1" + }, + "time": "2026-02-03T19:29:21+00:00" + }, + { + "name": "php-stubs/wp-cli-stubs", + "version": "v2.12.0", + "source": { + "type": "git", + "url": "https://github.com/php-stubs/wp-cli-stubs.git", + "reference": "af16401e299a3fd2229bd0fa9a037638a4174a9d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-stubs/wp-cli-stubs/zipball/af16401e299a3fd2229bd0fa9a037638a4174a9d", + "reference": "af16401e299a3fd2229bd0fa9a037638a4174a9d", + "shasum": "" + }, + "require": { + "php-stubs/wordpress-stubs": "^4.7 || ^5.0 || ^6.0" + }, + "require-dev": { + "php": "~7.3 || ~8.0", + "php-stubs/generator": "^0.8.0" + }, + "suggest": { + "symfony/polyfill-php73": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", + "szepeviktor/phpstan-wordpress": "WordPress extensions for PHPStan" + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "WP-CLI function and class declaration stubs for static analysis.", + "homepage": "https://github.com/php-stubs/wp-cli-stubs", + "keywords": [ + "PHPStan", + "static analysis", + "wordpress", + "wp-cli" + ], + "support": { + "issues": "https://github.com/php-stubs/wp-cli-stubs/issues", + "source": "https://github.com/php-stubs/wp-cli-stubs/tree/v2.12.0" + }, + "time": "2025-06-10T09:58:05+00:00" + }, + { + "name": "phpcompatibility/php-compatibility", + "version": "9.3.5", + "source": { + "type": "git", + "url": "https://github.com/PHPCompatibility/PHPCompatibility.git", + "reference": "9fb324479acf6f39452e0655d2429cc0d3914243" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibility/zipball/9fb324479acf6f39452e0655d2429cc0d3914243", + "reference": "9fb324479acf6f39452e0655d2429cc0d3914243", + "shasum": "" + }, + "require": { + "php": ">=5.3", + "squizlabs/php_codesniffer": "^2.3 || ^3.0.2" + }, + "conflict": { + "squizlabs/php_codesniffer": "2.6.2" + }, + "require-dev": { + "phpunit/phpunit": "~4.5 || ^5.0 || ^6.0 || ^7.0" + }, + "suggest": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.5 || This Composer plugin will sort out the PHPCS 'installed_paths' automatically.", + "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues." + }, + "type": "phpcodesniffer-standard", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Wim Godden", + "homepage": "https://github.com/wimg", + "role": "lead" + }, + { + "name": "Juliette Reinders Folmer", + "homepage": "https://github.com/jrfnl", + "role": "lead" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCompatibility/PHPCompatibility/graphs/contributors" + } + ], + "description": "A set of sniffs for PHP_CodeSniffer that checks for PHP cross-version compatibility.", + "homepage": "http://techblog.wimgodden.be/tag/codesniffer/", + "keywords": [ + "compatibility", + "phpcs", + "standards" + ], + "support": { + "issues": "https://github.com/PHPCompatibility/PHPCompatibility/issues", + "source": "https://github.com/PHPCompatibility/PHPCompatibility" + }, + "time": "2019-12-27T09:44:58+00:00" + }, + { + "name": "phpcompatibility/phpcompatibility-paragonie", + "version": "1.3.4", + "source": { + "type": "git", + "url": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie.git", + "reference": "244d7b04fc4bc2117c15f5abe23eb933b5f02bbf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityParagonie/zipball/244d7b04fc4bc2117c15f5abe23eb933b5f02bbf", + "reference": "244d7b04fc4bc2117c15f5abe23eb933b5f02bbf", + "shasum": "" + }, + "require": { + "phpcompatibility/php-compatibility": "^9.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "^1.0", + "paragonie/random_compat": "dev-master", + "paragonie/sodium_compat": "dev-master" + }, + "suggest": { + "dealerdirect/phpcodesniffer-composer-installer": "^1.0 || This Composer plugin will sort out the PHP_CodeSniffer 'installed_paths' automatically.", + "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues." + }, + "type": "phpcodesniffer-standard", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Wim Godden", + "role": "lead" + }, + { + "name": "Juliette Reinders Folmer", + "role": "lead" + } + ], + "description": "A set of rulesets for PHP_CodeSniffer to check for PHP cross-version compatibility issues in projects, while accounting for polyfills provided by the Paragonie polyfill libraries.", + "homepage": "http://phpcompatibility.com/", + "keywords": [ + "compatibility", + "paragonie", + "phpcs", + "polyfill", + "standards", + "static analysis" + ], + "support": { + "issues": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie/issues", + "security": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie/security/policy", + "source": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie" + }, + "funding": [ + { + "url": "https://github.com/PHPCompatibility", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcompatibility", + "type": "thanks_dev" + } + ], + "time": "2025-09-19T17:43:28+00:00" + }, + { + "name": "phpcompatibility/phpcompatibility-wp", + "version": "2.1.8", + "source": { + "type": "git", + "url": "https://github.com/PHPCompatibility/PHPCompatibilityWP.git", + "reference": "7c8d18b4d90dac9e86b0869a608fa09158e168fa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityWP/zipball/7c8d18b4d90dac9e86b0869a608fa09158e168fa", + "reference": "7c8d18b4d90dac9e86b0869a608fa09158e168fa", + "shasum": "" + }, + "require": { + "phpcompatibility/php-compatibility": "^9.0", + "phpcompatibility/phpcompatibility-paragonie": "^1.0", + "squizlabs/php_codesniffer": "^3.3" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "^1.0" + }, + "suggest": { + "dealerdirect/phpcodesniffer-composer-installer": "^1.0 || This Composer plugin will sort out the PHP_CodeSniffer 'installed_paths' automatically.", + "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues." + }, + "type": "phpcodesniffer-standard", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Wim Godden", + "role": "lead" + }, + { + "name": "Juliette Reinders Folmer", + "role": "lead" + } + ], + "description": "A ruleset for PHP_CodeSniffer to check for PHP cross-version compatibility issues in projects, while accounting for polyfills provided by WordPress.", + "homepage": "http://phpcompatibility.com/", + "keywords": [ + "compatibility", + "phpcs", + "standards", + "static analysis", + "wordpress" + ], + "support": { + "issues": "https://github.com/PHPCompatibility/PHPCompatibilityWP/issues", + "security": "https://github.com/PHPCompatibility/PHPCompatibilityWP/security/policy", + "source": "https://github.com/PHPCompatibility/PHPCompatibilityWP" + }, + "funding": [ + { + "url": "https://github.com/PHPCompatibility", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcompatibility", + "type": "thanks_dev" + } + ], + "time": "2025-10-18T00:05:59+00:00" + }, + { + "name": "phpcsstandards/phpcsextra", + "version": "1.1.2", + "source": { + "type": "git", + "url": "https://github.com/PHPCSStandards/PHPCSExtra.git", + "reference": "746c3190ba8eb2f212087c947ba75f4f5b9a58d5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCSStandards/PHPCSExtra/zipball/746c3190ba8eb2f212087c947ba75f4f5b9a58d5", + "reference": "746c3190ba8eb2f212087c947ba75f4f5b9a58d5", + "shasum": "" + }, + "require": { + "php": ">=5.4", + "phpcsstandards/phpcsutils": "^1.0.8", + "squizlabs/php_codesniffer": "^3.7.1" + }, + "require-dev": { + "php-parallel-lint/php-console-highlighter": "^1.0", + "php-parallel-lint/php-parallel-lint": "^1.3.2", + "phpcsstandards/phpcsdevcs": "^1.1.6", + "phpcsstandards/phpcsdevtools": "^1.2.1", + "phpunit/phpunit": "^4.5 || ^5.0 || ^6.0 || ^7.0" + }, + "type": "phpcodesniffer-standard", + "extra": { + "branch-alias": { + "dev-stable": "1.x-dev", + "dev-develop": "1.x-dev" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Juliette Reinders Folmer", + "homepage": "https://github.com/jrfnl", + "role": "lead" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCSStandards/PHPCSExtra/graphs/contributors" + } + ], + "description": "A collection of sniffs and standards for use with PHP_CodeSniffer.", + "keywords": [ + "PHP_CodeSniffer", + "phpcbf", + "phpcodesniffer-standard", + "phpcs", + "standards", + "static analysis" + ], + "support": { + "issues": "https://github.com/PHPCSStandards/PHPCSExtra/issues", + "source": "https://github.com/PHPCSStandards/PHPCSExtra" + }, + "time": "2023-09-20T22:06:18+00:00" + }, + { + "name": "phpcsstandards/phpcsutils", + "version": "1.0.9", + "source": { + "type": "git", + "url": "https://github.com/PHPCSStandards/PHPCSUtils.git", + "reference": "908247bc65010c7b7541a9551e002db12e9dae70" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPCSStandards/PHPCSUtils/zipball/908247bc65010c7b7541a9551e002db12e9dae70", + "reference": "908247bc65010c7b7541a9551e002db12e9dae70", + "shasum": "" + }, + "require": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.4.1 || ^0.5 || ^0.6.2 || ^0.7 || ^1.0", + "php": ">=5.4", + "squizlabs/php_codesniffer": "^3.8.0 || 4.0.x-dev@dev" + }, + "require-dev": { + "ext-filter": "*", + "php-parallel-lint/php-console-highlighter": "^1.0", + "php-parallel-lint/php-parallel-lint": "^1.3.2", + "phpcsstandards/phpcsdevcs": "^1.1.6", + "yoast/phpunit-polyfills": "^1.1.0 || ^2.0.0" + }, + "type": "phpcodesniffer-standard", + "extra": { + "branch-alias": { + "dev-stable": "1.x-dev", + "dev-develop": "1.x-dev" + } + }, + "autoload": { + "classmap": [ + "PHPCSUtils/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-3.0-or-later" + ], + "authors": [ + { + "name": "Juliette Reinders Folmer", + "homepage": "https://github.com/jrfnl", + "role": "lead" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCSStandards/PHPCSUtils/graphs/contributors" + } + ], + "description": "A suite of utility functions for use with PHP_CodeSniffer", + "homepage": "https://phpcsutils.com/", + "keywords": [ + "PHP_CodeSniffer", + "phpcbf", + "phpcodesniffer-standard", + "phpcs", + "phpcs3", + "standards", + "static analysis", + "tokens", + "utility" + ], + "support": { + "docs": "https://phpcsutils.com/", + "issues": "https://github.com/PHPCSStandards/PHPCSUtils/issues", + "security": "https://github.com/PHPCSStandards/PHPCSUtils/security/policy", + "source": "https://github.com/PHPCSStandards/PHPCSUtils" + }, + "funding": [ + { + "url": "https://github.com/PHPCSStandards", + "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" + } + ], + "time": "2023-12-08T14:50:00+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, + "time": "2020-06-27T09:03:43+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "5.6.7", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "31a105931bc8ffa3a123383829772e832fd8d903" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/31a105931bc8ffa3a123383829772e832fd8d903", + "reference": "31a105931bc8ffa3a123383829772e832fd8d903", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.1", + "ext-filter": "*", + "php": "^7.4 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^1.7", + "phpstan/phpdoc-parser": "^1.7|^2.0", + "webmozart/assert": "^1.9.1 || ^2" + }, + "require-dev": { + "mockery/mockery": "~1.3.5 || ~1.6.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-webmozart-assert": "^1.2", + "phpunit/phpunit": "^9.5", + "psalm/phar": "^5.26" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.6.7" + }, + "time": "2026-03-18T20:47:46+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "1.12.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "92a98ada2b93d9b201a613cb5a33584dde25f195" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", - "reference": "54750ef60c58e43759730615a392c31c80e23176", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/92a98ada2b93d9b201a613cb5a33584dde25f195", + "reference": "92a98ada2b93d9b201a613cb5a33584dde25f195", "shasum": "" }, "require": { - "ext-dom": "*", - "ext-libxml": "*", - "ext-phar": "*", - "ext-xmlwriter": "*", - "phar-io/version": "^3.0.1", - "php": "^7.2 || ^8.0" + "doctrine/deprecations": "^1.0", + "php": "^7.3 || ^8.0", + "phpdocumentor/reflection-common": "^2.0", + "phpstan/phpdoc-parser": "^1.18|^2.0" + }, + "require-dev": { + "ext-tokenizer": "*", + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpunit/phpunit": "^9.5", + "rector/rector": "^0.13.9", + "vimeo/psalm": "^4.25" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-1.x": "1.x-dev" } }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" + "name": "Mike van Riel", + "email": "me@mikevanriel.com" } ], - "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "support": { - "issues": "https://github.com/phar-io/manifest/issues", - "source": "https://github.com/phar-io/manifest/tree/2.0.4" + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.12.0" }, - "funding": [ - { - "url": "https://github.com/theseer", - "type": "github" - } - ], - "time": "2024-03-03T12:33:53+00:00" + "time": "2025-11-21T15:09:14+00:00" }, { - "name": "phar-io/version", - "version": "3.2.1", + "name": "phpstan/phpdoc-parser", + "version": "2.3.2", "source": { "type": "git", - "url": "https://github.com/phar-io/version.git", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "a004701b11273a26cd7955a61d67a7f1e525a45a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/a004701b11273a26cd7955a61d67a7f1e525a45a", + "reference": "a004701b11273a26cd7955a61d67a7f1e525a45a", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0" + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "doctrine/annotations": "^2.0", + "nikic/php-parser": "^5.3.0", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^9.6", + "symfony/process": "^5.2" }, "type": "library", "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "PHPStan\\PhpDocParser\\": [ + "src/" + ] + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } + "MIT" ], - "description": "Library for handling version information and constraints", + "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { - "issues": "https://github.com/phar-io/version/issues", - "source": "https://github.com/phar-io/version/tree/3.2.1" + "issues": "https://github.com/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.2" }, - "time": "2022-02-21T01:04:05+00:00" + "time": "2026-01-25T14:56:51+00:00" }, { "name": "phpunit/php-code-coverage", @@ -736,16 +2236,16 @@ }, { "name": "phpunit/phpunit", - "version": "12.5.14", + "version": "12.5.16", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "47283cfd98d553edcb1353591f4e255dc1bb61f0" + "reference": "b2429f58ae75cae980b5bb9873abe4de6aac8b58" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/47283cfd98d553edcb1353591f4e255dc1bb61f0", - "reference": "47283cfd98d553edcb1353591f4e255dc1bb61f0", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b2429f58ae75cae980b5bb9873abe4de6aac8b58", + "reference": "b2429f58ae75cae980b5bb9873abe4de6aac8b58", "shasum": "" }, "require": { @@ -767,7 +2267,7 @@ "sebastian/cli-parser": "^4.2.0", "sebastian/comparator": "^7.1.4", "sebastian/diff": "^7.0.0", - "sebastian/environment": "^8.0.3", + "sebastian/environment": "^8.0.4", "sebastian/exporter": "^7.0.2", "sebastian/global-state": "^8.0.2", "sebastian/object-enumerator": "^7.0.0", @@ -814,31 +2314,15 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/12.5.14" + "source": "https://github.com/sebastianbergmann/phpunit/tree/12.5.16" }, "funding": [ { - "url": "https://phpunit.de/sponsors.html", - "type": "custom" - }, - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", - "type": "tidelift" + "url": "https://phpunit.de/sponsoring.html", + "type": "other" } ], - "time": "2026-02-18T12:38:40+00:00" + "time": "2026-04-03T05:26:42+00:00" }, { "name": "psr/container", @@ -893,6 +2377,122 @@ }, "time": "2021-11-05T16:47:00+00:00" }, + { + "name": "psr/log", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.2" + }, + "time": "2024-09-11T13:17:53+00:00" + }, + { + "name": "sabre/event", + "version": "5.1.7", + "source": { + "type": "git", + "url": "https://github.com/sabre-io/event.git", + "reference": "86d57e305c272898ba3c28e9bd3d65d5464587c2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sabre-io/event/zipball/86d57e305c272898ba3c28e9bd3d65d5464587c2", + "reference": "86d57e305c272898ba3c28e9bd3d65d5464587c2", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "~2.17.1||^3.63", + "phpstan/phpstan": "^0.12", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6" + }, + "type": "library", + "autoload": { + "files": [ + "lib/coroutine.php", + "lib/Loop/functions.php", + "lib/Promise/functions.php" + ], + "psr-4": { + "Sabre\\Event\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Evert Pot", + "email": "me@evertpot.com", + "homepage": "http://evertpot.com/", + "role": "Developer" + } + ], + "description": "sabre/event is a library for lightweight event-based programming", + "homepage": "http://sabre.io/event/", + "keywords": [ + "EventEmitter", + "async", + "coroutine", + "eventloop", + "events", + "hooks", + "plugin", + "promise", + "reactor", + "signal" + ], + "support": { + "forum": "https://groups.google.com/group/sabredav-discuss", + "issues": "https://github.com/sabre-io/event/issues", + "source": "https://github.com/fruux/sabre-event" + }, + "time": "2024-08-27T11:23:05+00:00" + }, { "name": "sebastian/cli-parser", "version": "4.2.0", @@ -1181,16 +2781,16 @@ }, { "name": "sebastian/environment", - "version": "8.0.3", + "version": "8.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "24a711b5c916efc6d6e62aa65aa2ec98fef77f68" + "reference": "7b8842c2d8e85d0c3a5831236bf5869af6ab2a11" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/24a711b5c916efc6d6e62aa65aa2ec98fef77f68", - "reference": "24a711b5c916efc6d6e62aa65aa2ec98fef77f68", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/7b8842c2d8e85d0c3a5831236bf5869af6ab2a11", + "reference": "7b8842c2d8e85d0c3a5831236bf5869af6ab2a11", "shasum": "" }, "require": { @@ -1233,7 +2833,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", "security": "https://github.com/sebastianbergmann/environment/security/policy", - "source": "https://github.com/sebastianbergmann/environment/tree/8.0.3" + "source": "https://github.com/sebastianbergmann/environment/tree/8.0.4" }, "funding": [ { @@ -1253,7 +2853,7 @@ "type": "tidelift" } ], - "time": "2025-08-12T14:11:56+00:00" + "time": "2026-03-15T07:05:40+00:00" }, { "name": "sebastian/exporter", @@ -1716,79 +3316,215 @@ "security": "https://github.com/sebastianbergmann/type/security/policy", "source": "https://github.com/sebastianbergmann/type/tree/6.0.3" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/type", + "type": "tidelift" + } + ], + "time": "2025-08-09T06:57:12+00:00" + }, + { + "name": "sebastian/version", + "version": "6.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "3e6ccf7657d4f0a59200564b08cead899313b53c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/3e6ccf7657d4f0a59200564b08cead899313b53c", + "reference": "3e6ccf7657d4f0a59200564b08cead899313b53c", + "shasum": "" + }, + "require": { + "php": ">=8.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "security": "https://github.com/sebastianbergmann/version/security/policy", + "source": "https://github.com/sebastianbergmann/version/tree/6.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2025-02-07T05:00:38+00:00" + }, + { + "name": "sirbrillig/phpcs-variable-analysis", + "version": "v2.13.0", + "source": { + "type": "git", + "url": "https://github.com/sirbrillig/phpcs-variable-analysis.git", + "reference": "a15e970b8a0bf64cfa5e86d941f5e6b08855f369" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sirbrillig/phpcs-variable-analysis/zipball/a15e970b8a0bf64cfa5e86d941f5e6b08855f369", + "reference": "a15e970b8a0bf64cfa5e86d941f5e6b08855f369", + "shasum": "" + }, + "require": { + "php": ">=5.4.0", + "squizlabs/php_codesniffer": "^3.5.7 || ^4.0.0" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "^0.7 || ^1.0", + "phpstan/phpstan": "^1.7 || ^2.0", + "phpunit/phpunit": "^4.8.36 || ^5.7.21 || ^6.5 || ^7.0 || ^8.0 || ^9.0 || ^10.5.32 || ^11.3.3", + "vimeo/psalm": "^0.2 || ^0.3 || ^1.1 || ^4.24 || ^5.0 || ^6.0 || ^7.0" + }, + "type": "phpcodesniffer-standard", + "autoload": { + "psr-4": { + "VariableAnalysis\\": "VariableAnalysis/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" + "name": "Sam Graham", + "email": "php-codesniffer-variableanalysis@illusori.co.uk" }, { - "url": "https://tidelift.com/funding/github/packagist/sebastian/type", - "type": "tidelift" + "name": "Payton Swick", + "email": "payton@foolord.com" } ], - "time": "2025-08-09T06:57:12+00:00" + "description": "A PHPCS sniff to detect problems with variables.", + "keywords": [ + "phpcs", + "static analysis" + ], + "support": { + "issues": "https://github.com/sirbrillig/phpcs-variable-analysis/issues", + "source": "https://github.com/sirbrillig/phpcs-variable-analysis", + "wiki": "https://github.com/sirbrillig/phpcs-variable-analysis/wiki" + }, + "time": "2025-09-30T22:22:48+00:00" }, { - "name": "sebastian/version", - "version": "6.0.0", + "name": "squizlabs/php_codesniffer", + "version": "3.8.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "3e6ccf7657d4f0a59200564b08cead899313b53c" + "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", + "reference": "14f5fff1e64118595db5408e946f3a22c75807f7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/3e6ccf7657d4f0a59200564b08cead899313b53c", - "reference": "3e6ccf7657d4f0a59200564b08cead899313b53c", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/14f5fff1e64118595db5408e946f3a22c75807f7", + "reference": "14f5fff1e64118595db5408e946f3a22c75807f7", "shasum": "" }, "require": { - "php": ">=8.3" + "ext-simplexml": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": ">=5.4.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4" }, + "bin": [ + "bin/phpcbf", + "bin/phpcs" + ], "type": "library", "extra": { "branch-alias": { - "dev-main": "6.0-dev" + "dev-master": "3.x-dev" } }, - "autoload": { - "classmap": [ - "src/" - ] - }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Greg Sherwood", + "role": "Former lead" + }, + { + "name": "Juliette Reinders Folmer", + "role": "Current lead" + }, + { + "name": "Contributors", + "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors" } ], - "description": "Library that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", + "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.", + "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer", + "keywords": [ + "phpcs", + "standards", + "static analysis" + ], "support": { - "issues": "https://github.com/sebastianbergmann/version/issues", - "security": "https://github.com/sebastianbergmann/version/security/policy", - "source": "https://github.com/sebastianbergmann/version/tree/6.0.0" + "issues": "https://github.com/PHPCSStandards/PHP_CodeSniffer/issues", + "security": "https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy", + "source": "https://github.com/PHPCSStandards/PHP_CodeSniffer", + "wiki": "https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://github.com/PHPCSStandards", "type": "github" + }, + { + "url": "https://github.com/jrfnl", + "type": "github" + }, + { + "url": "https://opencollective.com/php_codesniffer", + "type": "open_collective" } ], - "time": "2025-02-07T05:00:38+00:00" + "time": "2024-01-11T20:47:48+00:00" }, { "name": "staabm/side-effects-detector", @@ -1844,16 +3580,16 @@ }, { "name": "symfony/console", - "version": "v7.4.7", + "version": "v7.4.8", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "e1e6770440fb9c9b0cf725f81d1361ad1835329d" + "reference": "1e92e39c51f95b88e3d66fa2d9f06d1fb45dd707" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/e1e6770440fb9c9b0cf725f81d1361ad1835329d", - "reference": "e1e6770440fb9c9b0cf725f81d1361ad1835329d", + "url": "https://api.github.com/repos/symfony/console/zipball/1e92e39c51f95b88e3d66fa2d9f06d1fb45dd707", + "reference": "1e92e39c51f95b88e3d66fa2d9f06d1fb45dd707", "shasum": "" }, "require": { @@ -1918,7 +3654,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.4.7" + "source": "https://github.com/symfony/console/tree/v7.4.8" }, "funding": [ { @@ -1938,7 +3674,7 @@ "type": "tidelift" } ], - "time": "2026-03-06T14:06:20+00:00" + "time": "2026-03-30T13:54:39+00:00" }, { "name": "symfony/deprecation-contracts", @@ -2342,18 +4078,102 @@ ], "time": "2024-12-23T08:48:59+00:00" }, + { + "name": "symfony/polyfill-php80", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608", + "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2025-01-02T08:10:11+00:00" + }, { "name": "symfony/process", - "version": "v7.4.5", + "version": "v7.4.8", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "608476f4604102976d687c483ac63a79ba18cc97" + "reference": "60f19cd3badc8de688421e21e4305eba50f8089a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/608476f4604102976d687c483ac63a79ba18cc97", - "reference": "608476f4604102976d687c483ac63a79ba18cc97", + "url": "https://api.github.com/repos/symfony/process/zipball/60f19cd3badc8de688421e21e4305eba50f8089a", + "reference": "60f19cd3badc8de688421e21e4305eba50f8089a", "shasum": "" }, "require": { @@ -2385,7 +4205,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.4.5" + "source": "https://github.com/symfony/process/tree/v7.4.8" }, "funding": [ { @@ -2405,7 +4225,7 @@ "type": "tidelift" } ], - "time": "2026-01-26T15:07:59+00:00" + "time": "2026-03-24T13:12:05+00:00" }, { "name": "symfony/service-contracts", @@ -2496,16 +4316,16 @@ }, { "name": "symfony/string", - "version": "v8.0.6", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "6c9e1108041b5dce21a9a4984b531c4923aa9ec4" + "reference": "ae9488f874d7603f9d2dfbf120203882b645d963" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/6c9e1108041b5dce21a9a4984b531c4923aa9ec4", - "reference": "6c9e1108041b5dce21a9a4984b531c4923aa9ec4", + "url": "https://api.github.com/repos/symfony/string/zipball/ae9488f874d7603f9d2dfbf120203882b645d963", + "reference": "ae9488f874d7603f9d2dfbf120203882b645d963", "shasum": "" }, "require": { @@ -2562,7 +4382,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v8.0.6" + "source": "https://github.com/symfony/string/tree/v8.0.8" }, "funding": [ { @@ -2582,7 +4402,7 @@ "type": "tidelift" } ], - "time": "2026-02-09T10:14:57+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "theseer/tokenizer", @@ -2634,6 +4454,196 @@ ], "time": "2025-12-08T11:19:18+00:00" }, + { + "name": "tysonandre/var_representation_polyfill", + "version": "0.1.3", + "source": { + "type": "git", + "url": "https://github.com/TysonAndre/var_representation_polyfill.git", + "reference": "e9116c2c352bb0835ca428b442dde7767c11ad32" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/TysonAndre/var_representation_polyfill/zipball/e9116c2c352bb0835ca428b442dde7767c11ad32", + "reference": "e9116c2c352bb0835ca428b442dde7767c11ad32", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": "^7.2.0|^8.0.0" + }, + "provide": { + "ext-var_representation": "*" + }, + "require-dev": { + "phan/phan": "^5.4.1", + "phpunit/phpunit": "^8.5.0" + }, + "suggest": { + "ext-var_representation": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "0.1.3-dev" + } + }, + "autoload": { + "files": [ + "src/var_representation.php" + ], + "psr-4": { + "VarRepresentation\\": "src/VarRepresentation" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Tyson Andre" + } + ], + "description": "Polyfill for var_representation: convert a variable to a string in a way that fixes the shortcomings of var_export", + "keywords": [ + "var_export", + "var_representation" + ], + "support": { + "issues": "https://github.com/TysonAndre/var_representation_polyfill/issues", + "source": "https://github.com/TysonAndre/var_representation_polyfill/tree/0.1.3" + }, + "time": "2022-08-31T12:59:22+00:00" + }, + { + "name": "webmozart/assert", + "version": "2.1.6", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/assert.git", + "reference": "ff31ad6efc62e66e518fbab1cde3453d389bcdc8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/ff31ad6efc62e66e518fbab1cde3453d389bcdc8", + "reference": "ff31ad6efc62e66e518fbab1cde3453d389bcdc8", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-date": "*", + "ext-filter": "*", + "php": "^8.2" + }, + "suggest": { + "ext-intl": "", + "ext-simplexml": "", + "ext-spl": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-feature/2-0": "2.0-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Assert\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + }, + { + "name": "Woody Gilk", + "email": "woody.gilk@gmail.com" + } + ], + "description": "Assertions to validate method input/output with nice error messages.", + "keywords": [ + "assert", + "check", + "validate" + ], + "support": { + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/2.1.6" + }, + "time": "2026-02-27T10:28:38+00:00" + }, + { + "name": "wp-coding-standards/wpcs", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/WordPress/WordPress-Coding-Standards.git", + "reference": "b4caf9689f1a0e4a4c632679a44e638c1c67aff1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/WordPress/WordPress-Coding-Standards/zipball/b4caf9689f1a0e4a4c632679a44e638c1c67aff1", + "reference": "b4caf9689f1a0e4a4c632679a44e638c1c67aff1", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "ext-libxml": "*", + "ext-tokenizer": "*", + "ext-xmlreader": "*", + "php": ">=5.4", + "phpcsstandards/phpcsextra": "^1.1.0", + "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", + "phpcsstandards/phpcsdevtools": "^1.2.0", + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0" + }, + "suggest": { + "ext-iconv": "For improved results", + "ext-mbstring": "For improved results" + }, + "type": "phpcodesniffer-standard", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Contributors", + "homepage": "https://github.com/WordPress/WordPress-Coding-Standards/graphs/contributors" + } + ], + "description": "PHP_CodeSniffer rules (sniffs) to enforce WordPress coding conventions", + "keywords": [ + "phpcs", + "standards", + "static analysis", + "wordpress" + ], + "support": { + "issues": "https://github.com/WordPress/WordPress-Coding-Standards/issues", + "source": "https://github.com/WordPress/WordPress-Coding-Standards", + "wiki": "https://github.com/WordPress/WordPress-Coding-Standards/wiki" + }, + "funding": [ + { + "url": "https://opencollective.com/thewpcc/contribute/wp-php-63406", + "type": "custom" + } + ], + "time": "2023-09-14T07:06:09+00:00" + }, { "name": "yoast/phpunit-polyfills", "version": "4.0.0", @@ -2700,10 +4710,7 @@ ], "aliases": [], "minimum-stability": "dev", - "stability-flags": { - "automattic/jetpack-changelogger": 20, - "automattic/phpunit-select-config": 20 - }, + "stability-flags": {}, "prefer-stable": true, "prefer-lowest": false, "platform": {}, diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 00000000..08b98a39 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,14 @@ +import js from '@eslint/js'; +import globals from 'globals'; + +export default [ + js.configs.recommended, + { + languageOptions: { + globals: { + ...globals.browser, + ...globals.jquery, + }, + }, + }, +]; diff --git a/package.json b/package.json index 0b3fee2a..1db626fc 100644 --- a/package.json +++ b/package.json @@ -5,12 +5,11 @@ "description": "A very fast caching engine for WordPress that produces static html files.", "homepage": "https://jetpack.com", "bugs": { - "url": "https://github.com/Automattic/jetpack/labels/[Plugin] Super Cache" + "url": "https://github.com/Automattic/wp-super-cache/issues" }, "repository": { "type": "git", - "url": "https://github.com/Automattic/jetpack.git", - "directory": "projects/plugins/super-cache" + "url": "https://github.com/Automattic/wp-super-cache.git" }, "license": "GPL-2.0-or-later", "author": "Automattic", @@ -20,5 +19,10 @@ "build-production": "echo 'Not implemented.'", "build-production-js": "echo 'Not implemented.'", "clean": "true" + }, + "devDependencies": { + "@eslint/js": "^9.0", + "eslint": "^9.0", + "globals": "^15.0" } } diff --git a/phpunit.9.xml.dist b/phpunit.9.xml.dist new file mode 100644 index 00000000..3965963c --- /dev/null +++ b/phpunit.9.xml.dist @@ -0,0 +1,17 @@ + + + + + tests/php + + + diff --git a/tests/e2e/.env b/tests/e2e/.env new file mode 100644 index 00000000..40316769 --- /dev/null +++ b/tests/e2e/.env @@ -0,0 +1,4 @@ +COMPOSE_PROJECT_NAME=super-cache-e2e +SUPER_CACHE_E2E_PORT=2022 +SUPER_CACHE_E2E_ADMIN_USER=super_cache +SUPER_CACHE_E2E_ADMIN_PASSWORD=super_secure diff --git a/tests/e2e/Dockerfile b/tests/e2e/Dockerfile new file mode 100644 index 00000000..8108abbd --- /dev/null +++ b/tests/e2e/Dockerfile @@ -0,0 +1,15 @@ +FROM wordpress:latest + +# Install wp-cli +RUN curl -o /bin/wp-cli.phar https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar +RUN chmod +x /bin/wp-cli.phar +RUN ln -s /bin/wp-cli.phar /usr/bin/wp + +ENV WORDPRESS_DB_HOST=db +ENV WORDPRESS_DB_USER=wordpress +ENV WORDPRESS_DB_PASSWORD=wordpress +ENV WORDPRESS_DB_NAME=wordpress + +# Override standard entry-point with our own wrapper to finish installing WP. +ENTRYPOINT ["docker-entrypoint.sh"] +CMD ["apache2-wrapper"] diff --git a/tests/e2e/docker-compose.yml b/tests/e2e/docker-compose.yml new file mode 100644 index 00000000..19661f37 --- /dev/null +++ b/tests/e2e/docker-compose.yml @@ -0,0 +1,43 @@ +services: + db: + image: mariadb:latest + healthcheck: + test: ["CMD", "mariadb-admin", "-P", "3306", "-pwordpress", "ping", "--silent"] + interval: 5s + timeout: 5s + retries: 20 + volumes: + - db_data:/var/lib/mysql + environment: + - MARIADB_ROOT_PASSWORD=wordpress + - MYSQL_DATABASE=wordpress + - MYSQL_USER=wordpress + - MYSQL_PASSWORD=wordpress + expose: + - 3306 + - 33060 + + wordpress: + build: . + volumes: + - wp_data:/var/www/html + - ../../:/var/www/html/wp-content/plugins/wp-super-cache + - ./tools/mu-test-helpers.php:/var/www/html/wp-content/mu-plugins/mu-test-helpers.php + - ./tools/apache2-wrapper.sh:/bin/apache2-wrapper + hostname: super-cache-e2e + ports: + - "${SUPER_CACHE_E2E_PORT:-2022}:${SUPER_CACHE_E2E_PORT:-2022}" + env_file: + - .env + environment: + - WORDPRESS_DB_HOST=db + - WORDPRESS_DB_USER=wordpress + - WORDPRESS_DB_PASSWORD=wordpress + - WORDPRESS_DB_NAME=wordpress + depends_on: + db: + condition: service_healthy + +volumes: + db_data: + wp_data: diff --git a/tests/e2e/eslint.config.mjs b/tests/e2e/eslint.config.mjs new file mode 100644 index 00000000..ae471350 --- /dev/null +++ b/tests/e2e/eslint.config.mjs @@ -0,0 +1,11 @@ +import { makeBaseConfig, defineConfig } from 'jetpack-js-tools/eslintrc/base.mjs'; + +export default defineConfig( makeBaseConfig( import.meta.url, { envs: [ 'node' ] } ), { + rules: { + 'jsdoc/require-jsdoc': 'off', + 'jsdoc/require-hyphen-before-param-description': 'off', + 'jsdoc/require-param-description': 'off', + 'jsdoc/require-param-type': 'off', + 'jsdoc/require-returns': 'off', + }, +} ); diff --git a/tests/e2e/jest.config.js b/tests/e2e/jest.config.js new file mode 100644 index 00000000..c7648e60 --- /dev/null +++ b/tests/e2e/jest.config.js @@ -0,0 +1,10 @@ +import { fileURLToPath } from 'url'; +import { config as dotenvConfig } from 'dotenv'; + +// Read .env file so variables are available in tests. +dotenvConfig( { path: fileURLToPath( '.env', import.meta.url ) } ); + +export default { + preset: 'ts-jest', + testTimeout: 10000, +}; diff --git a/tests/e2e/lib/CheerioForm.ts b/tests/e2e/lib/CheerioForm.ts new file mode 100644 index 00000000..ddaea94a --- /dev/null +++ b/tests/e2e/lib/CheerioForm.ts @@ -0,0 +1,58 @@ +import { authenticatedRequest } from './plugin-tools'; +import type { Cheerio } from 'cheerio'; +import type { AnyNode } from 'domhandler'; + +/** + * Helper class for reading, updating and submitting HTML forms from a Cheerio DOMs + */ +export default class CheerioForm { + private readonly fields: Record< string, string > = {}; + + constructor( private readonly form: Cheerio< AnyNode > ) { + for ( const { name, value } of form.serializeArray() ) { + this.fields[ name ] = value; + } + } + + /** + * Checks or unchecks a checkbox on the form. + * + * @param {string} name - Name of the checkbox. + * @param {boolean} value - True for check, false for uncheck. + */ + public setCheckbox( name: string, value: boolean ): void { + if ( value ) { + this.fields[ name ] = ( this.element( name ).val() ?? '' ).toString(); + } else { + delete this.fields[ name ]; + } + } + + /** + * Sets the value of a radio button or text field. + * + * @param {string} name - Name of the field. + * @param {string} value - Value to set. + */ + public setValue( name: string, value: string ): void { + this.fields[ name ] = value; + } + + /** + * Submit this form as an authenticated Request, using the given cookie. + * + * @param {string} authCookie - Auth cookie for form submission. + */ + public async submit( authCookie: string ): Promise< void > { + await authenticatedRequest( authCookie, 'POST', this.form.attr( 'action' )!, this.fields ); + } + + private element( name: string ): Cheerio< AnyNode > { + const element = this.form.find( `input[name=${ name }]` ); + if ( ! element ) { + throw new Error( `Could not find element with name ${ name }` ); + } + + return element; + } +} diff --git a/tests/e2e/lib/docker-tools.ts b/tests/e2e/lib/docker-tools.ts new file mode 100644 index 00000000..09e1588d --- /dev/null +++ b/tests/e2e/lib/docker-tools.ts @@ -0,0 +1,123 @@ +import { exec } from './system-tools'; + +// Cache the docker container id +let containerId: string | undefined; + +export async function getContainerId(): Promise< string > { + // Docker-compose v2 + if ( ! containerId ) { + const { stdout } = await exec( + 'docker', + 'ps', + '-q', + '--filter', + 'ancestor=super-cache-e2e-wordpress ' + ); + containerId = stdout.trim(); + } + + // Docker-compose v1 + if ( ! containerId ) { + const { stdout } = await exec( + 'docker', + 'ps', + '-q', + '--filter', + 'ancestor=super-cache-e2e_wordpress ' + ); + containerId = stdout.trim(); + } + + // Fail. + if ( ! containerId ) { + throw new Error( 'Failed to determine docker container ID. Is the test environment running?' ); + } + + return containerId; +} + +/** + * Run the given command in the test Docker instance. + * + * @param {...string} command - The command to run, as a series of strings which will each be escaped. + */ +export async function dockerExec( ...command: string[] ) { + const result = await exec( + 'docker', + 'exec', + '-u', + 'www-data', + await getContainerId(), + ...command + ); + + return result.stdout; +} + +/** + * Delete any lines that match the regex from the specified file. + * + * @param {string} filename - The file to be filtered. + * @param {string} regex - A regex (without / / markers) for lines to remove. + */ +export async function deleteLinesFromContainerFile( filename: string, regex: string ) { + await dockerExec( 'sed', '-i', `/${ regex }/d`, filename ); +} + +/** + * Deletes the specified file from docker. + * + * @param {string} filename - The file to delete. + */ +export async function deleteContainerFile( filename: string ) { + await dockerExec( 'rm', '-f', filename ); +} + +/** + * Deletes the specified directory (and its contents) from docker. + * + * @param {string} filename - The file to delete. + */ +export async function deleteContainerDirectory( filename: string ) { + await dockerExec( 'rm', '-rf', filename ); +} + +/** + * Returns the contents of the specified file from docker. + * + * @param {string} filename - The file to read. + */ +export async function readContainerFile( filename: string ): Promise< Buffer > { + const encoded = await dockerExec( 'bash', '-c', `cat ${ filename } | base64 -w 0` ); + + return Buffer.from( encoded, 'base64' ); +} + +/** + * Returns the contents of the specified file from docker, converted to string. + * + * @param {string} filename - The file to read. + * @param encoding + */ +export async function decodeContainerFile( + filename: string, + encoding: BufferEncoding = 'utf8' +): Promise< string > { + return ( await readContainerFile( filename ) ).toString( encoding ); +} + +/** + * Writes the specified contents to the specified file in docker. + * + * @param {string} filename - The file to write. + * @param {Buffer | string} data - The file data to write. + */ +export async function writeContainerFile( filename: string, data: Buffer | string ) { + const buffer = data instanceof Buffer ? data : Buffer.from( data ); + + await dockerExec( + 'bash', + '-c', + `echo '${ buffer.toString( 'base64' ) }' | base64 --decode > ${ filename }` + ); +} diff --git a/tests/e2e/lib/plugin-settings.ts b/tests/e2e/lib/plugin-settings.ts new file mode 100644 index 00000000..e3e3355b --- /dev/null +++ b/tests/e2e/lib/plugin-settings.ts @@ -0,0 +1,113 @@ +import { expect } from '@jest/globals'; +import { load as parseDom } from 'cheerio'; +import HtmlForm from './CheerioForm'; +import { authenticatedRequest, getSiteUrl } from './plugin-tools'; + +export enum ModRewriteOptions { + Off = '0', + On = '1', +} + +export enum CacheNotLoggedInOptions { + EnableForAllVisitors = '0', + DisableForAnyCookie = '1', + DisableForLoggedIn = '2', +} + +/** + * Description of how to write each setting, by name. + */ +const settingsHandlers = { + wp_cache_enabled: async ( authCookie: string, value: boolean ) => { + await submitSettingsForm( authCookie, 'settings', 'scupdates', form => { + form.setCheckbox( 'wp_cache_enabled', value ); + } ); + }, + + wp_cache_mod_rewrite: async ( authCookie: string, value: ModRewriteOptions ) => { + await submitSettingsForm( authCookie, 'settings', 'scupdates', form => { + form.setValue( 'wp_cache_mod_rewrite', value ); + } ); + }, + + wp_cache_not_logged_in: async ( authCookie: string, value: CacheNotLoggedInOptions ) => { + await submitSettingsForm( authCookie, 'settings', 'scupdates', form => { + form.setValue( 'wp_cache_not_logged_in', value ); + } ); + }, + + wp_cache_no_cache_for_get: async ( authCookie: string, value: boolean ) => { + await submitSettingsForm( authCookie, 'settings', 'scupdates', form => { + form.setCheckbox( 'wp_cache_no_cache_for_get', value ); + } ); + }, + + cache_compression: async ( authCookie: string, value: boolean ) => { + await submitSettingsForm( authCookie, 'settings', 'scupdates', form => { + form.setCheckbox( 'cache_compression', value ); + } ); + }, +}; + +type SettingName = keyof typeof settingsHandlers; +type SettingMethod< Name extends SettingName > = ( typeof settingsHandlers )[ Name ]; +type SettingParams< Name extends SettingName > = Parameters< SettingMethod< Name > >; +type SettingValue< Name extends SettingName > = SettingParams< Name >[ 1 ]; + +type Settings = { + [ Name in SettingName ]: SettingValue< Name >; +}; + +/** + * Update the plugin settings as specified in the settings object. + * + * @param {string} authCookie - Auth cookie for the admin user. + * @param {Settings} settings - Object with settings to update and their values. + */ +export async function updateSettings( authCookie: string, settings: Partial< Settings > ) { + for ( const [ name, value ] of Object.entries( settings ) ) { + await ( + settingsHandlers as Record< + string, + ( authCookie: string, value: unknown ) => Promise< void > + > + )[ name ]( authCookie, value ); + } +} + +/** + * Helper method to load, edit and submit a settings form. + * + * @param {string} authCookie - Auth cookie for the admin user. + * @param {string} tab - The name of the settings tab to submit a form from. + * @param {string} action - The action name of the form to submit. + * @param {Function} callback - Callback to edit the form after loading it. + */ +async function submitSettingsForm( + authCookie: string, + tab: string, + action: string, + callback: ( form: HtmlForm ) => void +) { + const html = await authenticatedRequest( + authCookie, + 'GET', + getSiteUrl( `/wp-admin/options-general.php`, { + page: 'wpsupercache', + tab, + } ) + ); + + const dom = parseDom( html ); + + const actionInput = dom( 'input[name=action][value=' + action + ']' ); + expect( actionInput.length ).toBe( 1 ); + + const formElement = actionInput.closest( 'form' ); + expect( formElement.length ).toBe( 1 ); + + const form = new HtmlForm( formElement ); + callback( form ); + + await form.submit( authCookie ); +} diff --git a/tests/e2e/lib/plugin-tools.ts b/tests/e2e/lib/plugin-tools.ts new file mode 100644 index 00000000..c4e566ca --- /dev/null +++ b/tests/e2e/lib/plugin-tools.ts @@ -0,0 +1,106 @@ +import fsp from 'fs/promises'; +import pathLib from 'path'; +import { expect } from '@jest/globals'; +import axios from 'axios'; +import { deleteContainerDirectory } from './docker-tools'; +import { wpcli } from './wordpress-tools'; +import type { Method } from 'axios'; + +/** + * Returns the absolute path of a file in the plugin directory. + * + * @param {string} path - The path to the file, relative to the plugin directory. + * @return {string} The absolute path to the file. + */ +export function pluginFilePath( path: string ) { + return pathLib.join( __dirname, '../../../', path ); +} + +/** + * Returns the contents of the specified file from the plugin directory. + * + * @param {string} path - The path to the file, relative to the plugin directory. + * @return {string} The contents of the file. Assumed to be utf8. + */ +export async function readPluginFile( path: string ): Promise< string > { + return fsp.readFile( pluginFilePath( path ), 'utf8' ); +} + +/** + * Clears the cache. + */ +export async function clearCache(): Promise< void > { + await wpcli( 'eval', 'wp_cache_clear_cache();' ); +} + +/** + * Logs into the site and creates an auth cookie that can be used for authenticated requests. + * + * @return {string} The auth cookie. + */ +export async function getAuthCookie(): Promise< string > { + const user = process.env.SUPER_CACHE_E2E_ADMIN_USER; + const pass = process.env.SUPER_CACHE_E2E_ADMIN_PASSWORD; + const encodedAuth = Buffer.from( user + ':' + pass ).toString( 'base64' ); + const headers = { Authorization: 'test ' + encodedAuth }; + + const response = await axios.post( getSiteUrl(), {}, { headers } ); + expect( response.status ).toBe( 200 ); + + const cookies = response.headers[ 'set-cookie' ]; + if ( ! cookies ) { + throw new Error( 'No set-cookie header found in response' ); + } + + return cookies.map( ( c: string ) => c.replace( ' HttpOnly', '' ) ).join( '; ' ); +} + +/** + * Makes an authenticated request to the specified URL. + * + * @param {string} authCookie - Authentication cookie to use for the request. + * @param {Method} method - HTTP method to use. e.g.: 'GET'. + * @param {string} url - URL to request. + * @param {object} data - Key / value pairs (strings) to submit as post data. + */ +export async function authenticatedRequest( + authCookie: string, + method: Method, + url: string, + data: Record< string, string > | undefined = undefined +): Promise< string > { + const response = await axios( url, { + method, + data: data ? new URLSearchParams( data ).toString() : null, + headers: { + Accept: 'text/html', + Cookie: authCookie, + 'Content-Type': 'application/x-www-form-urlencoded', + }, + } ); + + expect( response.status ).toBe( 200 ); + + return response.data; +} + +/** + * Returns a URL within the plugin site. + * + * @param {string} path - The path relative to the site URL. + * @param {object} query - Query parameters to add to the URL. + * @return {string} The site URL. + */ +export function getSiteUrl( path = '/', query: Record< string, string > = {} ): string { + const domain = 'http://localhost:' + process.env.SUPER_CACHE_E2E_PORT; + const queryString = new URLSearchParams( query ).toString(); + + return domain + path + ( queryString ? '?' + queryString : '' ); +} + +/** + * Delete the cache directory. + */ +export async function deleteCacheDirectory(): Promise< void > { + return deleteContainerDirectory( '/var/www/html/wp-content/cache' ); +} diff --git a/tests/e2e/lib/system-tools.ts b/tests/e2e/lib/system-tools.ts new file mode 100644 index 00000000..dfeb0222 --- /dev/null +++ b/tests/e2e/lib/system-tools.ts @@ -0,0 +1,15 @@ +import childProcess from 'node:child_process'; +import util from 'util'; +import shellEscape from 'shell-escape'; + +const execPromise = util.promisify( childProcess.exec ); + +/** + * Execute a command in the local shell. Returns its stdout. + * + * @param {...string} command - Command to run. Each string will be escaped. + * @return {string} stdout contents. + */ +export function exec( ...command: string[] ) { + return execPromise( shellEscape( command ) ); +} diff --git a/tests/e2e/lib/test-tools.ts b/tests/e2e/lib/test-tools.ts new file mode 100644 index 00000000..c6031a07 --- /dev/null +++ b/tests/e2e/lib/test-tools.ts @@ -0,0 +1,18 @@ +import { expect } from '@jest/globals'; +import axios from 'axios'; +import { getSiteUrl } from './plugin-tools'; + +/** + * Loads the specified page and returns the response body. Expects a 200 response so tests fail otherwise. + * + * @param {string} path The path within the test site to load. + * @param {object} params GET parameters to add to the URL. + */ +export async function loadPage( path = '/', params = {} ): Promise< string > { + const response = await axios.get( getSiteUrl( path, params ), { + headers: { Accept: 'text/html' }, + } ); + expect( response.status ).toBe( 200 ); + + return response.data; +} diff --git a/tests/e2e/lib/wordpress-tools.ts b/tests/e2e/lib/wordpress-tools.ts new file mode 100644 index 00000000..124dabc3 --- /dev/null +++ b/tests/e2e/lib/wordpress-tools.ts @@ -0,0 +1,43 @@ +import { expect } from '@jest/globals'; +import { + dockerExec, + deleteLinesFromContainerFile, + deleteContainerFile, + decodeContainerFile, + writeContainerFile, +} from './docker-tools'; +import { deleteCacheDirectory, readPluginFile } from './plugin-tools'; + +/** + * Run the given wp-cli command (provided as a string array) in wp-cli in the docker. + * + * @param {...string} command - The command to run. + */ +export async function wpcli( ...command: string[] ) { + return dockerExec( 'wp', ...command ); +} + +/** + * Reset the environment; clear out files created by wp-super-cache, and deactivate the plugin. + */ +export async function resetEnvironmnt() { + await wpcli( 'plugin', 'deactivate', 'wp-super-cache', '--skip-themes' ); + await deleteContainerFile( '/var/www/html/wp-content/advanced-cache.php' ); + await deleteContainerFile( '/var/www/html/wp-content/wp-cache-config.php' ); + await deleteLinesFromContainerFile( '/var/www/html/wp-config.php', 'WPCACHEHOME' ); + await deleteLinesFromContainerFile( '/var/www/html/wp-config.php', 'WP_CACHE' ); + await writeContainerFile( + '/var/www/html/.htaccess', + await readPluginFile( 'tests/e2e/tools/htaccess.txt' ) + ); + + await deleteCacheDirectory(); + + // Make sure tests fail if the env isn't clean. + const config = await decodeContainerFile( '/var/www/html/wp-config.php' ); + expect( /define\(\s*'WP_CACHE'/.test( config ) ).toBe( false ); + expect( /define\(\s*'WPCACHEHOME'/.test( config ) ).toBe( false ); + + const htaccess = await decodeContainerFile( '/var/www/html/.htaccess' ); + expect( htaccess ).not.toContain( 'WPSuperCache' ); +} diff --git a/tests/e2e/package.json b/tests/e2e/package.json new file mode 100644 index 00000000..9e75c5d9 --- /dev/null +++ b/tests/e2e/package.json @@ -0,0 +1,30 @@ +{ + "private": true, + "type": "module", + "scripts": { + "build": "pnpm jetpack build plugins/super-cache -v --no-pnpm-install --production", + "clean": "rm -rf output", + "distclean": "rm -rf node_modules", + "env:down": "docker-compose down", + "env:up": "docker-compose up -d", + "pretest:run": "pnpm run clean", + "test:run": "jest --runInBand", + "typecheck": "tsgo --noEmit" + }, + "devDependencies": { + "@jest/globals": "^30.0.0", + "@types/node": "^24.12.0", + "@types/shell-escape": "0.2.3", + "@typescript/native-preview": "7.0.0-dev.20260225.1", + "_jetpack-e2e-commons": "workspace:*", + "axios": "1.13.5", + "cheerio": "1.2.0", + "domhandler": "5.0.3", + "dotenv": "16.6.1", + "jest": "^30.0.0", + "jest-util": "^30.0.0", + "shell-escape": "0.2.0", + "ts-jest": "29.4.6", + "typescript": "5.9.3" + } +} diff --git a/tests/e2e/specs/activation.test.ts b/tests/e2e/specs/activation.test.ts new file mode 100644 index 00000000..04afa45a --- /dev/null +++ b/tests/e2e/specs/activation.test.ts @@ -0,0 +1,34 @@ +import { describe, beforeAll, expect, test } from '@jest/globals'; +import { decodeContainerFile, dockerExec } from '../lib/docker-tools'; +import { readPluginFile } from '../lib/plugin-tools'; +import { resetEnvironmnt, wpcli } from '../lib/wordpress-tools'; + +describe( 'Plugin Activation', () => { + beforeAll( async () => { + await resetEnvironmnt(); + await wpcli( 'plugin', 'activate', 'wp-super-cache' ); + } ); + + test( 'Ensure wp-config.php is updated when activated', async () => { + const config = await decodeContainerFile( '/var/www/html/wp-config.php' ); + + expect( /define\(\s*'WP_CACHE'/.test( config ) ).toBe( true ); + expect( /define\(\s*'WPCACHEHOME'/.test( config ) ).toBe( true ); + } ); + + test( 'Ensure advanced-cache is populated correctly.', async () => { + const advancedCache = await decodeContainerFile( + '/var/www/html/wp-content/advanced-cache.php' + ); + const expectedContents = await readPluginFile( 'advanced-cache.php' ); + + expect( advancedCache ).toBe( expectedContents ); + expect( advancedCache ).not.toBe( '' ); + } ); + + test( 'Ensure a wp-cache-config.php file has been created and appears valid.', async () => { + const result = await dockerExec( 'php', '-l', '/var/www/html/wp-content/wp-cache-config.php' ); + + expect( result ).toContain( 'No syntax errors' ); + } ); +} ); diff --git a/tests/e2e/specs/default-settings.test.ts b/tests/e2e/specs/default-settings.test.ts new file mode 100644 index 00000000..f5f6f081 --- /dev/null +++ b/tests/e2e/specs/default-settings.test.ts @@ -0,0 +1,98 @@ +import { describe, expect, beforeAll, test, beforeEach } from '@jest/globals'; +import { dockerExec } from '../lib/docker-tools'; +import { updateSettings } from '../lib/plugin-settings'; +import { + authenticatedRequest, + clearCache, + getAuthCookie, + getSiteUrl, + deleteCacheDirectory, +} from '../lib/plugin-tools'; +import { loadPage } from '../lib/test-tools'; +import { resetEnvironmnt, wpcli } from '../lib/wordpress-tools'; + +describe( 'cache behavior with default settings', () => { + beforeAll( async () => { + await resetEnvironmnt(); + await wpcli( 'plugin', 'activate', 'wp-super-cache' ); + await updateSettings( await getAuthCookie(), { + wp_cache_enabled: true, + } ); + } ); + + beforeEach( async () => { + await deleteCacheDirectory(); + } ); + + test( 'caches URLs with no get parameters', async () => { + const first = await loadPage(); + const second = await loadPage(); + + expect( first ).toBe( second ); + } ); + + test( 'logged in users get cached pages', async () => { + const cookie = await getAuthCookie(); + const url = getSiteUrl(); + + const first = await authenticatedRequest( cookie, 'GET', url ); + const second = await authenticatedRequest( cookie, 'GET', url ); + + expect( first ).toBe( second ); + } ); + + test( 'pages with identical GET parameters should cache together', async () => { + const first = await loadPage( '/', { s: 'potato' } ); + const second = await loadPage( '/', { s: 'potato' } ); + + expect( first ).toBe( second ); + } ); + + test( 'pages with different GET parameters should not cache together', async () => { + const first = await loadPage( '/', { s: 'squid' } ); + const second = await loadPage( '/', { s: 'potato' } ); + + expect( first ).not.toBe( second ); + } ); + + test( 'tracking GET parameters should affect caching', async () => { + const first = await loadPage( '/', { utm_source: 'test' } ); + const second = await loadPage( '/', { fbclid: 'test' } ); + + expect( first ).not.toBe( second ); + } ); + + test( 'double slash at the start of URLs should not break URL processing', async () => { + const first = await loadPage( '//', { s: 'squid' } ); + const second = await loadPage( '//' ); + + expect( first ).not.toBe( second ); + } ); + + test( 'cached files expire', async () => { + const first = await loadPage(); + const second = await loadPage(); + + await dockerExec( + 'touch', + '-d', + '1 hour ago', + '/var/www/html/wp-content/cache/supercache/localhost/index.html' + ); + + const third = await loadPage(); + const fourth = await loadPage(); + + expect( first ).toBe( second ); + expect( third ).toBe( fourth ); + expect( first ).not.toBe( third ); + } ); + + test( 'clears the cache', async () => { + const first = await loadPage(); + await clearCache(); + const second = await loadPage(); + + expect( first ).not.toBe( second ); + } ); +} ); diff --git a/tests/e2e/specs/settings/cache-compression.test.ts b/tests/e2e/specs/settings/cache-compression.test.ts new file mode 100644 index 00000000..c09c2f8e --- /dev/null +++ b/tests/e2e/specs/settings/cache-compression.test.ts @@ -0,0 +1,46 @@ +import util from 'util'; +import zlib from 'zlib'; +import { describe, expect, beforeAll, test } from '@jest/globals'; +import { readContainerFile } from '../../lib/docker-tools'; +import { updateSettings } from '../../lib/plugin-settings'; +import { getAuthCookie } from '../../lib/plugin-tools'; +import { loadPage } from '../../lib/test-tools'; +import { resetEnvironmnt, wpcli } from '../../lib/wordpress-tools'; + +const gunzip = util.promisify( zlib.gunzip ); + +let authCookie: string; + +describe( 'cache_compression settings', () => { + beforeAll( async () => { + await resetEnvironmnt(); + await wpcli( 'plugin', 'activate', 'wp-super-cache' ); + + authCookie = await getAuthCookie(); + await updateSettings( authCookie, { + wp_cache_enabled: true, + cache_compression: true, + } ); + } ); + + test( 'caching works correctly', async () => { + const first = await loadPage(); + const second = await loadPage(); + + expect( first ).toBe( second ); + expect( first ).toMatch( /Compression = gzip/ ); + } ); + + test( 'cached files are stored gzipped', async () => { + // Load a page, and strip the WP Super Cache comment from the bottom. + const rawContent = await loadPage(); + const trimmed = rawContent.replace( '', '' ).trim(); + + const gzipped = await readContainerFile( + '/var/www/html/wp-content/cache/supercache/localhost/index.html.gz' + ); + const decompressed = ( await gunzip( gzipped ) ).toString().trim(); + + expect( trimmed ).toBe( decompressed ); + } ); +} ); diff --git a/tests/e2e/specs/settings/mod-rewrite.test.ts b/tests/e2e/specs/settings/mod-rewrite.test.ts new file mode 100644 index 00000000..e6f93388 --- /dev/null +++ b/tests/e2e/specs/settings/mod-rewrite.test.ts @@ -0,0 +1,95 @@ +import { describe, expect, beforeAll, test } from '@jest/globals'; +import { decodeContainerFile } from '../../lib/docker-tools'; +import { ModRewriteOptions, updateSettings } from '../../lib/plugin-settings'; +import { + authenticatedRequest, + clearCache, + getAuthCookie, + getSiteUrl, +} from '../../lib/plugin-tools'; +import { loadPage } from '../../lib/test-tools'; +import { resetEnvironmnt, wpcli } from '../../lib/wordpress-tools'; + +let authCookie: string; + +describe( 'cache behavior with mod_rewrite enabled', () => { + beforeAll( async () => { + await resetEnvironmnt(); + await wpcli( 'plugin', 'activate', 'wp-super-cache' ); + + authCookie = await getAuthCookie(); + await updateSettings( authCookie, { + wp_cache_enabled: true, + wp_cache_mod_rewrite: ModRewriteOptions.On, + } ); + } ); + + test( 'updates mod_rewrite rules', async () => { + const rules = await decodeContainerFile( '/var/www/html/.htaccess' ); + + expect( rules ).toContain( '# BEGIN WPSuperCache' ); + expect( rules ).toContain( '# END WPSuperCache' ); + expect( rules ).toContain( + 'RewriteRule ^(.*) "/wp-content/cache/supercache/%{SERVER_NAME}/$1/index.html" [L]' + ); + } ); + + test( 'caches URLs with no get parameters', async () => { + const first = await loadPage(); + const second = await loadPage(); + + expect( first ).toBe( second ); + } ); + + test( 'logged in users get cached pages by default', async () => { + const url = getSiteUrl(); + + const first = await authenticatedRequest( authCookie, 'GET', url ); + const second = await authenticatedRequest( authCookie, 'GET', url ); + + expect( first ).toBe( second ); + } ); + + test( 'GET parameters should affect caching', async () => { + const first = await loadPage( '/', { s: 'squid' } ); + const second = await loadPage( '/', { s: 'potato' } ); + + expect( first ).not.toBe( second ); + } ); + + test( 'tracking GET parameters should affect caching', async () => { + const first = await loadPage( '/', { utm_source: 'test' } ); + const second = await loadPage( '/', { fbclid: 'test' } ); + + expect( first ).not.toBe( second ); + } ); + + test( 'double slash at the start of URLs should not break URL processing', async () => { + const first = await loadPage( '//', { s: 'squid' } ); + const second = await loadPage( '//' ); + + expect( first ).not.toBe( second ); + } ); + + test( 'clears the cache', async () => { + const first = await loadPage(); + await clearCache(); + const second = await loadPage(); + + expect( first ).not.toBe( second ); + } ); + + test( 'removes mod_rewrite rules when turned off', async () => { + await updateSettings( authCookie, { + wp_cache_mod_rewrite: ModRewriteOptions.Off, + } ); + + const rules = await decodeContainerFile( '/var/www/html/.htaccess' ); + expect( rules ).not.toContain( 'cache/supercache' ); + + // Return things to the state other tests expect. + await updateSettings( authCookie, { + wp_cache_mod_rewrite: ModRewriteOptions.On, + } ); + } ); +} ); diff --git a/tests/e2e/specs/settings/no-cache-for-get.test.ts b/tests/e2e/specs/settings/no-cache-for-get.test.ts new file mode 100644 index 00000000..8d4d34e7 --- /dev/null +++ b/tests/e2e/specs/settings/no-cache-for-get.test.ts @@ -0,0 +1,34 @@ +import { describe, expect, beforeAll, test } from '@jest/globals'; +import { updateSettings } from '../../lib/plugin-settings'; +import { getAuthCookie } from '../../lib/plugin-tools'; +import { loadPage } from '../../lib/test-tools'; +import { resetEnvironmnt, wpcli } from '../../lib/wordpress-tools'; + +let authCookie: string; + +describe( 'wp_cache_no_cache_for_get settings', () => { + beforeAll( async () => { + await resetEnvironmnt(); + await wpcli( 'plugin', 'activate', 'wp-super-cache' ); + + authCookie = await getAuthCookie(); + await updateSettings( authCookie, { + wp_cache_enabled: true, + wp_cache_no_cache_for_get: true, + } ); + } ); + + test( 'caches URLs with no get parameters', async () => { + const first = await loadPage(); + const second = await loadPage(); + + expect( first ).toBe( second ); + } ); + + test( 'does not cache URLs with get parameters', async () => { + const first = await loadPage( '/', { s: 'potato' } ); + const second = await loadPage( '/', { s: 'potato' } ); + + expect( first ).not.toBe( second ); + } ); +} ); diff --git a/tests/e2e/specs/settings/not-logged-in.test.ts b/tests/e2e/specs/settings/not-logged-in.test.ts new file mode 100644 index 00000000..88ad84ca --- /dev/null +++ b/tests/e2e/specs/settings/not-logged-in.test.ts @@ -0,0 +1,83 @@ +import { describe, expect, beforeAll, test } from '@jest/globals'; +import { CacheNotLoggedInOptions, updateSettings } from '../../lib/plugin-settings'; +import { authenticatedRequest, getAuthCookie, getSiteUrl } from '../../lib/plugin-tools'; +import { loadPage } from '../../lib/test-tools'; +import { resetEnvironmnt, wpcli } from '../../lib/wordpress-tools'; + +let authCookie: string; + +describe( 'wp_cache_not_logged_in settings', () => { + beforeAll( async () => { + await resetEnvironmnt(); + await wpcli( 'plugin', 'activate', 'wp-super-cache' ); + + authCookie = await getAuthCookie(); + await updateSettings( authCookie, { + wp_cache_enabled: true, + } ); + } ); + + test( 'logged in users get cached pages when "Enable caching for all visitors"', async () => { + await updateSettings( authCookie, { + wp_cache_not_logged_in: CacheNotLoggedInOptions.EnableForAllVisitors, + } ); + + const url = getSiteUrl(); + + const first = await authenticatedRequest( authCookie, 'GET', url ); + const second = await authenticatedRequest( authCookie, 'GET', url ); + + expect( first ).toBe( second ); + } ); + + test( 'wp-admin is never cached, even when "Enable caching for all visitors"', async () => { + await updateSettings( authCookie, { + wp_cache_not_logged_in: CacheNotLoggedInOptions.EnableForAllVisitors, + } ); + + const url = getSiteUrl( '/wp-admin/' ); + + const first = await authenticatedRequest( authCookie, 'GET', url ); + const second = await authenticatedRequest( authCookie, 'GET', url ); + + expect( first ).not.toBe( second ); + } ); + + test( 'users with any cookie do not get cached when "Disable caching for visitors who have a cookie"', async () => { + await updateSettings( authCookie, { + wp_cache_not_logged_in: CacheNotLoggedInOptions.DisableForAnyCookie, + } ); + + const cookie = 'cooookie: OMNOMNOM'; + const url = getSiteUrl(); + + const first = await authenticatedRequest( cookie, 'GET', url ); + const second = await authenticatedRequest( cookie, 'GET', url ); + + expect( first ).not.toBe( second ); + } ); + + test( 'users with no cookie get cached when "Disable caching for visitors who have a cookie"', async () => { + await updateSettings( authCookie, { + wp_cache_not_logged_in: CacheNotLoggedInOptions.DisableForAnyCookie, + } ); + + const first = await loadPage(); + const second = await loadPage(); + + expect( first ).toBe( second ); + } ); + + test( 'logged in users do not get cached pages when "Disable caching for logged in visitors"', async () => { + await updateSettings( authCookie, { + wp_cache_not_logged_in: CacheNotLoggedInOptions.DisableForLoggedIn, + } ); + + const url = getSiteUrl(); + + const first = await authenticatedRequest( authCookie, 'GET', url ); + const second = await authenticatedRequest( authCookie, 'GET', url ); + + expect( first ).not.toBe( second ); + } ); +} ); diff --git a/tests/e2e/tools/apache2-wrapper.sh b/tests/e2e/tools/apache2-wrapper.sh new file mode 100644 index 00000000..032b9026 --- /dev/null +++ b/tests/e2e/tools/apache2-wrapper.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +## +## The WordPress base image installs WordPress, then launches apache directly. +## See: https://github.com/docker-library/wordpress/blob/b9af6087524edc719249f590940b34ef107c95ca/latest/php7.4/apache/docker-entrypoint.sh +## +## This wrapper hijacks that part of the process, and sets up WordPress too. +## + +sed -i "s/^Listen 80$/Listen $SUPER_CACHE_E2E_PORT/g" /etc/apache2/ports.conf + +wp core install \ + --allow-root \ + --url="http://localhost:$SUPER_CACHE_E2E_PORT" \ + --path=/var/www/html \ + --title='Super Cache e2e' \ + --admin_user="$SUPER_CACHE_E2E_ADMIN_USER" \ + --admin_password="$SUPER_CACHE_E2E_ADMIN_PASSWORD" \ + --admin_email=fake@example.com + +wp rewrite structure '/%year%/%monthnum%/%postname%/'\ + --allow-root \ + --path=/var/www/html + +# Start Apache. +apache2-foreground diff --git a/tests/e2e/tools/htaccess.txt b/tests/e2e/tools/htaccess.txt new file mode 100644 index 00000000..423c2f6e --- /dev/null +++ b/tests/e2e/tools/htaccess.txt @@ -0,0 +1,11 @@ +# BEGIN WordPress + +RewriteEngine On +RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] +RewriteBase / +RewriteRule ^index\.php$ - [L] +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d +RewriteRule . /index.php [L] + +# END WordPress diff --git a/tests/e2e/tools/mu-test-helpers.php b/tests/e2e/tools/mu-test-helpers.php new file mode 100644 index 00000000..91ae2708 --- /dev/null +++ b/tests/e2e/tools/mu-test-helpers.php @@ -0,0 +1,42 @@ +'; +} +add_action( 'wp_footer', 'wpsc_test_inject_footer' ); +add_action( 'admin_footer', 'wpsc_test_inject_footer' ); + +/** + * Allow per-request login via HTTP header for a single request. Makes testing easier. + */ +function wpsc_test_header_login() { + if ( ! empty( $_SERVER['HTTP_AUTHORIZATION'] ) ) { + $auth = sanitize_text_field( wp_unslash( $_SERVER['HTTP_AUTHORIZATION'] ) ); + if ( 0 === stripos( $auth, 'test ' ) ) { + // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode + list( $user, $pass ) = explode( ':', base64_decode( substr( $auth, 5 ) ) ); + + $user = wp_signon( + array( + 'user_login' => $user, + 'user_password' => $pass, + ) + ); + + wp_set_current_user( $user->ID, $user->user_login ); + do_action( 'wp_login', $user->user_login, $user ); + } + } +} +add_action( 'init', 'wpsc_test_header_login' ); diff --git a/tests/e2e/tsconfig.json b/tests/e2e/tsconfig.json new file mode 100644 index 00000000..ca184b2e --- /dev/null +++ b/tests/e2e/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "_jetpack-e2e-commons/tsconfig.json", + "compilerOptions": { + "esModuleInterop": true + } +} diff --git a/tests/php/bootstrap.php b/tests/php/bootstrap.php new file mode 100644 index 00000000..46763b04 --- /dev/null +++ b/tests/php/bootstrap.php @@ -0,0 +1,11 @@ + - * Jordi Boggiano - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Composer\Autoload; - -/** - * ClassLoader implements a PSR-0, PSR-4 and classmap class loader. - * - * $loader = new \Composer\Autoload\ClassLoader(); - * - * // register classes with namespaces - * $loader->add('Symfony\Component', __DIR__.'/component'); - * $loader->add('Symfony', __DIR__.'/framework'); - * - * // activate the autoloader - * $loader->register(); - * - * // to enable searching the include path (eg. for PEAR packages) - * $loader->setUseIncludePath(true); - * - * In this example, if you try to use a class in the Symfony\Component - * namespace or one of its children (Symfony\Component\Console for instance), - * the autoloader will first look for the class under the component/ - * directory, and it will then fallback to the framework/ directory if not - * found before giving up. - * - * This class is loosely based on the Symfony UniversalClassLoader. - * - * @author Fabien Potencier - * @author Jordi Boggiano - * @see https://www.php-fig.org/psr/psr-0/ - * @see https://www.php-fig.org/psr/psr-4/ - */ -class ClassLoader -{ - /** @var \Closure(string):void */ - private static $includeFile; - - /** @var string|null */ - private $vendorDir; - - // PSR-4 - /** - * @var array> - */ - private $prefixLengthsPsr4 = array(); - /** - * @var array> - */ - private $prefixDirsPsr4 = array(); - /** - * @var list - */ - private $fallbackDirsPsr4 = array(); - - // PSR-0 - /** - * List of PSR-0 prefixes - * - * Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2'))) - * - * @var array>> - */ - private $prefixesPsr0 = array(); - /** - * @var list - */ - private $fallbackDirsPsr0 = array(); - - /** @var bool */ - private $useIncludePath = false; - - /** - * @var array - */ - private $classMap = array(); - - /** @var bool */ - private $classMapAuthoritative = false; - - /** - * @var array - */ - private $missingClasses = array(); - - /** @var string|null */ - private $apcuPrefix; - - /** - * @var array - */ - private static $registeredLoaders = array(); - - /** - * @param string|null $vendorDir - */ - public function __construct($vendorDir = null) - { - $this->vendorDir = $vendorDir; - self::initializeIncludeClosure(); - } - - /** - * @return array> - */ - public function getPrefixes() - { - if (!empty($this->prefixesPsr0)) { - return call_user_func_array('array_merge', array_values($this->prefixesPsr0)); - } - - return array(); - } - - /** - * @return array> - */ - public function getPrefixesPsr4() - { - return $this->prefixDirsPsr4; - } - - /** - * @return list - */ - public function getFallbackDirs() - { - return $this->fallbackDirsPsr0; - } - - /** - * @return list - */ - public function getFallbackDirsPsr4() - { - return $this->fallbackDirsPsr4; - } - - /** - * @return array Array of classname => path - */ - public function getClassMap() - { - return $this->classMap; - } - - /** - * @param array $classMap Class to filename map - * - * @return void - */ - public function addClassMap(array $classMap) - { - if ($this->classMap) { - $this->classMap = array_merge($this->classMap, $classMap); - } else { - $this->classMap = $classMap; - } - } - - /** - * Registers a set of PSR-0 directories for a given prefix, either - * appending or prepending to the ones previously set for this prefix. - * - * @param string $prefix The prefix - * @param list|string $paths The PSR-0 root directories - * @param bool $prepend Whether to prepend the directories - * - * @return void - */ - public function add($prefix, $paths, $prepend = false) - { - $paths = (array) $paths; - if (!$prefix) { - if ($prepend) { - $this->fallbackDirsPsr0 = array_merge( - $paths, - $this->fallbackDirsPsr0 - ); - } else { - $this->fallbackDirsPsr0 = array_merge( - $this->fallbackDirsPsr0, - $paths - ); - } - - return; - } - - $first = $prefix[0]; - if (!isset($this->prefixesPsr0[$first][$prefix])) { - $this->prefixesPsr0[$first][$prefix] = $paths; - - return; - } - if ($prepend) { - $this->prefixesPsr0[$first][$prefix] = array_merge( - $paths, - $this->prefixesPsr0[$first][$prefix] - ); - } else { - $this->prefixesPsr0[$first][$prefix] = array_merge( - $this->prefixesPsr0[$first][$prefix], - $paths - ); - } - } - - /** - * Registers a set of PSR-4 directories for a given namespace, either - * appending or prepending to the ones previously set for this namespace. - * - * @param string $prefix The prefix/namespace, with trailing '\\' - * @param list|string $paths The PSR-4 base directories - * @param bool $prepend Whether to prepend the directories - * - * @throws \InvalidArgumentException - * - * @return void - */ - public function addPsr4($prefix, $paths, $prepend = false) - { - $paths = (array) $paths; - if (!$prefix) { - // Register directories for the root namespace. - if ($prepend) { - $this->fallbackDirsPsr4 = array_merge( - $paths, - $this->fallbackDirsPsr4 - ); - } else { - $this->fallbackDirsPsr4 = array_merge( - $this->fallbackDirsPsr4, - $paths - ); - } - } elseif (!isset($this->prefixDirsPsr4[$prefix])) { - // Register directories for a new namespace. - $length = strlen($prefix); - if ('\\' !== $prefix[$length - 1]) { - throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); - } - $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; - $this->prefixDirsPsr4[$prefix] = $paths; - } elseif ($prepend) { - // Prepend directories for an already registered namespace. - $this->prefixDirsPsr4[$prefix] = array_merge( - $paths, - $this->prefixDirsPsr4[$prefix] - ); - } else { - // Append directories for an already registered namespace. - $this->prefixDirsPsr4[$prefix] = array_merge( - $this->prefixDirsPsr4[$prefix], - $paths - ); - } - } - - /** - * Registers a set of PSR-0 directories for a given prefix, - * replacing any others previously set for this prefix. - * - * @param string $prefix The prefix - * @param list|string $paths The PSR-0 base directories - * - * @return void - */ - public function set($prefix, $paths) - { - if (!$prefix) { - $this->fallbackDirsPsr0 = (array) $paths; - } else { - $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; - } - } - - /** - * Registers a set of PSR-4 directories for a given namespace, - * replacing any others previously set for this namespace. - * - * @param string $prefix The prefix/namespace, with trailing '\\' - * @param list|string $paths The PSR-4 base directories - * - * @throws \InvalidArgumentException - * - * @return void - */ - public function setPsr4($prefix, $paths) - { - if (!$prefix) { - $this->fallbackDirsPsr4 = (array) $paths; - } else { - $length = strlen($prefix); - if ('\\' !== $prefix[$length - 1]) { - throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); - } - $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; - $this->prefixDirsPsr4[$prefix] = (array) $paths; - } - } - - /** - * Turns on searching the include path for class files. - * - * @param bool $useIncludePath - * - * @return void - */ - public function setUseIncludePath($useIncludePath) - { - $this->useIncludePath = $useIncludePath; - } - - /** - * Can be used to check if the autoloader uses the include path to check - * for classes. - * - * @return bool - */ - public function getUseIncludePath() - { - return $this->useIncludePath; - } - - /** - * Turns off searching the prefix and fallback directories for classes - * that have not been registered with the class map. - * - * @param bool $classMapAuthoritative - * - * @return void - */ - public function setClassMapAuthoritative($classMapAuthoritative) - { - $this->classMapAuthoritative = $classMapAuthoritative; - } - - /** - * Should class lookup fail if not found in the current class map? - * - * @return bool - */ - public function isClassMapAuthoritative() - { - return $this->classMapAuthoritative; - } - - /** - * APCu prefix to use to cache found/not-found classes, if the extension is enabled. - * - * @param string|null $apcuPrefix - * - * @return void - */ - public function setApcuPrefix($apcuPrefix) - { - $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null; - } - - /** - * The APCu prefix in use, or null if APCu caching is not enabled. - * - * @return string|null - */ - public function getApcuPrefix() - { - return $this->apcuPrefix; - } - - /** - * Registers this instance as an autoloader. - * - * @param bool $prepend Whether to prepend the autoloader or not - * - * @return void - */ - public function register($prepend = false) - { - spl_autoload_register(array($this, 'loadClass'), true, $prepend); - - if (null === $this->vendorDir) { - return; - } - - if ($prepend) { - self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders; - } else { - unset(self::$registeredLoaders[$this->vendorDir]); - self::$registeredLoaders[$this->vendorDir] = $this; - } - } - - /** - * Unregisters this instance as an autoloader. - * - * @return void - */ - public function unregister() - { - spl_autoload_unregister(array($this, 'loadClass')); - - if (null !== $this->vendorDir) { - unset(self::$registeredLoaders[$this->vendorDir]); - } - } - - /** - * Loads the given class or interface. - * - * @param string $class The name of the class - * @return true|null True if loaded, null otherwise - */ - public function loadClass($class) - { - if ($file = $this->findFile($class)) { - $includeFile = self::$includeFile; - $includeFile($file); - - return true; - } - - return null; - } - - /** - * Finds the path to the file where the class is defined. - * - * @param string $class The name of the class - * - * @return string|false The path if found, false otherwise - */ - public function findFile($class) - { - // class map lookup - if (isset($this->classMap[$class])) { - return $this->classMap[$class]; - } - if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { - return false; - } - if (null !== $this->apcuPrefix) { - $file = apcu_fetch($this->apcuPrefix.$class, $hit); - if ($hit) { - return $file; - } - } - - $file = $this->findFileWithExtension($class, '.php'); - - // Search for Hack files if we are running on HHVM - if (false === $file && defined('HHVM_VERSION')) { - $file = $this->findFileWithExtension($class, '.hh'); - } - - if (null !== $this->apcuPrefix) { - apcu_add($this->apcuPrefix.$class, $file); - } - - if (false === $file) { - // Remember that this class does not exist. - $this->missingClasses[$class] = true; - } - - return $file; - } - - /** - * Returns the currently registered loaders keyed by their corresponding vendor directories. - * - * @return array - */ - public static function getRegisteredLoaders() - { - return self::$registeredLoaders; - } - - /** - * @param string $class - * @param string $ext - * @return string|false - */ - private function findFileWithExtension($class, $ext) - { - // PSR-4 lookup - $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; - - $first = $class[0]; - if (isset($this->prefixLengthsPsr4[$first])) { - $subPath = $class; - while (false !== $lastPos = strrpos($subPath, '\\')) { - $subPath = substr($subPath, 0, $lastPos); - $search = $subPath . '\\'; - if (isset($this->prefixDirsPsr4[$search])) { - $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1); - foreach ($this->prefixDirsPsr4[$search] as $dir) { - if (file_exists($file = $dir . $pathEnd)) { - return $file; - } - } - } - } - } - - // PSR-4 fallback dirs - foreach ($this->fallbackDirsPsr4 as $dir) { - if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { - return $file; - } - } - - // PSR-0 lookup - if (false !== $pos = strrpos($class, '\\')) { - // namespaced class name - $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) - . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); - } else { - // PEAR-like class name - $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; - } - - if (isset($this->prefixesPsr0[$first])) { - foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { - if (0 === strpos($class, $prefix)) { - foreach ($dirs as $dir) { - if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { - return $file; - } - } - } - } - } - - // PSR-0 fallback dirs - foreach ($this->fallbackDirsPsr0 as $dir) { - if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { - return $file; - } - } - - // PSR-0 include paths. - if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { - return $file; - } - - return false; - } - - /** - * @return void - */ - private static function initializeIncludeClosure() - { - if (self::$includeFile !== null) { - return; - } - - /** - * Scope isolated include. - * - * Prevents access to $this/self from included files. - * - * @param string $file - * @return void - */ - self::$includeFile = \Closure::bind(static function($file) { - include $file; - }, null, null); - } -} diff --git a/vendor/composer/InstalledVersions.php b/vendor/composer/InstalledVersions.php deleted file mode 100644 index 2052022f..00000000 --- a/vendor/composer/InstalledVersions.php +++ /dev/null @@ -1,396 +0,0 @@ - - * Jordi Boggiano - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Composer; - -use Composer\Autoload\ClassLoader; -use Composer\Semver\VersionParser; - -/** - * This class is copied in every Composer installed project and available to all - * - * See also https://getcomposer.org/doc/07-runtime.md#installed-versions - * - * To require its presence, you can require `composer-runtime-api ^2.0` - * - * @final - */ -class InstalledVersions -{ - /** - * @var string|null if set (by reflection by Composer), this should be set to the path where this class is being copied to - * @internal - */ - private static $selfDir = null; - - /** - * @var mixed[]|null - * @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array}|array{}|null - */ - private static $installed; - - /** - * @var bool - */ - private static $installedIsLocalDir; - - /** - * @var bool|null - */ - private static $canGetVendors; - - /** - * @var array[] - * @psalm-var array}> - */ - private static $installedByVendor = array(); - - /** - * Returns a list of all package names which are present, either by being installed, replaced or provided - * - * @return string[] - * @psalm-return list - */ - public static function getInstalledPackages() - { - $packages = array(); - foreach (self::getInstalled() as $installed) { - $packages[] = array_keys($installed['versions']); - } - - if (1 === \count($packages)) { - return $packages[0]; - } - - return array_keys(array_flip(\call_user_func_array('array_merge', $packages))); - } - - /** - * Returns a list of all package names with a specific type e.g. 'library' - * - * @param string $type - * @return string[] - * @psalm-return list - */ - public static function getInstalledPackagesByType($type) - { - $packagesByType = array(); - - foreach (self::getInstalled() as $installed) { - foreach ($installed['versions'] as $name => $package) { - if (isset($package['type']) && $package['type'] === $type) { - $packagesByType[] = $name; - } - } - } - - return $packagesByType; - } - - /** - * Checks whether the given package is installed - * - * This also returns true if the package name is provided or replaced by another package - * - * @param string $packageName - * @param bool $includeDevRequirements - * @return bool - */ - public static function isInstalled($packageName, $includeDevRequirements = true) - { - foreach (self::getInstalled() as $installed) { - if (isset($installed['versions'][$packageName])) { - return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false; - } - } - - return false; - } - - /** - * Checks whether the given package satisfies a version constraint - * - * e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call: - * - * Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3') - * - * @param VersionParser $parser Install composer/semver to have access to this class and functionality - * @param string $packageName - * @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package - * @return bool - */ - public static function satisfies(VersionParser $parser, $packageName, $constraint) - { - $constraint = $parser->parseConstraints((string) $constraint); - $provided = $parser->parseConstraints(self::getVersionRanges($packageName)); - - return $provided->matches($constraint); - } - - /** - * Returns a version constraint representing all the range(s) which are installed for a given package - * - * It is easier to use this via isInstalled() with the $constraint argument if you need to check - * whether a given version of a package is installed, and not just whether it exists - * - * @param string $packageName - * @return string Version constraint usable with composer/semver - */ - public static function getVersionRanges($packageName) - { - foreach (self::getInstalled() as $installed) { - if (!isset($installed['versions'][$packageName])) { - continue; - } - - $ranges = array(); - if (isset($installed['versions'][$packageName]['pretty_version'])) { - $ranges[] = $installed['versions'][$packageName]['pretty_version']; - } - if (array_key_exists('aliases', $installed['versions'][$packageName])) { - $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']); - } - if (array_key_exists('replaced', $installed['versions'][$packageName])) { - $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']); - } - if (array_key_exists('provided', $installed['versions'][$packageName])) { - $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']); - } - - return implode(' || ', $ranges); - } - - throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); - } - - /** - * @param string $packageName - * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present - */ - public static function getVersion($packageName) - { - foreach (self::getInstalled() as $installed) { - if (!isset($installed['versions'][$packageName])) { - continue; - } - - if (!isset($installed['versions'][$packageName]['version'])) { - return null; - } - - return $installed['versions'][$packageName]['version']; - } - - throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); - } - - /** - * @param string $packageName - * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present - */ - public static function getPrettyVersion($packageName) - { - foreach (self::getInstalled() as $installed) { - if (!isset($installed['versions'][$packageName])) { - continue; - } - - if (!isset($installed['versions'][$packageName]['pretty_version'])) { - return null; - } - - return $installed['versions'][$packageName]['pretty_version']; - } - - throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); - } - - /** - * @param string $packageName - * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference - */ - public static function getReference($packageName) - { - foreach (self::getInstalled() as $installed) { - if (!isset($installed['versions'][$packageName])) { - continue; - } - - if (!isset($installed['versions'][$packageName]['reference'])) { - return null; - } - - return $installed['versions'][$packageName]['reference']; - } - - throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); - } - - /** - * @param string $packageName - * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path. - */ - public static function getInstallPath($packageName) - { - foreach (self::getInstalled() as $installed) { - if (!isset($installed['versions'][$packageName])) { - continue; - } - - return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null; - } - - throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); - } - - /** - * @return array - * @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool} - */ - public static function getRootPackage() - { - $installed = self::getInstalled(); - - return $installed[0]['root']; - } - - /** - * Returns the raw installed.php data for custom implementations - * - * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect. - * @return array[] - * @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} - */ - public static function getRawData() - { - @trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED); - - if (null === self::$installed) { - // only require the installed.php file if this file is loaded from its dumped location, - // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 - if (substr(__DIR__, -8, 1) !== 'C') { - self::$installed = include __DIR__ . '/installed.php'; - } else { - self::$installed = array(); - } - } - - return self::$installed; - } - - /** - * Returns the raw data of all installed.php which are currently loaded for custom implementations - * - * @return array[] - * @psalm-return list}> - */ - public static function getAllRawData() - { - return self::getInstalled(); - } - - /** - * Lets you reload the static array from another file - * - * This is only useful for complex integrations in which a project needs to use - * this class but then also needs to execute another project's autoloader in process, - * and wants to ensure both projects have access to their version of installed.php. - * - * A typical case would be PHPUnit, where it would need to make sure it reads all - * the data it needs from this class, then call reload() with - * `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure - * the project in which it runs can then also use this class safely, without - * interference between PHPUnit's dependencies and the project's dependencies. - * - * @param array[] $data A vendor/composer/installed.php data set - * @return void - * - * @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $data - */ - public static function reload($data) - { - self::$installed = $data; - self::$installedByVendor = array(); - - // when using reload, we disable the duplicate protection to ensure that self::$installed data is - // always returned, but we cannot know whether it comes from the installed.php in __DIR__ or not, - // so we have to assume it does not, and that may result in duplicate data being returned when listing - // all installed packages for example - self::$installedIsLocalDir = false; - } - - /** - * @return string - */ - private static function getSelfDir() - { - if (self::$selfDir === null) { - self::$selfDir = strtr(__DIR__, '\\', '/'); - } - - return self::$selfDir; - } - - /** - * @return array[] - * @psalm-return list}> - */ - private static function getInstalled() - { - if (null === self::$canGetVendors) { - self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders'); - } - - $installed = array(); - $copiedLocalDir = false; - - if (self::$canGetVendors) { - $selfDir = self::getSelfDir(); - foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) { - $vendorDir = strtr($vendorDir, '\\', '/'); - if (isset(self::$installedByVendor[$vendorDir])) { - $installed[] = self::$installedByVendor[$vendorDir]; - } elseif (is_file($vendorDir.'/composer/installed.php')) { - /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $required */ - $required = require $vendorDir.'/composer/installed.php'; - self::$installedByVendor[$vendorDir] = $required; - $installed[] = $required; - if (self::$installed === null && $vendorDir.'/composer' === $selfDir) { - self::$installed = $required; - self::$installedIsLocalDir = true; - } - } - if (self::$installedIsLocalDir && $vendorDir.'/composer' === $selfDir) { - $copiedLocalDir = true; - } - } - } - - if (null === self::$installed) { - // only require the installed.php file if this file is loaded from its dumped location, - // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 - if (substr(__DIR__, -8, 1) !== 'C') { - /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $required */ - $required = require __DIR__ . '/installed.php'; - self::$installed = $required; - } else { - self::$installed = array(); - } - } - - if (self::$installed !== array() && !$copiedLocalDir) { - $installed[] = self::$installed; - } - - return $installed; - } -} diff --git a/vendor/composer/LICENSE b/vendor/composer/LICENSE deleted file mode 100644 index f27399a0..00000000 --- a/vendor/composer/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ - -Copyright (c) Nils Adermann, Jordi Boggiano - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is furnished -to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php deleted file mode 100644 index 5522a610..00000000 --- a/vendor/composer/autoload_classmap.php +++ /dev/null @@ -1,12 +0,0 @@ - $baseDir . '/src/device-detection/class-device-detection.php', - 'Automattic\\WPSC\\Device_Detection\\User_Agent_Info' => $baseDir . '/src/device-detection/class-user-agent-info.php', - 'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php', -); diff --git a/vendor/composer/autoload_namespaces.php b/vendor/composer/autoload_namespaces.php deleted file mode 100644 index 15a2ff3a..00000000 --- a/vendor/composer/autoload_namespaces.php +++ /dev/null @@ -1,9 +0,0 @@ -setClassMapAuthoritative(true); - $loader->register(true); - - return $loader; - } -} diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php deleted file mode 100644 index dae8dc37..00000000 --- a/vendor/composer/autoload_static.php +++ /dev/null @@ -1,22 +0,0 @@ - __DIR__ . '/../..' . '/src/device-detection/class-device-detection.php', - 'Automattic\\WPSC\\Device_Detection\\User_Agent_Info' => __DIR__ . '/../..' . '/src/device-detection/class-user-agent-info.php', - 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', - ); - - public static function getInitializer(ClassLoader $loader) - { - return \Closure::bind(function () use ($loader) { - $loader->classMap = ComposerStaticInit6fe342bc02f0b440f7b3c8d8ade42286_super_cacheⓥ4_0_0_alpha::$classMap; - - }, null, ClassLoader::class); - } -} diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json deleted file mode 100644 index f20a6c47..00000000 --- a/vendor/composer/installed.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "packages": [], - "dev": false, - "dev-package-names": [] -} diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php deleted file mode 100644 index 7253fa84..00000000 --- a/vendor/composer/installed.php +++ /dev/null @@ -1,23 +0,0 @@ - array( - 'name' => 'automattic/wp-super-cache', - 'pretty_version' => 'dev-trunk', - 'version' => 'dev-trunk', - 'reference' => null, - 'type' => 'wordpress-plugin', - 'install_path' => __DIR__ . '/../../', - 'aliases' => array(), - 'dev' => false, - ), - 'versions' => array( - 'automattic/wp-super-cache' => array( - 'pretty_version' => 'dev-trunk', - 'version' => 'dev-trunk', - 'reference' => null, - 'type' => 'wordpress-plugin', - 'install_path' => __DIR__ . '/../../', - 'aliases' => array(), - 'dev_requirement' => false, - ), - ), -);