From 9321582f8d3805076bfda52dbd4acb0d70f142c8 Mon Sep 17 00:00:00 2001 From: Fulvio Notarstefano Date: Tue, 23 Jun 2026 16:47:55 +0900 Subject: [PATCH 01/16] =?UTF-8?q?build:=20support=20PHPUnit=209=E2=80=9313?= =?UTF-8?q?=20and=20PHP=207.4+=20(composer=20range=20+=20dual=20configs)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Widen phpunit/phpunit to "^9.6 || ^10 || ^11 || ^12 || ^13"; php to ">=7.4" - Remove config.platform.php pin (it forced PHPUnit 9 and blocked testing 10+) - Bump mockery to ^1.6.12, phpstan trio to ^2, phpcs installer to ^1.0 - Drop abandoned sempro/phpunit-pretty-print; use --testdox - Relax sebastian/comparator to a floor (>=4.0.8) so it never caps newer PHPUnit - Split phpunit.xml into phpunit.xml.dist (modern 10+) and phpunit9.xml.dist (9.x) Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01K8HGMxCuSjUX6p4PQL7t5Y --- composer.json | 24 +- composer.lock | 3742 ++++++++++++++++++++---------- phpunit.xml.dist | 30 + phpunit.xml => phpunit9.xml.dist | 1 - 4 files changed, 2499 insertions(+), 1298 deletions(-) create mode 100644 phpunit.xml.dist rename phpunit.xml => phpunit9.xml.dist (94%) diff --git a/composer.json b/composer.json index 38108df..69ab56c 100644 --- a/composer.json +++ b/composer.json @@ -4,24 +4,23 @@ "license": "BSD-3-Clause", "prefer-stable": true, "require": { - "php": ">=7.4 < 9", - "phpunit/phpunit": "^9.6", - "mockery/mockery": "^1.6", + "php": ">=7.4", + "phpunit/phpunit": "^9.6 || ^10 || ^11 || ^12 || ^13", + "mockery/mockery": "^1.6.12", "antecedent/patchwork": "^2.1" }, "require-dev": { "behat/behat": "^v3.11.0", - "sebastian/comparator": "^4.0.8", + "sebastian/comparator": ">=4.0.8", "php-coveralls/php-coveralls": "^v2.7", - "sempro/phpunit-pretty-print": "^1.4", - "phpstan/phpstan": "^1.10", - "phpstan/phpstan-phpunit": "^1.3", - "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-mockery": "^2.0", "phpcompatibility/php-compatibility": "^9.3", "php-stubs/wordpress-globals": "^0.2", "php-stubs/wordpress-stubs": "^6.3", "friendsofphp/php-cs-fixer": "^3.4", - "dealerdirect/phpcodesniffer-composer-installer": "^0.7" + "dealerdirect/phpcodesniffer-composer-installer": "^1.0" }, "autoload": { "psr-4": { @@ -40,17 +39,14 @@ ] }, "config": { - "platform": { - "php": "7.4" - }, "allow-plugins": { "dealerdirect/phpcodesniffer-composer-installer": true } }, "scripts": { "test:behat": "behat", - "test:phpunit": "phpunit", - "test:phpunitcov": "phpunit --coverage-clover build/logs/clover.xml", + "test:phpunit": "phpunit --testdox", + "test:phpunitcov": "phpunit --testdox --coverage-clover build/logs/clover.xml", "test": [ "@test:behat", "@test:phpunit" diff --git a/composer.lock b/composer.lock index 0104ad1..a42df82 100644 --- a/composer.lock +++ b/composer.lock @@ -4,24 +4,24 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "a1855a9cad4977b6e16eb92ce3a72929", + "content-hash": "6e8e14f416aba6dd568c922410d26017", "packages": [ { "name": "antecedent/patchwork", - "version": "2.1.27", + "version": "2.2.3", "source": { "type": "git", "url": "https://github.com/antecedent/patchwork.git", - "reference": "16a1ab81559aabf14acb616141e801b32777f085" + "reference": "8b6b235f405af175259c8f56aea5fc23ab9f03ce" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/antecedent/patchwork/zipball/16a1ab81559aabf14acb616141e801b32777f085", - "reference": "16a1ab81559aabf14acb616141e801b32777f085", + "url": "https://api.github.com/repos/antecedent/patchwork/zipball/8b6b235f405af175259c8f56aea5fc23ab9f03ce", + "reference": "8b6b235f405af175259c8f56aea5fc23ab9f03ce", "shasum": "" }, "require": { - "php": ">=5.4.0" + "php": ">=7.1.0" }, "require-dev": { "phpunit/phpunit": ">=4" @@ -50,96 +50,26 @@ ], "support": { "issues": "https://github.com/antecedent/patchwork/issues", - "source": "https://github.com/antecedent/patchwork/tree/2.1.27" - }, - "time": "2023-12-03T18:46:49+00:00" - }, - { - "name": "doctrine/instantiator", - "version": "1.5.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/instantiator.git", - "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/instantiator/zipball/0a0fa9780f5d4e507415a065172d26a98d02047b", - "reference": "0a0fa9780f5d4e507415a065172d26a98d02047b", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "require-dev": { - "doctrine/coding-standard": "^9 || ^11", - "ext-pdo": "*", - "ext-phar": "*", - "phpbench/phpbench": "^0.16 || ^1", - "phpstan/phpstan": "^1.4", - "phpstan/phpstan-phpunit": "^1", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "vimeo/psalm": "^4.30 || ^5.4" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Marco Pivetta", - "email": "ocramius@gmail.com", - "homepage": "https://ocramius.github.io/" - } - ], - "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", - "homepage": "https://www.doctrine-project.org/projects/instantiator.html", - "keywords": [ - "constructor", - "instantiate" - ], - "support": { - "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/1.5.0" + "source": "https://github.com/antecedent/patchwork/tree/2.2.3" }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator", - "type": "tidelift" - } - ], - "time": "2022-12-30T00:15:36+00:00" + "time": "2025-09-17T09:00:56+00:00" }, { "name": "hamcrest/hamcrest-php", - "version": "v2.0.1", + "version": "v2.1.1", "source": { "type": "git", "url": "https://github.com/hamcrest/hamcrest-php.git", - "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3" + "reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/8c3d0a3f6af734494ad8f6fbbee0ba92422859f3", - "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3", + "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487", + "reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487", "shasum": "" }, "require": { - "php": "^5.3|^7.0|^8.0" + "php": "^7.4|^8.0" }, "replace": { "cordoval/hamcrest-php": "*", @@ -147,8 +77,8 @@ "kodova/hamcrest-php": "*" }, "require-dev": { - "phpunit/php-file-iterator": "^1.4 || ^2.0", - "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0" + "phpunit/php-file-iterator": "^1.4 || ^2.0 || ^3.0", + "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0 || ^8.0 || ^9.0" }, "type": "library", "extra": { @@ -171,22 +101,22 @@ ], "support": { "issues": "https://github.com/hamcrest/hamcrest-php/issues", - "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.0.1" + "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.1.1" }, - "time": "2020-07-09T08:09:16+00:00" + "time": "2025-04-30T06:54:44+00:00" }, { "name": "mockery/mockery", - "version": "1.6.7", + "version": "1.6.12", "source": { "type": "git", "url": "https://github.com/mockery/mockery.git", - "reference": "0cc058854b3195ba21dc6b1f7b1f60f4ef3a9c06" + "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mockery/mockery/zipball/0cc058854b3195ba21dc6b1f7b1f60f4ef3a9c06", - "reference": "0cc058854b3195ba21dc6b1f7b1f60f4ef3a9c06", + "url": "https://api.github.com/repos/mockery/mockery/zipball/1f4efdd7d3beafe9807b08156dfcb176d18f1699", + "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699", "shasum": "" }, "require": { @@ -198,8 +128,8 @@ "phpunit/phpunit": "<8.0" }, "require-dev": { - "phpunit/phpunit": "^8.5 || ^9.6.10", - "symplify/easy-coding-standard": "^12.0.8" + "phpunit/phpunit": "^8.5 || ^9.6.17", + "symplify/easy-coding-standard": "^12.1.14" }, "type": "library", "autoload": { @@ -256,20 +186,20 @@ "security": "https://github.com/mockery/mockery/security/advisories", "source": "https://github.com/mockery/mockery" }, - "time": "2023-12-10T02:24:34+00:00" + "time": "2024-05-16T03:13:13+00:00" }, { "name": "myclabs/deep-copy", - "version": "1.11.1", + "version": "1.13.4", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", "shasum": "" }, "require": { @@ -277,11 +207,12 @@ }, "conflict": { "doctrine/collections": "<1.6.8", - "doctrine/common": "<2.13.3 || >=3,<3.2.2" + "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", @@ -307,7 +238,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" }, "funding": [ { @@ -315,29 +246,31 @@ "type": "tidelift" } ], - "time": "2023-03-08T13:26:56+00:00" + "time": "2025-08-01T08:46:24+00:00" }, { "name": "nikic/php-parser", - "version": "v4.18.0", + "version": "v5.7.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "1bcbb2179f97633e98bbbc87044ee2611c7d7999" + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/1bcbb2179f97633e98bbbc87044ee2611c7d7999", - "reference": "1bcbb2179f97633e98bbbc87044ee2611c7d7999", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82", + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82", "shasum": "" }, "require": { + "ext-ctype": "*", + "ext-json": "*", "ext-tokenizer": "*", - "php": ">=7.0" + "php": ">=7.4" }, "require-dev": { "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" + "phpunit/phpunit": "^9.0" }, "bin": [ "bin/php-parse" @@ -345,7 +278,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.9-dev" + "dev-master": "5.x-dev" } }, "autoload": { @@ -369,26 +302,27 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.18.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.7.0" }, - "time": "2023-12-10T21:03:43+00:00" + "time": "2025-12-06T11:56:16+00:00" }, { "name": "phar-io/manifest", - "version": "2.0.3", + "version": "2.0.4", "source": { "type": "git", "url": "https://github.com/phar-io/manifest.git", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53" + "reference": "54750ef60c58e43759730615a392c31c80e23176" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/97803eca37d319dfa7826cc2437fc020857acb53", - "reference": "97803eca37d319dfa7826cc2437fc020857acb53", + "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", @@ -429,9 +363,15 @@ "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.3" + "source": "https://github.com/phar-io/manifest/tree/2.0.4" }, - "time": "2021-07-20T11:28:43+00:00" + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" }, { "name": "phar-io/version", @@ -486,35 +426,35 @@ }, { "name": "phpunit/php-code-coverage", - "version": "9.2.30", + "version": "14.2.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "ca2bd87d2f9215904682a9cb9bb37dda98e76089" + "reference": "10d7da3628a99289cdf4c662dd7f0d73f1baec83" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ca2bd87d2f9215904682a9cb9bb37dda98e76089", - "reference": "ca2bd87d2f9215904682a9cb9bb37dda98e76089", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/10d7da3628a99289cdf4c662dd7f0d73f1baec83", + "reference": "10d7da3628a99289cdf4c662dd7f0d73f1baec83", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", + "ext-mbstring": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^4.18 || ^5.0", - "php": ">=7.3", - "phpunit/php-file-iterator": "^3.0.3", - "phpunit/php-text-template": "^2.0.2", - "sebastian/code-unit-reverse-lookup": "^2.0.2", - "sebastian/complexity": "^2.0", - "sebastian/environment": "^5.1.2", - "sebastian/lines-of-code": "^1.0.3", - "sebastian/version": "^3.0.1", - "theseer/tokenizer": "^1.2.0" + "nikic/php-parser": "^5.7.0", + "php": ">=8.4", + "phpunit/php-text-template": "^6.0", + "sebastian/complexity": "^6.0", + "sebastian/environment": "^9.3.2", + "sebastian/git-state": "^1.0", + "sebastian/lines-of-code": "^5.0.1", + "sebastian/version": "^7.0", + "theseer/tokenizer": "^2.0.1" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^13.2.0" }, "suggest": { "ext-pcov": "PHP extension that provides line coverage", @@ -523,7 +463,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "9.2-dev" + "dev-main": "14.2.x-dev" } }, "autoload": { @@ -552,40 +492,52 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.30" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/14.2.2" }, "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/phpunit/php-code-coverage", + "type": "tidelift" } ], - "time": "2023-12-22T06:47:57+00:00" + "time": "2026-06-08T11:50:38+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "3.0.6", + "version": "7.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" + "reference": "6e5aa1fb0a95b1703d83e721299ee18bb4e2de50" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", - "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/6e5aa1fb0a95b1703d83e721299ee18bb4e2de50", + "reference": "6e5aa1fb0a95b1703d83e721299ee18bb4e2de50", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.4" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^13.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -612,36 +564,49 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" + "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/7.0.0" }, "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/phpunit/php-file-iterator", + "type": "tidelift" } ], - "time": "2021-12-02T12:48:52+00:00" + "time": "2026-02-06T04:33:26+00:00" }, { "name": "phpunit/php-invoker", - "version": "3.1.1", + "version": "7.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-invoker.git", - "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67" + "reference": "42e5c5cae0c65df12d1b1a3ab52bf3f50f244d88" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/5a10147d0aaf65b58940a0b72f71c9ac0423cc67", - "reference": "5a10147d0aaf65b58940a0b72f71c9ac0423cc67", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/42e5c5cae0c65df12d1b1a3ab52bf3f50f244d88", + "reference": "42e5c5cae0c65df12d1b1a3ab52bf3f50f244d88", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.4" }, "require-dev": { "ext-pcntl": "*", - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^13.0" }, "suggest": { "ext-pcntl": "*" @@ -649,7 +614,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.1-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -675,40 +640,53 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-invoker/issues", - "source": "https://github.com/sebastianbergmann/php-invoker/tree/3.1.1" + "security": "https://github.com/sebastianbergmann/php-invoker/security/policy", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/7.0.0" }, "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/phpunit/php-invoker", + "type": "tidelift" } ], - "time": "2020-09-28T05:58:55+00:00" + "time": "2026-02-06T04:34:47+00:00" }, { "name": "phpunit/php-text-template", - "version": "2.0.4", + "version": "6.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28" + "reference": "a47af19f93f76aa3368303d752aa5272ca3299f4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", - "reference": "5da5f67fc95621df9ff4c4e5a84d6a8a2acf7c28", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/a47af19f93f76aa3368303d752aa5272ca3299f4", + "reference": "a47af19f93f76aa3368303d752aa5272ca3299f4", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.4" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^13.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -734,40 +712,53 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-text-template/issues", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/2.0.4" + "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/6.0.0" }, "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/phpunit/php-text-template", + "type": "tidelift" } ], - "time": "2020-10-26T05:33:50+00:00" + "time": "2026-02-06T04:36:37+00:00" }, { "name": "phpunit/php-timer", - "version": "5.0.3", + "version": "9.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2" + "reference": "a0e12065831f6ab0d83120dc61513eb8d9a966f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", - "reference": "5a63ce20ed1b5bf577850e2c4e87f4aa902afbd2", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/a0e12065831f6ab0d83120dc61513eb8d9a966f6", + "reference": "a0e12065831f6ab0d83120dc61513eb8d9a966f6", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.4" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^13.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-main": "9.0-dev" } }, "autoload": { @@ -793,62 +784,72 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-timer/issues", - "source": "https://github.com/sebastianbergmann/php-timer/tree/5.0.3" + "security": "https://github.com/sebastianbergmann/php-timer/security/policy", + "source": "https://github.com/sebastianbergmann/php-timer/tree/9.0.0" }, "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/phpunit/php-timer", + "type": "tidelift" } ], - "time": "2020-10-26T13:16:10+00:00" + "time": "2026-02-06T04:37:53+00:00" }, { "name": "phpunit/phpunit", - "version": "9.6.15", + "version": "13.2.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "05017b80304e0eb3f31d90194a563fd53a6021f1" + "reference": "60da0ff1e10a0f72ee18a24117ec3b613a346bba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/05017b80304e0eb3f31d90194a563fd53a6021f1", - "reference": "05017b80304e0eb3f31d90194a563fd53a6021f1", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/60da0ff1e10a0f72ee18a24117ec3b613a346bba", + "reference": "60da0ff1e10a0f72ee18a24117ec3b613a346bba", "shasum": "" }, "require": { - "doctrine/instantiator": "^1.3.1 || ^2", "ext-dom": "*", "ext-json": "*", "ext-libxml": "*", "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.10.1", - "phar-io/manifest": "^2.0.3", - "phar-io/version": "^3.0.2", - "php": ">=7.3", - "phpunit/php-code-coverage": "^9.2.28", - "phpunit/php-file-iterator": "^3.0.5", - "phpunit/php-invoker": "^3.1.1", - "phpunit/php-text-template": "^2.0.3", - "phpunit/php-timer": "^5.0.2", - "sebastian/cli-parser": "^1.0.1", - "sebastian/code-unit": "^1.0.6", - "sebastian/comparator": "^4.0.8", - "sebastian/diff": "^4.0.3", - "sebastian/environment": "^5.1.3", - "sebastian/exporter": "^4.0.5", - "sebastian/global-state": "^5.0.1", - "sebastian/object-enumerator": "^4.0.3", - "sebastian/resource-operations": "^3.0.3", - "sebastian/type": "^3.2", - "sebastian/version": "^3.0.2" - }, - "suggest": { - "ext-soap": "To be able to generate mocks based on WSDL files", - "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + "myclabs/deep-copy": "^1.13.4", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", + "php": ">=8.4.1", + "phpunit/php-code-coverage": "^14.2.2", + "phpunit/php-file-iterator": "^7.0.0", + "phpunit/php-invoker": "^7.0.0", + "phpunit/php-text-template": "^6.0.0", + "phpunit/php-timer": "^9.0.0", + "sebastian/cli-parser": "^5.0.0", + "sebastian/comparator": "^8.3.0", + "sebastian/diff": "^9.0", + "sebastian/environment": "^9.3.2", + "sebastian/exporter": "^8.1.0", + "sebastian/file-filter": "^1.0", + "sebastian/git-state": "^1.0", + "sebastian/global-state": "^9.0.1", + "sebastian/object-enumerator": "^8.0.0", + "sebastian/recursion-context": "^8.0.0", + "sebastian/type": "^7.0.1", + "sebastian/version": "^7.0.0", + "staabm/side-effects-detector": "^1.0.5" }, "bin": [ "phpunit" @@ -856,7 +857,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "9.6-dev" + "dev-main": "13.2-dev" } }, "autoload": { @@ -888,48 +889,40 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.15" + "source": "https://github.com/sebastianbergmann/phpunit/tree/13.2.1" }, "funding": [ { - "url": "https://phpunit.de/sponsors.html", - "type": "custom" - }, - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", - "type": "tidelift" + "url": "https://phpunit.de/sponsoring.html", + "type": "other" } ], - "time": "2023-12-01T16:55:19+00:00" + "time": "2026-06-15T13:14:22+00:00" }, { "name": "sebastian/cli-parser", - "version": "1.0.1", + "version": "5.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2" + "reference": "48a4654fa5e48c1c81214e9930048a572d4b23ca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/442e7c7e687e42adc03470c7b668bc4b2402c0b2", - "reference": "442e7c7e687e42adc03470c7b668bc4b2402c0b2", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/48a4654fa5e48c1c81214e9930048a572d4b23ca", + "reference": "48a4654fa5e48c1c81214e9930048a572d4b23ca", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.4" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^13.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -952,153 +945,60 @@ "homepage": "https://github.com/sebastianbergmann/cli-parser", "support": { "issues": "https://github.com/sebastianbergmann/cli-parser/issues", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/1.0.1" + "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/5.0.0" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" - } - ], - "time": "2020-09-28T06:08:49+00:00" - }, - { - "name": "sebastian/code-unit", - "version": "1.0.8", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit.git", - "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/1fc9f64c0927627ef78ba436c9b17d967e68e120", - "reference": "1fc9f64c0927627ef78ba436c9b17d967e68e120", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.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": "Collection of value objects that represent the PHP code units", - "homepage": "https://github.com/sebastianbergmann/code-unit", - "support": { - "issues": "https://github.com/sebastianbergmann/code-unit/issues", - "source": "https://github.com/sebastianbergmann/code-unit/tree/1.0.8" - }, - "funding": [ + }, { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2020-10-26T13:08:54+00:00" - }, - { - "name": "sebastian/code-unit-reverse-lookup", - "version": "2.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", - "reference": "ac91f01ccec49fb77bdc6fd1e548bc70f7faa3e5", - "shasum": "" - }, - "require": { - "php": ">=7.3" - }, - "require-dev": { - "phpunit/phpunit": "^9.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Looks up which function or method a line of code belongs to", - "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "support": { - "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", - "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/2.0.3" - }, - "funding": [ + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, { - "url": "https://github.com/sebastianbergmann", - "type": "github" + "url": "https://tidelift.com/funding/github/packagist/sebastian/cli-parser", + "type": "tidelift" } ], - "time": "2020-09-28T05:30:19+00:00" + "time": "2026-02-06T04:39:44+00:00" }, { "name": "sebastian/comparator", - "version": "4.0.8", + "version": "8.3.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a" + "reference": "c025fc7604afab3f195fab7cdaf72327331af241" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/fa0f136dd2334583309d32b62544682ee972b51a", - "reference": "fa0f136dd2334583309d32b62544682ee972b51a", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/c025fc7604afab3f195fab7cdaf72327331af241", + "reference": "c025fc7604afab3f195fab7cdaf72327331af241", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/diff": "^4.0", - "sebastian/exporter": "^4.0" + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=8.4", + "sebastian/diff": "^9.0", + "sebastian/exporter": "^8.1.0" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^13.2" + }, + "suggest": { + "ext-bcmath": "For comparing BcMath\\Number objects" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-main": "8.3-dev" } }, "autoload": { @@ -1137,41 +1037,54 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/4.0.8" + "security": "https://github.com/sebastianbergmann/comparator/security/policy", + "source": "https://github.com/sebastianbergmann/comparator/tree/8.3.0" }, "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/comparator", + "type": "tidelift" } ], - "time": "2022-09-14T12:41:17+00:00" + "time": "2026-06-05T03:06:45+00:00" }, { "name": "sebastian/complexity", - "version": "2.0.3", + "version": "6.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a" + "reference": "c5651c795c98093480df79350cb050813fc7a2f3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/25f207c40d62b8b7aa32f5ab026c53561964053a", - "reference": "25f207c40d62b8b7aa32f5ab026c53561964053a", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/c5651c795c98093480df79350cb050813fc7a2f3", + "reference": "c5651c795c98093480df79350cb050813fc7a2f3", "shasum": "" }, "require": { - "nikic/php-parser": "^4.18 || ^5.0", - "php": ">=7.3" + "nikic/php-parser": "^5.0", + "php": ">=8.4" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^13.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -1194,41 +1107,54 @@ "homepage": "https://github.com/sebastianbergmann/complexity", "support": { "issues": "https://github.com/sebastianbergmann/complexity/issues", - "source": "https://github.com/sebastianbergmann/complexity/tree/2.0.3" + "security": "https://github.com/sebastianbergmann/complexity/security/policy", + "source": "https://github.com/sebastianbergmann/complexity/tree/6.0.0" }, "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/complexity", + "type": "tidelift" } ], - "time": "2023-12-22T06:19:30+00:00" + "time": "2026-02-06T04:41:32+00:00" }, { "name": "sebastian/diff", - "version": "4.0.5", + "version": "9.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131" + "reference": "a3fb6a298a265ff487a91bbea46e03cd01dbb226" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/74be17022044ebaaecfdf0c5cd504fc9cd5a7131", - "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/a3fb6a298a265ff487a91bbea46e03cd01dbb226", + "reference": "a3fb6a298a265ff487a91bbea46e03cd01dbb226", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.4" }, "require-dev": { - "phpunit/phpunit": "^9.3", - "symfony/process": "^4.2 || ^5" + "phpunit/phpunit": "^13.2", + "symfony/process": "^7.4.13" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-main": "9.0-dev" } }, "autoload": { @@ -1260,35 +1186,48 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/4.0.5" + "security": "https://github.com/sebastianbergmann/diff/security/policy", + "source": "https://github.com/sebastianbergmann/diff/tree/9.0.0" }, "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/diff", + "type": "tidelift" } ], - "time": "2023-05-07T05:35:17+00:00" + "time": "2026-06-05T03:04:51+00:00" }, { "name": "sebastian/environment", - "version": "5.1.5", + "version": "9.3.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed" + "reference": "6c9e487c9eb706a8d258102a1c0b0a3e53e86c2e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", - "reference": "830c43a844f1f8d5b7a1f6d6076b784454d8b7ed", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/6c9e487c9eb706a8d258102a1c0b0a3e53e86c2e", + "reference": "6c9e487c9eb706a8d258102a1c0b0a3e53e86c2e", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.4" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^13.1.11" }, "suggest": { "ext-posix": "*" @@ -1296,7 +1235,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "5.1-dev" + "dev-main": "9.3-dev" } }, "autoload": { @@ -1315,7 +1254,7 @@ } ], "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "http://www.github.com/sebastianbergmann/environment", + "homepage": "https://github.com/sebastianbergmann/environment", "keywords": [ "Xdebug", "environment", @@ -1323,42 +1262,55 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/5.1.5" + "security": "https://github.com/sebastianbergmann/environment/security/policy", + "source": "https://github.com/sebastianbergmann/environment/tree/9.3.2" }, "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/environment", + "type": "tidelift" } ], - "time": "2023-02-03T06:03:51+00:00" + "time": "2026-05-25T13:41:38+00:00" }, { "name": "sebastian/exporter", - "version": "4.0.5", + "version": "8.1.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d" + "reference": "c0d29a945f8cf82f300a05e69874508e307ca4c6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", - "reference": "ac230ed27f0f98f597c8a2b6eb7ac563af5e5b9d", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/c0d29a945f8cf82f300a05e69874508e307ca4c6", + "reference": "c0d29a945f8cf82f300a05e69874508e307ca4c6", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/recursion-context": "^4.0" + "ext-mbstring": "*", + "php": ">=8.4", + "sebastian/recursion-context": "^8.0" }, "require-dev": { - "ext-mbstring": "*", - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^13.1.10" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-main": "8.1-dev" } }, "autoload": { @@ -1400,46 +1352,53 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.5" + "security": "https://github.com/sebastianbergmann/exporter/security/policy", + "source": "https://github.com/sebastianbergmann/exporter/tree/8.1.0" }, "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/exporter", + "type": "tidelift" } ], - "time": "2022-09-14T06:03:37+00:00" + "time": "2026-05-21T11:50:56+00:00" }, { - "name": "sebastian/global-state", - "version": "5.0.6", + "name": "sebastian/file-filter", + "version": "1.0.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "bde739e7565280bda77be70044ac1047bc007e34" + "url": "https://github.com/sebastianbergmann/file-filter.git", + "reference": "33a26f394330f6faa7684bb9cc73afb7727aae93" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bde739e7565280bda77be70044ac1047bc007e34", - "reference": "bde739e7565280bda77be70044ac1047bc007e34", + "url": "https://api.github.com/repos/sebastianbergmann/file-filter/zipball/33a26f394330f6faa7684bb9cc73afb7727aae93", + "reference": "33a26f394330f6faa7684bb9cc73afb7727aae93", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/object-reflector": "^2.0", - "sebastian/recursion-context": "^4.0" + "php": ">=8.4" }, "require-dev": { - "ext-dom": "*", - "phpunit/phpunit": "^9.3" - }, - "suggest": { - "ext-uopz": "*" + "phpunit/phpunit": "^13.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-main": "1.0-dev" } }, "autoload": { @@ -1454,51 +1413,61 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Snapshotting of global state", - "homepage": "http://www.github.com/sebastianbergmann/global-state", - "keywords": [ - "global state" - ], + "description": "Library for filtering files", + "homepage": "https://github.com/sebastianbergmann/file-filter", "support": { - "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.6" + "issues": "https://github.com/sebastianbergmann/file-filter/issues", + "security": "https://github.com/sebastianbergmann/file-filter/security/policy", + "source": "https://github.com/sebastianbergmann/file-filter/tree/1.0.0" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" - } - ], - "time": "2023-08-02T09:26:13+00:00" - }, - { - "name": "sebastian/lines-of-code", - "version": "1.0.4", + }, + { + "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/file-filter", + "type": "tidelift" + } + ], + "time": "2026-04-22T07:20:04+00:00" + }, + { + "name": "sebastian/git-state", + "version": "1.0.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5" + "url": "https://github.com/sebastianbergmann/git-state.git", + "reference": "792a952e0eba55b6960a48aeceb9f371aad1f76b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/e1e4a170560925c26d424b6a03aed157e7dcc5c5", - "reference": "e1e4a170560925c26d424b6a03aed157e7dcc5c5", + "url": "https://api.github.com/repos/sebastianbergmann/git-state/zipball/792a952e0eba55b6960a48aeceb9f371aad1f76b", + "reference": "792a952e0eba55b6960a48aeceb9f371aad1f76b", "shasum": "" }, "require": { - "nikic/php-parser": "^4.18 || ^5.0", - "php": ">=7.3" + "php": ">=8.4" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^13.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-main": "1.0-dev" } }, "autoload": { @@ -1517,46 +1486,60 @@ "role": "lead" } ], - "description": "Library for counting the lines of code in PHP source code", - "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "description": "Library for describing the state of a Git checkout", + "homepage": "https://github.com/sebastianbergmann/git-state", "support": { - "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/1.0.4" + "issues": "https://github.com/sebastianbergmann/git-state/issues", + "security": "https://github.com/sebastianbergmann/git-state/security/policy", + "source": "https://github.com/sebastianbergmann/git-state/tree/1.0.0" }, "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/git-state", + "type": "tidelift" } ], - "time": "2023-12-22T06:20:34+00:00" + "time": "2026-03-21T12:54:28+00:00" }, { - "name": "sebastian/object-enumerator", - "version": "4.0.4", + "name": "sebastian/global-state", + "version": "9.0.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "5c9eeac41b290a3712d88851518825ad78f45c71" + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "ba68ba79da690cf7eddefd3ce5b78b20b9ba9945" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71", - "reference": "5c9eeac41b290a3712d88851518825ad78f45c71", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/ba68ba79da690cf7eddefd3ce5b78b20b9ba9945", + "reference": "ba68ba79da690cf7eddefd3ce5b78b20b9ba9945", "shasum": "" }, "require": { - "php": ">=7.3", - "sebastian/object-reflector": "^2.0", - "sebastian/recursion-context": "^4.0" + "php": ">=8.4", + "sebastian/object-reflector": "^6.0", + "sebastian/recursion-context": "^8.0" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "ext-dom": "*", + "phpunit/phpunit": "^13.1.13" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-main": "9.0-dev" } }, "autoload": { @@ -1574,44 +1557,61 @@ "email": "sebastian@phpunit.de" } ], - "description": "Traverses array structures and object graphs to enumerate all referenced objects", - "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "description": "Snapshotting of global state", + "homepage": "https://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], "support": { - "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/4.0.4" + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "security": "https://github.com/sebastianbergmann/global-state/security/policy", + "source": "https://github.com/sebastianbergmann/global-state/tree/9.0.1" }, "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/global-state", + "type": "tidelift" } ], - "time": "2020-10-26T13:12:34+00:00" + "time": "2026-06-01T15:11:33+00:00" }, { - "name": "sebastian/object-reflector", - "version": "2.0.4", + "name": "sebastian/lines-of-code", + "version": "5.0.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7" + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "d2cff273a90c79b0eb590baa682d4b5c318bdbb7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", - "reference": "b4f479ebdbf63ac605d183ece17d8d7fe49c15c7", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/d2cff273a90c79b0eb590baa682d4b5c318bdbb7", + "reference": "d2cff273a90c79b0eb590baa682d4b5c318bdbb7", "shasum": "" }, "require": { - "php": ">=7.3" + "nikic/php-parser": "^5.7.0", + "php": ">=8.4" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^13.1.10" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -1626,47 +1626,63 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Allows reflection of object attributes, including inherited and non-public ones", - "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", "support": { - "issues": "https://github.com/sebastianbergmann/object-reflector/issues", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/2.0.4" + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/5.0.1" }, "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/lines-of-code", + "type": "tidelift" } ], - "time": "2020-10-26T13:14:26+00:00" + "time": "2026-05-19T16:23:37+00:00" }, { - "name": "sebastian/recursion-context", - "version": "4.0.5", + "name": "sebastian/object-enumerator", + "version": "8.0.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1" + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "b39ab125fd9a7434b0ecbc4202eebce11a98cfc5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", - "reference": "e75bd0f07204fec2a0af9b0f3cfe97d05f92efc1", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/b39ab125fd9a7434b0ecbc4202eebce11a98cfc5", + "reference": "b39ab125fd9a7434b0ecbc4202eebce11a98cfc5", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.4", + "sebastian/object-reflector": "^6.0", + "sebastian/recursion-context": "^8.0" }, "require-dev": { - "phpunit/phpunit": "^9.3" + "phpunit/phpunit": "^13.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "4.0-dev" + "dev-main": "8.0-dev" } }, "autoload": { @@ -1682,54 +1698,127 @@ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/8.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" }, { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" }, { - "name": "Adam Harvey", - "email": "aharvey@php.net" + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/object-enumerator", + "type": "tidelift" } ], - "description": "Provides functionality to recursively process PHP variables", - "homepage": "https://github.com/sebastianbergmann/recursion-context", + "time": "2026-02-06T04:46:36+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "6.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "3ca042c2c60b0eab094f8a1b6a7093f4d4c72200" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/3ca042c2c60b0eab094f8a1b6a7093f4d4c72200", + "reference": "3ca042c2c60b0eab094f8a1b6a7093f4d4c72200", + "shasum": "" + }, + "require": { + "php": ">=8.4" + }, + "require-dev": { + "phpunit/phpunit": "^13.0" + }, + "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" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", "support": { - "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/4.0.5" + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "security": "https://github.com/sebastianbergmann/object-reflector/security/policy", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/6.0.0" }, "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/object-reflector", + "type": "tidelift" } ], - "time": "2023-02-03T06:07:39+00:00" + "time": "2026-02-06T04:47:13+00:00" }, { - "name": "sebastian/resource-operations", - "version": "3.0.3", + "name": "sebastian/recursion-context", + "version": "8.0.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/resource-operations.git", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8" + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "74c5af21f6a5833e91767ca068c4d3dfec15317e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", - "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/74c5af21f6a5833e91767ca068c4d3dfec15317e", + "reference": "74c5af21f6a5833e91767ca068c4d3dfec15317e", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.4" }, "require-dev": { - "phpunit/phpunit": "^9.0" + "phpunit/phpunit": "^13.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-main": "8.0-dev" } }, "autoload": { @@ -1745,46 +1834,67 @@ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" } ], - "description": "Provides a list of PHP built-in functions that operate on resources", - "homepage": "https://www.github.com/sebastianbergmann/resource-operations", + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", "support": { - "issues": "https://github.com/sebastianbergmann/resource-operations/issues", - "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3" + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/8.0.0" }, "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/recursion-context", + "type": "tidelift" } ], - "time": "2020-09-28T06:45:17+00:00" + "time": "2026-02-06T04:51:28+00:00" }, { "name": "sebastian/type", - "version": "3.2.1", + "version": "7.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/type.git", - "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7" + "reference": "fee0309275847fefd7636167085e379c1dbf6990" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", - "reference": "75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/fee0309275847fefd7636167085e379c1dbf6990", + "reference": "fee0309275847fefd7636167085e379c1dbf6990", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.4" }, "require-dev": { - "phpunit/phpunit": "^9.5" + "phpunit/phpunit": "^13.1.10" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.2-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -1807,37 +1917,50 @@ "homepage": "https://github.com/sebastianbergmann/type", "support": { "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/3.2.1" + "security": "https://github.com/sebastianbergmann/type/security/policy", + "source": "https://github.com/sebastianbergmann/type/tree/7.0.1" }, "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": "2023-02-03T06:13:03+00:00" + "time": "2026-05-20T06:49:11+00:00" }, { "name": "sebastian/version", - "version": "3.0.2", + "version": "7.0.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/version.git", - "reference": "c6c1022351a901512170118436c764e473f6de8c" + "reference": "ad37a5552c8e2b88572249fdc19b6da7792e021b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c", - "reference": "c6c1022351a901512170118436c764e473f6de8c", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/ad37a5552c8e2b88572249fdc19b6da7792e021b", + "reference": "ad37a5552c8e2b88572249fdc19b6da7792e021b", "shasum": "" }, "require": { - "php": ">=7.3" + "php": ">=8.4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.0-dev" + "dev-main": "7.0-dev" } }, "autoload": { @@ -1860,35 +1983,100 @@ "homepage": "https://github.com/sebastianbergmann/version", "support": { "issues": "https://github.com/sebastianbergmann/version/issues", - "source": "https://github.com/sebastianbergmann/version/tree/3.0.2" + "security": "https://github.com/sebastianbergmann/version/security/policy", + "source": "https://github.com/sebastianbergmann/version/tree/7.0.0" }, "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/version", + "type": "tidelift" + } + ], + "time": "2026-02-06T04:52:52+00:00" + }, + { + "name": "staabm/side-effects-detector", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/staabm/side-effects-detector.git", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/staabm/side-effects-detector/zipball/d8334211a140ce329c13726d4a715adbddd0a163", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^1.12.6", + "phpunit/phpunit": "^9.6.21", + "symfony/var-dumper": "^5.4.43", + "tomasvotruba/type-coverage": "1.0.0", + "tomasvotruba/unused-public": "1.0.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "lib/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A static analysis tool to detect side effects in PHP code", + "keywords": [ + "static analysis" + ], + "support": { + "issues": "https://github.com/staabm/side-effects-detector/issues", + "source": "https://github.com/staabm/side-effects-detector/tree/1.0.5" + }, + "funding": [ + { + "url": "https://github.com/staabm", + "type": "github" } ], - "time": "2020-09-28T06:39:44+00:00" + "time": "2024-10-20T05:08:20+00:00" }, { "name": "theseer/tokenizer", - "version": "1.2.2", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/theseer/tokenizer.git", - "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96" + "reference": "7989e43bf381af0eac72e4f0ca5bcbfa81658be4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b2ad5003ca10d4ee50a12da31de12a5774ba6b96", - "reference": "b2ad5003ca10d4ee50a12da31de12a5774ba6b96", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/7989e43bf381af0eac72e4f0ca5bcbfa81658be4", + "reference": "7989e43bf381af0eac72e4f0ca5bcbfa81658be4", "shasum": "" }, "require": { "ext-dom": "*", "ext-tokenizer": "*", "ext-xmlwriter": "*", - "php": "^7.2 || ^8.0" + "php": "^8.1" }, "type": "library", "autoload": { @@ -1910,7 +2098,7 @@ "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "support": { "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.2" + "source": "https://github.com/theseer/tokenizer/tree/2.0.1" }, "funding": [ { @@ -1918,43 +2106,49 @@ "type": "github" } ], - "time": "2023-11-20T00:12:19+00:00" + "time": "2025-12-08T11:19:18+00:00" } ], "packages-dev": [ { "name": "behat/behat", - "version": "v3.13.0", + "version": "v3.32.0", "source": { "type": "git", "url": "https://github.com/Behat/Behat.git", - "reference": "9dd7cdb309e464ddeab095cd1a5151c2dccba4ab" + "reference": "b9b89cf7a3b24c04d6e2d2865ed51bc5e1700de9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Behat/Behat/zipball/9dd7cdb309e464ddeab095cd1a5151c2dccba4ab", - "reference": "9dd7cdb309e464ddeab095cd1a5151c2dccba4ab", + "url": "https://api.github.com/repos/Behat/Behat/zipball/b9b89cf7a3b24c04d6e2d2865ed51bc5e1700de9", + "reference": "b9b89cf7a3b24c04d6e2d2865ed51bc5e1700de9", "shasum": "" }, "require": { - "behat/gherkin": "^4.9.0", - "behat/transliterator": "^1.2", + "behat/gherkin": "^4.17.0", + "composer-runtime-api": "^2.2", + "composer/xdebug-handler": "^1.4 || ^2.0 || ^3.0", "ext-mbstring": "*", - "php": "^7.2 || ^8.0", + "nikic/php-parser": "^4.19.2 || ^5.2", + "php": ">=8.2 <8.6", "psr/container": "^1.0 || ^2.0", - "symfony/config": "^4.4 || ^5.0 || ^6.0", - "symfony/console": "^4.4 || ^5.0 || ^6.0", - "symfony/dependency-injection": "^4.4 || ^5.0 || ^6.0", - "symfony/event-dispatcher": "^4.4 || ^5.0 || ^6.0", - "symfony/translation": "^4.4 || ^5.0 || ^6.0", - "symfony/yaml": "^4.4 || ^5.0 || ^6.0" + "symfony/config": "^5.4 || ^6.4 || ^7.0", + "symfony/console": "^5.4.9 || ^6.4 || ^7.0", + "symfony/dependency-injection": "^5.4 || ^6.4 || ^7.0", + "symfony/event-dispatcher": "^5.4 || ^6.4 || ^7.0", + "symfony/translation": "^5.4 || ^6.4 || ^7.0", + "symfony/yaml": "^5.4 || ^6.4 || ^7.0" }, "require-dev": { - "herrera-io/box": "~1.6.1", - "phpspec/prophecy": "^1.15", - "phpunit/phpunit": "^8.5 || ^9.0", - "symfony/process": "^4.4 || ^5.0 || ^6.0", - "vimeo/psalm": "^4.8" + "opis/json-schema": "^2.5", + "php-cs-fixer/shim": "^3.89", + "phpstan/phpstan": "2.1.46", + "phpunit/phpunit": "^9.6", + "rector/rector": "2.3.9", + "sebastian/diff": "^4.0", + "symfony/filesystem": "^5.4 || ^6.4 || ^7.0", + "symfony/polyfill-php84": "^1.31", + "symfony/process": "^5.4 || ^6.4 || ^7.0" }, "suggest": { "ext-dom": "Needed to output test results in JUnit format." @@ -1963,17 +2157,14 @@ "bin/behat" ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } - }, "autoload": { "psr-4": { "Behat\\Hook\\": "src/Behat/Hook/", "Behat\\Step\\": "src/Behat/Step/", "Behat\\Behat\\": "src/Behat/Behat/", - "Behat\\Testwork\\": "src/Behat/Testwork/" + "Behat\\Config\\": "src/Behat/Config/", + "Behat\\Testwork\\": "src/Behat/Testwork/", + "Behat\\Transformation\\": "src/Behat/Transformation/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1988,7 +2179,7 @@ } ], "description": "Scenario-oriented BDD framework for PHP", - "homepage": "http://behat.org/", + "homepage": "https://behat.org/", "keywords": [ "Agile", "BDD", @@ -2005,31 +2196,51 @@ ], "support": { "issues": "https://github.com/Behat/Behat/issues", - "source": "https://github.com/Behat/Behat/tree/v3.13.0" + "source": "https://github.com/Behat/Behat/tree/v3.32.0" }, - "time": "2023-04-18T15:40:53+00:00" + "funding": [ + { + "url": "https://github.com/acoulton", + "type": "github" + }, + { + "url": "https://github.com/carlos-granados", + "type": "github" + }, + { + "url": "https://github.com/stof", + "type": "github" + } + ], + "time": "2026-06-20T08:34:52+00:00" }, { "name": "behat/gherkin", - "version": "v4.9.0", + "version": "v4.17.0", "source": { "type": "git", "url": "https://github.com/Behat/Gherkin.git", - "reference": "0bc8d1e30e96183e4f36db9dc79caead300beff4" + "reference": "5c8b3149fac39b5a79942b64eeec59a5ee4001c0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Behat/Gherkin/zipball/0bc8d1e30e96183e4f36db9dc79caead300beff4", - "reference": "0bc8d1e30e96183e4f36db9dc79caead300beff4", + "url": "https://api.github.com/repos/Behat/Gherkin/zipball/5c8b3149fac39b5a79942b64eeec59a5ee4001c0", + "reference": "5c8b3149fac39b5a79942b64eeec59a5ee4001c0", "shasum": "" }, "require": { - "php": "~7.2|~8.0" + "composer-runtime-api": "^2.2", + "php": ">=8.1 <8.6" }, "require-dev": { - "cucumber/cucumber": "dev-gherkin-22.0.0", - "phpunit/phpunit": "~8|~9", - "symfony/yaml": "~3|~4|~5" + "cucumber/gherkin-monorepo": "dev-gherkin-v39.1.0", + "friendsofphp/php-cs-fixer": "^3.77", + "mikey179/vfsstream": "^1.6", + "phpstan/extension-installer": "^1", + "phpstan/phpstan": "^2", + "phpstan/phpstan-phpunit": "^2", + "phpunit/phpunit": "^10.5", + "symfony/yaml": "^5.4 || ^6.4 || ^7.0" }, "suggest": { "symfony/yaml": "If you want to parse features, represented in YAML files" @@ -2041,8 +2252,8 @@ } }, "autoload": { - "psr-0": { - "Behat\\Gherkin": "src/" + "psr-4": { + "Behat\\Gherkin\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -2053,11 +2264,11 @@ { "name": "Konstantin Kudryashov", "email": "ever.zet@gmail.com", - "homepage": "http://everzet.com" + "homepage": "https://everzet.com" } ], "description": "Gherkin DSL parser for PHP", - "homepage": "http://behat.org/", + "homepage": "https://behat.org/", "keywords": [ "BDD", "Behat", @@ -2068,83 +2279,121 @@ ], "support": { "issues": "https://github.com/Behat/Gherkin/issues", - "source": "https://github.com/Behat/Gherkin/tree/v4.9.0" + "source": "https://github.com/Behat/Gherkin/tree/v4.17.0" }, - "time": "2021-10-12T13:05:09+00:00" + "funding": [ + { + "url": "https://github.com/acoulton", + "type": "github" + }, + { + "url": "https://github.com/carlos-granados", + "type": "github" + }, + { + "url": "https://github.com/stof", + "type": "github" + } + ], + "time": "2026-05-18T09:33:47+00:00" }, { - "name": "behat/transliterator", - "version": "v1.5.0", + "name": "clue/ndjson-react", + "version": "v1.3.0", "source": { "type": "git", - "url": "https://github.com/Behat/Transliterator.git", - "reference": "baac5873bac3749887d28ab68e2f74db3a4408af" + "url": "https://github.com/clue/reactphp-ndjson.git", + "reference": "392dc165fce93b5bb5c637b67e59619223c931b0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Behat/Transliterator/zipball/baac5873bac3749887d28ab68e2f74db3a4408af", - "reference": "baac5873bac3749887d28ab68e2f74db3a4408af", + "url": "https://api.github.com/repos/clue/reactphp-ndjson/zipball/392dc165fce93b5bb5c637b67e59619223c931b0", + "reference": "392dc165fce93b5bb5c637b67e59619223c931b0", "shasum": "" }, "require": { - "php": ">=7.2" + "php": ">=5.3", + "react/stream": "^1.2" }, "require-dev": { - "chuyskywalker/rolling-curl": "^3.1", - "php-yaoi/php-yaoi": "^1.0", - "phpunit/phpunit": "^8.5.25 || ^9.5.19" + "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35", + "react/event-loop": "^1.2" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, "autoload": { "psr-4": { - "Behat\\Transliterator\\": "src/Behat/Transliterator" + "Clue\\React\\NDJson\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "Artistic-1.0" + "MIT" + ], + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering" + } ], - "description": "String transliterator", + "description": "Streaming newline-delimited JSON (NDJSON) parser and encoder for ReactPHP.", + "homepage": "https://github.com/clue/reactphp-ndjson", "keywords": [ - "i18n", - "slug", - "transliterator" + "NDJSON", + "json", + "jsonlines", + "newline", + "reactphp", + "streaming" ], "support": { - "issues": "https://github.com/Behat/Transliterator/issues", - "source": "https://github.com/Behat/Transliterator/tree/v1.5.0" + "issues": "https://github.com/clue/reactphp-ndjson/issues", + "source": "https://github.com/clue/reactphp-ndjson/tree/v1.3.0" }, - "time": "2022-03-30T09:27:43+00:00" + "funding": [ + { + "url": "https://clue.engineering/support", + "type": "custom" + }, + { + "url": "https://github.com/clue", + "type": "github" + } + ], + "time": "2022-12-23T10:58:28+00:00" }, { "name": "composer/pcre", - "version": "3.1.1", + "version": "3.4.0", "source": { "type": "git", "url": "https://github.com/composer/pcre.git", - "reference": "00104306927c7a0919b4ced2aaa6782c1e61a3c9" + "reference": "d5a341b3fb61f3001970940afb1d332968a183ed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/pcre/zipball/00104306927c7a0919b4ced2aaa6782c1e61a3c9", - "reference": "00104306927c7a0919b4ced2aaa6782c1e61a3c9", + "url": "https://api.github.com/repos/composer/pcre/zipball/d5a341b3fb61f3001970940afb1d332968a183ed", + "reference": "d5a341b3fb61f3001970940afb1d332968a183ed", "shasum": "" }, "require": { "php": "^7.4 || ^8.0" }, + "conflict": { + "phpstan/phpstan": "<2.2.2" + }, "require-dev": { - "phpstan/phpstan": "^1.3", - "phpstan/phpstan-strict-rules": "^1.1", - "symfony/phpunit-bridge": "^5" + "phpstan/phpstan": "^2", + "phpstan/phpstan-deprecation-rules": "^2", + "phpstan/phpstan-strict-rules": "^2", + "phpunit/phpunit": "^9" }, "type": "library", "extra": { + "phpstan": { + "includes": [ + "extension.neon" + ] + }, "branch-alias": { "dev-main": "3.x-dev" } @@ -2174,7 +2423,7 @@ ], "support": { "issues": "https://github.com/composer/pcre/issues", - "source": "https://github.com/composer/pcre/tree/3.1.1" + "source": "https://github.com/composer/pcre/tree/3.4.0" }, "funding": [ { @@ -2184,34 +2433,30 @@ { "url": "https://github.com/composer", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" } ], - "time": "2023-10-11T07:11:09+00:00" + "time": "2026-06-07T11:47:49+00:00" }, { "name": "composer/semver", - "version": "3.4.0", + "version": "3.4.4", "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32" + "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/35e8d0af4486141bc745f23a29cc2091eb624a32", - "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32", + "url": "https://api.github.com/repos/composer/semver/zipball/198166618906cb2de69b95d7d47e5fa8aa1b2b95", + "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95", "shasum": "" }, "require": { "php": "^5.3.2 || ^7.0 || ^8.0" }, "require-dev": { - "phpstan/phpstan": "^1.4", - "symfony/phpunit-bridge": "^4.2 || ^5" + "phpstan/phpstan": "^1.11", + "symfony/phpunit-bridge": "^3 || ^7" }, "type": "library", "extra": { @@ -2255,7 +2500,7 @@ "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" + "source": "https://github.com/composer/semver/tree/3.4.4" }, "funding": [ { @@ -2265,26 +2510,22 @@ { "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" + "time": "2025-08-20T19:15:30+00:00" }, { "name": "composer/xdebug-handler", - "version": "3.0.3", + "version": "3.0.5", "source": { "type": "git", "url": "https://github.com/composer/xdebug-handler.git", - "reference": "ced299686f41dce890debac69273b47ffe98a40c" + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/ced299686f41dce890debac69273b47ffe98a40c", - "reference": "ced299686f41dce890debac69273b47ffe98a40c", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/6c1925561632e83d60a44492e0b344cf48ab85ef", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef", "shasum": "" }, "require": { @@ -2295,7 +2536,7 @@ "require-dev": { "phpstan/phpstan": "^1.0", "phpstan/phpstan-strict-rules": "^1.1", - "symfony/phpunit-bridge": "^6.0" + "phpunit/phpunit": "^8.5 || ^9.6 || ^10.5" }, "type": "library", "autoload": { @@ -2319,9 +2560,9 @@ "performance" ], "support": { - "irc": "irc://irc.freenode.org/composer", + "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.3" + "source": "https://github.com/composer/xdebug-handler/tree/3.0.5" }, "funding": [ { @@ -2337,39 +2578,42 @@ "type": "tidelift" } ], - "time": "2022-02-25T21:32:43+00:00" + "time": "2024-05-06T16:37:16+00:00" }, { "name": "dealerdirect/phpcodesniffer-composer-installer", - "version": "v0.7.2", + "version": "v1.2.1", "source": { "type": "git", - "url": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer.git", - "reference": "1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db" + "url": "https://github.com/PHPCSStandards/composer-installer.git", + "reference": "963f0c67bffde0eac41b56be71ac0e8ba132f0bd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Dealerdirect/phpcodesniffer-composer-installer/zipball/1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db", - "reference": "1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db", + "url": "https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/963f0c67bffde0eac41b56be71ac0e8ba132f0bd", + "reference": "963f0c67bffde0eac41b56be71ac0e8ba132f0bd", "shasum": "" }, "require": { - "composer-plugin-api": "^1.0 || ^2.0", - "php": ">=5.3", - "squizlabs/php_codesniffer": "^2.0 || ^3.1.0 || ^4.0" + "composer-plugin-api": "^2.2", + "php": ">=5.4", + "squizlabs/php_codesniffer": "^3.1.0 || ^4.0" }, "require-dev": { - "composer/composer": "*", - "php-parallel-lint/php-parallel-lint": "^1.3.1", - "phpcompatibility/php-compatibility": "^9.0" + "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": "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin" + "class": "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin" }, "autoload": { "psr-4": { - "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/" + "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -2379,17 +2623,16 @@ "authors": [ { "name": "Franck Nijhof", - "email": "franck.nijhof@dealerdirect.com", - "homepage": "http://www.frenck.nl", - "role": "Developer / IT Manager" + "email": "opensource@frenck.dev", + "homepage": "https://frenck.dev", + "role": "Open source developer" }, { "name": "Contributors", - "homepage": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer/graphs/contributors" + "homepage": "https://github.com/PHPCSStandards/composer-installer/graphs/contributors" } ], "description": "PHP_CodeSniffer Standards Composer Installer Plugin", - "homepage": "http://www.dealerdirect.com", "keywords": [ "PHPCodeSniffer", "PHP_CodeSniffer", @@ -2409,54 +2652,262 @@ "tests" ], "support": { - "issues": "https://github.com/dealerdirect/phpcodesniffer-composer-installer/issues", - "source": "https://github.com/dealerdirect/phpcodesniffer-composer-installer" + "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": "2026-05-06T08:26:05+00:00" + }, + { + "name": "ergebnis/agent-detector", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/ergebnis/agent-detector.git", + "reference": "e211f17928c8b95a51e06040792d57f5462fb271" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ergebnis/agent-detector/zipball/e211f17928c8b95a51e06040792d57f5462fb271", + "reference": "e211f17928c8b95a51e06040792d57f5462fb271", + "shasum": "" + }, + "require": { + "php": "~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0 || ~8.6.0" + }, + "require-dev": { + "ergebnis/composer-normalize": "^2.51.0", + "ergebnis/license": "^2.7.0", + "ergebnis/php-cs-fixer-config": "^6.60.2", + "ergebnis/phpstan-rules": "^2.13.1", + "ergebnis/phpunit-slow-test-detector": "^2.24.0", + "ergebnis/rector-rules": "^1.18.1", + "fakerphp/faker": "^1.24.1", + "infection/infection": "^0.26.6", + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^2.1.54", + "phpstan/phpstan-deprecation-rules": "^2.0.4", + "phpstan/phpstan-phpunit": "^2.0.16", + "phpstan/phpstan-strict-rules": "^2.0.10", + "phpunit/phpunit": "^9.6.34", + "rector/rector": "^2.4.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.2-dev" + }, + "composer-normalize": { + "indent-size": 2, + "indent-style": "space" + } + }, + "autoload": { + "psr-4": { + "Ergebnis\\AgentDetector\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Andreas Möller", + "email": "am@localheinz.com", + "homepage": "https://localheinz.com" + } + ], + "description": "Provides a detector for detecting the presence of an agent.", + "homepage": "https://github.com/ergebnis/agent-detector", + "support": { + "issues": "https://github.com/ergebnis/agent-detector/issues", + "security": "https://github.com/ergebnis/agent-detector/blob/main/.github/SECURITY.md", + "source": "https://github.com/ergebnis/agent-detector" + }, + "time": "2026-05-07T08:19:07+00:00" + }, + { + "name": "evenement/evenement", + "version": "v3.0.2", + "source": { + "type": "git", + "url": "https://github.com/igorw/evenement.git", + "reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/igorw/evenement/zipball/0a16b0d71ab13284339abb99d9d2bd813640efbc", + "reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc", + "shasum": "" + }, + "require": { + "php": ">=7.0" + }, + "require-dev": { + "phpunit/phpunit": "^9 || ^6" + }, + "type": "library", + "autoload": { + "psr-4": { + "Evenement\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" + } + ], + "description": "Événement is a very simple event dispatching library for PHP", + "keywords": [ + "event-dispatcher", + "event-emitter" + ], + "support": { + "issues": "https://github.com/igorw/evenement/issues", + "source": "https://github.com/igorw/evenement/tree/v3.0.2" }, - "time": "2022-02-04T12:51:07+00:00" + "time": "2023-08-08T05:53:35+00:00" + }, + { + "name": "fidry/cpu-core-counter", + "version": "1.3.0", + "source": { + "type": "git", + "url": "https://github.com/theofidry/cpu-core-counter.git", + "reference": "db9508f7b1474469d9d3c53b86f817e344732678" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/db9508f7b1474469d9d3c53b86f817e344732678", + "reference": "db9508f7b1474469d9d3c53b86f817e344732678", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "fidry/makefile": "^0.2.0", + "fidry/php-cs-fixer-config": "^1.1.2", + "phpstan/extension-installer": "^1.2.0", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-deprecation-rules": "^2.0.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^8.5.31 || ^9.5.26", + "webmozarts/strict-phpunit": "^7.5" + }, + "type": "library", + "autoload": { + "psr-4": { + "Fidry\\CpuCoreCounter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Théo FIDRY", + "email": "theo.fidry@gmail.com" + } + ], + "description": "Tiny utility to get the number of CPU cores.", + "keywords": [ + "CPU", + "core" + ], + "support": { + "issues": "https://github.com/theofidry/cpu-core-counter/issues", + "source": "https://github.com/theofidry/cpu-core-counter/tree/1.3.0" + }, + "funding": [ + { + "url": "https://github.com/theofidry", + "type": "github" + } + ], + "time": "2025-08-14T07:29:31+00:00" }, { "name": "friendsofphp/php-cs-fixer", - "version": "v3.42.0", + "version": "v3.95.10", "source": { "type": "git", "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", - "reference": "632ef1be3447a9b890bef06147475facee535d0f" + "reference": "93e1ab3e1f153024bd3ab23c8a349556063c6f17" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/632ef1be3447a9b890bef06147475facee535d0f", - "reference": "632ef1be3447a9b890bef06147475facee535d0f", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/93e1ab3e1f153024bd3ab23c8a349556063c6f17", + "reference": "93e1ab3e1f153024bd3ab23c8a349556063c6f17", "shasum": "" }, "require": { + "clue/ndjson-react": "^1.3", "composer/semver": "^3.4", - "composer/xdebug-handler": "^3.0.3", + "composer/xdebug-handler": "^3.0.5", + "ergebnis/agent-detector": "^1.2", + "ext-filter": "*", + "ext-hash": "*", "ext-json": "*", "ext-tokenizer": "*", + "fidry/cpu-core-counter": "^1.3", "php": "^7.4 || ^8.0", - "sebastian/diff": "^4.0 || ^5.0", - "symfony/console": "^5.4 || ^6.0 || ^7.0", - "symfony/event-dispatcher": "^5.4 || ^6.0 || ^7.0", - "symfony/filesystem": "^5.4 || ^6.0 || ^7.0", - "symfony/finder": "^5.4 || ^6.0 || ^7.0", - "symfony/options-resolver": "^5.4 || ^6.0 || ^7.0", - "symfony/polyfill-mbstring": "^1.28", - "symfony/polyfill-php80": "^1.28", - "symfony/polyfill-php81": "^1.28", - "symfony/process": "^5.4 || ^6.0 || ^7.0", - "symfony/stopwatch": "^5.4 || ^6.0 || ^7.0" + "react/child-process": "^0.6.6", + "react/event-loop": "^1.5", + "react/socket": "^1.16", + "react/stream": "^1.4", + "sebastian/diff": "^4.0.6 || ^5.1.1 || ^6.0.2 || ^7.0 || ^8.0 || ^9.0", + "symfony/console": "^5.4.47 || ^6.4.24 || ^7.0 || ^8.0", + "symfony/event-dispatcher": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0", + "symfony/filesystem": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0", + "symfony/finder": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0", + "symfony/options-resolver": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0", + "symfony/polyfill-mbstring": "^1.37", + "symfony/polyfill-php80": "^1.37", + "symfony/polyfill-php81": "^1.37", + "symfony/polyfill-php84": "^1.37", + "symfony/process": "^5.4.47 || ^6.4.24 || ^7.2 || ^8.0", + "symfony/stopwatch": "^5.4.45 || ^6.4.24 || ^7.0 || ^8.0" }, "require-dev": { - "facile-it/paraunit": "^1.3 || ^2.0", - "justinrainbow/json-schema": "^5.2", - "keradus/cli-executor": "^2.1", - "mikey179/vfsstream": "^1.6.11", - "php-coveralls/php-coveralls": "^2.7", - "php-cs-fixer/accessible-object": "^1.1", - "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.4", - "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.4", - "phpunit/phpunit": "^9.6", - "symfony/yaml": "^5.4 || ^6.0 || ^7.0" + "facile-it/paraunit": "^1.3.1 || ^2.11.0", + "infection/infection": "^0.32.7", + "justinrainbow/json-schema": "^6.9.0", + "keradus/cli-executor": "^2.3", + "mikey179/vfsstream": "^1.6.12", + "php-coveralls/php-coveralls": "^2.9.1", + "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.8", + "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.8", + "phpunit/phpunit": "^9.6.34 || ^10.5.63 || ^11.5.55", + "symfony/polyfill-php85": "^1.38", + "symfony/var-dumper": "^5.4.48 || ^6.4.36 || ^7.4.8 || ^8.1.0", + "symfony/yaml": "^5.4.53 || ^6.4.41 || ^7.4.13 || ^8.1.0" }, "suggest": { "ext-dom": "For handling output formats in XML", @@ -2469,7 +2920,10 @@ "autoload": { "psr-4": { "PhpCsFixer\\": "src/" - } + }, + "exclude-from-classmap": [ + "src/**/Internal/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2494,7 +2948,7 @@ ], "support": { "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", - "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.42.0" + "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.95.10" }, "funding": [ { @@ -2502,29 +2956,30 @@ "type": "github" } ], - "time": "2023-12-24T14:38:51+00:00" + "time": "2026-06-19T14:45:22+00:00" }, { "name": "guzzlehttp/guzzle", - "version": "7.8.1", + "version": "7.12.1", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "41042bc7ab002487b876a0683fc8dce04ddce104" + "reference": "d34627490fbc03bf5c5d7cfed81f2faa19519425" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/41042bc7ab002487b876a0683fc8dce04ddce104", - "reference": "41042bc7ab002487b876a0683fc8dce04ddce104", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/d34627490fbc03bf5c5d7cfed81f2faa19519425", + "reference": "d34627490fbc03bf5c5d7cfed81f2faa19519425", "shasum": "" }, "require": { "ext-json": "*", - "guzzlehttp/promises": "^1.5.3 || ^2.0.1", - "guzzlehttp/psr7": "^1.9.1 || ^2.5.1", + "guzzlehttp/promises": "^2.5", + "guzzlehttp/psr7": "^2.12.1", "php": "^7.2.5 || ^8.0", "psr/http-client": "^1.0", - "symfony/deprecation-contracts": "^2.2 || ^3.0" + "symfony/deprecation-contracts": "^2.5 || ^3.0", + "symfony/polyfill-php80": "^1.24" }, "provide": { "psr/http-client-implementation": "1.0" @@ -2532,9 +2987,10 @@ "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", "ext-curl": "*", - "php-http/client-integration-tests": "dev-master#2c025848417c1135031fdf9c728ee53d0a7ceaee as 3.0.999", + "guzzle/client-integration-tests": "3.0.2", + "guzzlehttp/test-server": "^0.5.1", "php-http/message-factory": "^1.1", - "phpunit/phpunit": "^8.5.36 || ^9.6.15", + "phpunit/phpunit": "^8.5.52 || ^9.6.34", "psr/log": "^1.1 || ^2.0 || ^3.0" }, "suggest": { @@ -2612,7 +3068,7 @@ ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.8.1" + "source": "https://github.com/guzzle/guzzle/tree/7.12.1" }, "funding": [ { @@ -2628,28 +3084,29 @@ "type": "tidelift" } ], - "time": "2023-12-03T20:35:24+00:00" + "time": "2026-06-18T14:12:49+00:00" }, { "name": "guzzlehttp/promises", - "version": "2.0.2", + "version": "2.5.0", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "bbff78d96034045e58e13dedd6ad91b5d1253223" + "reference": "4360e982f87f5f258bf872d094647791db2f4c8e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/bbff78d96034045e58e13dedd6ad91b5d1253223", - "reference": "bbff78d96034045e58e13dedd6ad91b5d1253223", + "url": "https://api.github.com/repos/guzzle/promises/zipball/4360e982f87f5f258bf872d094647791db2f4c8e", + "reference": "4360e982f87f5f258bf872d094647791db2f4c8e", "shasum": "" }, "require": { - "php": "^7.2.5 || ^8.0" + "php": "^7.2.5 || ^8.0", + "symfony/deprecation-contracts": "^2.5 || ^3.0" }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "phpunit/phpunit": "^8.5.36 || ^9.6.15" + "phpunit/phpunit": "^8.5.52 || ^9.6.34" }, "type": "library", "extra": { @@ -2695,7 +3152,7 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/2.0.2" + "source": "https://github.com/guzzle/promises/tree/2.5.0" }, "funding": [ { @@ -2711,27 +3168,29 @@ "type": "tidelift" } ], - "time": "2023-12-03T20:19:20+00:00" + "time": "2026-06-02T12:23:43+00:00" }, { "name": "guzzlehttp/psr7", - "version": "2.6.2", + "version": "2.12.1", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "45b30f99ac27b5ca93cb4831afe16285f57b8221" + "reference": "172ef2f4e9824c1e058b7f30be8ae25a02c0f2b7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/45b30f99ac27b5ca93cb4831afe16285f57b8221", - "reference": "45b30f99ac27b5ca93cb4831afe16285f57b8221", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/172ef2f4e9824c1e058b7f30be8ae25a02c0f2b7", + "reference": "172ef2f4e9824c1e058b7f30be8ae25a02c0f2b7", "shasum": "" }, "require": { "php": "^7.2.5 || ^8.0", "psr/http-factory": "^1.0", "psr/http-message": "^1.1 || ^2.0", - "ralouphie/getallheaders": "^3.0" + "ralouphie/getallheaders": "^3.0", + "symfony/deprecation-contracts": "^2.5 || ^3.0", + "symfony/polyfill-php80": "^1.24" }, "provide": { "psr/http-factory-implementation": "1.0", @@ -2739,8 +3198,9 @@ }, "require-dev": { "bamarni/composer-bin-plugin": "^1.8.2", - "http-interop/http-factory-tests": "^0.9", - "phpunit/phpunit": "^8.5.36 || ^9.6.15" + "http-interop/http-factory-tests": "1.1.0", + "jshttp/mime-db": "1.54.0.1", + "phpunit/phpunit": "^8.5.52 || ^9.6.34" }, "suggest": { "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" @@ -2811,7 +3271,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.6.2" + "source": "https://github.com/guzzle/psr7/tree/2.12.1" }, "funding": [ { @@ -2827,36 +3287,36 @@ "type": "tidelift" } ], - "time": "2023-12-03T20:05:35+00:00" + "time": "2026-06-18T09:49:37+00:00" }, { "name": "php-coveralls/php-coveralls", - "version": "v2.7.0", + "version": "v2.9.1", "source": { "type": "git", "url": "https://github.com/php-coveralls/php-coveralls.git", - "reference": "b36fa4394e519dafaddc04ae03976bc65a25ba15" + "reference": "916bdb118597f61ce6715fb738ab8f234b89a2cb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-coveralls/php-coveralls/zipball/b36fa4394e519dafaddc04ae03976bc65a25ba15", - "reference": "b36fa4394e519dafaddc04ae03976bc65a25ba15", + "url": "https://api.github.com/repos/php-coveralls/php-coveralls/zipball/916bdb118597f61ce6715fb738ab8f234b89a2cb", + "reference": "916bdb118597f61ce6715fb738ab8f234b89a2cb", "shasum": "" }, "require": { "ext-json": "*", "ext-simplexml": "*", "guzzlehttp/guzzle": "^6.0 || ^7.0", - "php": "^7.0 || ^8.0", - "psr/log": "^1.0 || ^2.0", - "symfony/config": "^2.1 || ^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0", - "symfony/console": "^2.1 || ^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0", - "symfony/stopwatch": "^2.0 || ^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0", - "symfony/yaml": "^2.0.5 || ^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0" + "php": "^7.4 || ^8.0", + "psr/log": "^1.0 || ^2.0 || ^3.0", + "symfony/config": "^5.4 || ^6.4 || ^7.0 || ^8.0", + "symfony/console": "^5.4 || ^6.4 || ^7.0 || ^8.0", + "symfony/stopwatch": "^5.4 || ^6.4 || ^7.0 || ^8.0", + "symfony/yaml": "^5.4 || ^6.4 || ^7.0 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.4.3 || ^6.0 || ^7.0 || >=8.0 <8.5.29 || >=9.0 <9.5.23", - "sanmai/phpunit-legacy-adapter": "^6.1 || ^8.0" + "phpspec/prophecy-phpunit": "^2.4", + "phpunit/phpunit": "^9.6.29 || ^10.5.58 || ^11.5.43" }, "suggest": { "symfony/http-kernel": "Allows Symfony integration" @@ -2908,9 +3368,9 @@ ], "support": { "issues": "https://github.com/php-coveralls/php-coveralls/issues", - "source": "https://github.com/php-coveralls/php-coveralls/tree/v2.7.0" + "source": "https://github.com/php-coveralls/php-coveralls/tree/v2.9.1" }, - "time": "2023-11-22T10:21:01+00:00" + "time": "2025-12-18T13:08:37+00:00" }, { "name": "php-stubs/wordpress-globals", @@ -2955,27 +3415,32 @@ }, { "name": "php-stubs/wordpress-stubs", - "version": "v6.4.1", + "version": "v6.9.4", "source": { "type": "git", "url": "https://github.com/php-stubs/wordpress-stubs.git", - "reference": "6d6063cf9464a306ca2a0529705d41312b08500b" + "reference": "90a9412826b9944f93b10bf41d795b5fe68abcd5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-stubs/wordpress-stubs/zipball/6d6063cf9464a306ca2a0529705d41312b08500b", - "reference": "6d6063cf9464a306ca2a0529705d41312b08500b", + "url": "https://api.github.com/repos/php-stubs/wordpress-stubs/zipball/90a9412826b9944f93b10bf41d795b5fe68abcd5", + "reference": "90a9412826b9944f93b10bf41d795b5fe68abcd5", "shasum": "" }, + "conflict": { + "phpdocumentor/reflection-docblock": "5.6.1" + }, "require-dev": { "dealerdirect/phpcodesniffer-composer-installer": "^1.0", - "nikic/php-parser": "^4.13", - "php": "^7.4 || ~8.0.0", - "php-stubs/generator": "^0.8.3", - "phpdocumentor/reflection-docblock": "^5.3", - "phpstan/phpstan": "^1.10.12", + "nikic/php-parser": "^5.5", + "php": "^7.4 || ^8.0", + "php-stubs/generator": "^0.8.6", + "phpdocumentor/reflection-docblock": "^6.0", + "phpstan/phpstan": "^2.1", "phpunit/phpunit": "^9.5", - "szepeviktor/phpcs-psr-12-neutron-hybrid-ruleset": "^0.8" + "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", @@ -2996,9 +3461,9 @@ ], "support": { "issues": "https://github.com/php-stubs/wordpress-stubs/issues", - "source": "https://github.com/php-stubs/wordpress-stubs/tree/v6.4.1" + "source": "https://github.com/php-stubs/wordpress-stubs/tree/v6.9.4" }, - "time": "2023-11-10T00:33:47+00:00" + "time": "2026-05-01T20:36:01+00:00" }, { "name": "phpcompatibility/php-compatibility", @@ -3064,20 +3529,15 @@ }, { "name": "phpstan/phpstan", - "version": "1.10.50", - "source": { - "type": "git", - "url": "https://github.com/phpstan/phpstan.git", - "reference": "06a98513ac72c03e8366b5a0cb00750b487032e4" - }, + "version": "2.2.2", "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/06a98513ac72c03e8366b5a0cb00750b487032e4", - "reference": "06a98513ac72c03e8366b5a0cb00750b487032e4", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/e5cc34d491a90e79c216d824f60fe21fd4d93bd6", + "reference": "e5cc34d491a90e79c216d824f60fe21fd4d93bd6", "shasum": "" }, "require": { - "php": "^7.2|^8.0" + "php": "^7.4|^8.0" }, "conflict": { "phpstan/phpstan-shim": "*" @@ -3096,6 +3556,17 @@ "license": [ "MIT" ], + "authors": [ + { + "name": "Ondřej Mirtes" + }, + { + "name": "Markus Staab" + }, + { + "name": "Vincent Langlet" + } + ], "description": "PHPStan - PHP Static Analysis Tool", "keywords": [ "dev", @@ -3116,39 +3587,34 @@ { "url": "https://github.com/phpstan", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan", - "type": "tidelift" } ], - "time": "2023-12-13T10:59:42+00:00" + "time": "2026-06-05T09:00:01+00:00" }, { "name": "phpstan/phpstan-mockery", - "version": "1.1.1", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-mockery.git", - "reference": "6aa86bd8e9c9a1be97baf0558d4a2ed1374736a6" + "reference": "89a949d0ac64298e88b7c7fa00caee565c198394" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-mockery/zipball/6aa86bd8e9c9a1be97baf0558d4a2ed1374736a6", - "reference": "6aa86bd8e9c9a1be97baf0558d4a2ed1374736a6", + "url": "https://api.github.com/repos/phpstan/phpstan-mockery/zipball/89a949d0ac64298e88b7c7fa00caee565c198394", + "reference": "89a949d0ac64298e88b7c7fa00caee565c198394", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0", - "phpstan/phpstan": "^1.10" + "php": "^7.4 || ^8.0", + "phpstan/phpstan": "^2.0" }, "require-dev": { - "mockery/mockery": "^1.2.4", - "nikic/php-parser": "^4.13.0", + "mockery/mockery": "^1.6.11", "php-parallel-lint/php-parallel-lint": "^1.2", - "phpstan/phpstan-phpunit": "^1.0", - "phpstan/phpstan-strict-rules": "^1.0", - "phpunit/phpunit": "^9.5" + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^9.6" }, "type": "phpstan-extension", "extra": { @@ -3170,36 +3636,37 @@ "description": "PHPStan Mockery extension", "support": { "issues": "https://github.com/phpstan/phpstan-mockery/issues", - "source": "https://github.com/phpstan/phpstan-mockery/tree/1.1.1" + "source": "https://github.com/phpstan/phpstan-mockery/tree/2.0.0" }, - "time": "2023-02-18T13:54:03+00:00" + "time": "2024-10-14T03:18:12+00:00" }, { "name": "phpstan/phpstan-phpunit", - "version": "1.3.15", + "version": "2.0.16", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-phpunit.git", - "reference": "70ecacc64fe8090d8d2a33db5a51fe8e88acd93a" + "reference": "6ab598e1bc106e6827fd346ae4a12b4a5d634c32" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/70ecacc64fe8090d8d2a33db5a51fe8e88acd93a", - "reference": "70ecacc64fe8090d8d2a33db5a51fe8e88acd93a", + "url": "https://api.github.com/repos/phpstan/phpstan-phpunit/zipball/6ab598e1bc106e6827fd346ae4a12b4a5d634c32", + "reference": "6ab598e1bc106e6827fd346ae4a12b4a5d634c32", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0", - "phpstan/phpstan": "^1.10" + "php": "^7.4 || ^8.0", + "phpstan/phpstan": "^2.1.32" }, "conflict": { "phpunit/phpunit": "<7.0" }, "require-dev": { - "nikic/php-parser": "^4.13.0", + "nikic/php-parser": "^5", "php-parallel-lint/php-parallel-lint": "^1.2", - "phpstan/phpstan-strict-rules": "^1.5.1", - "phpunit/phpunit": "^9.5" + "phpstan/phpstan-deprecation-rules": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^9.6" }, "type": "phpstan-extension", "extra": { @@ -3220,30 +3687,38 @@ "MIT" ], "description": "PHPUnit extensions and rules for PHPStan", + "keywords": [ + "static analysis" + ], "support": { "issues": "https://github.com/phpstan/phpstan-phpunit/issues", - "source": "https://github.com/phpstan/phpstan-phpunit/tree/1.3.15" + "source": "https://github.com/phpstan/phpstan-phpunit/tree/2.0.16" }, - "time": "2023-10-09T18:58:39+00:00" + "time": "2026-02-14T09:05:21+00:00" }, { "name": "psr/container", - "version": "1.1.2", + "version": "2.0.2", "source": { "type": "git", "url": "https://github.com/php-fig/container.git", - "reference": "513e0666f7216c7459170d56df27dfcefe1689ea" + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/513e0666f7216c7459170d56df27dfcefe1689ea", - "reference": "513e0666f7216c7459170d56df27dfcefe1689ea", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", "shasum": "" }, "require": { "php": ">=7.4.0" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, "autoload": { "psr-4": { "Psr\\Container\\": "src/" @@ -3270,9 +3745,9 @@ ], "support": { "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/1.1.2" + "source": "https://github.com/php-fig/container/tree/2.0.2" }, - "time": "2021-11-05T16:50:12+00:00" + "time": "2021-11-05T16:47:00+00:00" }, { "name": "psr/event-dispatcher", @@ -3299,7 +3774,310 @@ }, "autoload": { "psr-4": { - "Psr\\EventDispatcher\\": "src/" + "Psr\\EventDispatcher\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Standard interfaces for event handling.", + "keywords": [ + "events", + "psr", + "psr-14" + ], + "support": { + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + }, + "time": "2019-01-08T18:20:26+00:00" + }, + { + "name": "psr/http-client", + "version": "1.0.3", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client" + }, + "time": "2023-09-23T14:17:50+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory" + }, + "time": "2024-04-15T12:06:14+00:00" + }, + { + "name": "psr/http-message", + "version": "2.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/2.0" + }, + "time": "2023-04-04T09:54:51+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": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, + "time": "2019-03-08T08:55:37+00:00" + }, + { + "name": "react/cache", + "version": "v1.2.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/cache.git", + "reference": "d47c472b64aa5608225f47965a484b75c7817d5b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/cache/zipball/d47c472b64aa5608225f47965a484b75c7817d5b", + "reference": "d47c472b64aa5608225f47965a484b75c7817d5b", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "react/promise": "^3.0 || ^2.0 || ^1.1" + }, + "require-dev": { + "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35" + }, + "type": "library", + "autoload": { + "psr-4": { + "React\\Cache\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -3308,49 +4086,74 @@ ], "authors": [ { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" } ], - "description": "Standard interfaces for event handling.", + "description": "Async, Promise-based cache interface for ReactPHP", "keywords": [ - "events", - "psr", - "psr-14" + "cache", + "caching", + "promise", + "reactphp" ], "support": { - "issues": "https://github.com/php-fig/event-dispatcher/issues", - "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + "issues": "https://github.com/reactphp/cache/issues", + "source": "https://github.com/reactphp/cache/tree/v1.2.0" }, - "time": "2019-01-08T18:20:26+00:00" + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2022-11-30T15:59:55+00:00" }, { - "name": "psr/http-client", - "version": "1.0.3", + "name": "react/child-process", + "version": "v0.6.7", "source": { "type": "git", - "url": "https://github.com/php-fig/http-client.git", - "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90" + "url": "https://github.com/reactphp/child-process.git", + "reference": "970f0e71945556422ee4570ccbabaedc3cf04ad3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90", - "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", + "url": "https://api.github.com/repos/reactphp/child-process/zipball/970f0e71945556422ee4570ccbabaedc3cf04ad3", + "reference": "970f0e71945556422ee4570ccbabaedc3cf04ad3", "shasum": "" }, "require": { - "php": "^7.0 || ^8.0", - "psr/http-message": "^1.0 || ^2.0" + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3.0", + "react/event-loop": "^1.2", + "react/stream": "^1.4" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } + "require-dev": { + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36", + "react/socket": "^1.16", + "sebastian/environment": "^5.0 || ^3.0 || ^2.0 || ^1.0" }, + "type": "library", "autoload": { "psr-4": { - "Psr\\Http\\Client\\": "src/" + "React\\ChildProcess\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -3359,50 +4162,73 @@ ], "authors": [ { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" } ], - "description": "Common interface for HTTP clients", - "homepage": "https://github.com/php-fig/http-client", + "description": "Event-driven library for executing child processes with ReactPHP.", "keywords": [ - "http", - "http-client", - "psr", - "psr-18" + "event-driven", + "process", + "reactphp" ], "support": { - "source": "https://github.com/php-fig/http-client" + "issues": "https://github.com/reactphp/child-process/issues", + "source": "https://github.com/reactphp/child-process/tree/v0.6.7" }, - "time": "2023-09-23T14:17:50+00:00" + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2025-12-23T15:25:20+00:00" }, { - "name": "psr/http-factory", - "version": "1.0.2", + "name": "react/dns", + "version": "v1.14.0", "source": { "type": "git", - "url": "https://github.com/php-fig/http-factory.git", - "reference": "e616d01114759c4c489f93b099585439f795fe35" + "url": "https://github.com/reactphp/dns.git", + "reference": "7562c05391f42701c1fccf189c8225fece1cd7c3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-factory/zipball/e616d01114759c4c489f93b099585439f795fe35", - "reference": "e616d01114759c4c489f93b099585439f795fe35", + "url": "https://api.github.com/repos/reactphp/dns/zipball/7562c05391f42701c1fccf189c8225fece1cd7c3", + "reference": "7562c05391f42701c1fccf189c8225fece1cd7c3", "shasum": "" }, "require": { - "php": ">=7.0.0", - "psr/http-message": "^1.0 || ^2.0" + "php": ">=5.3.0", + "react/cache": "^1.0 || ^0.6 || ^0.5", + "react/event-loop": "^1.2", + "react/promise": "^3.2 || ^2.7 || ^1.2.1" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } + "require-dev": { + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36", + "react/async": "^4.3 || ^3 || ^2", + "react/promise-timer": "^1.11" }, + "type": "library", "autoload": { "psr-4": { - "Psr\\Http\\Message\\": "src/" + "React\\Dns\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -3411,52 +4237,72 @@ ], "authors": [ { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" } ], - "description": "Common interfaces for PSR-7 HTTP message factories", + "description": "Async DNS resolver for ReactPHP", "keywords": [ - "factory", - "http", - "message", - "psr", - "psr-17", - "psr-7", - "request", - "response" + "async", + "dns", + "dns-resolver", + "reactphp" ], "support": { - "source": "https://github.com/php-fig/http-factory/tree/1.0.2" + "issues": "https://github.com/reactphp/dns/issues", + "source": "https://github.com/reactphp/dns/tree/v1.14.0" }, - "time": "2023-04-10T20:10:41+00:00" + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2025-11-18T19:34:28+00:00" }, { - "name": "psr/http-message", - "version": "2.0", + "name": "react/event-loop", + "version": "v1.6.0", "source": { "type": "git", - "url": "https://github.com/php-fig/http-message.git", - "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71" + "url": "https://github.com/reactphp/event-loop.git", + "reference": "ba276bda6083df7e0050fd9b33f66ad7a4ac747a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71", - "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "url": "https://api.github.com/repos/reactphp/event-loop/zipball/ba276bda6083df7e0050fd9b33f66ad7a4ac747a", + "reference": "ba276bda6083df7e0050fd9b33f66ad7a4ac747a", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0" + "php": ">=5.3.0" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } + "require-dev": { + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" + }, + "suggest": { + "ext-pcntl": "For signal handling support when using the StreamSelectLoop" }, + "type": "library", "autoload": { "psr-4": { - "Psr\\Http\\Message\\": "src/" + "React\\EventLoop\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -3465,51 +4311,71 @@ ], "authors": [ { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" } ], - "description": "Common interface for HTTP messages", - "homepage": "https://github.com/php-fig/http-message", + "description": "ReactPHP's core reactor event loop that libraries can use for evented I/O.", "keywords": [ - "http", - "http-message", - "psr", - "psr-7", - "request", - "response" + "asynchronous", + "event-loop" ], "support": { - "source": "https://github.com/php-fig/http-message/tree/2.0" + "issues": "https://github.com/reactphp/event-loop/issues", + "source": "https://github.com/reactphp/event-loop/tree/v1.6.0" }, - "time": "2023-04-04T09:54:51+00:00" + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2025-11-17T20:46:25+00:00" }, { - "name": "psr/log", - "version": "1.1.4", + "name": "react/promise", + "version": "v3.3.0", "source": { "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11" + "url": "https://github.com/reactphp/promise.git", + "reference": "23444f53a813a3296c1368bb104793ce8d88f04a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", - "reference": "d49695b909c3b7628b6289db5479a1c204601f11", + "url": "https://api.github.com/repos/reactphp/promise/zipball/23444f53a813a3296c1368bb104793ce8d88f04a", + "reference": "23444f53a813a3296c1368bb104793ce8d88f04a", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=7.1.0" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1.x-dev" - } + "require-dev": { + "phpstan/phpstan": "1.12.28 || 1.4.10", + "phpunit/phpunit": "^9.6 || ^7.5" }, + "type": "library", "autoload": { + "files": [ + "src/functions_include.php" + ], "psr-4": { - "Psr\\Log\\": "Psr/Log/" + "React\\Promise\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -3518,48 +4384,76 @@ ], "authors": [ { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" } ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", + "description": "A lightweight implementation of CommonJS Promises/A for PHP", "keywords": [ - "log", - "psr", - "psr-3" + "promise", + "promises" ], "support": { - "source": "https://github.com/php-fig/log/tree/1.1.4" + "issues": "https://github.com/reactphp/promise/issues", + "source": "https://github.com/reactphp/promise/tree/v3.3.0" }, - "time": "2021-05-03T11:20:27+00:00" + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2025-08-19T18:57:03+00:00" }, { - "name": "ralouphie/getallheaders", - "version": "3.0.3", + "name": "react/socket", + "version": "v1.17.0", "source": { "type": "git", - "url": "https://github.com/ralouphie/getallheaders.git", - "reference": "120b605dfeb996808c31b6477290a714d356e822" + "url": "https://github.com/reactphp/socket.git", + "reference": "ef5b17b81f6f60504c539313f94f2d826c5faa08" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", - "reference": "120b605dfeb996808c31b6477290a714d356e822", + "url": "https://api.github.com/repos/reactphp/socket/zipball/ef5b17b81f6f60504c539313f94f2d826c5faa08", + "reference": "ef5b17b81f6f60504c539313f94f2d826c5faa08", "shasum": "" }, "require": { - "php": ">=5.6" + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3.0", + "react/dns": "^1.13", + "react/event-loop": "^1.2", + "react/promise": "^3.2 || ^2.6 || ^1.2.1", + "react/stream": "^1.4" }, "require-dev": { - "php-coveralls/php-coveralls": "^2.1", - "phpunit/phpunit": "^5 || ^6.5" + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36", + "react/async": "^4.3 || ^3.3 || ^2", + "react/promise-stream": "^1.4", + "react/promise-timer": "^1.11" }, "type": "library", "autoload": { - "files": [ - "src/getallheaders.php" - ] + "psr-4": { + "React\\Socket\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -3567,67 +4461,136 @@ ], "authors": [ { - "name": "Ralph Khattar", - "email": "ralph.khattar@gmail.com" + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" } ], - "description": "A polyfill for getallheaders.", + "description": "Async, streaming plaintext TCP/IP and secure TLS socket server and client connections for ReactPHP", + "keywords": [ + "Connection", + "Socket", + "async", + "reactphp", + "stream" + ], "support": { - "issues": "https://github.com/ralouphie/getallheaders/issues", - "source": "https://github.com/ralouphie/getallheaders/tree/develop" + "issues": "https://github.com/reactphp/socket/issues", + "source": "https://github.com/reactphp/socket/tree/v1.17.0" }, - "time": "2019-03-08T08:55:37+00:00" + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2025-11-19T20:47:34+00:00" }, { - "name": "sempro/phpunit-pretty-print", - "version": "1.4.0", + "name": "react/stream", + "version": "v1.4.0", "source": { "type": "git", - "url": "https://github.com/s360digital/phpunit-pretty-print.git", - "reference": "fa623aa8a17aece4a2b69e54b07a5e37572d1f1d" + "url": "https://github.com/reactphp/stream.git", + "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/s360digital/phpunit-pretty-print/zipball/fa623aa8a17aece4a2b69e54b07a5e37572d1f1d", - "reference": "fa623aa8a17aece4a2b69e54b07a5e37572d1f1d", + "url": "https://api.github.com/repos/reactphp/stream/zipball/1e5b0acb8fe55143b5b426817155190eb6f5b18d", + "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d", "shasum": "" }, "require": { - "php": ">=7.1.0", - "phpunit/phpunit": "^7 || ^8 || ^9" + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3.8", + "react/event-loop": "^1.2" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^2.16" + "clue/stream-filter": "~1.2", + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" }, "type": "library", "autoload": { "psr-4": { - "Sempro\\PHPUnitPrettyPrinter\\": "src/" + "React\\Stream\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "description": "Prettify PHPUnit output", + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "Event-driven readable and writable streams for non-blocking I/O in ReactPHP", + "keywords": [ + "event-driven", + "io", + "non-blocking", + "pipe", + "reactphp", + "readable", + "stream", + "writable" + ], "support": { - "issues": "https://github.com/s360digital/phpunit-pretty-print/issues", - "source": "https://github.com/s360digital/phpunit-pretty-print/tree/1.4.0" + "issues": "https://github.com/reactphp/stream/issues", + "source": "https://github.com/reactphp/stream/tree/v1.4.0" }, - "time": "2021-01-04T13:25:10+00:00" + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2024-06-11T12:45:25+00:00" }, { "name": "squizlabs/php_codesniffer", - "version": "3.8.0", + "version": "3.13.5", "source": { "type": "git", "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git", - "reference": "5805f7a4e4958dbb5e944ef1e6edae0a303765e7" + "reference": "0ca86845ce43291e8f5692c7356fccf3bcf02bf4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/5805f7a4e4958dbb5e944ef1e6edae0a303765e7", - "reference": "5805f7a4e4958dbb5e944ef1e6edae0a303765e7", + "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/0ca86845ce43291e8f5692c7356fccf3bcf02bf4", + "reference": "0ca86845ce43291e8f5692c7356fccf3bcf02bf4", "shasum": "" }, "require": { @@ -3637,18 +4600,13 @@ "php": ">=5.4.0" }, "require-dev": { - "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.0" + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4" }, "bin": [ - "bin/phpcs", - "bin/phpcbf" + "bin/phpcbf", + "bin/phpcs" ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } - }, "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" @@ -3692,44 +4650,44 @@ { "url": "https://opencollective.com/php_codesniffer", "type": "open_collective" + }, + { + "url": "https://thanks.dev/u/gh/phpcsstandards", + "type": "thanks_dev" } ], - "time": "2023-12-08T12:32:31+00:00" + "time": "2025-11-04T16:30:35+00:00" }, { "name": "symfony/config", - "version": "v5.4.31", + "version": "v7.4.10", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "dd5ea39de228813aba0c23c3a4153da2a4cf3cd9" + "reference": "d91b6c7cd2a8c9a9c2b8d26c8f5ed48edf99ef57" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/dd5ea39de228813aba0c23c3a4153da2a4cf3cd9", - "reference": "dd5ea39de228813aba0c23c3a4153da2a4cf3cd9", + "url": "https://api.github.com/repos/symfony/config/zipball/d91b6c7cd2a8c9a9c2b8d26c8f5ed48edf99ef57", + "reference": "d91b6c7cd2a8c9a9c2b8d26c8f5ed48edf99ef57", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/filesystem": "^4.4|^5.0|^6.0", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-php80": "^1.16", - "symfony/polyfill-php81": "^1.22" + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/filesystem": "^7.1|^8.0", + "symfony/polyfill-ctype": "~1.8" }, "conflict": { - "symfony/finder": "<4.4" + "symfony/finder": "<6.4", + "symfony/service-contracts": "<2.5" }, "require-dev": { - "symfony/event-dispatcher": "^4.4|^5.0|^6.0", - "symfony/finder": "^4.4|^5.0|^6.0", - "symfony/messenger": "^4.4|^5.0|^6.0", - "symfony/service-contracts": "^1.1|^2|^3", - "symfony/yaml": "^4.4|^5.0|^6.0" - }, - "suggest": { - "symfony/yaml": "To use the yaml reference dumper" + "symfony/event-dispatcher": "^6.4|^7.0|^8.0", + "symfony/finder": "^6.4|^7.0|^8.0", + "symfony/messenger": "^6.4|^7.0|^8.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/yaml": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -3757,7 +4715,7 @@ "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/config/tree/v5.4.31" + "source": "https://github.com/symfony/config/tree/v7.4.10" }, "funding": [ { @@ -3768,61 +4726,60 @@ "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": "2023-11-09T08:22:43+00:00" + "time": "2026-05-03T14:20:49+00:00" }, { "name": "symfony/console", - "version": "v5.4.32", + "version": "v7.4.13", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "c70df1ffaf23a8d340bded3cfab1b86752ad6ed7" + "reference": "85095d2573eaefaf35e40b9513a9bf09f72cd217" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/c70df1ffaf23a8d340bded3cfab1b86752ad6ed7", - "reference": "c70df1ffaf23a8d340bded3cfab1b86752ad6ed7", + "url": "https://api.github.com/repos/symfony/console/zipball/85095d2573eaefaf35e40b9513a9bf09f72cd217", + "reference": "85095d2573eaefaf35e40b9513a9bf09f72cd217", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php73": "^1.9", - "symfony/polyfill-php80": "^1.16", - "symfony/service-contracts": "^1.1|^2|^3", - "symfony/string": "^5.1|^6.0" + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^7.2|^8.0" }, "conflict": { - "psr/log": ">=3", - "symfony/dependency-injection": "<4.4", - "symfony/dotenv": "<5.1", - "symfony/event-dispatcher": "<4.4", - "symfony/lock": "<4.4", - "symfony/process": "<4.4" + "symfony/dependency-injection": "<6.4", + "symfony/dotenv": "<6.4", + "symfony/event-dispatcher": "<6.4", + "symfony/lock": "<6.4", + "symfony/process": "<6.4" }, "provide": { - "psr/log-implementation": "1.0|2.0" + "psr/log-implementation": "1.0|2.0|3.0" }, "require-dev": { - "psr/log": "^1|^2", - "symfony/config": "^4.4|^5.0|^6.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/event-dispatcher": "^4.4|^5.0|^6.0", - "symfony/lock": "^4.4|^5.0|^6.0", - "symfony/process": "^4.4|^5.0|^6.0", - "symfony/var-dumper": "^4.4|^5.0|^6.0" - }, - "suggest": { - "psr/log": "For using the console logger", - "symfony/event-dispatcher": "", - "symfony/lock": "", - "symfony/process": "" + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/event-dispatcher": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/lock": "^6.4|^7.0|^8.0", + "symfony/messenger": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/stopwatch": "^6.4|^7.0|^8.0", + "symfony/var-dumper": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -3856,7 +4813,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v5.4.32" + "source": "https://github.com/symfony/console/tree/v7.4.13" }, "funding": [ { @@ -3867,57 +4824,52 @@ "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": "2023-11-18T18:23:04+00:00" + "time": "2026-05-24T08:56:14+00:00" }, { "name": "symfony/dependency-injection", - "version": "v5.4.33", + "version": "v7.4.13", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "14969a558cd6382b2a12b14b20ef9a851a02da79" + "reference": "f299e20ce983be6c0744952533c6dfeaaa1448e2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/14969a558cd6382b2a12b14b20ef9a851a02da79", - "reference": "14969a558cd6382b2a12b14b20ef9a851a02da79", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/f299e20ce983be6c0744952533c6dfeaaa1448e2", + "reference": "f299e20ce983be6c0744952533c6dfeaaa1448e2", "shasum": "" }, "require": { - "php": ">=7.2.5", - "psr/container": "^1.1.1", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-php80": "^1.16", - "symfony/polyfill-php81": "^1.22", - "symfony/service-contracts": "^1.1.6|^2" + "php": ">=8.2", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/service-contracts": "^3.6", + "symfony/var-exporter": "^6.4.20|^7.2.5|^8.0" }, "conflict": { "ext-psr": "<1.1|>=2", - "symfony/config": "<5.3", - "symfony/finder": "<4.4", - "symfony/proxy-manager-bridge": "<4.4", - "symfony/yaml": "<4.4.26" + "symfony/config": "<6.4", + "symfony/finder": "<6.4", + "symfony/yaml": "<6.4" }, "provide": { - "psr/container-implementation": "1.0", - "symfony/service-implementation": "1.0|2.0" + "psr/container-implementation": "1.1|2.0", + "symfony/service-implementation": "1.1|2.0|3.0" }, "require-dev": { - "symfony/config": "^5.3|^6.0", - "symfony/expression-language": "^4.4|^5.0|^6.0", - "symfony/yaml": "^4.4.26|^5.0|^6.0" - }, - "suggest": { - "symfony/config": "", - "symfony/expression-language": "For using expressions in service container configuration", - "symfony/finder": "For using double-star glob patterns or when GLOB_BRACE portability is required", - "symfony/proxy-manager-bridge": "Generate service proxies to lazy load them", - "symfony/yaml": "" + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/yaml": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -3945,7 +4897,7 @@ "description": "Allows you to standardize and centralize the way objects are constructed in your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dependency-injection/tree/v5.4.33" + "source": "https://github.com/symfony/dependency-injection/tree/v7.4.13" }, "funding": [ { @@ -3956,38 +4908,42 @@ "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": "2023-11-30T08:15:37+00:00" + "time": "2026-05-20T14:07:29+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v2.5.2", + "version": "v3.7.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66" + "reference": "50f59d1f3ca46d41ac911f97a78626b6756af35b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e8b495ea28c1d97b5e0c121748d6f9b53d075c66", - "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/50f59d1f3ca46d41ac911f97a78626b6756af35b", + "reference": "50f59d1f3ca46d41ac911f97a78626b6756af35b", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=8.1" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "2.5-dev" - }, "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.7-dev" } }, "autoload": { @@ -4012,7 +4968,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.2" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.7.0" }, "funding": [ { @@ -4023,53 +4979,53 @@ "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": "2022-01-02T09:53:40+00:00" + "time": "2026-04-13T15:52:40+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v5.4.26", + "version": "v7.4.9", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "5dcc00e03413f05c1e7900090927bb7247cb0aac" + "reference": "e4a2e29753c7801f7a8340e066cfa788f3bc8101" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/5dcc00e03413f05c1e7900090927bb7247cb0aac", - "reference": "5dcc00e03413f05c1e7900090927bb7247cb0aac", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/e4a2e29753c7801f7a8340e066cfa788f3bc8101", + "reference": "e4a2e29753c7801f7a8340e066cfa788f3bc8101", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/event-dispatcher-contracts": "^2|^3", - "symfony/polyfill-php80": "^1.16" + "php": ">=8.2", + "symfony/event-dispatcher-contracts": "^2.5|^3" }, "conflict": { - "symfony/dependency-injection": "<4.4" + "symfony/dependency-injection": "<6.4", + "symfony/service-contracts": "<2.5" }, "provide": { "psr/event-dispatcher-implementation": "1.0", - "symfony/event-dispatcher-implementation": "2.0" + "symfony/event-dispatcher-implementation": "2.0|3.0" }, "require-dev": { "psr/log": "^1|^2|^3", - "symfony/config": "^4.4|^5.0|^6.0", - "symfony/dependency-injection": "^4.4|^5.0|^6.0", - "symfony/error-handler": "^4.4|^5.0|^6.0", - "symfony/expression-language": "^4.4|^5.0|^6.0", - "symfony/http-foundation": "^4.4|^5.0|^6.0", - "symfony/service-contracts": "^1.1|^2|^3", - "symfony/stopwatch": "^4.4|^5.0|^6.0" - }, - "suggest": { - "symfony/dependency-injection": "", - "symfony/http-kernel": "" + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/error-handler": "^6.4|^7.0|^8.0", + "symfony/expression-language": "^6.4|^7.0|^8.0", + "symfony/framework-bundle": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/stopwatch": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -4097,7 +5053,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v5.4.26" + "source": "https://github.com/symfony/event-dispatcher/tree/v7.4.9" }, "funding": [ { @@ -4108,42 +5064,43 @@ "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": "2023-07-06T06:34:20+00:00" + "time": "2026-04-18T13:18:21+00:00" }, { "name": "symfony/event-dispatcher-contracts", - "version": "v2.5.2", + "version": "v3.7.0", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "f98b54df6ad059855739db6fcbc2d36995283fe1" + "reference": "ccba7060602b7fed0b03c85bf025257f76d9ef32" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/f98b54df6ad059855739db6fcbc2d36995283fe1", - "reference": "f98b54df6ad059855739db6fcbc2d36995283fe1", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/ccba7060602b7fed0b03c85bf025257f76d9ef32", + "reference": "ccba7060602b7fed0b03c85bf025257f76d9ef32", "shasum": "" }, "require": { - "php": ">=7.2.5", + "php": ">=8.1", "psr/event-dispatcher": "^1" }, - "suggest": { - "symfony/event-dispatcher-implementation": "" - }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "2.5-dev" - }, "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.7-dev" } }, "autoload": { @@ -4176,7 +5133,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v2.5.2" + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.7.0" }, "funding": [ { @@ -4187,32 +5144,39 @@ "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": "2022-01-02T09:53:40+00:00" + "time": "2026-01-05T13:30:16+00:00" }, { "name": "symfony/filesystem", - "version": "v5.4.25", + "version": "v8.1.0", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "0ce3a62c9579a53358d3a7eb6b3dfb79789a6364" + "reference": "99aec13b82b4967ec5088222c4a3ecca955949c2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/0ce3a62c9579a53358d3a7eb6b3dfb79789a6364", - "reference": "0ce3a62c9579a53358d3a7eb6b3dfb79789a6364", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/99aec13b82b4967ec5088222c4a3ecca955949c2", + "reference": "99aec13b82b4967ec5088222c4a3ecca955949c2", "shasum": "" }, "require": { - "php": ">=7.2.5", + "php": ">=8.4.1", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-mbstring": "~1.8", - "symfony/polyfill-php80": "^1.16" + "symfony/polyfill-mbstring": "~1.8" + }, + "require-dev": { + "symfony/process": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -4240,7 +5204,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v5.4.25" + "source": "https://github.com/symfony/filesystem/tree/v8.1.0" }, "funding": [ { @@ -4251,31 +5215,36 @@ "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": "2023-05-31T13:04:02+00:00" + "time": "2026-05-29T05:06:50+00:00" }, { "name": "symfony/finder", - "version": "v5.4.27", + "version": "v8.1.0", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "ff4bce3c33451e7ec778070e45bd23f74214cd5d" + "reference": "58d2e767a66052c1487356f953445634a8194c64" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/ff4bce3c33451e7ec778070e45bd23f74214cd5d", - "reference": "ff4bce3c33451e7ec778070e45bd23f74214cd5d", + "url": "https://api.github.com/repos/symfony/finder/zipball/58d2e767a66052c1487356f953445634a8194c64", + "reference": "58d2e767a66052c1487356f953445634a8194c64", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-php80": "^1.16" + "php": ">=8.4.1" + }, + "require-dev": { + "symfony/filesystem": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -4303,7 +5272,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v5.4.27" + "source": "https://github.com/symfony/finder/tree/v8.1.0" }, "funding": [ { @@ -4314,32 +5283,34 @@ "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": "2023-07-31T08:02:31+00:00" + "time": "2026-05-29T05:06:50+00:00" }, { "name": "symfony/options-resolver", - "version": "v5.4.21", + "version": "v8.1.0", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "4fe5cf6ede71096839f0e4b4444d65dd3a7c1eb9" + "reference": "88f9c561f678a02d54b897014049fa839e33ff82" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/4fe5cf6ede71096839f0e4b4444d65dd3a7c1eb9", - "reference": "4fe5cf6ede71096839f0e4b4444d65dd3a7c1eb9", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/88f9c561f678a02d54b897014049fa839e33ff82", + "reference": "88f9c561f678a02d54b897014049fa839e33ff82", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", - "symfony/polyfill-php73": "~1.0", - "symfony/polyfill-php80": "^1.16" + "php": ">=8.4.1", + "symfony/deprecation-contracts": "^2.5|^3" }, "type": "library", "autoload": { @@ -4372,7 +5343,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v5.4.21" + "source": "https://github.com/symfony/options-resolver/tree/v8.1.0" }, "funding": [ { @@ -4383,29 +5354,33 @@ "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": "2023-02-14T08:03:56+00:00" + "time": "2026-05-29T05:06:50+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.28.0", + "version": "v1.37.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb" + "reference": "141046a8f9477948ff284fa65be2095baafb94f2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", - "reference": "ea208ce43cbb04af6867b4fdddb1bdbf84cc28cb", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/141046a8f9477948ff284fa65be2095baafb94f2", + "reference": "141046a8f9477948ff284fa65be2095baafb94f2", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "provide": { "ext-ctype": "*" @@ -4415,12 +5390,9 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -4454,7 +5426,94 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.28.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.37.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": "2026-04-10T16:19:22+00:00" + }, + { + "name": "symfony/polyfill-deepclone", + "version": "v1.40.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-deepclone.git", + "reference": "dca4ccba5f360070b574414dce4c1e7a559844fa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-deepclone/zipball/dca4ccba5f360070b574414dce4c1e7a559844fa", + "reference": "dca4ccba5f360070b574414dce4c1e7a559844fa", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "provide": { + "ext-deepclone": "*" + }, + "suggest": { + "ext-deepclone": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\DeepClone\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the deepclone extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "deepclone", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-deepclone/tree/v1.40.0" }, "funding": [ { @@ -4465,41 +5524,42 @@ "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": "2023-01-26T09:26:14+00:00" + "time": "2026-06-12T07:27:17+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.28.0", + "version": "v1.38.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "875e90aeea2777b6f135677f618529449334a612" + "reference": "e9247d281d694a5120554d9afaf54e070e88a603" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/875e90aeea2777b6f135677f618529449334a612", - "reference": "875e90aeea2777b6f135677f618529449334a612", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/e9247d281d694a5120554d9afaf54e070e88a603", + "reference": "e9247d281d694a5120554d9afaf54e070e88a603", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "suggest": { "ext-intl": "For best performance" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -4535,7 +5595,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.28.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.38.1" }, "funding": [ { @@ -4546,41 +5606,42 @@ "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": "2023-01-26T09:26:14+00:00" + "time": "2026-05-26T05:58:03+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.28.0", + "version": "v1.38.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92" + "reference": "2d446c214bdbe5b71bde5011b060a05fece3ae6b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92", - "reference": "8c4ad05dd0120b6a53c1ca374dca2ad0a1c4ed92", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/2d446c214bdbe5b71bde5011b060a05fece3ae6b", + "reference": "2d446c214bdbe5b71bde5011b060a05fece3ae6b", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "suggest": { "ext-intl": "For best performance" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -4619,7 +5680,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.28.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.38.0" }, "funding": [ { @@ -4630,29 +5691,34 @@ "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": "2023-01-26T09:26:14+00:00" + "time": "2026-05-25T13:48:31+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.28.0", + "version": "v1.38.2", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "42292d99c55abe617799667f454222c54c60e229" + "reference": "d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/42292d99c55abe617799667f454222c54c60e229", - "reference": "42292d99c55abe617799667f454222c54c60e229", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6", + "reference": "d3d318bad5e7a1bfbd026009c8bfb8d8f99ae6b6", "shasum": "" }, "require": { - "php": ">=7.1" + "ext-iconv": "*", + "php": ">=7.2" }, "provide": { "ext-mbstring": "*" @@ -4662,12 +5728,9 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -4702,7 +5765,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.28.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.38.2" }, "funding": [ { @@ -4713,38 +5776,39 @@ "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": "2023-07-28T09:04:16+00:00" + "time": "2026-05-27T06:59:30+00:00" }, { - "name": "symfony/polyfill-php73", - "version": "v1.28.0", + "name": "symfony/polyfill-php80", + "version": "v1.37.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php73.git", - "reference": "fe2f306d1d9d346a7fee353d0d5012e401e984b5" + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/fe2f306d1d9d346a7fee353d0d5012e401e984b5", - "reference": "fe2f306d1d9d346a7fee353d0d5012e401e984b5", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dfb55726c3a76ea3b6459fcfda1ec2d80a682411", + "reference": "dfb55726c3a76ea3b6459fcfda1ec2d80a682411", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -4752,7 +5816,7 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Php73\\": "" + "Symfony\\Polyfill\\Php80\\": "" }, "classmap": [ "Resources/stubs" @@ -4763,6 +5827,10 @@ "MIT" ], "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, { "name": "Nicolas Grekas", "email": "p@tchwork.com" @@ -4772,7 +5840,7 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", @@ -4781,7 +5849,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php73/tree/v1.28.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.37.0" }, "funding": [ { @@ -4792,25 +5860,29 @@ "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": "2023-01-26T09:26:14+00:00" + "time": "2026-04-10T16:19:22+00:00" }, { - "name": "symfony/polyfill-php80", - "version": "v1.31.0", + "name": "symfony/polyfill-php81", + "version": "v1.38.1", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8" + "url": "https://github.com/symfony/polyfill-php81.git", + "reference": "6bfb9c766cacffbc8e118cb87217d08ed84e5cd7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", - "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/6bfb9c766cacffbc8e118cb87217d08ed84e5cd7", + "reference": "6bfb9c766cacffbc8e118cb87217d08ed84e5cd7", "shasum": "" }, "require": { @@ -4819,8 +5891,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -4828,7 +5900,7 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Php80\\": "" + "Symfony\\Polyfill\\Php81\\": "" }, "classmap": [ "Resources/stubs" @@ -4839,10 +5911,6 @@ "MIT" ], "authors": [ - { - "name": "Ion Bazan", - "email": "ion.bazan@gmail.com" - }, { "name": "Nicolas Grekas", "email": "p@tchwork.com" @@ -4852,7 +5920,7 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", @@ -4861,7 +5929,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.31.0" + "source": "https://github.com/symfony/polyfill-php81/tree/v1.38.1" }, "funding": [ { @@ -4872,38 +5940,39 @@ "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": "2024-09-09T11:45:10+00:00" + "time": "2026-05-26T12:45:58+00:00" }, { - "name": "symfony/polyfill-php81", - "version": "v1.28.0", + "name": "symfony/polyfill-php84", + "version": "v1.38.1", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php81.git", - "reference": "7581cd600fa9fd681b797d00b02f068e2f13263b" + "url": "https://github.com/symfony/polyfill-php84.git", + "reference": "f4e1dfaee5b74aba5964fe1fd4dfc7ba5e3085fa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/7581cd600fa9fd681b797d00b02f068e2f13263b", - "reference": "7581cd600fa9fd681b797d00b02f068e2f13263b", + "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/f4e1dfaee5b74aba5964fe1fd4dfc7ba5e3085fa", + "reference": "f4e1dfaee5b74aba5964fe1fd4dfc7ba5e3085fa", "shasum": "" }, "require": { - "php": ">=7.1" + "php": ">=7.2" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -4911,7 +5980,7 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Php81\\": "" + "Symfony\\Polyfill\\Php84\\": "" }, "classmap": [ "Resources/stubs" @@ -4931,7 +6000,7 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", + "description": "Symfony polyfill backporting some PHP 8.4+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", @@ -4940,7 +6009,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php81/tree/v1.28.0" + "source": "https://github.com/symfony/polyfill-php84/tree/v1.38.1" }, "funding": [ { @@ -4951,30 +6020,33 @@ "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": "2023-01-26T09:26:14+00:00" + "time": "2026-05-26T12:51:13+00:00" }, { "name": "symfony/process", - "version": "v5.4.46", + "version": "v8.1.0", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "01906871cb9b5e3cf872863b91aba4ec9767daf4" + "reference": "c4a9e58f235a6bf7f97ffbfedae2687353ac79e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/01906871cb9b5e3cf872863b91aba4ec9767daf4", - "reference": "01906871cb9b5e3cf872863b91aba4ec9767daf4", + "url": "https://api.github.com/repos/symfony/process/zipball/c4a9e58f235a6bf7f97ffbfedae2687353ac79e5", + "reference": "c4a9e58f235a6bf7f97ffbfedae2687353ac79e5", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/polyfill-php80": "^1.16" + "php": ">=8.4.1" }, "type": "library", "autoload": { @@ -5002,7 +6074,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v5.4.46" + "source": "https://github.com/symfony/process/tree/v8.1.0" }, "funding": [ { @@ -5013,52 +6085,56 @@ "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": "2024-11-06T09:18:28+00:00" + "time": "2026-05-29T05:06:50+00:00" }, { "name": "symfony/service-contracts", - "version": "v2.5.2", + "version": "v3.7.0", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "4b426aac47d6427cc1a1d0f7e2ac724627f5966c" + "reference": "d25d82433a80eba6aa0e6c24b61d7370d99e444a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/4b426aac47d6427cc1a1d0f7e2ac724627f5966c", - "reference": "4b426aac47d6427cc1a1d0f7e2ac724627f5966c", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/d25d82433a80eba6aa0e6c24b61d7370d99e444a", + "reference": "d25d82433a80eba6aa0e6c24b61d7370d99e444a", "shasum": "" }, "require": { - "php": ">=7.2.5", - "psr/container": "^1.1", - "symfony/deprecation-contracts": "^2.1|^3" + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" }, "conflict": { "ext-psr": "<1.1|>=2" }, - "suggest": { - "symfony/service-implementation": "" - }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "2.5-dev" - }, "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.7-dev" } }, "autoload": { "psr-4": { "Symfony\\Contracts\\Service\\": "" - } + }, + "exclude-from-classmap": [ + "/Test/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -5085,7 +6161,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v2.5.2" + "source": "https://github.com/symfony/service-contracts/tree/v3.7.0" }, "funding": [ { @@ -5096,30 +6172,34 @@ "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": "2022-05-30T19:17:29+00:00" + "time": "2026-03-28T09:44:51+00:00" }, { "name": "symfony/stopwatch", - "version": "v5.4.21", + "version": "v8.1.0", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", - "reference": "f83692cd869a6f2391691d40a01e8acb89e76fee" + "reference": "21c07b026905d596e8379caeb115d87aa479499d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/f83692cd869a6f2391691d40a01e8acb89e76fee", - "reference": "f83692cd869a6f2391691d40a01e8acb89e76fee", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/21c07b026905d596e8379caeb115d87aa479499d", + "reference": "21c07b026905d596e8379caeb115d87aa479499d", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/service-contracts": "^1|^2|^3" + "php": ">=8.4.1", + "symfony/service-contracts": "^2.5|^3" }, "type": "library", "autoload": { @@ -5147,7 +6227,7 @@ "description": "Provides a way to profile code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/stopwatch/tree/v5.4.21" + "source": "https://github.com/symfony/stopwatch/tree/v8.1.0" }, "funding": [ { @@ -5158,43 +6238,47 @@ "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": "2023-02-14T08:03:56+00:00" + "time": "2026-05-29T05:06:50+00:00" }, { "name": "symfony/string", - "version": "v5.4.32", + "version": "v8.1.0", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "91bf4453d65d8231688a04376c3a40efe0770f04" + "reference": "afd5944f4005862d961efb85c8bbd5c523c4e3c9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/91bf4453d65d8231688a04376c3a40efe0770f04", - "reference": "91bf4453d65d8231688a04376c3a40efe0770f04", + "url": "https://api.github.com/repos/symfony/string/zipball/afd5944f4005862d961efb85c8bbd5c523c4e3c9", + "reference": "afd5944f4005862d961efb85c8bbd5c523c4e3c9", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-intl-grapheme": "~1.0", - "symfony/polyfill-intl-normalizer": "~1.0", - "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php80": "~1.15" + "php": ">=8.4.1", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-intl-grapheme": "^1.33", + "symfony/polyfill-intl-normalizer": "^1.0", + "symfony/polyfill-mbstring": "^1.0" }, "conflict": { - "symfony/translation-contracts": ">=3.0" + "symfony/translation-contracts": "<2.5" }, "require-dev": { - "symfony/error-handler": "^4.4|^5.0|^6.0", - "symfony/http-client": "^4.4|^5.0|^6.0", - "symfony/translation-contracts": "^1.1|^2", - "symfony/var-exporter": "^4.4|^5.0|^6.0" + "symfony/emoji": "^7.4|^8.0", + "symfony/http-client": "^7.4|^8.0", + "symfony/intl": "^7.4|^8.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -5233,7 +6317,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v5.4.32" + "source": "https://github.com/symfony/string/tree/v8.1.0" }, "funding": [ { @@ -5244,62 +6328,65 @@ "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": "2023-11-26T13:43:46+00:00" + "time": "2026-05-29T05:06:50+00:00" }, { "name": "symfony/translation", - "version": "v5.4.31", + "version": "v7.4.10", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "ba72f72fceddf36f00bd495966b5873f2d17ad8f" + "reference": "ada7578c30dd5feaa8259cff3e885069ea81ddde" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/ba72f72fceddf36f00bd495966b5873f2d17ad8f", - "reference": "ba72f72fceddf36f00bd495966b5873f2d17ad8f", + "url": "https://api.github.com/repos/symfony/translation/zipball/ada7578c30dd5feaa8259cff3e885069ea81ddde", + "reference": "ada7578c30dd5feaa8259cff3e885069ea81ddde", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0", - "symfony/polyfill-php80": "^1.16", - "symfony/translation-contracts": "^2.3" + "symfony/translation-contracts": "^2.5.3|^3.3" }, "conflict": { - "symfony/config": "<4.4", - "symfony/console": "<5.3", - "symfony/dependency-injection": "<5.0", - "symfony/http-kernel": "<5.0", - "symfony/twig-bundle": "<5.0", - "symfony/yaml": "<4.4" + "nikic/php-parser": "<5.0", + "symfony/config": "<6.4", + "symfony/console": "<6.4", + "symfony/dependency-injection": "<6.4", + "symfony/http-client-contracts": "<2.5", + "symfony/http-kernel": "<6.4", + "symfony/service-contracts": "<2.5", + "symfony/twig-bundle": "<6.4", + "symfony/yaml": "<6.4" }, "provide": { - "symfony/translation-implementation": "2.3" + "symfony/translation-implementation": "2.3|3.0" }, "require-dev": { + "nikic/php-parser": "^5.0", "psr/log": "^1|^2|^3", - "symfony/config": "^4.4|^5.0|^6.0", - "symfony/console": "^5.4|^6.0", - "symfony/dependency-injection": "^5.0|^6.0", - "symfony/finder": "^4.4|^5.0|^6.0", - "symfony/http-client-contracts": "^1.1|^2.0|^3.0", - "symfony/http-kernel": "^5.0|^6.0", - "symfony/intl": "^4.4|^5.0|^6.0", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/finder": "^6.4|^7.0|^8.0", + "symfony/http-client-contracts": "^2.5|^3.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/intl": "^6.4|^7.0|^8.0", "symfony/polyfill-intl-icu": "^1.21", - "symfony/service-contracts": "^1.1.2|^2|^3", - "symfony/yaml": "^4.4|^5.0|^6.0" - }, - "suggest": { - "psr/log-implementation": "To use logging capability in translator", - "symfony/config": "", - "symfony/yaml": "" + "symfony/routing": "^6.4|^7.0|^8.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/yaml": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -5330,7 +6417,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v5.4.31" + "source": "https://github.com/symfony/translation/tree/v7.4.10" }, "funding": [ { @@ -5341,47 +6428,51 @@ "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": "2023-11-03T16:16:43+00:00" + "time": "2026-05-06T11:19:24+00:00" }, { "name": "symfony/translation-contracts", - "version": "v2.5.2", + "version": "v3.7.0", "source": { "type": "git", "url": "https://github.com/symfony/translation-contracts.git", - "reference": "136b19dd05cdf0709db6537d058bcab6dd6e2dbe" + "reference": "0ab302977a952b42fd51475c4ebac81f8da0a95d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/136b19dd05cdf0709db6537d058bcab6dd6e2dbe", - "reference": "136b19dd05cdf0709db6537d058bcab6dd6e2dbe", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/0ab302977a952b42fd51475c4ebac81f8da0a95d", + "reference": "0ab302977a952b42fd51475c4ebac81f8da0a95d", "shasum": "" }, "require": { - "php": ">=7.2.5" - }, - "suggest": { - "symfony/translation-implementation": "" + "php": ">=8.1" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "2.5-dev" - }, "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.7-dev" } }, "autoload": { "psr-4": { "Symfony\\Contracts\\Translation\\": "" - } + }, + "exclude-from-classmap": [ + "/Test/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -5408,7 +6499,90 @@ "standards" ], "support": { - "source": "https://github.com/symfony/translation-contracts/tree/v2.5.2" + "source": "https://github.com/symfony/translation-contracts/tree/v3.7.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": "2026-01-05T13:30:16+00:00" + }, + { + "name": "symfony/var-exporter", + "version": "v8.1.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/var-exporter.git", + "reference": "2dd18582c5f6c024db9fc0ff9c76d873af726f34" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/2dd18582c5f6c024db9fc0ff9c76d873af726f34", + "reference": "2dd18582c5f6c024db9fc0ff9c76d873af726f34", + "shasum": "" + }, + "require": { + "php": ">=8.4.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-deepclone": "^1.37" + }, + "require-dev": { + "symfony/property-access": "^7.4|^8.0", + "symfony/serializer": "^7.4|^8.0", + "symfony/var-dumper": "^7.4|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\VarExporter\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides tools to export, instantiate, hydrate, clone and lazy-load PHP objects", + "homepage": "https://symfony.com", + "keywords": [ + "clone", + "construct", + "deep-clone", + "export", + "hydrate", + "instantiate", + "lazy-loading", + "proxy", + "serialize" + ], + "support": { + "source": "https://github.com/symfony/var-exporter/tree/v8.1.0" }, "funding": [ { @@ -5419,40 +6593,41 @@ "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": "2022-06-27T16:58:25+00:00" + "time": "2026-05-29T05:06:50+00:00" }, { "name": "symfony/yaml", - "version": "v5.4.31", + "version": "v7.4.13", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "f387675d7f5fc4231f7554baa70681f222f73563" + "reference": "a7ec3b1156faf8815db7683ec7c1e7338e6f977c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/f387675d7f5fc4231f7554baa70681f222f73563", - "reference": "f387675d7f5fc4231f7554baa70681f222f73563", + "url": "https://api.github.com/repos/symfony/yaml/zipball/a7ec3b1156faf8815db7683ec7c1e7338e6f977c", + "reference": "a7ec3b1156faf8815db7683ec7c1e7338e6f977c", "shasum": "" }, "require": { - "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1|^3", + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-ctype": "^1.8" }, "conflict": { - "symfony/console": "<5.3" + "symfony/console": "<6.4" }, "require-dev": { - "symfony/console": "^5.3|^6.0" - }, - "suggest": { - "symfony/console": "For validating YAML files using the lint command" + "symfony/console": "^6.4|^7.0|^8.0" }, "bin": [ "Resources/bin/yaml-lint" @@ -5483,7 +6658,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v5.4.31" + "source": "https://github.com/symfony/yaml/tree/v7.4.13" }, "funding": [ { @@ -5494,25 +6669,26 @@ "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": "2023-11-03T14:41:28+00:00" + "time": "2026-05-25T06:06:12+00:00" } ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": {}, "prefer-stable": true, "prefer-lowest": false, "platform": { - "php": ">=7.4 < 9" - }, - "platform-dev": [], - "platform-overrides": { - "php": "7.4" + "php": ">=7.4" }, - "plugin-api-version": "2.6.0" + "platform-dev": {}, + "plugin-api-version": "2.9.0" } diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..1dcca93 --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,30 @@ + + + + + + ./tests/Unit + + + ./tests/Integration + + + + + + ./php + + + + diff --git a/phpunit.xml b/phpunit9.xml.dist similarity index 94% rename from phpunit.xml rename to phpunit9.xml.dist index 937ae22..45f900f 100644 --- a/phpunit.xml +++ b/phpunit9.xml.dist @@ -2,7 +2,6 @@ Date: Tue, 23 Jun 2026 16:52:06 +0900 Subject: [PATCH 02/16] refactor: IsEqualHtml uses matches()/toString(), drop IsEqual coupling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Override matches()+toString() instead of evaluate() (stable across PHPUnit 9–13) - Single-arg constructor (delta/canonicalize/ignoreCase were always defaults) - Avoid Constraint::exporter() (removed in PHPUnit 11) — render message directly - Preserve the existing "html is equal to '...'" failure-message format - Dual-declare #[CoversClass]/#[DataProvider]; make data provider static (PHPUnit 10+) Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01K8HGMxCuSjUX6p4PQL7t5Y --- php/WP_Mock/Tools/Constraints/IsEqualHtml.php | 73 ++++++------------- .../Tools/Constraints/IsEqualHtmlTest.php | 71 ++++++------------ 2 files changed, 43 insertions(+), 101 deletions(-) diff --git a/php/WP_Mock/Tools/Constraints/IsEqualHtml.php b/php/WP_Mock/Tools/Constraints/IsEqualHtml.php index 0d0b525..c07e3f6 100644 --- a/php/WP_Mock/Tools/Constraints/IsEqualHtml.php +++ b/php/WP_Mock/Tools/Constraints/IsEqualHtml.php @@ -2,93 +2,64 @@ namespace WP_Mock\Tools\Constraints; -use Exception; use PHPUnit\Framework\Constraint\Constraint; -use PHPUnit\Framework\Constraint\IsEqual; -use PHPUnit\Framework\ExpectationFailedException; /** * HTML string constraint. + * + * Compares two HTML strings for equality while ignoring insignificant whitespace + * (tabs, newlines, carriage returns and collapsed runs of whitespace). */ class IsEqualHtml extends Constraint { /** @var string */ protected $value; - /** @var float */ - private $delta; - - /** @var bool */ - private $canonicalize; - - /** @var bool */ - private $ignoreCase; - /** * Constructor. * - * @param string $value - * @param float $delta - * @param bool $canonicalize - * @param bool $ignoreCase + * @param string $value the expected HTML */ - public function __construct(string $value, float $delta = 0.0, bool $canonicalize = false, bool $ignoreCase = false) + public function __construct(string $value) { $this->value = $value; - $this->delta = $delta; - $this->canonicalize = $canonicalize; - $this->ignoreCase = $ignoreCase; } /** - * Trims and removes tabs, newlines and return carriages from a string. + * Evaluates whether $other equals the expected HTML, ignoring insignificant whitespace. * - * @param string $value - * @return string + * @param mixed $other value to evaluate (untyped for PHP 7.4 compatibility; the parent declares `mixed` on PHPUnit 10+) + * @return bool */ - protected function clean(string $value): string + public function matches($other): bool { - $value = preg_replace('/\n\s+/', '', $value) ?: ''; - $value = preg_replace('/\s\s+/', ' ', $value) ?: ''; - - return str_replace(array( "\r", "\n", "\t" ), '', $value); + return $this->clean((string) $other) === $this->clean($this->value); } /** - * Evaluates the constraint for parameter $other. + * Returns a string representation of the constraint. * - * If $returnResult is false (default), an exception is thrown in case of a failure. null is returned otherwise. - * If $returnResult is true, the result of the evaluation is returned as a boolean instead, based on success or failure. + * @see Constraint::toString() * - * @param string $other value to evaluate - * @param string $description message used in failures - * @param bool $returnResult whether to throw an exception in case of failure or return boolean - * @return bool|null - * @throws ExpectationFailedException + * @return string */ - public function evaluate($other, string $description = '', bool $returnResult = false): ?bool + public function toString(): string { - $other = $this->clean($other); - $this->value = $this->clean($this->value); - - $isEqual = new IsEqual($this->value, $this->delta, $this->canonicalize, $this->ignoreCase); - $result = $isEqual->evaluate($other, $description, $returnResult); - - return $returnResult ? $result : null; + // Note: do NOT use Constraint::exporter() here — it was removed in PHPUnit 11.0. + return sprintf("html is equal to '%s'", $this->clean($this->value)); } /** - * Returns a string representation of the constraint. - * - * @see Constraint::toString() + * Trims and removes tabs, newlines and return carriages from a string. * + * @param string $value * @return string - * @throws Exception */ - public function toString(): string + protected function clean(string $value): string { - $isEqual = new IsEqual($this->value, $this->delta, $this->canonicalize, $this->ignoreCase); + $value = preg_replace('/\n\s+/', '', $value) ?: ''; + $value = preg_replace('/\s\s+/', ' ', $value) ?: ''; - return 'html '.$isEqual->toString(); + return str_replace(["\r", "\n", "\t"], '', $value); } } diff --git a/tests/Unit/WP_Mock/Tools/Constraints/IsEqualHtmlTest.php b/tests/Unit/WP_Mock/Tools/Constraints/IsEqualHtmlTest.php index 444650f..18935db 100644 --- a/tests/Unit/WP_Mock/Tools/Constraints/IsEqualHtmlTest.php +++ b/tests/Unit/WP_Mock/Tools/Constraints/IsEqualHtmlTest.php @@ -4,7 +4,8 @@ use Exception; use Generator; -use PHPUnit\Framework\ExpectationFailedException; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\DataProvider; use ReflectionException; use ReflectionMethod; use ReflectionProperty; @@ -14,6 +15,7 @@ /** * @covers \WP_Mock\Tools\Constraints\IsEqualHtml */ +#[CoversClass(IsEqualHtml::class)] final class IsEqualHtmlTest extends WP_MockTestCase { /** @@ -24,21 +26,12 @@ final class IsEqualHtmlTest extends WP_MockTestCase */ public function testConstructor(): void { - $props = [ - 'value' => 'Test', - 'delta' => 1.2, - 'canonicalize' => true, - 'ignoreCase' => true, - ]; - - $constraint = new IsEqualHtml($props['value'], $props['delta'], $props['canonicalize'], $props['ignoreCase']); + $constraint = new IsEqualHtml('Test'); - foreach ($props as $key => $value) { - $property = new ReflectionProperty($constraint, $key); - $property->setAccessible(true); + $property = new ReflectionProperty($constraint, 'value'); + $property->setAccessible(true); - $this->assertSame($value, $property->getValue($constraint)); - } + $this->assertSame('Test', $property->getValue($constraint)); } /** @@ -58,65 +51,43 @@ public function testCanClean(): void } /** - * @covers \WP_Mock\Tools\Constraints\IsEqualHtml::evaluate() - * @dataProvider providerEvaluate + * @covers \WP_Mock\Tools\Constraints\IsEqualHtml::matches() + * @dataProvider providerMatches * * @param string $value * @param string $otherValue - * @param bool $returnResult - * @param bool|null $expected - * @param bool|null $throwsException + * @param bool $expected * @return void * @throws Exception */ - public function testCanEvaluate( - string $value, - string $otherValue, - bool $returnResult, - ?bool $expected, - ?bool $throwsException = null - ): void { + #[DataProvider('providerMatches')] + public function testCanMatch(string $value, string $otherValue, bool $expected): void + { $constraint = new IsEqualHtml($value); - if ($throwsException) { - $this->expectException(ExpectationFailedException::class); - } - - $this->assertSame($expected, $constraint->evaluate($otherValue, 'Test error message', $returnResult)); + $this->assertSame($expected, $constraint->matches($otherValue)); } - /** @see testCanEvaluate */ - public function providerEvaluate(): Generator + /** @see testCanMatch */ + public static function providerMatches(): Generator { - yield 'The two HTML strings are the same (return bool)' => [ + yield 'identical HTML' => [ 'value' => 'Test', 'otherValue' => 'Test', - 'returnResult' => true, 'expected' => true, ]; - yield 'The two HTML strings are the same (throw exception)' => [ - 'value' => 'Test', + yield 'whitespace-insensitive match' => [ + 'value' => "\n\t Test", 'otherValue' => 'Test', - 'returnResult' => false, - 'expected' => null, - 'throwsException' => false, + 'expected' => true, ]; - yield 'The two HTML strings are not the same (return bool)' => [ + yield 'different HTML' => [ 'value' => 'Test', 'otherValue' => 'Test', - 'returnResult' => true, 'expected' => false, ]; - - yield 'The two HTML strings are not the same (throw exception)' => [ - 'value' => 'Test', - 'otherValue' => 'Test', - 'returnResult' => false, - 'expected' => null, - 'throwsException' => true, - ]; } /** From ce80705bd317d8c56f08e96ba8d53b48a96893be Mon Sep 17 00:00:00 2001 From: Fulvio Notarstefano Date: Tue, 23 Jun 2026 17:06:16 +0900 Subject: [PATCH 03/16] refactor!: drop removed PHPUnit-10+ internals from TestCase + deprecation listener BREAKING (internal/behavioral): - DeprecatedMethodListener now emits E_USER_DEPRECATED via trigger_error() instead of injecting a RiskyTestError into a TestResult. Removes setTestResult/setTestCase/checkCalls (TestResult + RiskyTestError were removed in PHPUnit 10). A deprecated call now surfaces as a deprecation (fails the suite under failOnDeprecation / convertDeprecationsToExceptions). - TestCase::run() override removed (existed only to wire the listener to TestResult). - Content-filtering magic removed: setUpContentFiltering(), the expectOutputString() override, setOutputCallback() (removed in PHPUnit 10), expectOutputString() became final in 10, the @stripTabsAndNewlinesFromOutput annotation, stripTabsAndNewlines(), $__contentFilterCallback. Replaced by an explicit, cross-version helper: assertOutputEqualsHtml(string, callable). - setUp()/tearDown() retained; Util\Test::parseTestMethodAnnotations()/getName() no longer used. Tests: rewrite DeprecatedMethodListenerTest (capture E_USER_DEPRECATED) and TestCaseTest (createPartialMock instead of removed getMockForAbstractClass; dual annotation+attribute; static data providers). Green on PHPUnit 9.6 (full suite) and 13 (these files). Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01K8HGMxCuSjUX6p4PQL7t5Y --- php/WP_Mock/DeprecatedMethodListener.php | 144 ++------ php/WP_Mock/Tools/TestCase.php | 122 +------ .../WP_Mock/DeprecatedMethodListenerTest.php | 316 ++++-------------- tests/Unit/WP_Mock/Tools/TestCaseTest.php | 205 +++--------- 4 files changed, 161 insertions(+), 626 deletions(-) diff --git a/php/WP_Mock/DeprecatedMethodListener.php b/php/WP_Mock/DeprecatedMethodListener.php index dffbfc6..e17b853 100644 --- a/php/WP_Mock/DeprecatedMethodListener.php +++ b/php/WP_Mock/DeprecatedMethodListener.php @@ -2,19 +2,18 @@ namespace WP_Mock; -use Mockery\MockInterface; -use PHPUnit\Framework\RiskyTestError; -use PHPUnit\Framework\TestCase; -use PHPUnit\Framework\TestResult; - /** * Internal handler for deprecated method calls. * - * This handler is used by WP_Mock to alert developers if they are using any WP_Mock deprecated methods. - * Test cases using WP_Mock deprecated methods will report as risky. - * In this way we can ensure that developers are aware of the deprecation and can update their code before any deprecated methods are permanently removed. + * Flags usage of deprecated WP_Mock methods by emitting an {@see E_USER_DEPRECATED} notice, + * which PHPUnit captures and attributes to the running test natively across all supported + * versions: + * - PHPUnit 9.x: surfaces (and, with `convertDeprecationsToExceptions="true"`, fails the test). + * - PHPUnit 10+: reported per test; fails the suite when `failOnDeprecation="true"`. + * + * To flag a method as deprecated, call the following from within the deprecated method's logic: * - * To flag a method as deprecated use {@see \WP_Mock::getDeprecatedMethodListener()->logDeprecatedCall()} within a deprecated method's logic. + * \WP_Mock::getDeprecatedMethodListener()->logDeprecatedCall(__METHOD__, func_get_args()); */ class DeprecatedMethodListener { @@ -24,12 +23,6 @@ class DeprecatedMethodListener /** @var string */ protected $testName = 'test'; - /** @var TestCase|MockInterface */ - protected $testCase; - - /** @var TestResult|MockInterface */ - protected $testResult; - /** * Sets the test name in context. * @@ -44,33 +37,11 @@ public function setTestName(string $testName): DeprecatedMethodListener } /** - * Sets the test case in context. + * Logs a deprecated method call and emits a deprecation notice. * - * @param TestCase|MockInterface $testCase - * @return $this - */ - public function setTestCase($testCase): DeprecatedMethodListener - { - $this->testCase = $testCase; - - return $this; - } - - /** - * Sets the test result in context. - * - * @param TestResult|MockInterface $testResult - * @return $this - */ - public function setTestResult($testResult): DeprecatedMethodListener - { - $this->testResult = $testResult; - - return $this; - } - - /** - * Logs a deprecated method call. + * The call is recorded (for inspection via {@see DeprecatedMethodListener::reset()} consumers) + * and an {@see E_USER_DEPRECATED} notice is triggered immediately so PHPUnit reports it against + * the running test. * * @param string $method * @param array $args @@ -80,6 +51,8 @@ public function logDeprecatedCall(string $method, array $args = []): DeprecatedM { $this->deprecatedCalls[] = [$method, $args]; + trigger_error($this->buildMessage($method, $args), E_USER_DEPRECATED); + return $this; } @@ -96,98 +69,23 @@ public function reset(): DeprecatedMethodListener } /** - * Checks for deprecated method calls. - * - * Adds failures to the test result if any are found. - * - * @return void - */ - public function checkCalls(): void - { - if (empty($this->deprecatedCalls)) { - return; - } - - $error = new RiskyTestError($this->buildErrorMessage()); - - /** @phpstan-ignore-next-line */ - $this->testResult->addFailure($this->testCase, $error, 0); - } - - /** - * Gets a deprecated method call usage message. + * Builds the deprecation message for a single deprecated method call. * + * @param string $method + * @param array $args * @return string */ - protected function buildErrorMessage(): string + protected function buildMessage(string $method, array $args): string { - $maxLength = array_reduce($this->getDeprecatedMethods(), function ($carry, $item) { - return max($carry, strlen($item)); - }, 0) + 1; - - $message = sprintf('Deprecated WP Mock calls inside %s:', $this->testName); - - foreach ($this->getDeprecatedMethodsWithArgs() as $method => $args) { - $firstRun = true; - $extra = $maxLength - strlen($method); - - foreach ($args as $arg) { - $message .= "\n "; - - if ($firstRun) { - $message .= $method . str_repeat(' ', $extra); - $firstRun = false; - $extra = $maxLength; - } else { - $message .= str_repeat(' ', $extra); - } + $message = sprintf('Deprecated WP_Mock call inside %s: %s', $this->testName, $method); - $message .= $arg; - } + if (! empty($args)) { + $message .= ' '.json_encode(array_map([$this, 'toScalar'], $args)); } return $message; } - /** - * Gets a list of deprecated methods having been called. - * - * @return string[] - */ - protected function getDeprecatedMethods(): array - { - $methods = []; - - foreach ($this->deprecatedCalls as $call) { - $methods[] = $call[0]; - } - - return array_unique($methods); - } - - /** - * Gets a list of deprecated methods having been called, with their arguments formatted as JSON. - * - * @return array> - */ - protected function getDeprecatedMethodsWithArgs(): array - { - $collection = []; - - foreach ($this->deprecatedCalls as $call) { - $method = $call[0]; - $args = json_encode(array_map([$this, 'toScalar'], $call[1])); - - if (empty($collection[$method])) { - $collection[$method] = []; - } - - $collection[$method][] = $args; - } - - return array_map('array_unique', $collection); - } - /** * Transforms a value for use in a JSON string. * diff --git a/php/WP_Mock/Tools/TestCase.php b/php/WP_Mock/Tools/TestCase.php index 28d5f3c..4927639 100644 --- a/php/WP_Mock/Tools/TestCase.php +++ b/php/WP_Mock/Tools/TestCase.php @@ -7,8 +7,6 @@ use Mockery; use PHPUnit\Framework\ExpectationFailedException; use PHPUnit\Framework\TestCase as PhpUnitTestCase; -use PHPUnit\Framework\TestResult; -use PHPUnit\Util\Test; use ReflectionException; use ReflectionMethod; use RuntimeException; @@ -40,9 +38,6 @@ abstract class TestCase extends PhpUnitTestCase /** @var array */ protected $__default_request = []; - /** @var bool|callable */ - protected $__contentFilterCallback = false; - /** @var array */ protected $testFiles = []; @@ -64,7 +59,6 @@ public function setUp(): void $_POST = (array) $this->__default_post; $_REQUEST = (array) $this->__default_request; - $this->setUpContentFiltering(); $this->cleanGlobals(); } @@ -101,53 +95,6 @@ public function tearDown(): void $_GET = $_POST = $_REQUEST = []; } - /** - * Runs the test case and collects the results in a {@see TestResult} object. - * - * If no {@see TestResult} object is passed a new one will be created. - * - * @param TestResult|null $result - * @return TestResult - * @throws Exception - */ - public function run(?TestResult $result = null): TestResult - { - if ($result === null) { - $result = $this->createResult(); - } - - WP_Mock::getDeprecatedMethodListener() - ->setTestResult($result) - ->setTestCase($this); - - return parent::run($result); - } - - /** - * Runs logic after every test. - * - * @after - * - * @return void - */ - public function after(): void - { - $this->checkDeprecatedCalls(); - } - - /** - * Checks for deprecated usage calls. - * - * This method is called after every test to check if any deprecated WP_Mock functions are used. - * - * @return void - */ - protected function checkDeprecatedCalls(): void - { - WP_Mock::getDeprecatedMethodListener()->checkCalls(); - WP_Mock::getDeprecatedMethodListener()->reset(); - } - /** * Cleans common WordPress globals that may have been used in between tests. * @@ -167,49 +114,6 @@ protected function cleanGlobals(): void } } - /** - * Sets up content filtering. - * - * @return void - * @throws Exception - */ - protected function setUpContentFiltering(): void - { - $this->__contentFilterCallback = false; - - $annotations = Test::parseTestMethodAnnotations( - static::class, - $this->getName(false) - ); - - if ( - ! isset($annotations['stripTabsAndNewlinesFromOutput']) || - $annotations['stripTabsAndNewlinesFromOutput'][0] !== 'disabled' || - ( - /** @phpstan-ignore-next-line */ - is_numeric($annotations['stripTabsAndNewlinesFromOutput'][0]) && - (int) $annotations['stripTabsAndNewlinesFromOutput'][0] !== 0 - ) - ) { - $this->__contentFilterCallback = [$this, 'stripTabsAndNewlines']; - $this->setOutputCallback($this->__contentFilterCallback); - } - } - - /** - * Strips tabs, newlines and carriage returns from a value. - * - * @internal may change to protected access in future versions - * @see TestCase::setUpContentFiltering() - * - * @param string|string[] $value - * @return string|string[] - */ - public function stripTabsAndNewlines($value) - { - return str_replace([ "\t", "\r", "\n"], '', $value); - } - /** * Asserts that all actions have been called. * @@ -292,23 +196,29 @@ public function assertEqualsHtml(string $expected, string $actual, string $messa } /** - * Sets the expectation that a string will be output. + * Asserts that the HTML output produced by the given callback equals the expected HTML, ignoring insignificant whitespace. + * + * Cross-version replacement for the implicit output filtering that previously relied on + * PHPUnit's setOutputCallback() (removed in PHPUnit 10) and the expectOutputString() override + * (expectOutputString() became final in PHPUnit 10). * - * @param string $expectedString + * @param string $expectedHtml the expected HTML + * @param callable $callback code that echoes or prints output + * @param string $message * @return void - * @throws InvalidArgumentException + * @throws ExpectationFailedException|Exception */ - public function expectOutputString(string $expectedString): void + public function assertOutputEqualsHtml(string $expectedHtml, callable $callback, string $message = ''): void { - if (is_callable($this->__contentFilterCallback)) { - $expectedString = call_user_func($this->__contentFilterCallback, $expectedString); - } + ob_start(); - if (! is_string($expectedString)) { - throw new InvalidArgumentException(sprintf('%1$s expects string, %2$s passed from content filter callback.', __METHOD__, gettype($expectedString))); + try { + $callback(); + } finally { + $actual = ob_get_clean(); } - parent::expectOutputString($expectedString); + $this->assertThat((string) $actual, new IsEqualHtml($expectedHtml), $message); } /** diff --git a/tests/Unit/WP_Mock/DeprecatedMethodListenerTest.php b/tests/Unit/WP_Mock/DeprecatedMethodListenerTest.php index 98f76af..2f90257 100644 --- a/tests/Unit/WP_Mock/DeprecatedMethodListenerTest.php +++ b/tests/Unit/WP_Mock/DeprecatedMethodListenerTest.php @@ -4,11 +4,8 @@ use Exception; use Generator; -use Mockery; -use PHPUnit\Framework\Assert; -use PHPUnit\Framework\RiskyTestError; -use PHPUnit\Framework\TestCase; -use PHPUnit\Framework\TestResult; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\DataProvider; use ReflectionException; use ReflectionMethod; use ReflectionProperty; @@ -20,6 +17,7 @@ /** * @covers \WP_Mock\DeprecatedMethodListener */ +#[CoversClass(DeprecatedMethodListener::class)] final class DeprecatedMethodListenerTest extends WP_MockTestCase { /** @var DeprecatedMethodListener */ @@ -32,6 +30,8 @@ final class DeprecatedMethodListenerTest extends WP_MockTestCase */ protected function setUp(): void { + parent::setUp(); + $this->object = new DeprecatedMethodListener(); } @@ -40,82 +40,101 @@ protected function setUp(): void * * @return void */ - public function tearDown(): void + protected function tearDown(): void { $this->object->reset(); + + parent::tearDown(); } /** - * @covers \WP_Mock\DeprecatedMethodListener::setTestName() + * Captures the {@see E_USER_DEPRECATED} messages emitted while running $callback. * - * @return void - * @throws ReflectionException|Exception + * A local error handler intercepts the notices so neither PHPUnit 9's + * `convertDeprecationsToExceptions` nor PHPUnit 10+'s `failOnDeprecation` + * interferes with the assertions. + * + * @param callable $callback + * @return string[] the captured deprecation messages, in order */ - public function testCanSetTestName(): void + protected function captureDeprecations(callable $callback): array { - $property = new ReflectionProperty($this->object, 'testName'); - $property->setAccessible(true); + $messages = []; - $this->assertSame('test', $property->getValue($this->object)); + set_error_handler(static function (int $errno, string $errstr) use (&$messages): bool { + $messages[] = $errstr; - $this->assertSame($this->object, $this->object->setTestName('FooBar')); + return true; + }, E_USER_DEPRECATED); - $this->assertSame('FooBar', $property->getValue($this->object)); + try { + $callback(); + } finally { + restore_error_handler(); + } + + return $messages; } /** - * @covers \WP_Mock\DeprecatedMethodListener::setTestCase() + * @covers \WP_Mock\DeprecatedMethodListener::setTestName() * * @return void * @throws ReflectionException|Exception */ - public function testCanSetTestCase(): void + public function testCanSetTestName(): void { - /** @var TestCase&Mockery\MockInterface $testCase */ - $testCase = Mockery::mock(TestCase::class); + $property = new ReflectionProperty($this->object, 'testName'); + $property->setAccessible(true); - $this->assertSame($this->object, $this->object->setTestCase($testCase)); + $this->assertSame('test', $property->getValue($this->object)); - $property = new ReflectionProperty($this->object, 'testCase'); - $property->setAccessible(true); + $this->assertSame($this->object, $this->object->setTestName('FooBar')); - $this->assertSame($testCase, $property->getValue($this->object)); + $this->assertSame('FooBar', $property->getValue($this->object)); } /** - * @covers \WP_Mock\DeprecatedMethodListener::setTestResult() + * @covers \WP_Mock\DeprecatedMethodListener::logDeprecatedCall() * * @return void * @throws ReflectionException|Exception */ - public function testCanSetTestResult(): void + public function testLogDeprecatedCallRecordsAndTriggersDeprecation(): void { - $concreteTestResult = new TestResult(); - /** @var TestResult&Mockery\MockInterface $mockTestResult @phpstan-ignore-line */ - $mockTestResult = Mockery::mock($concreteTestResult); + $method = 'Foo::bar'; + $args = [42]; - $this->assertSame($this->object, $this->object->setTestResult($mockTestResult)); + $messages = $this->captureDeprecations(function () use ($method, $args) { + $this->assertSame($this->object, $this->object->logDeprecatedCall($method, $args)); + }); - $property = new ReflectionProperty($this->object, 'testResult'); - $property->setAccessible(true); + // the call is recorded for inspection + $this->assertSame([[$method, $args]], $this->getDeprecatedMethodCalls($this->object)); - $this->assertSame($mockTestResult, $property->getValue($this->object)); + // exactly one E_USER_DEPRECATED was emitted, mentioning the method and its args + $this->assertCount(1, $messages); + $this->assertStringContainsString('Foo::bar', $messages[0]); + $this->assertStringContainsString('[42]', $messages[0]); } /** * @covers \WP_Mock\DeprecatedMethodListener::logDeprecatedCall() + * @covers \WP_Mock\DeprecatedMethodListener::buildMessage() * * @return void - * @throws ReflectionException|Exception + * @throws Exception */ - public function testCanLogDeprecatedCall(): void + public function testDeprecationMessageIncludesTestNameAndArgs(): void { - $method = 'Foo::bar'.rand(0, 9); - $args = [rand(10, 99)]; + $this->object->setTestName('MyTest'); - $this->assertSame($this->object, $this->object->logDeprecatedCall($method, $args)); + $messages = $this->captureDeprecations(function () { + $this->object->logDeprecatedCall('Foo::bar', ['baz']); + }); - $this->assertEquals([[$method, $args]], $this->getDeprecatedMethodCalls($this->object)); + $this->assertCount(1, $messages); + $this->assertSame('Deprecated WP_Mock call inside MyTest: Foo::bar ["baz"]', $messages[0]); } /** @@ -126,198 +145,14 @@ public function testCanLogDeprecatedCall(): void */ public function testCanResetDeprecatedCallsLog(): void { - $this->assertSame($this->object, $this->object->logDeprecatedCall('Foo::bar', ['baz'])); + $this->captureDeprecations(function () { + $this->object->logDeprecatedCall('Foo::bar', ['baz']); + }); + $this->assertSame($this->object, $this->object->reset()); $this->assertSame([], $this->getDeprecatedMethodCalls($this->object)); } - /** - * @covers \WP_Mock\DeprecatedMethodListener::checkCalls() - * - * @return void - * @throws Exception - */ - public function testCheckDeprecatedMethodCallsWithNoCallsMade(): void - { - $concreteTestResult = new TestResult(); - /** @var TestResult&Mockery\MockInterface $mockTestResult @phpstan-ignore-line */ - $mockTestResult = Mockery::mock($concreteTestResult); - $mockTestResult->expects('addFailure')->never(); - - $this->object->setTestResult($mockTestResult); - $this->object->checkCalls(); - - $this->assertConditionsMet(); - } - - /** - * @covers \WP_Mock\DeprecatedMethodListener::checkCalls() - * - * @return void - */ - public function testCanCheckDeprecatedMethodCallsWithScalarArgs(): void - { - $this->object->logDeprecatedCall('FooBar::bazBat', ['string', true, 42]); - $this->object->setTestName('TestName'); - - /** @var TestCase&Mockery\MockInterface $mockTestCase */ - $mockTestCase = Mockery::mock(TestCase::class); - - $this->object->setTestCase($mockTestCase); - - $concreteTestResult = new TestResult(); - /** @var TestResult&Mockery\MockInterface $mockTestResult @phpstan-ignore-line */ - $mockTestResult = Mockery::mock($concreteTestResult)->makePartial(); - - $mockTestResult->expects('addFailure') - ->once() - ->andReturnUsing(function ($concreteCase, $exception, $int) use ($mockTestCase) { - $int = (int) $int; // It's coming as 0.0 - Assert::assertSame($mockTestCase, $concreteCase); - Assert::assertTrue($exception instanceof RiskyTestError); - $message = <<getMessage()); - Assert::assertTrue(0 === $int); - }); - - $this->object->setTestResult($mockTestResult); - $this->object->checkCalls(); - } - - /** - * @covers \WP_Mock\DeprecatedMethodListener::checkCalls() - * - * @return void - * @throws Exception - */ - public function testCanCheckDeprecatedMethodCallsWithNonScalarArgs(): void - { - $object1 = Mockery::mock('WP_Query'); - $range = rand(5, 10); - $resource = fopen('php://temp', 'r'); - $callback1 = function () { - }; - - $this->object->logDeprecatedCall('BazBat::fooBar', [$callback1]); - $this->object->logDeprecatedCall('BazBat::fooBar', [$object1]); - $this->object->logDeprecatedCall('BazBat::fooBar', [$object1]); - $this->object->logDeprecatedCall('LongerClassName::callback', [[$object1, 'shouldReceive']]); - $this->object->logDeprecatedCall('BazBat::fooBar', [range(1, $range), $resource]); - $this->object->setTestName('OtherTest'); - - /** @var TestCase&Mockery\MockInterface $mockTestCase @phpstan-ignore-line */ - $mockTestCase = Mockery::mock(TestCase::class); - - $this->object->setTestCase($mockTestCase); - - $concreteTestResult = new TestResult(); - /** @var TestResult&Mockery\MockInterface $mockTestResult @phpstan-ignore-line */ - $mockTestResult = Mockery::mock($concreteTestResult); - - $testClosure = function ($case, $exception, $int) use ($mockTestCase, $callback1, $object1, $range) { - $int = (int) $int; // It's coming as 0.0 - $callback1 = get_class($callback1) . ':' . spl_object_hash($callback1); - $object1 = get_class($object1) . ':' . spl_object_hash($object1); - - Assert::assertSame($mockTestCase, $case); - Assert::assertTrue($exception instanceof RiskyTestError); - - $message = <<"] - ["<$object1>"] - ["Array([$range] ...)","Resource"] - LongerClassName::callback ["[<$object1>,shouldReceive]"] -EOT; - Assert::assertEquals($message, $exception->getMessage()); - Assert::assertTrue(0 === $int); - }; - - $mockTestResult->expects('addFailure') - ->once() - ->andReturnUsing($testClosure); - - $this->object->setTestResult($mockTestResult); - - try { - $this->object->checkCalls(); - } catch (Exception $exception) { - fclose($resource); // @phpstan-ignore-line - - throw $exception; - } - - fclose($resource); // @phpstan-ignore-line - } - - /** - * @covers \WP_Mock\DeprecatedMethodListener::buildErrorMessage() - * - * @return void - * @throws ReflectionException|Exception - */ - public function testCanBuildErrorMessage(): void - { - $instance = new DeprecatedMethodListener(); - $instance->setTestName('MyTest'); - $instance->logDeprecatedCall('Foo::bar', ['baz']); - - $method = new ReflectionMethod($instance, 'buildErrorMessage'); - $method->setAccessible(true); - - $expectedMessage = 'Deprecated WP Mock calls inside MyTest:'."\n ".'Foo::bar ["baz"]'; - - $this->assertSame($expectedMessage, $method->invoke($instance)); - } - - /** - * @covers \WP_Mock\DeprecatedMethodListener::getDeprecatedMethods() - * - * @return void - * @throws ReflectionException|Exception - */ - public function testCanGetDeprecatedMethods(): void - { - $instance = new DeprecatedMethodListener(); - $instance->logDeprecatedCall('Foo::bar', ['baz']); - $instance->logDeprecatedCall('Boz::qux'); - - $method = new ReflectionMethod($instance, 'getDeprecatedMethods'); - $method->setAccessible(true); - - $this->assertSame(['Foo::bar', 'Boz::qux'], $method->invoke($instance)); - } - - /** - * @covers \WP_Mock\DeprecatedMethodListener::getDeprecatedMethodsWithArgs() - * - * @return void - * @throws ReflectionException|Exception - */ - public function testCanGetDeprecatedMethodsWithArgs(): void - { - $instance = new DeprecatedMethodListener(); - $instance->logDeprecatedCall('Foo::bar', ['baz']); - $instance->logDeprecatedCall('Boz::qux'); - - $method = new ReflectionMethod($instance, 'getDeprecatedMethodsWithArgs'); - $method->setAccessible(true); - - $expected = [ - 'Foo::bar' => [ - '["baz"]' - ], - 'Boz::qux' => [ - '[]' - ], - ]; - - $this->assertSame($expected, $method->invoke($instance)); - } - /** * @covers \WP_Mock\DeprecatedMethodListener::toScalar() * @dataProvider providerConvertsArgumentsToScalarValue @@ -327,6 +162,7 @@ public function testCanGetDeprecatedMethodsWithArgs(): void * @return void * @throws ReflectionException|Exception */ + #[DataProvider('providerConvertsArgumentsToScalarValue')] public function testCanConvertArgumentsToScalarValue($arg, $expected): void { $instance = new DeprecatedMethodListener(); @@ -343,7 +179,7 @@ public function testCanConvertArgumentsToScalarValue($arg, $expected): void } /** @see testCanConvertArgumentsToScalarValue */ - public function providerConvertsArgumentsToScalarValue(): Generator + public static function providerConvertsArgumentsToScalarValue(): Generator { yield 'null' => [null, null]; yield 'true' => [true, true]; @@ -365,16 +201,10 @@ public function providerConvertsArgumentsToScalarValue(): Generator * @return void * @throws Exception */ - public function testCanHandleDeprecatedMethodCall(): void + public function testCanHandleDeprecatedMethodCallThroughWpMock(): void { $deprecatedMethodListener = new DeprecatedMethodListener(); - $concreteTestResult = new TestResult(); - /** @var TestResult&Mockery\MockInterface $mockTestResult @phpstan-ignore-line */ - $mockTestResult = Mockery::mock($concreteTestResult); - /** @var TestCase&Mockery\MockInterface $mockTestCase */ - $mockTestCase = Mockery::mock(TestCase::class); - $instance = new class ($deprecatedMethodListener) extends WP_Mock { /** * @param DeprecatedMethodListener $deprecatedMethodListener @@ -396,18 +226,16 @@ public function deprecatedMethod(array $args = []): string } }; - $mockTestResult->expects('addFailure') - ->once() - ->with($mockTestCase, Mockery::type(RiskyTestError::class), 0); - - $deprecatedMethodListener->setTestCase($mockTestCase); - $deprecatedMethodListener->setTestResult($mockTestResult); - - $instance->deprecatedMethod(['foo' => 'bar']); + $result = null; - $deprecatedMethodListener->checkCalls(); + $messages = $this->captureDeprecations(function () use ($instance, &$result) { + $result = $instance->deprecatedMethod(['foo' => 'bar']); + }); - $this->assertConditionsMet(); + // the deprecated method still returns normally (a deprecation is a notice, not a hard stop) + $this->assertSame('test', $result); + $this->assertCount(1, $messages); + $this->assertStringContainsString('deprecatedMethod', $messages[0]); } /** diff --git a/tests/Unit/WP_Mock/Tools/TestCaseTest.php b/tests/Unit/WP_Mock/Tools/TestCaseTest.php index f9e6e84..4f138a7 100644 --- a/tests/Unit/WP_Mock/Tools/TestCaseTest.php +++ b/tests/Unit/WP_Mock/Tools/TestCaseTest.php @@ -4,20 +4,25 @@ use Exception; use Generator; -use InvalidArgumentException; use Mockery; +use PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\PreserveGlobalState; +use PHPUnit\Framework\Attributes\RunInSeparateProcess; use PHPUnit\Framework\ExpectationFailedException; use ReflectionException; use ReflectionMethod; use ReflectionProperty; use WP_Mock; -use WP_Mock\DeprecatedMethodListener; use WP_Mock\Tests\WP_MockTestCase; use WP_Mock\Tools\TestCase; /** * @covers \WP_Mock\Tools\TestCase */ +#[CoversClass(TestCase::class)] +#[AllowMockObjectsWithoutExpectations] final class TestCaseTest extends WP_MockTestCase { /** @@ -32,12 +37,10 @@ public function testCanSetUpTests(): void $_GET = 'test_get'; $_REQUEST = 'test_request'; - $methods = ['requireFileDependencies', 'setUpContentFiltering', 'cleanGlobals']; - $instance = $this->getMockForAbstractClass(TestCase::class, [], '', false, false, true, $methods); + $instance = $this->createPartialMock(TestCase::class, ['requireFileDependencies', 'cleanGlobals']); - foreach ($methods as $method) { - $instance->expects($this->once())->method($method); - } + $instance->expects($this->once())->method('requireFileDependencies'); + $instance->expects($this->once())->method('cleanGlobals'); $instance->setUp(); @@ -55,14 +58,14 @@ public function testCanSetUpTests(): void * @return void * @throws Exception|ReflectionException */ + #[RunInSeparateProcess] + #[PreserveGlobalState(false)] public function testCanTearDownTests(): void { $wpMock = Mockery::mock('overload:WP_Mock'); $wpMock->shouldReceive('tearDown'); - $instance = $this->getMockForAbstractClass(TestCase::class, [], '', false, false, true, [ - 'cleanGlobals', - ]); + $instance = $this->createPartialMock(TestCase::class, ['cleanGlobals']); $instance->expects($this->once()) ->method('cleanGlobals'); @@ -76,71 +79,6 @@ public function testCanTearDownTests(): void $this->assertSame([], $property->getValue($instance)); } - /** - * @covers \WP_Mock\Tools\TestCase::run() - * - * @doesNotPerformAssertions - * - * @return void - * @throws Exception - */ - public function testCanRunTests(): void - { - $this->markTestSkipped('Cannot create test doubles for final classes from PHPUnit.'); - } - - /** - * @covers \WP_Mock\Tools\TestCase::after() - * - * @return void - * @throws Exception - */ - public function testCanPerformLogicAfterTests(): void - { - $instance = $this->getMockForAbstractClass(TestCase::class, [], '', false, false, true, [ - 'checkDeprecatedCalls', - ]); - - $instance->expects($this->once()) - ->method('checkDeprecatedCalls'); - - $instance->after(); - } - - /** - * @covers \WP_Mock\Tools\TestCase::checkDeprecatedCalls() - * - * @runInSeparateProcess - * @preserveGlobalState disabled - * - * @return void - * @throws Exception - */ - public function testCanCheckDeprecatedCalls(): void - { - $deprecatedMethodListener = $this->getMockBuilder(DeprecatedMethodListener::class) - ->disableOriginalConstructor() - ->onlyMethods(['checkCalls', 'reset']) - ->getMock(); - - $deprecatedMethodListener->expects($this->atMost(1)) - ->method('checkCalls'); - - $deprecatedMethodListener->expects($this->atMost(1)) - ->method('reset'); - - /** @var Mockery\Mock $wpMock */ - $wpMock = Mockery::mock('overload:WP_Mock'); - /** @phpstan-ignore-next-line */ - $wpMock->shouldReceive('getDeprecatedMethodListener') - ->andReturn($deprecatedMethodListener); - - $instance = $this->getMockForAbstractClass(TestCase::class); - $method = new ReflectionMethod($instance, 'checkDeprecatedCalls'); - $method->setAccessible(true); - $method->invoke($instance); - } - /** * @covers \WP_Mock\Tools\TestCase::cleanGlobals() * @@ -154,7 +92,7 @@ public function testCanCleanGlobals(): void $post = 'foo'; $wp_query = 'bar'; - $instance = $this->getMockForAbstractClass(TestCase::class); + $instance = $this->createPartialMock(TestCase::class, []); $method = new ReflectionMethod($instance, 'cleanGlobals'); $method->setAccessible(true); $method->invoke($instance); @@ -163,41 +101,6 @@ public function testCanCleanGlobals(): void $this->assertNull($GLOBALS['wp_query'] ?? null); } - /** - * @covers \WP_Mock\Tools\TestCase::setUpContentFiltering() - * - * @return void - * @throws ReflectionException|Exception - */ - public function testCanSetUpContentFiltering(): void - { - $instance = $this->getMockForAbstractClass(TestCase::class); - - $property = new ReflectionProperty($instance, '__contentFilterCallback'); - $property->setAccessible(true); - - $this->assertFalse($property->getValue($instance)); - - $method = new ReflectionMethod($instance, 'setUpContentFiltering'); - $method->setAccessible(true); - $method->invoke($instance); - - $this->assertSame([$instance, 'stripTabsAndNewlines'], $property->getValue($instance)); - } - - /** - * @covers \WP_Mock\Tools\TestCase::stripTabsAndNewlines() - * - * @return void - * @throws Exception - */ - public function testCanStripTabsAndNewlinesForContentFiltering(): void - { - $instance = $this->getMockForAbstractClass(TestCase::class); - - $this->assertSame('Test', $instance->stripTabsAndNewlines("\n\n\tTest\r\t")); - } - /** * @covers \WP_Mock\Tools\TestCase::assertActionsCalled() * @dataProvider providerAssertActionsCalled @@ -208,9 +111,13 @@ public function testCanStripTabsAndNewlinesForContentFiltering(): void * @param bool $throwsException * @return void * @throws Exception - */public function testCanAssertExpectedActionsWereCalled(bool $throwsException): void + */ + #[DataProvider('providerAssertActionsCalled')] + #[RunInSeparateProcess] + #[PreserveGlobalState(false)] + public function testCanAssertExpectedActionsWereCalled(bool $throwsException): void { - $instance = $this->getMockForAbstractClass(TestCase::class); + $instance = $this->createPartialMock(TestCase::class, []); /** @var Mockery\Mock $wpMock */ $wpMock = Mockery::mock('overload:WP_Mock'); @@ -227,7 +134,7 @@ public function testCanStripTabsAndNewlinesForContentFiltering(): void } /** @see testCanAssertExpectedActionsWereCalled */ - public function providerAssertActionsCalled(): Generator + public static function providerAssertActionsCalled(): Generator { yield 'Actions were not called' => [true]; yield 'Actions were called' => [false]; @@ -244,9 +151,12 @@ public function providerAssertActionsCalled(): Generator * @return void * @throws Exception */ + #[DataProvider('providerAssertHooksAdded')] + #[RunInSeparateProcess] + #[PreserveGlobalState(false)] public function testCanAssertExpectedHooksWereAdded(bool $throwsException): void { - $instance = $this->getMockForAbstractClass(TestCase::class); + $instance = $this->createPartialMock(TestCase::class, []); /** @var Mockery\Mock $wpMock */ $wpMock = Mockery::mock('overload:WP_Mock'); @@ -263,7 +173,7 @@ public function testCanAssertExpectedHooksWereAdded(bool $throwsException): void } /** @see testCanAssertExpectedHooksWereAdded */ - public function providerAssertHooksAdded(): Generator + public static function providerAssertHooksAdded(): Generator { yield 'Hooks were not added' => [true]; yield 'Hooks were added' => [false]; @@ -277,9 +187,7 @@ public function providerAssertHooksAdded(): Generator */ public function testCanAssertCurrentTestConditionsWereMet(): void { - $instance = $this->getMockForAbstractClass(TestCase::class, [], '', true, true, true, [ - 'assertConditionsMet' - ]); + $instance = $this->createPartialMock(TestCase::class, ['assertConditionsMet']); $instance->expects($this->once()) ->method('assertConditionsMet') @@ -295,62 +203,50 @@ public function testCanAssertCurrentTestConditionsWereMet(): void */ public function testCanAssertTestConditionsWereMet(): void { - $instance = $this->getMockForAbstractClass(TestCase::class); + $instance = $this->createPartialMock(TestCase::class, []); - // this will intentionally always pass and there are no assertions to be made + // this will intentionally always pass $instance->assertConditionsMet('test'); } /** - * @covers \WP_Mock\Tools\TestCase::expectOutputString() - * @dataProvider providerExpectOutputString + * @covers \WP_Mock\Tools\TestCase::assertEqualsHtml() * - * @param bool $expectException * @return void - * @throws ReflectionException|Exception + * @throws Exception */ - public function testCanExpectOutputString(bool $expectException): void + public function testCanAssertEqualsHtml(): void { - $instance = $this->getMockForAbstractClass(TestCase::class); - - if ($expectException) { - $property = new ReflectionProperty($instance, '__contentFilterCallback'); - $property->setAccessible(true); - $property->setValue($instance, function () { - return false; - }); - - $this->expectException(InvalidArgumentException::class); - } + $instance = $this->createPartialMock(TestCase::class, []); - $instance->expectOutputString('test'); + $instance->assertEqualsHtml('

test

', "

test

"); - // parent method will not run in the context of this test, this will prevent method flagging no assertions performed - $instance->assertConditionsMet(); - } + $this->expectException(ExpectationFailedException::class); - /** @see testCanExpectOutputString */ - public function providerExpectOutputString(): Generator - { - yield 'Should not throw an exception' => [false]; - yield 'Should throw an exception' => [true]; + $instance->assertEqualsHtml('

foo

', '

bar

'); } /** - * @covers \WP_Mock\Tools\TestCase::assertEqualsHtml() + * @covers \WP_Mock\Tools\TestCase::assertOutputEqualsHtml() * * @return void * @throws Exception */ - public function testCanAssertEqualsHtml(): void + public function testCanAssertOutputEqualsHtml(): void { - $instance = $this->getMockForAbstractClass(TestCase::class); + $instance = $this->createPartialMock(TestCase::class, []); - $instance->assertEqualsHtml('

test

', "

test

"); + // whitespace-insensitive match passes + $instance->assertOutputEqualsHtml('

test

', static function () { + echo "

\n\t test

"; + }); + // a mismatch throws $this->expectException(ExpectationFailedException::class); - $instance->assertEqualsHtml('

foo

', '

bar

'); + $instance->assertOutputEqualsHtml('

foo

', static function () { + echo '

bar

'; + }); } /** @@ -365,6 +261,9 @@ public function testCanAssertEqualsHtml(): void * @return void * @throws ReflectionException|Exception */ + #[DataProvider('providerMockStaticMethod')] + #[RunInSeparateProcess] + #[PreserveGlobalState(false)] public function testCanMockStaticMethod(bool $usingPatchwork, bool $invalidMethod): void { $wpMock = Mockery::mock('overload:WP_Mock'); @@ -380,7 +279,7 @@ public static function testMethod(): bool $this->assertTrue($class::testMethod()); - $instance = $this->getMockForAbstractClass(TestCase::class); + $instance = $this->createPartialMock(TestCase::class, []); $method = new ReflectionMethod($instance, 'mockStaticMethod'); $method->setAccessible(true); @@ -396,7 +295,7 @@ public static function testMethod(): bool } /** @see testCanMockStaticMethod */ - public function providerMockStaticMethod(): Generator + public static function providerMockStaticMethod(): Generator { yield 'Patchwork is disabled' => [false, false]; yield 'Referencing invalid method' => [true, true]; From fb21c79037ebffbb83973d739fd09920cbc5019d Mon Sep 17 00:00:00 2001 From: Fulvio Notarstefano Date: Tue, 23 Jun 2026 17:29:27 +0900 Subject: [PATCH 04/16] =?UTF-8?q?test:=20dual-declare=20PHPUnit=20attribut?= =?UTF-8?q?es=20for=20PHPUnit=209=E2=80=9313=20compatibility?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Doc-comment metadata is removed in PHPUnit 12 and data providers must be static in 10+. Keep existing annotations (for the PHPUnit 9 / PHP 7.4 legs) and add equivalent single-line attributes (#[DataProvider], #[RunInSeparateProcess], #[PreserveGlobalState(false)]) for 12+. - Make all data provider methods static (#[DataProvider]); annotations retained. - Add process-isolation attributes alongside @runInSeparateProcess/@preserveGlobalState. - Replace getMockForAbstractClass (removed in 12) with createPartialMock; HookTest lists Hook's abstract methods so the generated double is concrete. MockWordPressObjectsTraitTest replaces getMockForTrait (also removed) with an anonymous class using the trait. - Integration test: add cross-version isRunningInIsolation() shim to WP_MockTestCase (PHPUnit's isInIsolation() was removed in 10); convert $defaultMockedFunctions to a const so its data provider can be static. Verified green: PHPUnit 9.6, 11, 12, 13 (PHP 8.4). On PHPUnit 11, non-failing doc-comment metadata deprecation notices are emitted because annotations are retained for PHPUnit 9 support. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01K8HGMxCuSjUX6p4PQL7t5Y --- tests/Integration/WP_MockTest.php | 23 +++++++++---- tests/Unit/WP_Mock/API/FunctionMocksTest.php | 6 ++++ tests/Unit/WP_Mock/Functions/HandlerTest.php | 12 +++++++ tests/Unit/WP_Mock/FunctionsTest.php | 27 +++++++++++---- tests/Unit/WP_Mock/HookTest.php | 8 +++-- .../Unit/WP_Mock/Matcher/FuzzyObjectTest.php | 25 ++++++++------ .../Traits/MockWordPressObjectsTraitTest.php | 8 +++-- tests/Unit/WP_MockTest.php | 34 ++++++++++++++++++- tests/WP_MockTestCase.php | 26 ++++++++++++++ 9 files changed, 141 insertions(+), 28 deletions(-) diff --git a/tests/Integration/WP_MockTest.php b/tests/Integration/WP_MockTest.php index 3af7bb3..cb87ceb 100644 --- a/tests/Integration/WP_MockTest.php +++ b/tests/Integration/WP_MockTest.php @@ -4,6 +4,9 @@ use Exception; use Generator; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\PreserveGlobalState; +use PHPUnit\Framework\Attributes\RunInSeparateProcess; use PHPUnit\Framework\ExpectationFailedException; use WP_Mock; use WP_Mock\Tests\WP_MockTestCase; @@ -14,7 +17,7 @@ class WP_MockTest extends WP_MockTestCase { /** @var string[] */ - private array $defaultMockedFunctions = [ + private const DEFAULT_MOCKED_FUNCTIONS = [ '__', '_e', '_n', @@ -44,7 +47,7 @@ class WP_MockTest extends WP_MockTestCase */ protected function setUp(): void { - if (! $this->isInIsolation()) { + if (! $this->isRunningInIsolation()) { WP_Mock::setUp(); } @@ -62,17 +65,19 @@ protected function setUp(): void * @return void * @throws Exception */ + #[RunInSeparateProcess] + #[PreserveGlobalState(false)] public function testCommonFunctionsAreDefined(): void { // First we assert that all common functions get removed from the returned array. // If any one of these functions doesn't get removed, that means it already exists. - $this->assertEmpty(array_filter($this->defaultMockedFunctions, 'function_exists')); + $this->assertEmpty(array_filter(self::DEFAULT_MOCKED_FUNCTIONS, 'function_exists')); WP_Mock::bootstrap(); // Now we assert that the array doesn't lose any items after bootstrap, // meaning all expected functions got defined correctly. - $this->assertEquals($this->defaultMockedFunctions, array_filter($this->defaultMockedFunctions, 'function_exists')); + $this->assertEquals(self::DEFAULT_MOCKED_FUNCTIONS, array_filter(self::DEFAULT_MOCKED_FUNCTIONS, 'function_exists')); } /** @@ -87,6 +92,7 @@ public function testCommonFunctionsAreDefined(): void * @return void * @throws Exception */ + #[DataProvider('providerCommonFunctionsDefaultFunctionality')] public function testCommonFunctionsDefaultFunctionality($function, string $action) { $input = $expected = 'Something Random '.rand(0, 99); @@ -110,9 +116,9 @@ public function testCommonFunctionsDefaultFunctionality($function, string $actio * * @return array */ - public function providerCommonFunctionsDefaultFunctionality(): array + public static function providerCommonFunctionsDefaultFunctionality(): array { - $functions = $this->defaultMockedFunctions; + $functions = self::DEFAULT_MOCKED_FUNCTIONS; return array_filter(array_map(function ($function) { // skip hook functions - only gettext functions under test @@ -131,6 +137,8 @@ public function providerCommonFunctionsDefaultFunctionality(): array * * @return void */ + #[RunInSeparateProcess] + #[PreserveGlobalState(false)] public function testDefaultFailsInStrictMode(): void { $this->expectExceptionMessageMatches('/No handler found for \w+/'); @@ -198,6 +206,7 @@ public function testBotchedMocksStillOverridesDefault(): void * @return void * @throws Exception */ + #[DataProvider('providerUserFunctionExpectationArgs')] public function testCanSetUserFunctionExpectationArgs(array $expectationArgs, array $expectedResults): void { WP_Mock::userFunction('wpMockTestReturnFunction', $expectationArgs); @@ -215,7 +224,7 @@ public function testCanSetUserFunctionExpectationArgs(array $expectationArgs, ar } /** @see testCanSetUserFunctionExpectationArgs */ - public function providerUserFunctionExpectationArgs(): Generator + public static function providerUserFunctionExpectationArgs(): Generator { yield 'Function never called' => [ 'expectationArgs' => [ diff --git a/tests/Unit/WP_Mock/API/FunctionMocksTest.php b/tests/Unit/WP_Mock/API/FunctionMocksTest.php index 2ba4ce1..77a3d31 100644 --- a/tests/Unit/WP_Mock/API/FunctionMocksTest.php +++ b/tests/Unit/WP_Mock/API/FunctionMocksTest.php @@ -3,6 +3,8 @@ namespace Unit\WP_Mock\API; use Exception; +use PHPUnit\Framework\Attributes\PreserveGlobalState; +use PHPUnit\Framework\Attributes\RunInSeparateProcess; use WP_Mock; use WP_Mock\Tests\WP_MockTestCase; @@ -32,6 +34,8 @@ final class FunctionMocksTest extends WP_MockTestCase * * @return void */ + #[RunInSeparateProcess] + #[PreserveGlobalState(false)] public function testPredefinedReturnFunctions(): void { WP_Mock::bootstrap(); @@ -75,6 +79,8 @@ public function testPredefinedReturnFunctions(): void * @return void * @throws Exception */ + #[RunInSeparateProcess] + #[PreserveGlobalState(false)] public function testPredefinedEchoFunctions(): void { WP_Mock::bootstrap(); diff --git a/tests/Unit/WP_Mock/Functions/HandlerTest.php b/tests/Unit/WP_Mock/Functions/HandlerTest.php index 4d8eec6..3389295 100644 --- a/tests/Unit/WP_Mock/Functions/HandlerTest.php +++ b/tests/Unit/WP_Mock/Functions/HandlerTest.php @@ -3,6 +3,8 @@ namespace Unit\WP_Mock\Functions; use Exception; +use PHPUnit\Framework\Attributes\PreserveGlobalState; +use PHPUnit\Framework\Attributes\RunInSeparateProcess; use ReflectionProperty; use WP_Mock; use WP_Mock\Functions\Handler; @@ -51,6 +53,8 @@ public function testCanRegisterHandler(): void * @return void * @throws Exception */ + #[RunInSeparateProcess] + #[PreserveGlobalState(false)] public function testCanHandleFunction(): void { $this->assertNull(Handler::handleFunction('invalid')); @@ -74,6 +78,8 @@ public function testCanHandleFunction(): void * @return void * @throws Exception */ + #[RunInSeparateProcess] + #[PreserveGlobalState(false)] public function testCanDetermineHandlerExists(): void { $functionName = 'test_function'; @@ -97,6 +103,8 @@ public function testCanDetermineHandlerExists(): void * @return void * @throws Exception */ + #[RunInSeparateProcess] + #[PreserveGlobalState(false)] public function testCanCleanup(): void { $property = new ReflectionProperty(Handler::class, 'handlers'); @@ -122,6 +130,8 @@ public function testCanCleanup(): void * @return void * @throws Exception */ + #[RunInSeparateProcess] + #[PreserveGlobalState(false)] public function testCanHandlePredefinedReturnFunction(): void { WP_Mock::bootstrap(); @@ -143,6 +153,8 @@ public function testCanHandlePredefinedReturnFunction(): void * @return void * @throws Exception */ + #[RunInSeparateProcess] + #[PreserveGlobalState(false)] public function testCanHandlePredefinedEchoFunction(): void { WP_Mock::bootstrap(); diff --git a/tests/Unit/WP_Mock/FunctionsTest.php b/tests/Unit/WP_Mock/FunctionsTest.php index f82e980..58602dd 100644 --- a/tests/Unit/WP_Mock/FunctionsTest.php +++ b/tests/Unit/WP_Mock/FunctionsTest.php @@ -9,6 +9,9 @@ use Mockery\CountValidator\Exact; use Mockery\Expectation; use Mockery\Mock; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\PreserveGlobalState; +use PHPUnit\Framework\Attributes\RunInSeparateProcess; use ReflectionException; use ReflectionMethod; use ReflectionProperty; @@ -30,6 +33,8 @@ final class FunctionsTest extends WP_MockTestCase * @return void * @throws ReflectionException|Exception */ + #[RunInSeparateProcess] + #[PreserveGlobalState(false)] public function testCanInitialize(): void { $functions = new Functions(); @@ -88,6 +93,8 @@ public function testCanInitialize(): void * @return void * @throws Exception */ + #[RunInSeparateProcess] + #[PreserveGlobalState(false)] public function testCanRegister(): void { $handler = new ReflectionProperty(Handler::class, 'handlers'); @@ -125,6 +132,7 @@ public function testCanRegister(): void * @return void * @throws Exception */ + #[DataProvider('providerCanSetUpMock')] public function testCanSetupMock(array $expectationArgs): void { $functions = new Functions(); @@ -172,7 +180,7 @@ public function testCanSetupMock(array $expectationArgs): void } /** @see testCanSetupMock */ - public function providerCanSetUpMock(): Generator + public static function providerCanSetUpMock(): Generator { yield 'With args' => [['args' => ['foo', 'bar']]]; yield 'With return value' => [['return' => 'foo']]; @@ -188,6 +196,7 @@ public function providerCanSetUpMock(): Generator * @return void * @throws ReflectionException|Exception */ + #[DataProvider('providerCanGenerateFunction')] public function testCanGenerateFunction(bool $willCreate, bool $willReplace): void { $functionName = 'myFunction'; @@ -221,7 +230,7 @@ public function testCanGenerateFunction(bool $willCreate, bool $willReplace): vo } /** @see testCanGenerateFunction */ - public function providerCanGenerateFunction(): Generator + public static function providerCanGenerateFunction(): Generator { yield 'Function is created' => [true, false]; yield 'Function is replaced' => [false, true]; @@ -243,6 +252,9 @@ public function providerCanGenerateFunction(): Generator * @return void * @throws ReflectionException|Exception */ + #[DataProvider('providerCanCreateFunction')] + #[RunInSeparateProcess] + #[PreserveGlobalState(false)] public function testCanCreateFunction(string $functionName, array $functionsList, bool $functionWillExist, bool $functionWillBeRegistered, bool $expectedReturnValue): void { $functions = new Functions(); @@ -266,7 +278,7 @@ public function testCanCreateFunction(string $functionName, array $functionsList } /** @see testCanCreateFunction */ - public function providerCanCreateFunction(): Generator + public static function providerCanCreateFunction(): Generator { yield 'Function is already registered' => ['myWpMockFunction', ['myWpMockFunction'], false, true, true]; yield 'Function already exists' => ['str_replace', [], true, false, false]; @@ -320,6 +332,7 @@ public function testCanSanitizeFunctionName(): void * @return void * @throws ReflectionException|Exception */ + #[DataProvider('providerCanValidateFunction')] public function testCanValidateFunction(string $functionName, bool $validates): void { if (! $validates) { @@ -335,7 +348,7 @@ public function testCanValidateFunction(string $functionName, bool $validates): } /** @see testCanValidateFunction */ - public function providerCanValidateFunction(): Generator + public static function providerCanValidateFunction(): Generator { yield 'Invalid function name' => ['#!?', false]; yield 'Internal PHP function' => ['str_replace', false]; @@ -353,6 +366,7 @@ public function providerCanValidateFunction(): Generator * @return void * @throws Exception */ + #[DataProvider('providerMatchAnyTypes')] public function testCanSetUpArgumentPlaceholderOfAnyType(bool $expected, $matchedValue, $typesToMatch): void { $anyType = Functions::anyOf($typesToMatch); @@ -361,7 +375,7 @@ public function testCanSetUpArgumentPlaceholderOfAnyType(bool $expected, $matche } /** @see testCanSetUpArgumentPlaceholderOfAnyType */ - public function providerMatchAnyTypes(): Generator + public static function providerMatchAnyTypes(): Generator { yield 'Match expected string' => [true, 'string', 'string']; yield 'Does not match expected string' => [false, 'string', 123]; @@ -380,6 +394,7 @@ public function providerMatchAnyTypes(): Generator * @return void * @throws Exception */ + #[DataProvider('providerMatchTypes')] public function testCanSetUpArgumentPlaceholderOfStrictType(string $typeToMatch, $matchedValue): void { $type = Functions::type($typeToMatch); @@ -388,7 +403,7 @@ public function testCanSetUpArgumentPlaceholderOfStrictType(string $typeToMatch, } /** @see testCanSetUpArgumentPlaceholderOfType */ - public function providerMatchTypes(): Generator + public static function providerMatchTypes(): Generator { yield ['string', 'string']; yield ['integer', 1]; diff --git a/tests/Unit/WP_Mock/HookTest.php b/tests/Unit/WP_Mock/HookTest.php index fcfecf3..1a2e880 100644 --- a/tests/Unit/WP_Mock/HookTest.php +++ b/tests/Unit/WP_Mock/HookTest.php @@ -6,6 +6,8 @@ use Generator; use Exception; use Mockery; +use PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use ReflectionException; use stdClass; @@ -15,6 +17,7 @@ /** * @covers \WP_Mock\Hook */ +#[AllowMockObjectsWithoutExpectations] final class HookTest extends TestCase { use AccessInaccessibleClassMembersTrait; @@ -28,16 +31,17 @@ final class HookTest extends TestCase * @return void * @throws ReflectionException|Exception */ + #[DataProvider('providerSafeOffset')] public function testCanParseSafeOffSet($value, string $expected): void { - $instance = $this->getMockForAbstractClass(Hook::class, [], '', false); + $instance = $this->createPartialMock(Hook::class, ['new_responder', 'get_strict_mode_message']); $method = $this->getInaccessibleMethod($instance, 'safe_offset'); $this->assertSame($expected, $method->invokeArgs($instance, [$value])); } /** @see testCanParseSafeOffset */ - public function providerSafeOffset(): Generator + public static function providerSafeOffset(): Generator { $callbackInstance = new class () { public function callback(): bool diff --git a/tests/Unit/WP_Mock/Matcher/FuzzyObjectTest.php b/tests/Unit/WP_Mock/Matcher/FuzzyObjectTest.php index 679e501..cc9b04d 100644 --- a/tests/Unit/WP_Mock/Matcher/FuzzyObjectTest.php +++ b/tests/Unit/WP_Mock/Matcher/FuzzyObjectTest.php @@ -6,6 +6,7 @@ use Generator; use Mockery; use Mockery\Exception as MockeryException; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use ReflectionException; use ReflectionMethod; @@ -28,6 +29,7 @@ final class FuzzyObjectTest extends TestCase * @return void * @throws Exception */ + #[DataProvider('providerCanConstruct')] public function testCanConstruct($expected, bool $shouldThrowException): void { if ($shouldThrowException) { @@ -40,7 +42,7 @@ public function testCanConstruct($expected, bool $shouldThrowException): void } /** @see testCanConstruct */ - public function providerCanConstruct(): Generator + public static function providerCanConstruct(): Generator { yield 'Can construct when $expected is object' => [ 'expected' => new SampleClass(), @@ -88,6 +90,7 @@ public function providerCanConstruct(): Generator * @return void * @throws Exception */ + #[DataProvider('providerCanMatch')] public function testCanMatch($testClass, object $expectedClass, bool $expectedResult): void { /** @var FuzzyObject&Mockery\LegacyMockInterface&Mockery\MockInterface $partialMock */ @@ -107,7 +110,7 @@ public function testCanMatch($testClass, object $expectedClass, bool $expectedRe } /** @see testCanMatch */ - public function providerCanMatch(): Generator + public static function providerCanMatch(): Generator { yield 'False when test class is not a class.' => [ 'testClass' => 'not a class', @@ -187,6 +190,7 @@ public function providerCanMatch(): Generator * @return void * @throws ReflectionException|Exception */ + #[DataProvider('providerCanDetermineHaveCommonAncestor')] public function testCanDetermineIfTwoObjectsHaveCommonAncestor($object1, $object2, bool $expectedResult): void { $instance = new FuzzyObject(new SampleClass()); @@ -198,7 +202,7 @@ public function testCanDetermineIfTwoObjectsHaveCommonAncestor($object1, $object } /** @see testCanDetermineIfTwoObjectsHaveCommonAncestor */ - public function providerCanDetermineHaveCommonAncestor(): Generator + public static function providerCanDetermineHaveCommonAncestor(): Generator { yield 'False when object1 is not an object' => [ 'object1' => 'not an object', @@ -240,6 +244,7 @@ public function providerCanDetermineHaveCommonAncestor(): Generator * @return void * @throws Exception */ + #[DataProvider('providerToString')] public function testCanConvertToString($object, string $expectedResult): void { $instance = new FuzzyObject($object); @@ -248,10 +253,10 @@ public function testCanConvertToString($object, string $expectedResult): void } /** @see testCanConvertToString */ - public function providerToString(): Generator + public static function providerToString(): Generator { yield 'With expected object with all types of properties' => [ - 'expected' => new class() { + 'object' => new class() { /** @var string[] */ public $testPropertyIsArray = ['foo','bar']; @@ -271,17 +276,17 @@ public function __construct() $this->testPropertyIsResource = stream_context_create(); } }, - 'expectedString' => '', + 'expectedResult' => '', ]; yield 'With expected object with no properties' => [ - 'expected' => new class() {}, - 'expectedString' => '', + 'object' => new class() {}, + 'expectedResult' => '', ]; yield 'With array' => [ - 'expected' => ['foo','bar'], - 'expectedString' => '', + 'object' => ['foo','bar'], + 'expectedResult' => '', ]; } } diff --git a/tests/Unit/WP_Mock/Traits/MockWordPressObjectsTraitTest.php b/tests/Unit/WP_Mock/Traits/MockWordPressObjectsTraitTest.php index b0284a1..6990a5d 100644 --- a/tests/Unit/WP_Mock/Traits/MockWordPressObjectsTraitTest.php +++ b/tests/Unit/WP_Mock/Traits/MockWordPressObjectsTraitTest.php @@ -23,7 +23,9 @@ final class MockWordPressObjectsTraitTest extends WP_MockTestCase */ public function testCanMockWordPressPost(): void { - $trait = $this->getMockForTrait(MockWordPressObjectsTrait::class); + $trait = new class () { + use MockWordPressObjectsTrait; + }; $postData = [ 'ID' => 123, 'post_author' => 'johndoe', @@ -65,7 +67,9 @@ public function testCanMockWordPressPost(): void */ public function testCanMockWordPressInstance(): void { - $trait = $this->getMockForTrait(MockWordPressObjectsTrait::class); + $trait = new class () { + use MockWordPressObjectsTrait; + }; $method = new ReflectionMethod($trait, 'mockWp'); $method->setAccessible(true); diff --git a/tests/Unit/WP_MockTest.php b/tests/Unit/WP_MockTest.php index 82f34c0..7e632a3 100644 --- a/tests/Unit/WP_MockTest.php +++ b/tests/Unit/WP_MockTest.php @@ -7,6 +7,9 @@ use stdClass; use Generator; use PHPUnit\Framework\Exception; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\PreserveGlobalState; +use PHPUnit\Framework\Attributes\RunInSeparateProcess; use Mockery\ExpectationInterface; use WP_Mock\Tests\WP_MockTestCase; use WP_Mock\Tests\Mocks\SampleClass; @@ -30,6 +33,8 @@ class WP_MockTest extends WP_MockTestCase * @return void * @throws ExpectationFailedException|InvalidArgumentException */ + #[RunInSeparateProcess] + #[PreserveGlobalState(false)] public function testStrictModeOffByDefault(): void { $this->assertFalse(WP_Mock::strictMode()); @@ -44,6 +49,8 @@ public function testStrictModeOffByDefault(): void * @return void * @throws ExpectationFailedException|InvalidArgumentException */ + #[RunInSeparateProcess] + #[PreserveGlobalState(false)] public function testActivateStrictModeTurnsStrictModeOn(): void { WP_Mock::activateStrictMode(); @@ -60,6 +67,8 @@ public function testActivateStrictModeTurnsStrictModeOn(): void * @return void * @throws ExpectationFailedException|InvalidArgumentException */ + #[RunInSeparateProcess] + #[PreserveGlobalState(false)] public function testActivateStrictModeDoesNotWorkAfterBootstrap(): void { WP_Mock::bootstrap(); @@ -77,6 +86,8 @@ public function testActivateStrictModeDoesNotWorkAfterBootstrap(): void * @return void * @throws Exception|InvalidArgumentException|\InvalidArgumentException */ + #[RunInSeparateProcess] + #[PreserveGlobalState(false)] public function testUserFunctionReturnsExpectationContract(): void { WP_Mock::bootstrap(); @@ -95,6 +106,8 @@ public function testUserFunctionReturnsExpectationContract(): void * * @return void */ + #[RunInSeparateProcess] + #[PreserveGlobalState(false)] public function testAssertHooksAddedForFiltersAndActionsPasses(): void { WP_Mock::bootstrap(); @@ -121,6 +134,8 @@ public function testAssertHooksAddedForFiltersAndActionsPasses(): void * * @return void */ + #[RunInSeparateProcess] + #[PreserveGlobalState(false)] public function testAssertHooksAddedForFiltersAndActionsFails(): void { try { @@ -148,6 +163,8 @@ public function testAssertHooksAddedForFiltersAndActionsFails(): void * * @return void */ + #[RunInSeparateProcess] + #[PreserveGlobalState(false)] public function testAssertActionsCalledPasses(): void { WP_Mock::bootstrap(); @@ -168,6 +185,8 @@ public function testAssertActionsCalledPasses(): void * * @return void */ + #[RunInSeparateProcess] + #[PreserveGlobalState(false)] public function testAssertActionsCalledFails(): void { try { @@ -192,6 +211,8 @@ public function testAssertActionsCalledFails(): void * * @return void */ + #[RunInSeparateProcess] + #[PreserveGlobalState(false)] public function testAssertFiltersCalledPasses(): void { WP_Mock::bootstrap(); @@ -213,6 +234,8 @@ public function testAssertFiltersCalledPasses(): void * * @return void */ + #[RunInSeparateProcess] + #[PreserveGlobalState(false)] public function testAssertFiltersCalledFails(): void { WP_Mock::bootstrap(); @@ -232,6 +255,8 @@ public function testAssertFiltersCalledFails(): void * * @return void */ + #[RunInSeparateProcess] + #[PreserveGlobalState(false)] public function testAssertFiltersPassesWithTypes(): void { WP_Mock::bootstrap(); @@ -275,6 +300,7 @@ public function testCanAliasFunction(): void * @return void * @throws Exception|Mockery\Exception|InvalidArgumentException */ + #[DataProvider('providerFuzzyObject')] public function testCanInstantiateFuzzyObject($object, string $expected): void { if (! is_object($object) && ! is_array($object)) { @@ -288,7 +314,7 @@ public function testCanInstantiateFuzzyObject($object, string $expected): void } /** @see testCanInstantiateFuzzyObject */ - public function providerFuzzyObject(): Generator + public static function providerFuzzyObject(): Generator { $stdClass = new stdClass(); $stdClass->baz = 'boz'; @@ -322,6 +348,8 @@ public function testCanGetDeprecatedMethodListener(): void * @return void * @throws Exception|InvalidArgumentException */ + #[RunInSeparateProcess] + #[PreserveGlobalState(false)] public function testOnFilterPasses(): void { WP_Mock::bootstrap(); @@ -347,6 +375,8 @@ public function testOnFilterPasses(): void * @return void * @throws Exception|InvalidArgumentException */ + #[RunInSeparateProcess] + #[PreserveGlobalState(false)] public function testOnFilterPassesWithAnyArgs(): void { WP_Mock::bootstrap(); @@ -372,6 +402,8 @@ public function testOnFilterPassesWithAnyArgs(): void * @return void * @throws Exception|InvalidArgumentException */ + #[RunInSeparateProcess] + #[PreserveGlobalState(false)] public function testMultipleOnFilterPassesWithAnyArgs(): void { WP_Mock::bootstrap(); diff --git a/tests/WP_MockTestCase.php b/tests/WP_MockTestCase.php index 0d1ea61..801c1ee 100644 --- a/tests/WP_MockTestCase.php +++ b/tests/WP_MockTestCase.php @@ -49,4 +49,30 @@ protected function assertConditionsMet(): void { $this->assertThat(null, new ExpectationsMet()); } + + /** + * Determines whether the current test is running in process isolation. + * + * Cross-version replacement for PHPUnit's {@see \PHPUnit\Framework\TestCase::isInIsolation()}, + * which was public in PHPUnit 9 but removed in PHPUnit 10+ (where the state lives in a private + * `$inIsolation` property). + * + * @return bool + */ + protected function isRunningInIsolation(): bool + { + if (method_exists($this, 'isInIsolation')) { + /** @phpstan-ignore-next-line method exists on PHPUnit 9 only */ + return (bool) $this->isInIsolation(); + } + + try { + $property = new \ReflectionProperty(TestCase::class, 'inIsolation'); + $property->setAccessible(true); + + return (bool) $property->getValue($this); + } catch (\ReflectionException $exception) { + return false; + } + } } From 953afa7d63e4dc4746fb325ce03f2cb1fe36aa25 Mon Sep 17 00:00:00 2001 From: Fulvio Notarstefano Date: Tue, 23 Jun 2026 17:33:48 +0900 Subject: [PATCH 05/16] chore: phpstan v2 baseline + harden IsEqualHtml::matches() for level max - IsEqualHtml::matches(): cast only scalars to string (avoids cast.string on mixed under level max) - Regenerate phpstan-baseline.neon for phpstan ^2 (analyzed against PHPUnit 13). Pre-existing level-max suppressions + test-doc-strictness; no new library bugs. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01K8HGMxCuSjUX6p4PQL7t5Y --- php/WP_Mock/Tools/Constraints/IsEqualHtml.php | 4 +- phpstan-baseline.neon | 617 +++++++++++++++--- 2 files changed, 537 insertions(+), 84 deletions(-) diff --git a/php/WP_Mock/Tools/Constraints/IsEqualHtml.php b/php/WP_Mock/Tools/Constraints/IsEqualHtml.php index c07e3f6..9c0843a 100644 --- a/php/WP_Mock/Tools/Constraints/IsEqualHtml.php +++ b/php/WP_Mock/Tools/Constraints/IsEqualHtml.php @@ -33,7 +33,9 @@ public function __construct(string $value) */ public function matches($other): bool { - return $this->clean((string) $other) === $this->clean($this->value); + $actual = is_scalar($other) ? (string) $other : ''; + + return $this->clean($actual) === $this->clean($this->value); } /** diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 9ca9448..5544eb4 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,396 +1,847 @@ parameters: ignoreErrors: - - message: "#^Call to an undefined method Mockery\\\\ExpectationInterface\\|Mockery\\\\HigherOrderMessage\\:\\:atLeast\\(\\)\\.$#" + message: '#^Call to an undefined method Mockery\\ExpectationInterface\|Mockery\\HigherOrderMessage\:\:atLeast\(\)\.$#' + identifier: method.notFound count: 3 path: php/WP_Mock.php - - message: "#^Cannot call method perform\\(\\) on mixed\\.$#" + message: '#^Cannot call method andReturnUsing\(\) on mixed\.$#' + identifier: method.nonObject count: 1 path: php/WP_Mock.php - - message: "#^Cannot call method reply\\(\\) on mixed\\.$#" + message: '#^Cannot call method once\(\) on mixed\.$#' + identifier: method.nonObject + count: 3 + path: php/WP_Mock.php + + - + message: '#^Cannot call method perform\(\) on mixed\.$#' + identifier: method.nonObject + count: 1 + path: php/WP_Mock.php + + - + message: '#^Cannot call method reply\(\) on mixed\.$#' + identifier: method.nonObject count: 1 path: php/WP_Mock.php - - message: "#^Method WP_Mock\\:\\:activateStrictMode\\(\\) has no return type specified\\.$#" + message: '#^Method WP_Mock\:\:activateStrictMode\(\) has no return type specified\.$#' + identifier: missingType.return count: 1 path: php/WP_Mock.php - - message: "#^Method WP_Mock\\:\\:addAction\\(\\) has no return type specified\\.$#" + message: '#^Method WP_Mock\:\:addAction\(\) has no return type specified\.$#' + identifier: missingType.return count: 1 path: php/WP_Mock.php - - message: "#^Method WP_Mock\\:\\:addAction\\(\\) has parameter \\$hook with no type specified\\.$#" + message: '#^Method WP_Mock\:\:addAction\(\) has parameter \$hook with no type specified\.$#' + identifier: missingType.parameter count: 1 path: php/WP_Mock.php - - message: "#^Method WP_Mock\\:\\:addFilter\\(\\) has no return type specified\\.$#" + message: '#^Method WP_Mock\:\:addFilter\(\) has no return type specified\.$#' + identifier: missingType.return count: 1 path: php/WP_Mock.php - - message: "#^Method WP_Mock\\:\\:addFilter\\(\\) has parameter \\$hook with no type specified\\.$#" + message: '#^Method WP_Mock\:\:addFilter\(\) has parameter \$hook with no type specified\.$#' + identifier: missingType.parameter count: 1 path: php/WP_Mock.php - - message: "#^Method WP_Mock\\:\\:addHook\\(\\) has no return type specified\\.$#" + message: '#^Method WP_Mock\:\:addHook\(\) has no return type specified\.$#' + identifier: missingType.return count: 1 path: php/WP_Mock.php - - message: "#^Method WP_Mock\\:\\:addHook\\(\\) has parameter \\$hook with no type specified\\.$#" + message: '#^Method WP_Mock\:\:addHook\(\) has parameter \$hook with no type specified\.$#' + identifier: missingType.parameter count: 1 path: php/WP_Mock.php - - message: "#^Method WP_Mock\\:\\:addHook\\(\\) has parameter \\$type with no type specified\\.$#" + message: '#^Method WP_Mock\:\:addHook\(\) has parameter \$type with no type specified\.$#' + identifier: missingType.parameter count: 1 path: php/WP_Mock.php - - message: "#^Method WP_Mock\\:\\:assertActionsCalled\\(\\) throws checked exception PHPUnit\\\\Framework\\\\ExpectationFailedException but it's missing from the PHPDoc @throws tag\\.$#" + message: '#^Method WP_Mock\:\:assertActionsCalled\(\) throws checked exception PHPUnit\\Framework\\ExpectationFailedException but it''s missing from the PHPDoc @throws tag\.$#' + identifier: missingType.checkedException count: 1 path: php/WP_Mock.php - - message: "#^Method WP_Mock\\:\\:assertActionsCalled\\(\\) throws checked exception SebastianBergmann\\\\RecursionContext\\\\InvalidArgumentException but it's missing from the PHPDoc @throws tag\\.$#" + message: '#^Method WP_Mock\:\:assertFiltersCalled\(\) throws checked exception PHPUnit\\Framework\\ExpectationFailedException but it''s missing from the PHPDoc @throws tag\.$#' + identifier: missingType.checkedException count: 1 path: php/WP_Mock.php - - message: "#^Method WP_Mock\\:\\:assertFiltersCalled\\(\\) throws checked exception PHPUnit\\\\Framework\\\\ExpectationFailedException but it's missing from the PHPDoc @throws tag\\.$#" + message: '#^Method WP_Mock\:\:assertHooksAdded\(\) throws checked exception PHPUnit\\Framework\\ExpectationFailedException but it''s missing from the PHPDoc @throws tag\.$#' + identifier: missingType.checkedException count: 1 path: php/WP_Mock.php - - message: "#^Method WP_Mock\\:\\:assertFiltersCalled\\(\\) throws checked exception SebastianBergmann\\\\RecursionContext\\\\InvalidArgumentException but it's missing from the PHPDoc @throws tag\\.$#" + message: '#^Method WP_Mock\:\:invokeAction\(\) has no return type specified\.$#' + identifier: missingType.return count: 1 path: php/WP_Mock.php - - message: "#^Method WP_Mock\\:\\:assertHooksAdded\\(\\) throws checked exception PHPUnit\\\\Framework\\\\ExpectationFailedException but it's missing from the PHPDoc @throws tag\\.$#" + message: '#^Method WP_Mock\:\:onHookAdded\(\) should return WP_Mock\\HookedCallback but returns mixed\.$#' + identifier: return.type count: 1 path: php/WP_Mock.php - - message: "#^Method WP_Mock\\:\\:assertHooksAdded\\(\\) throws checked exception SebastianBergmann\\\\RecursionContext\\\\InvalidArgumentException but it's missing from the PHPDoc @throws tag\\.$#" + message: '#^Method WP_Mock\:\:setUsePatchwork\(\) has no return type specified\.$#' + identifier: missingType.return count: 1 path: php/WP_Mock.php - - message: "#^Method WP_Mock\\:\\:invokeAction\\(\\) has no return type specified\\.$#" + message: '#^Method WP_Mock\:\:usingPatchwork\(\) has no return type specified\.$#' + identifier: missingType.return count: 1 path: php/WP_Mock.php - - message: "#^Method WP_Mock\\:\\:setUsePatchwork\\(\\) has no return type specified\\.$#" + message: '#^Parameter \#1 \$callable of class WP_Mock\\InvokedFilterValue constructor expects callable\(\)\: mixed, array\{Mockery\\MockInterface, ''intercepted''\} given\.$#' + identifier: argument.type count: 1 path: php/WP_Mock.php - - message: "#^Method WP_Mock\\:\\:usingPatchwork\\(\\) has no return type specified\\.$#" + message: '#^Parameter \#1 \(mixed\) of echo cannot be converted to string\.$#' + identifier: echo.nonString count: 1 path: php/WP_Mock.php - - message: "#^Parameter \\#1 \\$callable of class WP_Mock\\\\InvokedFilterValue constructor expects callable\\(\\)\\: mixed, array\\{Mockery\\\\LegacyMockInterface, 'intercepted'\\} given\\.$#" + message: '#^Parameter \#2 \$args of method WP_Mock\\Functions\:\:register\(\) expects array\, array\ given\.$#' + identifier: argument.type count: 1 path: php/WP_Mock.php - - message: "#^Property WP_Mock\\:\\:\\$__bootstrapped has no type specified\\.$#" + message: '#^Parameter \#2 \$array of function implode expects array\, array given\.$#' + identifier: argument.type + count: 3 + path: php/WP_Mock.php + + - + message: '#^Part \$hook \(mixed\) of encapsed string cannot be cast to string\.$#' + identifier: encapsedStringPart.nonString count: 1 path: php/WP_Mock.php - - message: "#^Property WP_Mock\\:\\:\\$__strict_mode has no type specified\\.$#" + message: '#^Part \$type \(mixed\) of encapsed string cannot be cast to string\.$#' + identifier: encapsedStringPart.nonString count: 1 path: php/WP_Mock.php - - message: "#^Property WP_Mock\\:\\:\\$__use_patchwork has no type specified\\.$#" + message: '#^Property WP_Mock\:\:\$__bootstrapped has no type specified\.$#' + identifier: missingType.property count: 1 path: php/WP_Mock.php - - message: "#^Function add_action\\(\\) has no return type specified\\.$#" + message: '#^Property WP_Mock\:\:\$__strict_mode has no type specified\.$#' + identifier: missingType.property + count: 1 + path: php/WP_Mock.php + + - + message: '#^Property WP_Mock\:\:\$__use_patchwork has no type specified\.$#' + identifier: missingType.property + count: 1 + path: php/WP_Mock.php + + - + message: '#^Function add_action\(\) has no return type specified\.$#' + identifier: missingType.return count: 1 path: php/WP_Mock/API/function-mocks.php - - message: "#^Function add_filter\\(\\) has no return type specified\\.$#" + message: '#^Function add_filter\(\) has no return type specified\.$#' + identifier: missingType.return count: 1 path: php/WP_Mock/API/function-mocks.php - - message: "#^PHPDoc tag @param references unknown parameter\\: \\$var$#" + message: '#^Function do_action\(\) should return null but returns mixed\.$#' + identifier: return.type count: 1 path: php/WP_Mock/API/function-mocks.php - - message: "#^Cannot access offset string on mixed\\.$#" + message: '#^PHPDoc tag @param references unknown parameter\: \$var$#' + identifier: parameter.notFound + count: 1 + path: php/WP_Mock/API/function-mocks.php + + - + message: '#^Cannot access offset int\<0, max\> on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible count: 2 path: php/WP_Mock/Action.php - - message: "#^Cannot call method react\\(\\) on mixed\\.$#" + message: '#^Cannot access offset string on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible count: 1 path: php/WP_Mock/Action.php - - message: "#^Method WP_Mock\\\\Action\\:\\:new_responder\\(\\) has no return type specified\\.$#" + message: '#^Cannot call method react\(\) on mixed\.$#' + identifier: method.nonObject + count: 2 + path: php/WP_Mock/Action.php + + - + message: '#^Method WP_Mock\\Action\:\:new_responder\(\) has no return type specified\.$#' + identifier: missingType.return count: 1 path: php/WP_Mock/Action.php - - message: "#^Method WP_Mock\\\\Action\\:\\:react\\(\\) has no return type specified\\.$#" + message: '#^Method WP_Mock\\Action\:\:react\(\) has no return type specified\.$#' + identifier: missingType.return count: 1 path: php/WP_Mock/Action.php - - message: "#^Method WP_Mock\\\\Action\\:\\:react\\(\\) has parameter \\$args with no type specified\\.$#" + message: '#^Method WP_Mock\\Action\:\:react\(\) has parameter \$args with no type specified\.$#' + identifier: missingType.parameter count: 1 path: php/WP_Mock/Action.php - - message: "#^Method WP_Mock\\\\Action\\:\\:react\\(\\) throws checked exception PHPUnit\\\\Framework\\\\ExpectationFailedException but it's missing from the PHPDoc @throws tag\\.$#" + message: '#^Method WP_Mock\\Action\:\:react\(\) throws checked exception PHPUnit\\Framework\\ExpectationFailedException but it''s missing from the PHPDoc @throws tag\.$#' + identifier: missingType.checkedException count: 3 path: php/WP_Mock/Action.php - - message: "#^Method WP_Mock\\\\Action_Responder\\:\\:perform\\(\\) has no return type specified\\.$#" + message: '#^Method WP_Mock\\Action_Responder\:\:perform\(\) has no return type specified\.$#' + identifier: missingType.return count: 1 path: php/WP_Mock/Action.php - - message: "#^Method WP_Mock\\\\Action_Responder\\:\\:perform\\(\\) has parameter \\$callable with no type specified\\.$#" + message: '#^Method WP_Mock\\Action_Responder\:\:perform\(\) has parameter \$callable with no type specified\.$#' + identifier: missingType.parameter count: 1 path: php/WP_Mock/Action.php - - message: "#^Method WP_Mock\\\\Action_Responder\\:\\:react\\(\\) has no return type specified\\.$#" + message: '#^Method WP_Mock\\Action_Responder\:\:react\(\) has no return type specified\.$#' + identifier: missingType.return count: 1 path: php/WP_Mock/Action.php - - message: "#^Parameter \\#1 \\$function of function call_user_func expects callable\\(\\)\\: mixed, mixed given\\.$#" + message: '#^Parameter \#1 \$callback of function call_user_func expects callable\(\)\: mixed, mixed given\.$#' + identifier: argument.type count: 1 path: php/WP_Mock/Action.php - - message: "#^Method WP_Mock\\\\EventManager\\:\\:callback\\(\\) has no return type specified\\.$#" + message: '#^Parameter \#1 \$value of function count expects array\|Countable, mixed given\.$#' + identifier: argument.type + count: 1 + path: php/WP_Mock/Action.php + + - + message: '#^Cannot access offset non\-falsy\-string on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 2 + path: php/WP_Mock/EventManager.php + + - + message: '#^Method WP_Mock\\EventManager\:\:action\(\) should return WP_Mock\\Action but returns mixed\.$#' + identifier: return.type + count: 1 + path: php/WP_Mock/EventManager.php + + - + message: '#^Method WP_Mock\\EventManager\:\:callback\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: php/WP_Mock/EventManager.php + + - + message: '#^Method WP_Mock\\EventManager\:\:callback\(\) has parameter \$name with no type specified\.$#' + identifier: missingType.parameter + count: 1 + path: php/WP_Mock/EventManager.php + + - + message: '#^Method WP_Mock\\EventManager\:\:callback\(\) has parameter \$type with no type specified\.$#' + identifier: missingType.parameter + count: 1 + path: php/WP_Mock/EventManager.php + + - + message: '#^Method WP_Mock\\EventManager\:\:called\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: php/WP_Mock/EventManager.php + + - + message: '#^Method WP_Mock\\EventManager\:\:expectedActions\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: php/WP_Mock/EventManager.php + + - + message: '#^Method WP_Mock\\EventManager\:\:expectedFilters\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: php/WP_Mock/EventManager.php + + - + message: '#^Method WP_Mock\\EventManager\:\:expectedHooks\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: php/WP_Mock/EventManager.php - - message: "#^Method WP_Mock\\\\EventManager\\:\\:callback\\(\\) has parameter \\$name with no type specified\\.$#" + message: '#^Method WP_Mock\\EventManager\:\:filter\(\) should return WP_Mock\\Filter but returns mixed\.$#' + identifier: return.type count: 1 path: php/WP_Mock/EventManager.php - - message: "#^Method WP_Mock\\\\EventManager\\:\\:callback\\(\\) has parameter \\$type with no type specified\\.$#" + message: '#^Method WP_Mock\\EventManager\:\:flush\(\) has no return type specified\.$#' + identifier: missingType.return count: 1 path: php/WP_Mock/EventManager.php - - message: "#^Method WP_Mock\\\\EventManager\\:\\:called\\(\\) has no return type specified\\.$#" + message: '#^Parameter \#1 \$array of function array_keys expects array, mixed given\.$#' + identifier: argument.type count: 1 path: php/WP_Mock/EventManager.php - - message: "#^Method WP_Mock\\\\EventManager\\:\\:expectedActions\\(\\) return type has no value type specified in iterable type array\\.$#" + message: '#^Parameter \#1 \$haystack of function strpos expects string, mixed given\.$#' + identifier: argument.type + count: 3 + path: php/WP_Mock/EventManager.php + + - + message: '#^Parameter \#1 \$name of class WP_Mock\\HookedCallback constructor expects string, mixed given\.$#' + identifier: argument.type count: 1 path: php/WP_Mock/EventManager.php - - message: "#^Method WP_Mock\\\\EventManager\\:\\:expectedFilters\\(\\) return type has no value type specified in iterable type array\\.$#" + message: '#^Parameter \#1 \$type of method WP_Mock\\HookedCallback\:\:setType\(\) expects string, mixed given\.$#' + identifier: argument.type count: 1 path: php/WP_Mock/EventManager.php - - message: "#^Method WP_Mock\\\\EventManager\\:\\:expectedHooks\\(\\) return type has no value type specified in iterable type array\\.$#" + message: '#^Parameter \#2 \$offset of function array_splice expects int, int\|string\|false given\.$#' + identifier: argument.type count: 1 path: php/WP_Mock/EventManager.php - - message: "#^Method WP_Mock\\\\EventManager\\:\\:flush\\(\\) has no return type specified\\.$#" + message: '#^Part \$name \(mixed\) of encapsed string cannot be cast to string\.$#' + identifier: encapsedStringPart.nonString count: 1 path: php/WP_Mock/EventManager.php - - message: "#^Parameter \\#2 \\$offset of function array_splice expects int, int\\|string\\|false given\\.$#" + message: '#^Part \$type \(mixed\) of encapsed string cannot be cast to string\.$#' + identifier: encapsedStringPart.nonString count: 1 path: php/WP_Mock/EventManager.php - - message: "#^Property WP_Mock\\\\EventManager\\:\\:\\$actions type has no value type specified in iterable type array\\.$#" + message: '#^Property WP_Mock\\EventManager\:\:\$actions type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: php/WP_Mock/EventManager.php - - message: "#^Property WP_Mock\\\\EventManager\\:\\:\\$callbacks has no type specified\\.$#" + message: '#^Property WP_Mock\\EventManager\:\:\$callbacks has no type specified\.$#' + identifier: missingType.property count: 1 path: php/WP_Mock/EventManager.php - - message: "#^Property WP_Mock\\\\EventManager\\:\\:\\$expected type has no value type specified in iterable type array\\.$#" + message: '#^Property WP_Mock\\EventManager\:\:\$expected type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: php/WP_Mock/EventManager.php - - message: "#^Property WP_Mock\\\\EventManager\\:\\:\\$filters type has no value type specified in iterable type array\\.$#" + message: '#^Property WP_Mock\\EventManager\:\:\$filters type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: php/WP_Mock/EventManager.php - - message: "#^Cannot call method send\\(\\) on mixed\\.$#" + message: '#^Cannot call method send\(\) on mixed\.$#' + identifier: method.nonObject count: 1 path: php/WP_Mock/Filter.php - - message: "#^Method WP_Mock\\\\Filter\\:\\:apply\\(\\) has parameter \\$args with no value type specified in iterable type array\\.$#" + message: '#^Method WP_Mock\\Filter\:\:apply\(\) has parameter \$args with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: php/WP_Mock/Filter.php - - message: "#^Method WP_Mock\\\\Filter\\:\\:apply\\(\\) throws checked exception PHPUnit\\\\Framework\\\\ExpectationFailedException but it's missing from the PHPDoc @throws tag\\.$#" + message: '#^Method WP_Mock\\Filter\:\:apply\(\) throws checked exception PHPUnit\\Framework\\ExpectationFailedException but it''s missing from the PHPDoc @throws tag\.$#' + identifier: missingType.checkedException count: 2 path: php/WP_Mock/Filter.php - - message: "#^Method WP_Mock\\\\Filter\\:\\:new_responder\\(\\) has no return type specified\\.$#" + message: '#^Method WP_Mock\\Filter_Responder\:\:reply\(\) has no return type specified\.$#' + identifier: missingType.return count: 1 path: php/WP_Mock/Filter.php - - message: "#^Method WP_Mock\\\\Filter_Responder\\:\\:reply\\(\\) has no return type specified\\.$#" + message: '#^Method WP_Mock\\Filter_Responder\:\:reply\(\) has parameter \$value with no type specified\.$#' + identifier: missingType.parameter count: 1 path: php/WP_Mock/Filter.php - - message: "#^Method WP_Mock\\\\Filter_Responder\\:\\:reply\\(\\) has parameter \\$value with no type specified\\.$#" + message: '#^Method WP_Mock\\Filter_Responder\:\:send\(\) has no return type specified\.$#' + identifier: missingType.return count: 1 path: php/WP_Mock/Filter.php - - message: "#^Method WP_Mock\\\\Filter_Responder\\:\\:send\\(\\) has no return type specified\\.$#" + message: '#^Parameter \#1 \$callback of function call_user_func_array expects callable\(\)\: mixed, array\{mixed, ''send''\} given\.$#' + identifier: argument.type count: 1 path: php/WP_Mock/Filter.php - - message: "#^Parameter \\#1 \\$function of function call_user_func_array expects callable\\(\\)\\: mixed, array\\{mixed, 'send'\\} given\\.$#" + message: '#^Call to an undefined method Mockery\\CompositeExpectation\|Mockery\\Expectation\:\:atLeast\(\)\.$#' + identifier: method.notFound count: 1 - path: php/WP_Mock/Filter.php + path: php/WP_Mock/Functions.php - - message: "#^Cannot access offset string on mixed\\.$#" - count: 4 + message: '#^Call to an undefined method Mockery\\CompositeExpectation\|Mockery\\Expectation\:\:atMost\(\)\.$#' + identifier: method.notFound + count: 1 + path: php/WP_Mock/Functions.php + + - + message: '#^Method WP_Mock\\Functions\:\:replaceFunction\(\) throws checked exception PHPUnit\\Framework\\ExpectationFailedException but it''s missing from the PHPDoc @throws tag\.$#' + identifier: missingType.checkedException + count: 1 + path: php/WP_Mock/Functions.php + + - + message: '#^Method WP_Mock\\Functions\:\:setExpectedTimes\(\) throws checked exception InvalidArgumentException but it''s missing from the PHPDoc @throws tag\.$#' + identifier: missingType.checkedException + count: 1 + path: php/WP_Mock/Functions.php + + - + message: '#^Parameter \#1 \$expectation of method WP_Mock\\Functions\:\:setExpectedReturn\(\) expects Mockery\\Expectation, Mockery\\CompositeExpectation\|Mockery\\Expectation given\.$#' + identifier: argument.type + count: 1 + path: php/WP_Mock/Functions.php + + - + message: '#^Cannot access offset string on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 3 path: php/WP_Mock/Hook.php - - message: "#^Method WP_Mock\\\\Hook\\:\\:new_responder\\(\\) has no return type specified\\.$#" + message: '#^Method WP_Mock\\Hook\:\:new_responder\(\) has no return type specified\.$#' + identifier: missingType.return count: 1 path: php/WP_Mock/Hook.php - - message: "#^Cannot access offset 0 on callable\\(\\)\\: mixed\\.$#" + message: '#^Method WP_Mock\\Hook\:\:with\(\) should return WP_Mock\\Action_Responder\|WP_Mock\\Filter_Responder\|WP_Mock\\HookedCallbackResponder but returns mixed\.$#' + identifier: return.type + count: 1 + path: php/WP_Mock/Hook.php + + - + message: '#^Cannot access offset 0 on callable\(\)\: mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible count: 1 path: php/WP_Mock/HookedCallback.php - - message: "#^Cannot access offset 1 on callable\\(\\)\\: mixed\\.$#" + message: '#^Cannot access offset 1 on callable\(\)\: mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible count: 1 path: php/WP_Mock/HookedCallback.php - - message: "#^Cannot access offset mixed on mixed\\.$#" - count: 5 + message: '#^Cannot access offset mixed on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 2 path: php/WP_Mock/HookedCallback.php - - message: "#^Cannot call method react\\(\\) on mixed\\.$#" + message: '#^Cannot call method react\(\) on mixed\.$#' + identifier: method.nonObject count: 1 path: php/WP_Mock/HookedCallback.php - - message: "#^Method WP_Mock\\\\HookedCallback\\:\\:new_responder\\(\\) has no return type specified\\.$#" + message: '#^Method WP_Mock\\HookedCallback\:\:new_responder\(\) has no return type specified\.$#' + identifier: missingType.return count: 1 path: php/WP_Mock/HookedCallback.php - - message: "#^Method WP_Mock\\\\HookedCallback\\:\\:react\\(\\) has no return type specified\\.$#" + message: '#^Method WP_Mock\\HookedCallback\:\:react\(\) has no return type specified\.$#' + identifier: missingType.return count: 1 path: php/WP_Mock/HookedCallback.php - - message: "#^Method WP_Mock\\\\HookedCallback\\:\\:react\\(\\) has parameter \\$argument_count with no type specified\\.$#" + message: '#^Method WP_Mock\\HookedCallback\:\:react\(\) has parameter \$argument_count with no type specified\.$#' + identifier: missingType.parameter count: 1 path: php/WP_Mock/HookedCallback.php - - message: "#^Method WP_Mock\\\\HookedCallback\\:\\:react\\(\\) has parameter \\$callback with no type specified\\.$#" + message: '#^Method WP_Mock\\HookedCallback\:\:react\(\) has parameter \$callback with no type specified\.$#' + identifier: missingType.parameter count: 1 path: php/WP_Mock/HookedCallback.php - - message: "#^Method WP_Mock\\\\HookedCallback\\:\\:react\\(\\) has parameter \\$priority with no type specified\\.$#" + message: '#^Method WP_Mock\\HookedCallback\:\:react\(\) has parameter \$priority with no type specified\.$#' + identifier: missingType.parameter count: 1 path: php/WP_Mock/HookedCallback.php - - message: "#^Method WP_Mock\\\\HookedCallback\\:\\:react\\(\\) throws checked exception Mockery\\\\Exception but it's missing from the PHPDoc @throws tag\\.$#" + message: '#^Method WP_Mock\\HookedCallback\:\:react\(\) throws checked exception Mockery\\Exception but it''s missing from the PHPDoc @throws tag\.$#' + identifier: missingType.checkedException count: 1 path: php/WP_Mock/HookedCallback.php - - message: "#^Method WP_Mock\\\\HookedCallback\\:\\:react\\(\\) throws checked exception PHPUnit\\\\Framework\\\\ExpectationFailedException but it's missing from the PHPDoc @throws tag\\.$#" + message: '#^Method WP_Mock\\HookedCallback\:\:react\(\) throws checked exception PHPUnit\\Framework\\ExpectationFailedException but it''s missing from the PHPDoc @throws tag\.$#' + identifier: missingType.checkedException count: 1 path: php/WP_Mock/HookedCallback.php - - message: "#^Method WP_Mock\\\\HookedCallback\\:\\:react\\(\\) throws checked exception ReflectionException but it's missing from the PHPDoc @throws tag\\.$#" + message: '#^Method WP_Mock\\HookedCallback\:\:react\(\) throws checked exception ReflectionException but it''s missing from the PHPDoc @throws tag\.$#' + identifier: missingType.checkedException count: 1 path: php/WP_Mock/HookedCallback.php - - message: "#^Method WP_Mock\\\\HookedCallback\\:\\:setType\\(\\) has no return type specified\\.$#" + message: '#^Method WP_Mock\\HookedCallback\:\:setType\(\) has no return type specified\.$#' + identifier: missingType.return count: 1 path: php/WP_Mock/HookedCallback.php - - message: "#^Method WP_Mock\\\\HookedCallbackResponder\\:\\:perform\\(\\) has no return type specified\\.$#" + message: '#^Method WP_Mock\\HookedCallbackResponder\:\:perform\(\) has no return type specified\.$#' + identifier: missingType.return count: 1 path: php/WP_Mock/HookedCallback.php - - message: "#^Method WP_Mock\\\\HookedCallbackResponder\\:\\:perform\\(\\) has parameter \\$callable with no type specified\\.$#" + message: '#^Method WP_Mock\\HookedCallbackResponder\:\:perform\(\) has parameter \$callable with no type specified\.$#' + identifier: missingType.parameter count: 1 path: php/WP_Mock/HookedCallback.php - - message: "#^Method WP_Mock\\\\HookedCallbackResponder\\:\\:react\\(\\) has no return type specified\\.$#" + message: '#^Method WP_Mock\\HookedCallbackResponder\:\:react\(\) has no return type specified\.$#' + identifier: missingType.return count: 1 path: php/WP_Mock/HookedCallback.php - - message: "#^Property WP_Mock\\\\HookedCallback\\:\\:\\$callback has no type specified\\.$#" + message: '#^Parameter \#1 \$callback of method WP_Mock\\HookedCallback\:\:callback_to_string\(\) expects callable\(\)\: mixed, mixed given\.$#' + identifier: argument.type count: 1 path: php/WP_Mock/HookedCallback.php - - message: "#^Property WP_Mock\\\\HookedCallback\\:\\:\\$type has no type specified\\.$#" + message: '#^Parameter \#1 \$expected of class WP_Mock\\Matcher\\AnyInstance constructor expects object\|string\|null, mixed given\.$#' + identifier: argument.type count: 1 path: php/WP_Mock/HookedCallback.php - - message: "#^Method WP_Mock\\\\InvokedFilterValue\\:\\:__invoke\\(\\) has no return type specified\\.$#" + message: '#^Parameter \#1 \$object of function get_class expects object, mixed given\.$#' + identifier: argument.type count: 1 - path: php/WP_Mock/InvokedFilterValue.php \ No newline at end of file + path: php/WP_Mock/HookedCallback.php + + - + message: '#^Parameter \#2 \.\.\.\$values of function sprintf expects bool\|float\|int\|string\|null, mixed given\.$#' + identifier: argument.type + count: 1 + path: php/WP_Mock/HookedCallback.php + + - + message: '#^Part \$method \(mixed\) of encapsed string cannot be cast to string\.$#' + identifier: encapsedStringPart.nonString + count: 1 + path: php/WP_Mock/HookedCallback.php + + - + message: '#^Property WP_Mock\\HookedCallback\:\:\$callback has no type specified\.$#' + identifier: missingType.property + count: 1 + path: php/WP_Mock/HookedCallback.php + + - + message: '#^Property WP_Mock\\HookedCallback\:\:\$type has no type specified\.$#' + identifier: missingType.property + count: 1 + path: php/WP_Mock/HookedCallback.php + + - + message: '#^Property WP_Mock\\HookedCallbackResponder\:\:\$callable \(callable\(\)\: mixed\) does not accept mixed\.$#' + identifier: assign.propertyType + count: 1 + path: php/WP_Mock/HookedCallback.php + + - + message: '#^Method WP_Mock\\InvokedFilterValue\:\:__invoke\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: php/WP_Mock/InvokedFilterValue.php + + - + message: '#^Binary operation "\." between mixed and string results in an error\.$#' + identifier: binaryOp.invalid + count: 2 + path: php/WP_Mock/Tools/TestCase.php + + - + message: '#^Method WP_Mock\\Tests\\Unit\\WP_Mock\\DeprecatedMethodListenerTest\:\:getDeprecatedMethodCalls\(\) should return array\\}\> but returns array\\.$#' + identifier: return.type + count: 1 + path: tests/Unit/WP_Mock/DeprecatedMethodListenerTest.php + + - + message: '#^Parameter \#1 \$prefix of method PHPUnit\\Framework\\Assert\:\:assertStringStartsWith\(\) expects non\-empty\-string, string given\.$#' + identifier: argument.type + count: 1 + path: tests/Unit/WP_Mock/DeprecatedMethodListenerTest.php + + - + message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertIsArray\(\) with array\{\} will always evaluate to true\.$#' + identifier: method.alreadyNarrowedType + count: 1 + path: tests/Unit/WP_Mock/FunctionsTest.php + + - + message: '#^Method WP_Mock\\Tests\\Unit\\WP_Mock\\FunctionsTest\:\:testCanRegister\(\) throws checked exception PHPUnit\\Framework\\MockObject\\Exception but it''s missing from the PHPDoc @throws tag\.$#' + identifier: missingType.checkedException + count: 1 + path: tests/Unit/WP_Mock/FunctionsTest.php + + - + message: '#^Method WP_Mock\\Tests\\Unit\\WP_Mock\\HookTest\:\:testCanParseSafeOffSet\(\) throws checked exception PHPUnit\\Framework\\MockObject\\Exception but it''s missing from the PHPDoc @throws tag\.$#' + identifier: missingType.checkedException + count: 1 + path: tests/Unit/WP_Mock/HookTest.php + + - + message: '#^Call to an undefined method Mockery\\ExpectationInterface\|Mockery\\HigherOrderMessage\:\:with\(\)\.$#' + identifier: method.notFound + count: 1 + path: tests/Unit/WP_Mock/Matcher/FuzzyObjectTest.php + + - + message: '#^Cannot call method andReturnTrue\(\) on mixed\.$#' + identifier: method.nonObject + count: 1 + path: tests/Unit/WP_Mock/Matcher/FuzzyObjectTest.php + + - + message: '#^Method WP_Mock\\Tests\\Unit\\WP_Mock\\Tools\\TestCaseTest\:\:testCanAssertCurrentTestConditionsWereMet\(\) throws checked exception PHPUnit\\Framework\\MockObject\\Exception but it''s missing from the PHPDoc @throws tag\.$#' + identifier: missingType.checkedException + count: 1 + path: tests/Unit/WP_Mock/Tools/TestCaseTest.php + + - + message: '#^Method WP_Mock\\Tests\\Unit\\WP_Mock\\Tools\\TestCaseTest\:\:testCanAssertEqualsHtml\(\) throws checked exception PHPUnit\\Framework\\MockObject\\Exception but it''s missing from the PHPDoc @throws tag\.$#' + identifier: missingType.checkedException + count: 1 + path: tests/Unit/WP_Mock/Tools/TestCaseTest.php + + - + message: '#^Method WP_Mock\\Tests\\Unit\\WP_Mock\\Tools\\TestCaseTest\:\:testCanAssertExpectedActionsWereCalled\(\) throws checked exception PHPUnit\\Framework\\MockObject\\Exception but it''s missing from the PHPDoc @throws tag\.$#' + identifier: missingType.checkedException + count: 1 + path: tests/Unit/WP_Mock/Tools/TestCaseTest.php + + - + message: '#^Method WP_Mock\\Tests\\Unit\\WP_Mock\\Tools\\TestCaseTest\:\:testCanAssertExpectedHooksWereAdded\(\) throws checked exception PHPUnit\\Framework\\MockObject\\Exception but it''s missing from the PHPDoc @throws tag\.$#' + identifier: missingType.checkedException + count: 1 + path: tests/Unit/WP_Mock/Tools/TestCaseTest.php + + - + message: '#^Method WP_Mock\\Tests\\Unit\\WP_Mock\\Tools\\TestCaseTest\:\:testCanAssertOutputEqualsHtml\(\) throws checked exception PHPUnit\\Framework\\MockObject\\Exception but it''s missing from the PHPDoc @throws tag\.$#' + identifier: missingType.checkedException + count: 1 + path: tests/Unit/WP_Mock/Tools/TestCaseTest.php + + - + message: '#^Method WP_Mock\\Tests\\Unit\\WP_Mock\\Tools\\TestCaseTest\:\:testCanAssertTestConditionsWereMet\(\) throws checked exception PHPUnit\\Framework\\InvalidArgumentException but it''s missing from the PHPDoc @throws tag\.$#' + identifier: missingType.checkedException + count: 1 + path: tests/Unit/WP_Mock/Tools/TestCaseTest.php + + - + message: '#^Method WP_Mock\\Tests\\Unit\\WP_Mock\\Tools\\TestCaseTest\:\:testCanAssertTestConditionsWereMet\(\) throws checked exception PHPUnit\\Framework\\MockObject\\Exception but it''s missing from the PHPDoc @throws tag\.$#' + identifier: missingType.checkedException + count: 1 + path: tests/Unit/WP_Mock/Tools/TestCaseTest.php + + - + message: '#^Method WP_Mock\\Tests\\Unit\\WP_Mock\\Tools\\TestCaseTest\:\:testCanCleanGlobals\(\) throws checked exception PHPUnit\\Framework\\MockObject\\Exception but it''s missing from the PHPDoc @throws tag\.$#' + identifier: missingType.checkedException + count: 1 + path: tests/Unit/WP_Mock/Tools/TestCaseTest.php + + - + message: '#^Method WP_Mock\\Tests\\Unit\\WP_Mock\\Tools\\TestCaseTest\:\:testCanMockStaticMethod\(\) throws checked exception PHPUnit\\Framework\\MockObject\\Exception but it''s missing from the PHPDoc @throws tag\.$#' + identifier: missingType.checkedException + count: 1 + path: tests/Unit/WP_Mock/Tools/TestCaseTest.php + + - + message: '#^Method WP_Mock\\Tests\\Unit\\WP_Mock\\Tools\\TestCaseTest\:\:testCanSetUpTests\(\) throws checked exception PHPUnit\\Framework\\MockObject\\Exception but it''s missing from the PHPDoc @throws tag\.$#' + identifier: missingType.checkedException + count: 1 + path: tests/Unit/WP_Mock/Tools/TestCaseTest.php + + - + message: '#^Method WP_Mock\\Tests\\Unit\\WP_Mock\\Tools\\TestCaseTest\:\:testCanTearDownTests\(\) throws checked exception PHPUnit\\Framework\\MockObject\\Exception but it''s missing from the PHPDoc @throws tag\.$#' + identifier: missingType.checkedException + count: 1 + path: tests/Unit/WP_Mock/Tools/TestCaseTest.php + + - + message: '#^Cannot call method getInaccessibleMethod\(\) on mixed\.$#' + identifier: method.nonObject + count: 1 + path: tests/Unit/WP_Mock/Traits/AccessInaccessibleClassMembersTraitTest.php + + - + message: '#^Cannot call method getInaccessibleProperty\(\) on mixed\.$#' + identifier: method.nonObject + count: 1 + path: tests/Unit/WP_Mock/Traits/AccessInaccessibleClassMembersTraitTest.php + + - + message: '#^Cannot call method getInaccessiblePropertyValue\(\) on mixed\.$#' + identifier: method.nonObject + count: 1 + path: tests/Unit/WP_Mock/Traits/AccessInaccessibleClassMembersTraitTest.php + + - + message: '#^Cannot call method getName\(\) on mixed\.$#' + identifier: method.nonObject + count: 2 + path: tests/Unit/WP_Mock/Traits/AccessInaccessibleClassMembersTraitTest.php + + - + message: '#^Cannot call method getValue\(\) on mixed\.$#' + identifier: method.nonObject + count: 3 + path: tests/Unit/WP_Mock/Traits/AccessInaccessibleClassMembersTraitTest.php + + - + message: '#^Cannot call method invoke\(\) on mixed\.$#' + identifier: method.nonObject + count: 1 + path: tests/Unit/WP_Mock/Traits/AccessInaccessibleClassMembersTraitTest.php + + - + message: '#^Cannot call method invokeInaccessibleMethod\(\) on mixed\.$#' + identifier: method.nonObject + count: 1 + path: tests/Unit/WP_Mock/Traits/AccessInaccessibleClassMembersTraitTest.php + + - + message: '#^Cannot call method setInaccessibleProperty\(\) on mixed\.$#' + identifier: method.nonObject + count: 1 + path: tests/Unit/WP_Mock/Traits/AccessInaccessibleClassMembersTraitTest.php + + - + message: '#^Cannot call method setInaccessiblePropertyValue\(\) on mixed\.$#' + identifier: method.nonObject + count: 1 + path: tests/Unit/WP_Mock/Traits/AccessInaccessibleClassMembersTraitTest.php + + - + message: '#^Parameter \#1 \$object of function get_class expects object, mixed given\.$#' + identifier: argument.type + count: 1 + path: tests/Unit/WP_Mock/Traits/AccessInaccessibleClassMembersTraitTest.php + + - + message: '#^Call to an undefined method WP_Mock\\Action_Responder\|WP_Mock\\Filter_Responder\|WP_Mock\\HookedCallbackResponder\:\:reply\(\)\.$#' + identifier: method.notFound + count: 5 + path: tests/Unit/WP_MockTest.php + + - + message: '#^PHPDoc tag @throws with type InvalidArgumentException\|PHPUnit\\Framework\\Exception\|SebastianBergmann\\RecursionContext\\InvalidArgumentException is not subtype of Throwable$#' + identifier: throws.notThrowable + count: 2 + path: tests/Unit/WP_MockTest.php + + - + message: '#^PHPDoc tag @throws with type Mockery\\Exception\|PHPUnit\\Framework\\Exception\|SebastianBergmann\\RecursionContext\\InvalidArgumentException is not subtype of Throwable$#' + identifier: throws.notThrowable + count: 1 + path: tests/Unit/WP_MockTest.php + + - + message: '#^PHPDoc tag @throws with type PHPUnit\\Framework\\Exception\|SebastianBergmann\\RecursionContext\\InvalidArgumentException is not subtype of Throwable$#' + identifier: throws.notThrowable + count: 4 + path: tests/Unit/WP_MockTest.php + + - + message: '#^PHPDoc tag @throws with type PHPUnit\\Framework\\ExpectationFailedException\|SebastianBergmann\\RecursionContext\\InvalidArgumentException is not subtype of Throwable$#' + identifier: throws.notThrowable + count: 3 + path: tests/Unit/WP_MockTest.php \ No newline at end of file From 09323ea65a305463ce021729897bf3b7191c3730 Mon Sep 17 00:00:00 2001 From: Fulvio Notarstefano Date: Tue, 23 Jun 2026 17:45:10 +0900 Subject: [PATCH 06/16] =?UTF-8?q?ci:=20test=20PHP=207.4=E2=80=938.4=20?= =?UTF-8?q?=C3=97=20PHPUnit=209=E2=80=9313=20matrix;=20phpcs=20testVersion?= =?UTF-8?q?=207.4-?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - CI matrix: 7.4→9, 7.4→9 (prefer-lowest), 8.0→9, 8.1→10, 8.2→11, 8.3→12, 8.4→13. Each leg runs `composer update` (a single lock can't satisfy every PHP/PHPUnit pair) and selects phpunit9.xml.dist (9.x) or phpunit.xml.dist (10+). Coverage uploaded from the 8.4 leg. - phpstan + php-compatibility jobs pinned to PHP 8.4 (phpstan analyzed against PHPUnit 13). - phpcs.xml testVersion 7.4 -> 7.4- so PHPCompatibility checks the whole 7.4+ range. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01K8HGMxCuSjUX6p4PQL7t5Y --- .github/workflows/ci.yml | 100 ++++++++++++++++++++++++--------------- phpcs.xml | 2 +- 2 files changed, 64 insertions(+), 38 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index aba44fa..a2be4f5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,22 +10,42 @@ on: jobs: php-tests: runs-on: ${{ matrix.os }} + name: ${{ matrix.label }} strategy: matrix: include: + - php: '8.4' + os: 'ubuntu-latest' + config: 'phpunit.xml.dist' + label: 'PHP 8.4 · PHPUnit 13' + coverage: true - php: '8.3' os: 'ubuntu-latest' + config: 'phpunit.xml.dist' + label: 'PHP 8.3 · PHPUnit 12' - php: '8.2' os: 'ubuntu-latest' + config: 'phpunit.xml.dist' + label: 'PHP 8.2 · PHPUnit 11' - php: '8.1' os: 'ubuntu-latest' + config: 'phpunit.xml.dist' + label: 'PHP 8.1 · PHPUnit 10' - php: '8.0' os: 'ubuntu-24.04' + config: 'phpunit9.xml.dist' + label: 'PHP 8.0 · PHPUnit 9' + - php: '7.4' + os: 'ubuntu-24.04' + config: 'phpunit9.xml.dist' + label: 'PHP 7.4 · PHPUnit 9' - php: '7.4' os: 'ubuntu-24.04' + config: 'phpunit9.xml.dist' + composer_flags: '--prefer-lowest' + label: 'PHP 7.4 · PHPUnit 9 (lowest deps)' fail-fast: false env: - COVERAGE_CACHE_PATH: phpunit-coverage-cache PHP_VERSION: ${{ matrix.php }} steps: @@ -39,86 +59,92 @@ jobs: - name: Get Composer cache directory id: composer-cache - run: | - echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + - uses: actions/cache@v4 with: path: ${{ steps.composer-cache.outputs.dir }} - key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + key: ${{ runner.os }}-php${{ matrix.php }}-composer-${{ matrix.composer_flags }}-${{ hashFiles('composer.json') }} restore-keys: | - ${{ runner.os }}-composer- + ${{ runner.os }}-php${{ matrix.php }}-composer- + # Resolve dependencies per PHP version: a single committed lock cannot satisfy + # every supported PHP/PHPUnit pair (PHPUnit 9–13 span PHP 7.4–8.4), so each leg + # updates to the highest PHPUnit its PHP allows (Composer picks 9/10/11/12/13). - name: Install dependencies - run: composer install --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist + run: composer update ${{ matrix.composer_flags }} --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist - - name: Restore phpunit coverage cache - uses: actions/cache@v4 - with: - path: ${{ env.COVERAGE_CACHE_PATH }} - key: ${{ runner.os }}-coverage-${{ github.ref }}-${{ github.sha }} - restore-keys: | - ${{ runner.os }}-coverage-${{ github.ref }}- - ${{ runner.os }}-coverage- + # Tests should be run randomly to catch any test dependency issue. + - name: Execute tests with PHPUnit + if: ${{ ! matrix.coverage }} + run: vendor/bin/phpunit -c ${{ matrix.config }} --order-by=random - name: Execute tests with PHPUnit and output code coverage report - env: - DB_CONNECTION: sqlite - DB_DATABASE: database/database.sqlite - # Generate coverage file, so it can be uploaded later if we choose. - # Otherwise, current coverage can be viewed in the action output. - # Tests should be run randomly to catch any test dependency issue. - run: | - vendor/bin/phpunit --order-by="random" --coverage-cache="$COVERAGE_CACHE_PATH" --coverage-clover=./clover.xml --coverage-text + if: ${{ matrix.coverage }} + run: vendor/bin/phpunit -c ${{ matrix.config }} --order-by=random --coverage-clover=./clover.xml --coverage-text - name: Upload test coverage results to Coveralls - if: ${{ env.PHP_VERSION == '8.1' }} + if: ${{ matrix.coverage }} env: COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - vendor/bin/php-coveralls --coverage_clover="./clover.xml" --json_path="./coveralls-upload.json" -v + run: vendor/bin/php-coveralls --coverage_clover="./clover.xml" --json_path="./coveralls-upload.json" -v php-compatibility: runs-on: ubuntu-latest + name: PHP compatibility (7.4+) steps: - uses: actions/checkout@v4 + - name: Set up PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.4' + coverage: none + - name: Get Composer Cache Directory id: composer-cache - run: | - echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - uses: actions/cache@v4 with: path: ${{ steps.composer-cache.outputs.dir }} - key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + key: ${{ runner.os }}-phpcompat-composer-${{ hashFiles('composer.json') }} restore-keys: | - ${{ runner.os }}-composer- + ${{ runner.os }}-phpcompat-composer- - name: Install Dependencies - run: composer install --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist --ignore-platform-req php + run: composer update --no-ansi --no-interaction --no-progress --prefer-dist --ignore-platform-req=php - name: Run PHP Code Sniffer using the PHPCompatibility standard run: vendor/bin/phpcs php-static-analysis: runs-on: ubuntu-latest + name: Static analysis steps: - uses: actions/checkout@v4 + - name: Set up PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.4' + coverage: none + - name: Get Composer Cache Directory id: composer-cache - run: | - echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + - uses: actions/cache@v4 with: path: ${{ steps.composer-cache.outputs.dir }} - key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + key: ${{ runner.os }}-phpstan-composer-${{ hashFiles('composer.json') }} restore-keys: | - ${{ runner.os }}-composer- + ${{ runner.os }}-phpstan-composer- + - name: Install Dependencies - run: | - composer install --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist + run: composer update --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist + - name: Run phpstan - run: vendor/bin/phpstan \ No newline at end of file + run: vendor/bin/phpstan analyse --no-progress diff --git a/phpcs.xml b/phpcs.xml index e18853e..4a8e898 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -4,5 +4,5 @@ ./tests - + \ No newline at end of file From 134eeba5184808bdba1cd9e213097d8cd28615ac Mon Sep 17 00:00:00 2001 From: Fulvio Notarstefano Date: Tue, 23 Jun 2026 17:47:47 +0900 Subject: [PATCH 07/16] =?UTF-8?q?docs:=20document=20PHPUnit=209=E2=80=9313?= =?UTF-8?q?=20support,=20assertOutputEqualsHtml,=20and=202.0.0=20upgrade?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - installation.md: PHPUnit ^9.6 || ^10 || ^11 || ^12 || ^13, mockery ^1.6.12, and a note that Composer installs the highest PHPUnit your PHP allows - wp-mock-test-case.md: replace the removed expectOutputString() override docs with assertOutputEqualsHtml(), plus a "Changed in 2.0.0" note - CHANGELOG.md: 2.0.0 entry - UPGRADE.md: 1.x -> 2.0 guide (PHP floor unchanged; the two behavior changes + migrations) Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01K8HGMxCuSjUX6p4PQL7t5Y --- CHANGELOG.md | 19 ++++++++++ UPGRADE.md | 66 +++++++++++++++++++++++++++++++++ docs/general/installation.md | 6 +-- docs/tools/wp-mock-test-case.md | 12 +++--- 4 files changed, 95 insertions(+), 8 deletions(-) create mode 100644 UPGRADE.md diff --git a/CHANGELOG.md b/CHANGELOG.md index e05343c..49c3aaf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,25 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [2.0.0](https://github.com/10up/wp_mock/compare/1.1.1...trunk) - Unreleased + +WP_Mock now supports PHPUnit 9.6, 10, 11, 12 and 13 from a single codebase. The minimum PHP version is unchanged (7.4); Composer installs the highest PHPUnit version your PHP allows. Most projects can upgrade by bumping only `10up/wp_mock` — see [UPGRADE.md](https://github.com/10up/wp_mock/blob/trunk/UPGRADE.md). + +### Added +- Support for PHPUnit 10, 11, 12 and 13 (in addition to 9.6) +- `TestCase::assertOutputEqualsHtml(string $expectedHtml, callable $callback)` for whitespace-insensitive assertions on echoed HTML output + +### Changed +- **BREAKING:** deprecated WP_Mock methods now emit a native `E_USER_DEPRECATED` notice (via `trigger_error()`) instead of forcing a test to be marked "risky". To fail tests on these, set `failOnDeprecation="true"` (PHPUnit 10+) or `convertDeprecationsToExceptions="true"` (PHPUnit 9) in your configuration. +- **BREAKING:** removed the implicit output filtering and the `@stripTabsAndNewlinesFromOutput` annotation — including the `TestCase::expectOutputString()` override — because PHPUnit 10 removed `setOutputCallback()` and made `expectOutputString()` `final`. Use `assertOutputEqualsHtml()` (or PHPUnit's native `expectOutputString()` for exact matches). +- `IsEqualHtml` now implements `matches()`/`toString()` and takes a single constructor argument +- Raised minimum dependencies: `mockery/mockery ^1.6.12`, PHPStan tooling to `^2` +- Test runner configuration is split into `phpunit.xml.dist` (PHPUnit 10+) and `phpunit9.xml.dist` (PHPUnit 9) + +### Removed +- The abandoned `sempro/phpunit-pretty-print` dev dependency (use `--testdox`) +- `DeprecatedMethodListener::setTestResult()`, `setTestCase()`, `checkCalls()` and the `TestCase::run()` override (relied on PHPUnit's removed `TestResult`/`RiskyTestError`) + ## [1.1.1](https://github.com/10up/wp_mock/compare/1.1.0...1.1.1) - 2025-12-03 ### Fixed - Address PHP deprecation warnings about implicitly nullable parameters diff --git a/UPGRADE.md b/UPGRADE.md new file mode 100644 index 0000000..818077f --- /dev/null +++ b/UPGRADE.md @@ -0,0 +1,66 @@ +# Upgrade Guide + +## 1.x → 2.0 + +WP_Mock 2.0 adds support for **PHPUnit 10, 11, 12 and 13** alongside the existing PHPUnit 9.6, from a single codebase. The **minimum PHP version is unchanged (7.4)**. + +Composer installs the highest PHPUnit version compatible with your PHP: + +| Your PHP | PHPUnit installed | +|----------|-------------------| +| 7.4 | 9 | +| 8.0 | 9 | +| 8.1 | 10 | +| 8.2 | 11 | +| 8.3 | 12 | +| 8.4 | 13 | + +For most projects, upgrading is just: + +```shell +composer require --dev 10up/wp_mock:^2.0 +``` + +You do **not** have to change your PHPUnit version to adopt 2.0 — if you stay on PHPUnit 9, WP_Mock 2.0 still works. The public assertion API (`assertConditionsMet()`, `assertHooksAdded()`, `assertActionsCalled()`, `assertEqualsHtml()`, `mockStaticMethod()`, …) is unchanged. + +There are two behavior changes to be aware of. + +### 1. Deprecated-method detection + +If you (or your tooling) relied on WP_Mock marking a test **risky** when a deprecated WP_Mock method was called, that signal is now a native PHP **deprecation** (`E_USER_DEPRECATED`) surfaced through PHPUnit's own deprecation reporting. + +To make these fail your test suite, enable strict deprecation handling in your PHPUnit configuration: + +- **PHPUnit 10+:** `failOnDeprecation="true"` +- **PHPUnit 9:** `convertDeprecationsToExceptions="true"` + +With default configuration, the deprecation is reported but does not fail the run. + +### 2. Output assertions + +Earlier versions overrode PHPUnit's `expectOutputString()` to automatically strip tabs and newlines from output (toggled with the `@stripTabsAndNewlinesFromOutput` annotation). PHPUnit 10 removed `setOutputCallback()` and made `expectOutputString()` `final`, so this is no longer possible. + +If you relied on that whitespace-insensitive output matching, switch to the new helper: + +```php +// Before (1.x): output was silently stripped of tabs/newlines before comparison +$this->expectOutputString('
Test
'); +my_function_that_echoes_html(); + +// After (2.0): wrap the output-producing code in a callback +$this->assertOutputEqualsHtml('
Test
', static function () { + my_function_that_echoes_html(); +}); +``` + +PHPUnit's native `expectOutputString()` is still available if you want an **exact** (whitespace-sensitive) match. + +### Removed internals + +These were internal/rarely-used and are gone in 2.0 (they depended on PHPUnit APIs removed in PHPUnit 10): + +- `WP_Mock\DeprecatedMethodListener::setTestResult()`, `setTestCase()`, `checkCalls()` +- The `WP_Mock\Tools\TestCase::run()` override +- The `@stripTabsAndNewlinesFromOutput` annotation and `TestCase::stripTabsAndNewlines()` + +`WP_Mock::getDeprecatedMethodListener()` and `DeprecatedMethodListener::logDeprecatedCall()` remain available. diff --git a/docs/general/installation.md b/docs/general/installation.md index 071fbdc..413a98c 100644 --- a/docs/general/installation.md +++ b/docs/general/installation.md @@ -17,11 +17,11 @@ composer require --dev 10up/wp_mock WP_Mock needs the following dependencies to work: -* PHPUnit ^9.5 (BSD 3-Clause license) -* Mockery ^1.6 (BSD 3-Clause license) +* PHPUnit ^9.6 || ^10 || ^11 || ^12 || ^13 (BSD 3-Clause license) +* Mockery ^1.6.12 (BSD 3-Clause license) * Patchwork ^2.1 (MIT license) -They will be installed for you by Composer. +They will be installed for you by Composer. WP_Mock supports PHPUnit 9 through 13 from a single codebase; Composer installs the highest PHPUnit version your PHP version allows (for example PHP 7.4 resolves PHPUnit 9, while PHP 8.4 resolves PHPUnit 13). Next, you will need to configure PHPUnit first before enabling WP_Mock. [Consult PHPUnit documentation](https://phpunit.de/documentation.html) for this step. diff --git a/docs/tools/wp-mock-test-case.md b/docs/tools/wp-mock-test-case.md index 5ea065b..b3d6bf4 100644 --- a/docs/tools/wp-mock-test-case.md +++ b/docs/tools/wp-mock-test-case.md @@ -54,9 +54,9 @@ final class MyTestCase extends TestCase } ``` -### Expect output string +### Assert output equals HTML -The `TestCase::expectOutputString()` function will assert that the output of a function matches a given string. This is useful when you want to test the output of a function that echoes HTML. +The `TestCase::assertOutputEqualsHtml()` function runs a callback, captures everything it echoes, and asserts that the captured output equals the expected HTML — ignoring insignificant whitespace (tabs, newlines and collapsed runs of spaces). This is useful when you want to test a function that echoes HTML without having to match its exact formatting. ```php use WP_Mock\Tools\TestCase as TestCase; @@ -65,13 +65,15 @@ final class MyTestCase extends TestCase { public function testMyFunction() : void { - $this->expectOutputString('
Test
'); - - echo '
Test
'; + $this->assertOutputEqualsHtml('
Test
', static function () { + my_function_that_echoes_html(); + }); } } ``` +> **Changed in 2.0.0:** earlier versions overrode PHPUnit's `expectOutputString()` to transparently strip tabs and newlines from output (toggled with the `@stripTabsAndNewlinesFromOutput` annotation). PHPUnit 10 removed `setOutputCallback()` and made `expectOutputString()` `final`, so that automatic behavior is no longer possible. Use `assertOutputEqualsHtml()` for whitespace-insensitive output assertions; PHPUnit's native `expectOutputString()` remains available for exact-match assertions. + ### Mock static method The `TestCase::mockStaticMethod()` function will mock a static method on a class, via Patchwork, returning a Mockery object. From 0398494b48db8135bb5aa64c650f5d8b1eea8a0f Mon Sep 17 00:00:00 2001 From: Fulvio Notarstefano Date: Tue, 23 Jun 2026 18:06:26 +0900 Subject: [PATCH 08/16] test: restore the Behat suite (broken since ~1.1.0) Behat could not even parse since a deprecated step was added, so it never ran in CI (CI runs phpunit only). Three pre-existing issues fixed: - FunctionsContext: missing semicolon (parse error) in the deprecated iExcpectWhenIRun() - FunctionsContext: remove the duplicate @Then on iExcpectWhenIRun() (the same step is defined by iExpectWhenIRun()); keep the deprecated alias as a plain method - FeatureContext: use the two-arg ReflectionProperty::setValue(null, $value) for the static __strict_mode property (single-arg form is deprecated since PHP 8.3) Behat: 40 scenarios / 118 steps passing. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01K8HGMxCuSjUX6p4PQL7t5Y --- features/bootstrap/FeatureContext.php | 6 ++++-- features/bootstrap/FunctionsContext.php | 6 ++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/features/bootstrap/FeatureContext.php b/features/bootstrap/FeatureContext.php index 8d02f6e..8a48ea0 100644 --- a/features/bootstrap/FeatureContext.php +++ b/features/bootstrap/FeatureContext.php @@ -44,7 +44,8 @@ public function ensureStrictModeOn() { public static function forceStrictModeOn() { $property = new ReflectionProperty( 'WP_Mock', '__strict_mode' ); $property->setAccessible( true ); - $property->setValue( true ); + // Two-arg form (null object for a static property) — single-arg setValue() is deprecated since PHP 8.3. + $property->setValue( null, true ); } /** @@ -57,7 +58,8 @@ public function ensureStrictModeOff() { public static function forceStrictModeOff() { $property = new ReflectionProperty( 'WP_Mock', '__strict_mode' ); $property->setAccessible( true ); - $property->setValue( false ); + // Two-arg form (null object for a static property) — single-arg setValue() is deprecated since PHP 8.3. + $property->setValue( null, false ); } /** diff --git a/features/bootstrap/FunctionsContext.php b/features/bootstrap/FunctionsContext.php index e4325af..2da3b92 100644 --- a/features/bootstrap/FunctionsContext.php +++ b/features/bootstrap/FunctionsContext.php @@ -88,12 +88,10 @@ public function iExpectWhenIRunWithArgs( $return, $function, TableNode $args ) { } /** - * @Then I expect :return when I run :function - * - * @deprected use static::iExpectWhenIRun instead + * @deprecated use iExpectWhenIRun() instead */ public function iExcpectWhenIRun( $return, $function ) { - static::iExpectWhenIRun( $return, $function ) + $this->iExpectWhenIRun( $return, $function ); } /** From 1c71d45f4dea677d17f7dbc7ab63165dff94d99e Mon Sep 17 00:00:00 2001 From: Fulvio Notarstefano Date: Tue, 23 Jun 2026 18:06:26 +0900 Subject: [PATCH 09/16] fix: expectFilterNotAdded() default $args 10 -> 1 so it actually guards the hook expectHookNotAdded() registers a shouldNotReceive expectation `->with($callback, $priority, $args)`. expectFilterNotAdded() defaulted $args to 10 (a copy of the priority value) while every sibling uses 1, so a standard add_filter() (1 accepted arg) never matched the guard and the "not added" expectation silently passed. Caught by the now-restored Behat scenario "expectFilterNotAdded fails when filter added". Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01K8HGMxCuSjUX6p4PQL7t5Y --- CHANGELOG.md | 3 +++ php/WP_Mock.php | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 49c3aaf..8ec6f86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,9 @@ WP_Mock now supports PHPUnit 9.6, 10, 11, 12 and 13 from a single codebase. The - The abandoned `sempro/phpunit-pretty-print` dev dependency (use `--testdox`) - `DeprecatedMethodListener::setTestResult()`, `setTestCase()`, `checkCalls()` and the `TestCase::run()` override (relied on PHPUnit's removed `TestResult`/`RiskyTestError`) +### Fixed +- `WP_Mock::expectFilterNotAdded()` defaulted `$args` to `10` instead of `1`, so the "not added" expectation never matched a standard `add_filter()` call and silently passed; it now correctly guards the hook + ## [1.1.1](https://github.com/10up/wp_mock/compare/1.1.0...1.1.1) - 2025-12-03 ### Fixed - Address PHP deprecation warnings about implicitly nullable parameters diff --git a/php/WP_Mock.php b/php/WP_Mock.php index 49ef1f3..1c44a29 100644 --- a/php/WP_Mock.php +++ b/php/WP_Mock.php @@ -335,7 +335,7 @@ public static function expectFilterAdded(string $filter, $callback, int $priorit * @param int $args the number of arguments that should be allowed * @return void */ - public static function expectFilterNotAdded(string $filter, $callback, int $priority = 10, int $args = 10) : void + public static function expectFilterNotAdded(string $filter, $callback, int $priority = 10, int $args = 1) : void { self::expectHookNotAdded('filter', $filter, $callback, $priority, $args); } From 69b0a646fa034b0fd90883d672c7eddad93a5291 Mon Sep 17 00:00:00 2001 From: Fulvio Notarstefano Date: Tue, 23 Jun 2026 18:35:27 +0900 Subject: [PATCH 10/16] ci: upload Coveralls from a single leg, consistent with origin Keep the Coveralls integration unchanged for the eventual merge to origin: one upload, gated to the PHP 8.1 leg (matching origin's `env.PHP_VERSION == '8.1'`), same token and command. Only that leg generates coverage; the other matrix legs just run tests. Coverage numbers are leg-independent (all 175 tests run on every leg). Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01K8HGMxCuSjUX6p4PQL7t5Y --- .github/workflows/ci.yml | 7 +- phpstan-baseline.neon | 617 ++++++--------------------------------- 2 files changed, 86 insertions(+), 538 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a2be4f5..b4e99a1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,6 @@ jobs: os: 'ubuntu-latest' config: 'phpunit.xml.dist' label: 'PHP 8.4 · PHPUnit 13' - coverage: true - php: '8.3' os: 'ubuntu-latest' config: 'phpunit.xml.dist' @@ -76,15 +75,15 @@ jobs: # Tests should be run randomly to catch any test dependency issue. - name: Execute tests with PHPUnit - if: ${{ ! matrix.coverage }} + if: ${{ matrix.php != '8.1' }} run: vendor/bin/phpunit -c ${{ matrix.config }} --order-by=random - name: Execute tests with PHPUnit and output code coverage report - if: ${{ matrix.coverage }} + if: ${{ matrix.php == '8.1' }} run: vendor/bin/phpunit -c ${{ matrix.config }} --order-by=random --coverage-clover=./clover.xml --coverage-text - name: Upload test coverage results to Coveralls - if: ${{ matrix.coverage }} + if: ${{ matrix.php == '8.1' }} env: COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: vendor/bin/php-coveralls --coverage_clover="./clover.xml" --json_path="./coveralls-upload.json" -v diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 5544eb4..9ca9448 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,847 +1,396 @@ parameters: ignoreErrors: - - message: '#^Call to an undefined method Mockery\\ExpectationInterface\|Mockery\\HigherOrderMessage\:\:atLeast\(\)\.$#' - identifier: method.notFound + message: "#^Call to an undefined method Mockery\\\\ExpectationInterface\\|Mockery\\\\HigherOrderMessage\\:\\:atLeast\\(\\)\\.$#" count: 3 path: php/WP_Mock.php - - message: '#^Cannot call method andReturnUsing\(\) on mixed\.$#' - identifier: method.nonObject + message: "#^Cannot call method perform\\(\\) on mixed\\.$#" count: 1 path: php/WP_Mock.php - - message: '#^Cannot call method once\(\) on mixed\.$#' - identifier: method.nonObject - count: 3 - path: php/WP_Mock.php - - - - message: '#^Cannot call method perform\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: php/WP_Mock.php - - - - message: '#^Cannot call method reply\(\) on mixed\.$#' - identifier: method.nonObject + message: "#^Cannot call method reply\\(\\) on mixed\\.$#" count: 1 path: php/WP_Mock.php - - message: '#^Method WP_Mock\:\:activateStrictMode\(\) has no return type specified\.$#' - identifier: missingType.return + message: "#^Method WP_Mock\\:\\:activateStrictMode\\(\\) has no return type specified\\.$#" count: 1 path: php/WP_Mock.php - - message: '#^Method WP_Mock\:\:addAction\(\) has no return type specified\.$#' - identifier: missingType.return + message: "#^Method WP_Mock\\:\\:addAction\\(\\) has no return type specified\\.$#" count: 1 path: php/WP_Mock.php - - message: '#^Method WP_Mock\:\:addAction\(\) has parameter \$hook with no type specified\.$#' - identifier: missingType.parameter + message: "#^Method WP_Mock\\:\\:addAction\\(\\) has parameter \\$hook with no type specified\\.$#" count: 1 path: php/WP_Mock.php - - message: '#^Method WP_Mock\:\:addFilter\(\) has no return type specified\.$#' - identifier: missingType.return + message: "#^Method WP_Mock\\:\\:addFilter\\(\\) has no return type specified\\.$#" count: 1 path: php/WP_Mock.php - - message: '#^Method WP_Mock\:\:addFilter\(\) has parameter \$hook with no type specified\.$#' - identifier: missingType.parameter + message: "#^Method WP_Mock\\:\\:addFilter\\(\\) has parameter \\$hook with no type specified\\.$#" count: 1 path: php/WP_Mock.php - - message: '#^Method WP_Mock\:\:addHook\(\) has no return type specified\.$#' - identifier: missingType.return + message: "#^Method WP_Mock\\:\\:addHook\\(\\) has no return type specified\\.$#" count: 1 path: php/WP_Mock.php - - message: '#^Method WP_Mock\:\:addHook\(\) has parameter \$hook with no type specified\.$#' - identifier: missingType.parameter + message: "#^Method WP_Mock\\:\\:addHook\\(\\) has parameter \\$hook with no type specified\\.$#" count: 1 path: php/WP_Mock.php - - message: '#^Method WP_Mock\:\:addHook\(\) has parameter \$type with no type specified\.$#' - identifier: missingType.parameter + message: "#^Method WP_Mock\\:\\:addHook\\(\\) has parameter \\$type with no type specified\\.$#" count: 1 path: php/WP_Mock.php - - message: '#^Method WP_Mock\:\:assertActionsCalled\(\) throws checked exception PHPUnit\\Framework\\ExpectationFailedException but it''s missing from the PHPDoc @throws tag\.$#' - identifier: missingType.checkedException + message: "#^Method WP_Mock\\:\\:assertActionsCalled\\(\\) throws checked exception PHPUnit\\\\Framework\\\\ExpectationFailedException but it's missing from the PHPDoc @throws tag\\.$#" count: 1 path: php/WP_Mock.php - - message: '#^Method WP_Mock\:\:assertFiltersCalled\(\) throws checked exception PHPUnit\\Framework\\ExpectationFailedException but it''s missing from the PHPDoc @throws tag\.$#' - identifier: missingType.checkedException + message: "#^Method WP_Mock\\:\\:assertActionsCalled\\(\\) throws checked exception SebastianBergmann\\\\RecursionContext\\\\InvalidArgumentException but it's missing from the PHPDoc @throws tag\\.$#" count: 1 path: php/WP_Mock.php - - message: '#^Method WP_Mock\:\:assertHooksAdded\(\) throws checked exception PHPUnit\\Framework\\ExpectationFailedException but it''s missing from the PHPDoc @throws tag\.$#' - identifier: missingType.checkedException + message: "#^Method WP_Mock\\:\\:assertFiltersCalled\\(\\) throws checked exception PHPUnit\\\\Framework\\\\ExpectationFailedException but it's missing from the PHPDoc @throws tag\\.$#" count: 1 path: php/WP_Mock.php - - message: '#^Method WP_Mock\:\:invokeAction\(\) has no return type specified\.$#' - identifier: missingType.return + message: "#^Method WP_Mock\\:\\:assertFiltersCalled\\(\\) throws checked exception SebastianBergmann\\\\RecursionContext\\\\InvalidArgumentException but it's missing from the PHPDoc @throws tag\\.$#" count: 1 path: php/WP_Mock.php - - message: '#^Method WP_Mock\:\:onHookAdded\(\) should return WP_Mock\\HookedCallback but returns mixed\.$#' - identifier: return.type + message: "#^Method WP_Mock\\:\\:assertHooksAdded\\(\\) throws checked exception PHPUnit\\\\Framework\\\\ExpectationFailedException but it's missing from the PHPDoc @throws tag\\.$#" count: 1 path: php/WP_Mock.php - - message: '#^Method WP_Mock\:\:setUsePatchwork\(\) has no return type specified\.$#' - identifier: missingType.return + message: "#^Method WP_Mock\\:\\:assertHooksAdded\\(\\) throws checked exception SebastianBergmann\\\\RecursionContext\\\\InvalidArgumentException but it's missing from the PHPDoc @throws tag\\.$#" count: 1 path: php/WP_Mock.php - - message: '#^Method WP_Mock\:\:usingPatchwork\(\) has no return type specified\.$#' - identifier: missingType.return + message: "#^Method WP_Mock\\:\\:invokeAction\\(\\) has no return type specified\\.$#" count: 1 path: php/WP_Mock.php - - message: '#^Parameter \#1 \$callable of class WP_Mock\\InvokedFilterValue constructor expects callable\(\)\: mixed, array\{Mockery\\MockInterface, ''intercepted''\} given\.$#' - identifier: argument.type + message: "#^Method WP_Mock\\:\\:setUsePatchwork\\(\\) has no return type specified\\.$#" count: 1 path: php/WP_Mock.php - - message: '#^Parameter \#1 \(mixed\) of echo cannot be converted to string\.$#' - identifier: echo.nonString + message: "#^Method WP_Mock\\:\\:usingPatchwork\\(\\) has no return type specified\\.$#" count: 1 path: php/WP_Mock.php - - message: '#^Parameter \#2 \$args of method WP_Mock\\Functions\:\:register\(\) expects array\, array\ given\.$#' - identifier: argument.type + message: "#^Parameter \\#1 \\$callable of class WP_Mock\\\\InvokedFilterValue constructor expects callable\\(\\)\\: mixed, array\\{Mockery\\\\LegacyMockInterface, 'intercepted'\\} given\\.$#" count: 1 path: php/WP_Mock.php - - message: '#^Parameter \#2 \$array of function implode expects array\, array given\.$#' - identifier: argument.type - count: 3 - path: php/WP_Mock.php - - - - message: '#^Part \$hook \(mixed\) of encapsed string cannot be cast to string\.$#' - identifier: encapsedStringPart.nonString + message: "#^Property WP_Mock\\:\\:\\$__bootstrapped has no type specified\\.$#" count: 1 path: php/WP_Mock.php - - message: '#^Part \$type \(mixed\) of encapsed string cannot be cast to string\.$#' - identifier: encapsedStringPart.nonString + message: "#^Property WP_Mock\\:\\:\\$__strict_mode has no type specified\\.$#" count: 1 path: php/WP_Mock.php - - message: '#^Property WP_Mock\:\:\$__bootstrapped has no type specified\.$#' - identifier: missingType.property + message: "#^Property WP_Mock\\:\\:\\$__use_patchwork has no type specified\\.$#" count: 1 path: php/WP_Mock.php - - message: '#^Property WP_Mock\:\:\$__strict_mode has no type specified\.$#' - identifier: missingType.property - count: 1 - path: php/WP_Mock.php - - - - message: '#^Property WP_Mock\:\:\$__use_patchwork has no type specified\.$#' - identifier: missingType.property - count: 1 - path: php/WP_Mock.php - - - - message: '#^Function add_action\(\) has no return type specified\.$#' - identifier: missingType.return + message: "#^Function add_action\\(\\) has no return type specified\\.$#" count: 1 path: php/WP_Mock/API/function-mocks.php - - message: '#^Function add_filter\(\) has no return type specified\.$#' - identifier: missingType.return + message: "#^Function add_filter\\(\\) has no return type specified\\.$#" count: 1 path: php/WP_Mock/API/function-mocks.php - - message: '#^Function do_action\(\) should return null but returns mixed\.$#' - identifier: return.type + message: "#^PHPDoc tag @param references unknown parameter\\: \\$var$#" count: 1 path: php/WP_Mock/API/function-mocks.php - - message: '#^PHPDoc tag @param references unknown parameter\: \$var$#' - identifier: parameter.notFound - count: 1 - path: php/WP_Mock/API/function-mocks.php - - - - message: '#^Cannot access offset int\<0, max\> on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible + message: "#^Cannot access offset string on mixed\\.$#" count: 2 path: php/WP_Mock/Action.php - - message: '#^Cannot access offset string on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible + message: "#^Cannot call method react\\(\\) on mixed\\.$#" count: 1 path: php/WP_Mock/Action.php - - message: '#^Cannot call method react\(\) on mixed\.$#' - identifier: method.nonObject - count: 2 - path: php/WP_Mock/Action.php - - - - message: '#^Method WP_Mock\\Action\:\:new_responder\(\) has no return type specified\.$#' - identifier: missingType.return + message: "#^Method WP_Mock\\\\Action\\:\\:new_responder\\(\\) has no return type specified\\.$#" count: 1 path: php/WP_Mock/Action.php - - message: '#^Method WP_Mock\\Action\:\:react\(\) has no return type specified\.$#' - identifier: missingType.return + message: "#^Method WP_Mock\\\\Action\\:\\:react\\(\\) has no return type specified\\.$#" count: 1 path: php/WP_Mock/Action.php - - message: '#^Method WP_Mock\\Action\:\:react\(\) has parameter \$args with no type specified\.$#' - identifier: missingType.parameter + message: "#^Method WP_Mock\\\\Action\\:\\:react\\(\\) has parameter \\$args with no type specified\\.$#" count: 1 path: php/WP_Mock/Action.php - - message: '#^Method WP_Mock\\Action\:\:react\(\) throws checked exception PHPUnit\\Framework\\ExpectationFailedException but it''s missing from the PHPDoc @throws tag\.$#' - identifier: missingType.checkedException + message: "#^Method WP_Mock\\\\Action\\:\\:react\\(\\) throws checked exception PHPUnit\\\\Framework\\\\ExpectationFailedException but it's missing from the PHPDoc @throws tag\\.$#" count: 3 path: php/WP_Mock/Action.php - - message: '#^Method WP_Mock\\Action_Responder\:\:perform\(\) has no return type specified\.$#' - identifier: missingType.return + message: "#^Method WP_Mock\\\\Action_Responder\\:\\:perform\\(\\) has no return type specified\\.$#" count: 1 path: php/WP_Mock/Action.php - - message: '#^Method WP_Mock\\Action_Responder\:\:perform\(\) has parameter \$callable with no type specified\.$#' - identifier: missingType.parameter + message: "#^Method WP_Mock\\\\Action_Responder\\:\\:perform\\(\\) has parameter \\$callable with no type specified\\.$#" count: 1 path: php/WP_Mock/Action.php - - message: '#^Method WP_Mock\\Action_Responder\:\:react\(\) has no return type specified\.$#' - identifier: missingType.return + message: "#^Method WP_Mock\\\\Action_Responder\\:\\:react\\(\\) has no return type specified\\.$#" count: 1 path: php/WP_Mock/Action.php - - message: '#^Parameter \#1 \$callback of function call_user_func expects callable\(\)\: mixed, mixed given\.$#' - identifier: argument.type + message: "#^Parameter \\#1 \\$function of function call_user_func expects callable\\(\\)\\: mixed, mixed given\\.$#" count: 1 path: php/WP_Mock/Action.php - - message: '#^Parameter \#1 \$value of function count expects array\|Countable, mixed given\.$#' - identifier: argument.type - count: 1 - path: php/WP_Mock/Action.php - - - - message: '#^Cannot access offset non\-falsy\-string on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 2 - path: php/WP_Mock/EventManager.php - - - - message: '#^Method WP_Mock\\EventManager\:\:action\(\) should return WP_Mock\\Action but returns mixed\.$#' - identifier: return.type - count: 1 - path: php/WP_Mock/EventManager.php - - - - message: '#^Method WP_Mock\\EventManager\:\:callback\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: php/WP_Mock/EventManager.php - - - - message: '#^Method WP_Mock\\EventManager\:\:callback\(\) has parameter \$name with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: php/WP_Mock/EventManager.php - - - - message: '#^Method WP_Mock\\EventManager\:\:callback\(\) has parameter \$type with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: php/WP_Mock/EventManager.php - - - - message: '#^Method WP_Mock\\EventManager\:\:called\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: php/WP_Mock/EventManager.php - - - - message: '#^Method WP_Mock\\EventManager\:\:expectedActions\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: php/WP_Mock/EventManager.php - - - - message: '#^Method WP_Mock\\EventManager\:\:expectedFilters\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue - count: 1 - path: php/WP_Mock/EventManager.php - - - - message: '#^Method WP_Mock\\EventManager\:\:expectedHooks\(\) return type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue + message: "#^Method WP_Mock\\\\EventManager\\:\\:callback\\(\\) has no return type specified\\.$#" count: 1 path: php/WP_Mock/EventManager.php - - message: '#^Method WP_Mock\\EventManager\:\:filter\(\) should return WP_Mock\\Filter but returns mixed\.$#' - identifier: return.type + message: "#^Method WP_Mock\\\\EventManager\\:\\:callback\\(\\) has parameter \\$name with no type specified\\.$#" count: 1 path: php/WP_Mock/EventManager.php - - message: '#^Method WP_Mock\\EventManager\:\:flush\(\) has no return type specified\.$#' - identifier: missingType.return + message: "#^Method WP_Mock\\\\EventManager\\:\\:callback\\(\\) has parameter \\$type with no type specified\\.$#" count: 1 path: php/WP_Mock/EventManager.php - - message: '#^Parameter \#1 \$array of function array_keys expects array, mixed given\.$#' - identifier: argument.type + message: "#^Method WP_Mock\\\\EventManager\\:\\:called\\(\\) has no return type specified\\.$#" count: 1 path: php/WP_Mock/EventManager.php - - message: '#^Parameter \#1 \$haystack of function strpos expects string, mixed given\.$#' - identifier: argument.type - count: 3 - path: php/WP_Mock/EventManager.php - - - - message: '#^Parameter \#1 \$name of class WP_Mock\\HookedCallback constructor expects string, mixed given\.$#' - identifier: argument.type + message: "#^Method WP_Mock\\\\EventManager\\:\\:expectedActions\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: php/WP_Mock/EventManager.php - - message: '#^Parameter \#1 \$type of method WP_Mock\\HookedCallback\:\:setType\(\) expects string, mixed given\.$#' - identifier: argument.type + message: "#^Method WP_Mock\\\\EventManager\\:\\:expectedFilters\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: php/WP_Mock/EventManager.php - - message: '#^Parameter \#2 \$offset of function array_splice expects int, int\|string\|false given\.$#' - identifier: argument.type + message: "#^Method WP_Mock\\\\EventManager\\:\\:expectedHooks\\(\\) return type has no value type specified in iterable type array\\.$#" count: 1 path: php/WP_Mock/EventManager.php - - message: '#^Part \$name \(mixed\) of encapsed string cannot be cast to string\.$#' - identifier: encapsedStringPart.nonString + message: "#^Method WP_Mock\\\\EventManager\\:\\:flush\\(\\) has no return type specified\\.$#" count: 1 path: php/WP_Mock/EventManager.php - - message: '#^Part \$type \(mixed\) of encapsed string cannot be cast to string\.$#' - identifier: encapsedStringPart.nonString + message: "#^Parameter \\#2 \\$offset of function array_splice expects int, int\\|string\\|false given\\.$#" count: 1 path: php/WP_Mock/EventManager.php - - message: '#^Property WP_Mock\\EventManager\:\:\$actions type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue + message: "#^Property WP_Mock\\\\EventManager\\:\\:\\$actions type has no value type specified in iterable type array\\.$#" count: 1 path: php/WP_Mock/EventManager.php - - message: '#^Property WP_Mock\\EventManager\:\:\$callbacks has no type specified\.$#' - identifier: missingType.property + message: "#^Property WP_Mock\\\\EventManager\\:\\:\\$callbacks has no type specified\\.$#" count: 1 path: php/WP_Mock/EventManager.php - - message: '#^Property WP_Mock\\EventManager\:\:\$expected type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue + message: "#^Property WP_Mock\\\\EventManager\\:\\:\\$expected type has no value type specified in iterable type array\\.$#" count: 1 path: php/WP_Mock/EventManager.php - - message: '#^Property WP_Mock\\EventManager\:\:\$filters type has no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue + message: "#^Property WP_Mock\\\\EventManager\\:\\:\\$filters type has no value type specified in iterable type array\\.$#" count: 1 path: php/WP_Mock/EventManager.php - - message: '#^Cannot call method send\(\) on mixed\.$#' - identifier: method.nonObject + message: "#^Cannot call method send\\(\\) on mixed\\.$#" count: 1 path: php/WP_Mock/Filter.php - - message: '#^Method WP_Mock\\Filter\:\:apply\(\) has parameter \$args with no value type specified in iterable type array\.$#' - identifier: missingType.iterableValue + message: "#^Method WP_Mock\\\\Filter\\:\\:apply\\(\\) has parameter \\$args with no value type specified in iterable type array\\.$#" count: 1 path: php/WP_Mock/Filter.php - - message: '#^Method WP_Mock\\Filter\:\:apply\(\) throws checked exception PHPUnit\\Framework\\ExpectationFailedException but it''s missing from the PHPDoc @throws tag\.$#' - identifier: missingType.checkedException + message: "#^Method WP_Mock\\\\Filter\\:\\:apply\\(\\) throws checked exception PHPUnit\\\\Framework\\\\ExpectationFailedException but it's missing from the PHPDoc @throws tag\\.$#" count: 2 path: php/WP_Mock/Filter.php - - message: '#^Method WP_Mock\\Filter_Responder\:\:reply\(\) has no return type specified\.$#' - identifier: missingType.return + message: "#^Method WP_Mock\\\\Filter\\:\\:new_responder\\(\\) has no return type specified\\.$#" count: 1 path: php/WP_Mock/Filter.php - - message: '#^Method WP_Mock\\Filter_Responder\:\:reply\(\) has parameter \$value with no type specified\.$#' - identifier: missingType.parameter + message: "#^Method WP_Mock\\\\Filter_Responder\\:\\:reply\\(\\) has no return type specified\\.$#" count: 1 path: php/WP_Mock/Filter.php - - message: '#^Method WP_Mock\\Filter_Responder\:\:send\(\) has no return type specified\.$#' - identifier: missingType.return + message: "#^Method WP_Mock\\\\Filter_Responder\\:\\:reply\\(\\) has parameter \\$value with no type specified\\.$#" count: 1 path: php/WP_Mock/Filter.php - - message: '#^Parameter \#1 \$callback of function call_user_func_array expects callable\(\)\: mixed, array\{mixed, ''send''\} given\.$#' - identifier: argument.type + message: "#^Method WP_Mock\\\\Filter_Responder\\:\\:send\\(\\) has no return type specified\\.$#" count: 1 path: php/WP_Mock/Filter.php - - message: '#^Call to an undefined method Mockery\\CompositeExpectation\|Mockery\\Expectation\:\:atLeast\(\)\.$#' - identifier: method.notFound + message: "#^Parameter \\#1 \\$function of function call_user_func_array expects callable\\(\\)\\: mixed, array\\{mixed, 'send'\\} given\\.$#" count: 1 - path: php/WP_Mock/Functions.php - - - - message: '#^Call to an undefined method Mockery\\CompositeExpectation\|Mockery\\Expectation\:\:atMost\(\)\.$#' - identifier: method.notFound - count: 1 - path: php/WP_Mock/Functions.php - - - - message: '#^Method WP_Mock\\Functions\:\:replaceFunction\(\) throws checked exception PHPUnit\\Framework\\ExpectationFailedException but it''s missing from the PHPDoc @throws tag\.$#' - identifier: missingType.checkedException - count: 1 - path: php/WP_Mock/Functions.php - - - - message: '#^Method WP_Mock\\Functions\:\:setExpectedTimes\(\) throws checked exception InvalidArgumentException but it''s missing from the PHPDoc @throws tag\.$#' - identifier: missingType.checkedException - count: 1 - path: php/WP_Mock/Functions.php - - - - message: '#^Parameter \#1 \$expectation of method WP_Mock\\Functions\:\:setExpectedReturn\(\) expects Mockery\\Expectation, Mockery\\CompositeExpectation\|Mockery\\Expectation given\.$#' - identifier: argument.type - count: 1 - path: php/WP_Mock/Functions.php - - - - message: '#^Cannot access offset string on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 3 - path: php/WP_Mock/Hook.php + path: php/WP_Mock/Filter.php - - message: '#^Method WP_Mock\\Hook\:\:new_responder\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 + message: "#^Cannot access offset string on mixed\\.$#" + count: 4 path: php/WP_Mock/Hook.php - - message: '#^Method WP_Mock\\Hook\:\:with\(\) should return WP_Mock\\Action_Responder\|WP_Mock\\Filter_Responder\|WP_Mock\\HookedCallbackResponder but returns mixed\.$#' - identifier: return.type + message: "#^Method WP_Mock\\\\Hook\\:\\:new_responder\\(\\) has no return type specified\\.$#" count: 1 path: php/WP_Mock/Hook.php - - message: '#^Cannot access offset 0 on callable\(\)\: mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 1 - path: php/WP_Mock/HookedCallback.php - - - - message: '#^Cannot access offset 1 on callable\(\)\: mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible + message: "#^Cannot access offset 0 on callable\\(\\)\\: mixed\\.$#" count: 1 path: php/WP_Mock/HookedCallback.php - - message: '#^Cannot access offset mixed on mixed\.$#' - identifier: offsetAccess.nonOffsetAccessible - count: 2 - path: php/WP_Mock/HookedCallback.php - - - - message: '#^Cannot call method react\(\) on mixed\.$#' - identifier: method.nonObject + message: "#^Cannot access offset 1 on callable\\(\\)\\: mixed\\.$#" count: 1 path: php/WP_Mock/HookedCallback.php - - message: '#^Method WP_Mock\\HookedCallback\:\:new_responder\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: php/WP_Mock/HookedCallback.php - - - - message: '#^Method WP_Mock\\HookedCallback\:\:react\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: php/WP_Mock/HookedCallback.php - - - - message: '#^Method WP_Mock\\HookedCallback\:\:react\(\) has parameter \$argument_count with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: php/WP_Mock/HookedCallback.php - - - - message: '#^Method WP_Mock\\HookedCallback\:\:react\(\) has parameter \$callback with no type specified\.$#' - identifier: missingType.parameter - count: 1 - path: php/WP_Mock/HookedCallback.php - - - - message: '#^Method WP_Mock\\HookedCallback\:\:react\(\) has parameter \$priority with no type specified\.$#' - identifier: missingType.parameter - count: 1 + message: "#^Cannot access offset mixed on mixed\\.$#" + count: 5 path: php/WP_Mock/HookedCallback.php - - message: '#^Method WP_Mock\\HookedCallback\:\:react\(\) throws checked exception Mockery\\Exception but it''s missing from the PHPDoc @throws tag\.$#' - identifier: missingType.checkedException + message: "#^Cannot call method react\\(\\) on mixed\\.$#" count: 1 path: php/WP_Mock/HookedCallback.php - - message: '#^Method WP_Mock\\HookedCallback\:\:react\(\) throws checked exception PHPUnit\\Framework\\ExpectationFailedException but it''s missing from the PHPDoc @throws tag\.$#' - identifier: missingType.checkedException + message: "#^Method WP_Mock\\\\HookedCallback\\:\\:new_responder\\(\\) has no return type specified\\.$#" count: 1 path: php/WP_Mock/HookedCallback.php - - message: '#^Method WP_Mock\\HookedCallback\:\:react\(\) throws checked exception ReflectionException but it''s missing from the PHPDoc @throws tag\.$#' - identifier: missingType.checkedException + message: "#^Method WP_Mock\\\\HookedCallback\\:\\:react\\(\\) has no return type specified\\.$#" count: 1 path: php/WP_Mock/HookedCallback.php - - message: '#^Method WP_Mock\\HookedCallback\:\:setType\(\) has no return type specified\.$#' - identifier: missingType.return + message: "#^Method WP_Mock\\\\HookedCallback\\:\\:react\\(\\) has parameter \\$argument_count with no type specified\\.$#" count: 1 path: php/WP_Mock/HookedCallback.php - - message: '#^Method WP_Mock\\HookedCallbackResponder\:\:perform\(\) has no return type specified\.$#' - identifier: missingType.return + message: "#^Method WP_Mock\\\\HookedCallback\\:\\:react\\(\\) has parameter \\$callback with no type specified\\.$#" count: 1 path: php/WP_Mock/HookedCallback.php - - message: '#^Method WP_Mock\\HookedCallbackResponder\:\:perform\(\) has parameter \$callable with no type specified\.$#' - identifier: missingType.parameter + message: "#^Method WP_Mock\\\\HookedCallback\\:\\:react\\(\\) has parameter \\$priority with no type specified\\.$#" count: 1 path: php/WP_Mock/HookedCallback.php - - message: '#^Method WP_Mock\\HookedCallbackResponder\:\:react\(\) has no return type specified\.$#' - identifier: missingType.return + message: "#^Method WP_Mock\\\\HookedCallback\\:\\:react\\(\\) throws checked exception Mockery\\\\Exception but it's missing from the PHPDoc @throws tag\\.$#" count: 1 path: php/WP_Mock/HookedCallback.php - - message: '#^Parameter \#1 \$callback of method WP_Mock\\HookedCallback\:\:callback_to_string\(\) expects callable\(\)\: mixed, mixed given\.$#' - identifier: argument.type + message: "#^Method WP_Mock\\\\HookedCallback\\:\\:react\\(\\) throws checked exception PHPUnit\\\\Framework\\\\ExpectationFailedException but it's missing from the PHPDoc @throws tag\\.$#" count: 1 path: php/WP_Mock/HookedCallback.php - - message: '#^Parameter \#1 \$expected of class WP_Mock\\Matcher\\AnyInstance constructor expects object\|string\|null, mixed given\.$#' - identifier: argument.type + message: "#^Method WP_Mock\\\\HookedCallback\\:\\:react\\(\\) throws checked exception ReflectionException but it's missing from the PHPDoc @throws tag\\.$#" count: 1 path: php/WP_Mock/HookedCallback.php - - message: '#^Parameter \#1 \$object of function get_class expects object, mixed given\.$#' - identifier: argument.type + message: "#^Method WP_Mock\\\\HookedCallback\\:\\:setType\\(\\) has no return type specified\\.$#" count: 1 path: php/WP_Mock/HookedCallback.php - - message: '#^Parameter \#2 \.\.\.\$values of function sprintf expects bool\|float\|int\|string\|null, mixed given\.$#' - identifier: argument.type + message: "#^Method WP_Mock\\\\HookedCallbackResponder\\:\\:perform\\(\\) has no return type specified\\.$#" count: 1 path: php/WP_Mock/HookedCallback.php - - message: '#^Part \$method \(mixed\) of encapsed string cannot be cast to string\.$#' - identifier: encapsedStringPart.nonString + message: "#^Method WP_Mock\\\\HookedCallbackResponder\\:\\:perform\\(\\) has parameter \\$callable with no type specified\\.$#" count: 1 path: php/WP_Mock/HookedCallback.php - - message: '#^Property WP_Mock\\HookedCallback\:\:\$callback has no type specified\.$#' - identifier: missingType.property + message: "#^Method WP_Mock\\\\HookedCallbackResponder\\:\\:react\\(\\) has no return type specified\\.$#" count: 1 path: php/WP_Mock/HookedCallback.php - - message: '#^Property WP_Mock\\HookedCallback\:\:\$type has no type specified\.$#' - identifier: missingType.property + message: "#^Property WP_Mock\\\\HookedCallback\\:\\:\\$callback has no type specified\\.$#" count: 1 path: php/WP_Mock/HookedCallback.php - - message: '#^Property WP_Mock\\HookedCallbackResponder\:\:\$callable \(callable\(\)\: mixed\) does not accept mixed\.$#' - identifier: assign.propertyType + message: "#^Property WP_Mock\\\\HookedCallback\\:\\:\\$type has no type specified\\.$#" count: 1 path: php/WP_Mock/HookedCallback.php - - message: '#^Method WP_Mock\\InvokedFilterValue\:\:__invoke\(\) has no return type specified\.$#' - identifier: missingType.return - count: 1 - path: php/WP_Mock/InvokedFilterValue.php - - - - message: '#^Binary operation "\." between mixed and string results in an error\.$#' - identifier: binaryOp.invalid - count: 2 - path: php/WP_Mock/Tools/TestCase.php - - - - message: '#^Method WP_Mock\\Tests\\Unit\\WP_Mock\\DeprecatedMethodListenerTest\:\:getDeprecatedMethodCalls\(\) should return array\\}\> but returns array\\.$#' - identifier: return.type - count: 1 - path: tests/Unit/WP_Mock/DeprecatedMethodListenerTest.php - - - - message: '#^Parameter \#1 \$prefix of method PHPUnit\\Framework\\Assert\:\:assertStringStartsWith\(\) expects non\-empty\-string, string given\.$#' - identifier: argument.type - count: 1 - path: tests/Unit/WP_Mock/DeprecatedMethodListenerTest.php - - - - message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertIsArray\(\) with array\{\} will always evaluate to true\.$#' - identifier: method.alreadyNarrowedType - count: 1 - path: tests/Unit/WP_Mock/FunctionsTest.php - - - - message: '#^Method WP_Mock\\Tests\\Unit\\WP_Mock\\FunctionsTest\:\:testCanRegister\(\) throws checked exception PHPUnit\\Framework\\MockObject\\Exception but it''s missing from the PHPDoc @throws tag\.$#' - identifier: missingType.checkedException - count: 1 - path: tests/Unit/WP_Mock/FunctionsTest.php - - - - message: '#^Method WP_Mock\\Tests\\Unit\\WP_Mock\\HookTest\:\:testCanParseSafeOffSet\(\) throws checked exception PHPUnit\\Framework\\MockObject\\Exception but it''s missing from the PHPDoc @throws tag\.$#' - identifier: missingType.checkedException - count: 1 - path: tests/Unit/WP_Mock/HookTest.php - - - - message: '#^Call to an undefined method Mockery\\ExpectationInterface\|Mockery\\HigherOrderMessage\:\:with\(\)\.$#' - identifier: method.notFound - count: 1 - path: tests/Unit/WP_Mock/Matcher/FuzzyObjectTest.php - - - - message: '#^Cannot call method andReturnTrue\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: tests/Unit/WP_Mock/Matcher/FuzzyObjectTest.php - - - - message: '#^Method WP_Mock\\Tests\\Unit\\WP_Mock\\Tools\\TestCaseTest\:\:testCanAssertCurrentTestConditionsWereMet\(\) throws checked exception PHPUnit\\Framework\\MockObject\\Exception but it''s missing from the PHPDoc @throws tag\.$#' - identifier: missingType.checkedException - count: 1 - path: tests/Unit/WP_Mock/Tools/TestCaseTest.php - - - - message: '#^Method WP_Mock\\Tests\\Unit\\WP_Mock\\Tools\\TestCaseTest\:\:testCanAssertEqualsHtml\(\) throws checked exception PHPUnit\\Framework\\MockObject\\Exception but it''s missing from the PHPDoc @throws tag\.$#' - identifier: missingType.checkedException - count: 1 - path: tests/Unit/WP_Mock/Tools/TestCaseTest.php - - - - message: '#^Method WP_Mock\\Tests\\Unit\\WP_Mock\\Tools\\TestCaseTest\:\:testCanAssertExpectedActionsWereCalled\(\) throws checked exception PHPUnit\\Framework\\MockObject\\Exception but it''s missing from the PHPDoc @throws tag\.$#' - identifier: missingType.checkedException - count: 1 - path: tests/Unit/WP_Mock/Tools/TestCaseTest.php - - - - message: '#^Method WP_Mock\\Tests\\Unit\\WP_Mock\\Tools\\TestCaseTest\:\:testCanAssertExpectedHooksWereAdded\(\) throws checked exception PHPUnit\\Framework\\MockObject\\Exception but it''s missing from the PHPDoc @throws tag\.$#' - identifier: missingType.checkedException + message: "#^Method WP_Mock\\\\InvokedFilterValue\\:\\:__invoke\\(\\) has no return type specified\\.$#" count: 1 - path: tests/Unit/WP_Mock/Tools/TestCaseTest.php - - - - message: '#^Method WP_Mock\\Tests\\Unit\\WP_Mock\\Tools\\TestCaseTest\:\:testCanAssertOutputEqualsHtml\(\) throws checked exception PHPUnit\\Framework\\MockObject\\Exception but it''s missing from the PHPDoc @throws tag\.$#' - identifier: missingType.checkedException - count: 1 - path: tests/Unit/WP_Mock/Tools/TestCaseTest.php - - - - message: '#^Method WP_Mock\\Tests\\Unit\\WP_Mock\\Tools\\TestCaseTest\:\:testCanAssertTestConditionsWereMet\(\) throws checked exception PHPUnit\\Framework\\InvalidArgumentException but it''s missing from the PHPDoc @throws tag\.$#' - identifier: missingType.checkedException - count: 1 - path: tests/Unit/WP_Mock/Tools/TestCaseTest.php - - - - message: '#^Method WP_Mock\\Tests\\Unit\\WP_Mock\\Tools\\TestCaseTest\:\:testCanAssertTestConditionsWereMet\(\) throws checked exception PHPUnit\\Framework\\MockObject\\Exception but it''s missing from the PHPDoc @throws tag\.$#' - identifier: missingType.checkedException - count: 1 - path: tests/Unit/WP_Mock/Tools/TestCaseTest.php - - - - message: '#^Method WP_Mock\\Tests\\Unit\\WP_Mock\\Tools\\TestCaseTest\:\:testCanCleanGlobals\(\) throws checked exception PHPUnit\\Framework\\MockObject\\Exception but it''s missing from the PHPDoc @throws tag\.$#' - identifier: missingType.checkedException - count: 1 - path: tests/Unit/WP_Mock/Tools/TestCaseTest.php - - - - message: '#^Method WP_Mock\\Tests\\Unit\\WP_Mock\\Tools\\TestCaseTest\:\:testCanMockStaticMethod\(\) throws checked exception PHPUnit\\Framework\\MockObject\\Exception but it''s missing from the PHPDoc @throws tag\.$#' - identifier: missingType.checkedException - count: 1 - path: tests/Unit/WP_Mock/Tools/TestCaseTest.php - - - - message: '#^Method WP_Mock\\Tests\\Unit\\WP_Mock\\Tools\\TestCaseTest\:\:testCanSetUpTests\(\) throws checked exception PHPUnit\\Framework\\MockObject\\Exception but it''s missing from the PHPDoc @throws tag\.$#' - identifier: missingType.checkedException - count: 1 - path: tests/Unit/WP_Mock/Tools/TestCaseTest.php - - - - message: '#^Method WP_Mock\\Tests\\Unit\\WP_Mock\\Tools\\TestCaseTest\:\:testCanTearDownTests\(\) throws checked exception PHPUnit\\Framework\\MockObject\\Exception but it''s missing from the PHPDoc @throws tag\.$#' - identifier: missingType.checkedException - count: 1 - path: tests/Unit/WP_Mock/Tools/TestCaseTest.php - - - - message: '#^Cannot call method getInaccessibleMethod\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: tests/Unit/WP_Mock/Traits/AccessInaccessibleClassMembersTraitTest.php - - - - message: '#^Cannot call method getInaccessibleProperty\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: tests/Unit/WP_Mock/Traits/AccessInaccessibleClassMembersTraitTest.php - - - - message: '#^Cannot call method getInaccessiblePropertyValue\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: tests/Unit/WP_Mock/Traits/AccessInaccessibleClassMembersTraitTest.php - - - - message: '#^Cannot call method getName\(\) on mixed\.$#' - identifier: method.nonObject - count: 2 - path: tests/Unit/WP_Mock/Traits/AccessInaccessibleClassMembersTraitTest.php - - - - message: '#^Cannot call method getValue\(\) on mixed\.$#' - identifier: method.nonObject - count: 3 - path: tests/Unit/WP_Mock/Traits/AccessInaccessibleClassMembersTraitTest.php - - - - message: '#^Cannot call method invoke\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: tests/Unit/WP_Mock/Traits/AccessInaccessibleClassMembersTraitTest.php - - - - message: '#^Cannot call method invokeInaccessibleMethod\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: tests/Unit/WP_Mock/Traits/AccessInaccessibleClassMembersTraitTest.php - - - - message: '#^Cannot call method setInaccessibleProperty\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: tests/Unit/WP_Mock/Traits/AccessInaccessibleClassMembersTraitTest.php - - - - message: '#^Cannot call method setInaccessiblePropertyValue\(\) on mixed\.$#' - identifier: method.nonObject - count: 1 - path: tests/Unit/WP_Mock/Traits/AccessInaccessibleClassMembersTraitTest.php - - - - message: '#^Parameter \#1 \$object of function get_class expects object, mixed given\.$#' - identifier: argument.type - count: 1 - path: tests/Unit/WP_Mock/Traits/AccessInaccessibleClassMembersTraitTest.php - - - - message: '#^Call to an undefined method WP_Mock\\Action_Responder\|WP_Mock\\Filter_Responder\|WP_Mock\\HookedCallbackResponder\:\:reply\(\)\.$#' - identifier: method.notFound - count: 5 - path: tests/Unit/WP_MockTest.php - - - - message: '#^PHPDoc tag @throws with type InvalidArgumentException\|PHPUnit\\Framework\\Exception\|SebastianBergmann\\RecursionContext\\InvalidArgumentException is not subtype of Throwable$#' - identifier: throws.notThrowable - count: 2 - path: tests/Unit/WP_MockTest.php - - - - message: '#^PHPDoc tag @throws with type Mockery\\Exception\|PHPUnit\\Framework\\Exception\|SebastianBergmann\\RecursionContext\\InvalidArgumentException is not subtype of Throwable$#' - identifier: throws.notThrowable - count: 1 - path: tests/Unit/WP_MockTest.php - - - - message: '#^PHPDoc tag @throws with type PHPUnit\\Framework\\Exception\|SebastianBergmann\\RecursionContext\\InvalidArgumentException is not subtype of Throwable$#' - identifier: throws.notThrowable - count: 4 - path: tests/Unit/WP_MockTest.php - - - - message: '#^PHPDoc tag @throws with type PHPUnit\\Framework\\ExpectationFailedException\|SebastianBergmann\\RecursionContext\\InvalidArgumentException is not subtype of Throwable$#' - identifier: throws.notThrowable - count: 3 - path: tests/Unit/WP_MockTest.php \ No newline at end of file + path: php/WP_Mock/InvokedFilterValue.php \ No newline at end of file From c297cc086fa7712279c0f5b0c47ba0115cb39b34 Mon Sep 17 00:00:00 2001 From: Fulvio Notarstefano Date: Tue, 23 Jun 2026 18:35:27 +0900 Subject: [PATCH 11/16] chore: keep the phpstan v2 baseline lean so the migration adds no debt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit phpstan ^1 cannot coexist with the modern toolchain (composer/pcre 3.4.0 conflicts with phpstan 1.x on PHP 8.4), so phpstan ^2 is required. To stop the migration from padding the baseline: - Declare PHPUnit's mock-creation exceptions (MockObject\Exception, InvalidArgumentException) as uncheckedExceptionClasses — they are test-runner infrastructure, not exceptions a test documents in @throws. - Relax getDeprecatedMethodCalls() return type; guard a non-empty-string assertion. Baseline entries in files this PR changes: 14 -> 1 (the remaining one is a pre-existing WP_MOCK_INCLUDE_DIR concat in untouched requireFileDependencies()). The net baseline change (79 -> 126) is phpstan v2 surfacing pre-existing issues in library code the PR never touches. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01K8HGMxCuSjUX6p4PQL7t5Y --- phpstan-baseline.neon | 527 +++++++++++++++--- phpstan.neon | 5 + .../WP_Mock/DeprecatedMethodListenerTest.php | 4 +- 3 files changed, 451 insertions(+), 85 deletions(-) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 9ca9448..bcf4e70 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,396 +1,757 @@ parameters: ignoreErrors: - - message: "#^Call to an undefined method Mockery\\\\ExpectationInterface\\|Mockery\\\\HigherOrderMessage\\:\\:atLeast\\(\\)\\.$#" + message: '#^Call to an undefined method Mockery\\ExpectationInterface\|Mockery\\HigherOrderMessage\:\:atLeast\(\)\.$#' + identifier: method.notFound count: 3 path: php/WP_Mock.php - - message: "#^Cannot call method perform\\(\\) on mixed\\.$#" + message: '#^Cannot call method andReturnUsing\(\) on mixed\.$#' + identifier: method.nonObject count: 1 path: php/WP_Mock.php - - message: "#^Cannot call method reply\\(\\) on mixed\\.$#" + message: '#^Cannot call method once\(\) on mixed\.$#' + identifier: method.nonObject + count: 3 + path: php/WP_Mock.php + + - + message: '#^Cannot call method perform\(\) on mixed\.$#' + identifier: method.nonObject + count: 1 + path: php/WP_Mock.php + + - + message: '#^Cannot call method reply\(\) on mixed\.$#' + identifier: method.nonObject + count: 1 + path: php/WP_Mock.php + + - + message: '#^Method WP_Mock\:\:activateStrictMode\(\) has no return type specified\.$#' + identifier: missingType.return count: 1 path: php/WP_Mock.php - - message: "#^Method WP_Mock\\:\\:activateStrictMode\\(\\) has no return type specified\\.$#" + message: '#^Method WP_Mock\:\:addAction\(\) has no return type specified\.$#' + identifier: missingType.return count: 1 path: php/WP_Mock.php - - message: "#^Method WP_Mock\\:\\:addAction\\(\\) has no return type specified\\.$#" + message: '#^Method WP_Mock\:\:addAction\(\) has parameter \$hook with no type specified\.$#' + identifier: missingType.parameter count: 1 path: php/WP_Mock.php - - message: "#^Method WP_Mock\\:\\:addAction\\(\\) has parameter \\$hook with no type specified\\.$#" + message: '#^Method WP_Mock\:\:addFilter\(\) has no return type specified\.$#' + identifier: missingType.return count: 1 path: php/WP_Mock.php - - message: "#^Method WP_Mock\\:\\:addFilter\\(\\) has no return type specified\\.$#" + message: '#^Method WP_Mock\:\:addFilter\(\) has parameter \$hook with no type specified\.$#' + identifier: missingType.parameter count: 1 path: php/WP_Mock.php - - message: "#^Method WP_Mock\\:\\:addFilter\\(\\) has parameter \\$hook with no type specified\\.$#" + message: '#^Method WP_Mock\:\:addHook\(\) has no return type specified\.$#' + identifier: missingType.return count: 1 path: php/WP_Mock.php - - message: "#^Method WP_Mock\\:\\:addHook\\(\\) has no return type specified\\.$#" + message: '#^Method WP_Mock\:\:addHook\(\) has parameter \$hook with no type specified\.$#' + identifier: missingType.parameter count: 1 path: php/WP_Mock.php - - message: "#^Method WP_Mock\\:\\:addHook\\(\\) has parameter \\$hook with no type specified\\.$#" + message: '#^Method WP_Mock\:\:addHook\(\) has parameter \$type with no type specified\.$#' + identifier: missingType.parameter count: 1 path: php/WP_Mock.php - - message: "#^Method WP_Mock\\:\\:addHook\\(\\) has parameter \\$type with no type specified\\.$#" + message: '#^Method WP_Mock\:\:assertActionsCalled\(\) throws checked exception PHPUnit\\Framework\\ExpectationFailedException but it''s missing from the PHPDoc @throws tag\.$#' + identifier: missingType.checkedException count: 1 path: php/WP_Mock.php - - message: "#^Method WP_Mock\\:\\:assertActionsCalled\\(\\) throws checked exception PHPUnit\\\\Framework\\\\ExpectationFailedException but it's missing from the PHPDoc @throws tag\\.$#" + message: '#^Method WP_Mock\:\:assertFiltersCalled\(\) throws checked exception PHPUnit\\Framework\\ExpectationFailedException but it''s missing from the PHPDoc @throws tag\.$#' + identifier: missingType.checkedException count: 1 path: php/WP_Mock.php - - message: "#^Method WP_Mock\\:\\:assertActionsCalled\\(\\) throws checked exception SebastianBergmann\\\\RecursionContext\\\\InvalidArgumentException but it's missing from the PHPDoc @throws tag\\.$#" + message: '#^Method WP_Mock\:\:assertHooksAdded\(\) throws checked exception PHPUnit\\Framework\\ExpectationFailedException but it''s missing from the PHPDoc @throws tag\.$#' + identifier: missingType.checkedException count: 1 path: php/WP_Mock.php - - message: "#^Method WP_Mock\\:\\:assertFiltersCalled\\(\\) throws checked exception PHPUnit\\\\Framework\\\\ExpectationFailedException but it's missing from the PHPDoc @throws tag\\.$#" + message: '#^Method WP_Mock\:\:invokeAction\(\) has no return type specified\.$#' + identifier: missingType.return count: 1 path: php/WP_Mock.php - - message: "#^Method WP_Mock\\:\\:assertFiltersCalled\\(\\) throws checked exception SebastianBergmann\\\\RecursionContext\\\\InvalidArgumentException but it's missing from the PHPDoc @throws tag\\.$#" + message: '#^Method WP_Mock\:\:onHookAdded\(\) should return WP_Mock\\HookedCallback but returns mixed\.$#' + identifier: return.type count: 1 path: php/WP_Mock.php - - message: "#^Method WP_Mock\\:\\:assertHooksAdded\\(\\) throws checked exception PHPUnit\\\\Framework\\\\ExpectationFailedException but it's missing from the PHPDoc @throws tag\\.$#" + message: '#^Method WP_Mock\:\:setUsePatchwork\(\) has no return type specified\.$#' + identifier: missingType.return count: 1 path: php/WP_Mock.php - - message: "#^Method WP_Mock\\:\\:assertHooksAdded\\(\\) throws checked exception SebastianBergmann\\\\RecursionContext\\\\InvalidArgumentException but it's missing from the PHPDoc @throws tag\\.$#" + message: '#^Method WP_Mock\:\:usingPatchwork\(\) has no return type specified\.$#' + identifier: missingType.return count: 1 path: php/WP_Mock.php - - message: "#^Method WP_Mock\\:\\:invokeAction\\(\\) has no return type specified\\.$#" + message: '#^Parameter \#1 \$callable of class WP_Mock\\InvokedFilterValue constructor expects callable\(\)\: mixed, array\{Mockery\\MockInterface, ''intercepted''\} given\.$#' + identifier: argument.type count: 1 path: php/WP_Mock.php - - message: "#^Method WP_Mock\\:\\:setUsePatchwork\\(\\) has no return type specified\\.$#" + message: '#^Parameter \#1 \(mixed\) of echo cannot be converted to string\.$#' + identifier: echo.nonString count: 1 path: php/WP_Mock.php - - message: "#^Method WP_Mock\\:\\:usingPatchwork\\(\\) has no return type specified\\.$#" + message: '#^Parameter \#2 \$args of method WP_Mock\\Functions\:\:register\(\) expects array\, array\ given\.$#' + identifier: argument.type count: 1 path: php/WP_Mock.php - - message: "#^Parameter \\#1 \\$callable of class WP_Mock\\\\InvokedFilterValue constructor expects callable\\(\\)\\: mixed, array\\{Mockery\\\\LegacyMockInterface, 'intercepted'\\} given\\.$#" + message: '#^Parameter \#2 \$array of function implode expects array\, array given\.$#' + identifier: argument.type + count: 3 + path: php/WP_Mock.php + + - + message: '#^Part \$hook \(mixed\) of encapsed string cannot be cast to string\.$#' + identifier: encapsedStringPart.nonString count: 1 path: php/WP_Mock.php - - message: "#^Property WP_Mock\\:\\:\\$__bootstrapped has no type specified\\.$#" + message: '#^Part \$type \(mixed\) of encapsed string cannot be cast to string\.$#' + identifier: encapsedStringPart.nonString count: 1 path: php/WP_Mock.php - - message: "#^Property WP_Mock\\:\\:\\$__strict_mode has no type specified\\.$#" + message: '#^Property WP_Mock\:\:\$__bootstrapped has no type specified\.$#' + identifier: missingType.property count: 1 path: php/WP_Mock.php - - message: "#^Property WP_Mock\\:\\:\\$__use_patchwork has no type specified\\.$#" + message: '#^Property WP_Mock\:\:\$__strict_mode has no type specified\.$#' + identifier: missingType.property count: 1 path: php/WP_Mock.php - - message: "#^Function add_action\\(\\) has no return type specified\\.$#" + message: '#^Property WP_Mock\:\:\$__use_patchwork has no type specified\.$#' + identifier: missingType.property + count: 1 + path: php/WP_Mock.php + + - + message: '#^Function add_action\(\) has no return type specified\.$#' + identifier: missingType.return count: 1 path: php/WP_Mock/API/function-mocks.php - - message: "#^Function add_filter\\(\\) has no return type specified\\.$#" + message: '#^Function add_filter\(\) has no return type specified\.$#' + identifier: missingType.return count: 1 path: php/WP_Mock/API/function-mocks.php - - message: "#^PHPDoc tag @param references unknown parameter\\: \\$var$#" + message: '#^Function do_action\(\) should return null but returns mixed\.$#' + identifier: return.type count: 1 path: php/WP_Mock/API/function-mocks.php - - message: "#^Cannot access offset string on mixed\\.$#" + message: '#^PHPDoc tag @param references unknown parameter\: \$var$#' + identifier: parameter.notFound + count: 1 + path: php/WP_Mock/API/function-mocks.php + + - + message: '#^Cannot access offset int\<0, max\> on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible count: 2 path: php/WP_Mock/Action.php - - message: "#^Cannot call method react\\(\\) on mixed\\.$#" + message: '#^Cannot access offset string on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible count: 1 path: php/WP_Mock/Action.php - - message: "#^Method WP_Mock\\\\Action\\:\\:new_responder\\(\\) has no return type specified\\.$#" + message: '#^Cannot call method react\(\) on mixed\.$#' + identifier: method.nonObject + count: 2 + path: php/WP_Mock/Action.php + + - + message: '#^Method WP_Mock\\Action\:\:new_responder\(\) has no return type specified\.$#' + identifier: missingType.return count: 1 path: php/WP_Mock/Action.php - - message: "#^Method WP_Mock\\\\Action\\:\\:react\\(\\) has no return type specified\\.$#" + message: '#^Method WP_Mock\\Action\:\:react\(\) has no return type specified\.$#' + identifier: missingType.return count: 1 path: php/WP_Mock/Action.php - - message: "#^Method WP_Mock\\\\Action\\:\\:react\\(\\) has parameter \\$args with no type specified\\.$#" + message: '#^Method WP_Mock\\Action\:\:react\(\) has parameter \$args with no type specified\.$#' + identifier: missingType.parameter count: 1 path: php/WP_Mock/Action.php - - message: "#^Method WP_Mock\\\\Action\\:\\:react\\(\\) throws checked exception PHPUnit\\\\Framework\\\\ExpectationFailedException but it's missing from the PHPDoc @throws tag\\.$#" + message: '#^Method WP_Mock\\Action\:\:react\(\) throws checked exception PHPUnit\\Framework\\ExpectationFailedException but it''s missing from the PHPDoc @throws tag\.$#' + identifier: missingType.checkedException count: 3 path: php/WP_Mock/Action.php - - message: "#^Method WP_Mock\\\\Action_Responder\\:\\:perform\\(\\) has no return type specified\\.$#" + message: '#^Method WP_Mock\\Action_Responder\:\:perform\(\) has no return type specified\.$#' + identifier: missingType.return count: 1 path: php/WP_Mock/Action.php - - message: "#^Method WP_Mock\\\\Action_Responder\\:\\:perform\\(\\) has parameter \\$callable with no type specified\\.$#" + message: '#^Method WP_Mock\\Action_Responder\:\:perform\(\) has parameter \$callable with no type specified\.$#' + identifier: missingType.parameter count: 1 path: php/WP_Mock/Action.php - - message: "#^Method WP_Mock\\\\Action_Responder\\:\\:react\\(\\) has no return type specified\\.$#" + message: '#^Method WP_Mock\\Action_Responder\:\:react\(\) has no return type specified\.$#' + identifier: missingType.return count: 1 path: php/WP_Mock/Action.php - - message: "#^Parameter \\#1 \\$function of function call_user_func expects callable\\(\\)\\: mixed, mixed given\\.$#" + message: '#^Parameter \#1 \$callback of function call_user_func expects callable\(\)\: mixed, mixed given\.$#' + identifier: argument.type count: 1 path: php/WP_Mock/Action.php - - message: "#^Method WP_Mock\\\\EventManager\\:\\:callback\\(\\) has no return type specified\\.$#" + message: '#^Parameter \#1 \$value of function count expects array\|Countable, mixed given\.$#' + identifier: argument.type count: 1 + path: php/WP_Mock/Action.php + + - + message: '#^Cannot access offset non\-falsy\-string on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 2 path: php/WP_Mock/EventManager.php - - message: "#^Method WP_Mock\\\\EventManager\\:\\:callback\\(\\) has parameter \\$name with no type specified\\.$#" + message: '#^Method WP_Mock\\EventManager\:\:action\(\) should return WP_Mock\\Action but returns mixed\.$#' + identifier: return.type count: 1 path: php/WP_Mock/EventManager.php - - message: "#^Method WP_Mock\\\\EventManager\\:\\:callback\\(\\) has parameter \\$type with no type specified\\.$#" + message: '#^Method WP_Mock\\EventManager\:\:callback\(\) has no return type specified\.$#' + identifier: missingType.return count: 1 path: php/WP_Mock/EventManager.php - - message: "#^Method WP_Mock\\\\EventManager\\:\\:called\\(\\) has no return type specified\\.$#" + message: '#^Method WP_Mock\\EventManager\:\:callback\(\) has parameter \$name with no type specified\.$#' + identifier: missingType.parameter count: 1 path: php/WP_Mock/EventManager.php - - message: "#^Method WP_Mock\\\\EventManager\\:\\:expectedActions\\(\\) return type has no value type specified in iterable type array\\.$#" + message: '#^Method WP_Mock\\EventManager\:\:callback\(\) has parameter \$type with no type specified\.$#' + identifier: missingType.parameter count: 1 path: php/WP_Mock/EventManager.php - - message: "#^Method WP_Mock\\\\EventManager\\:\\:expectedFilters\\(\\) return type has no value type specified in iterable type array\\.$#" + message: '#^Method WP_Mock\\EventManager\:\:called\(\) has no return type specified\.$#' + identifier: missingType.return count: 1 path: php/WP_Mock/EventManager.php - - message: "#^Method WP_Mock\\\\EventManager\\:\\:expectedHooks\\(\\) return type has no value type specified in iterable type array\\.$#" + message: '#^Method WP_Mock\\EventManager\:\:expectedActions\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: php/WP_Mock/EventManager.php - - message: "#^Method WP_Mock\\\\EventManager\\:\\:flush\\(\\) has no return type specified\\.$#" + message: '#^Method WP_Mock\\EventManager\:\:expectedFilters\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: php/WP_Mock/EventManager.php - - message: "#^Parameter \\#2 \\$offset of function array_splice expects int, int\\|string\\|false given\\.$#" + message: '#^Method WP_Mock\\EventManager\:\:expectedHooks\(\) return type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: php/WP_Mock/EventManager.php - - message: "#^Property WP_Mock\\\\EventManager\\:\\:\\$actions type has no value type specified in iterable type array\\.$#" + message: '#^Method WP_Mock\\EventManager\:\:filter\(\) should return WP_Mock\\Filter but returns mixed\.$#' + identifier: return.type count: 1 path: php/WP_Mock/EventManager.php - - message: "#^Property WP_Mock\\\\EventManager\\:\\:\\$callbacks has no type specified\\.$#" + message: '#^Method WP_Mock\\EventManager\:\:flush\(\) has no return type specified\.$#' + identifier: missingType.return count: 1 path: php/WP_Mock/EventManager.php - - message: "#^Property WP_Mock\\\\EventManager\\:\\:\\$expected type has no value type specified in iterable type array\\.$#" + message: '#^Parameter \#1 \$array of function array_keys expects array, mixed given\.$#' + identifier: argument.type count: 1 path: php/WP_Mock/EventManager.php - - message: "#^Property WP_Mock\\\\EventManager\\:\\:\\$filters type has no value type specified in iterable type array\\.$#" + message: '#^Parameter \#1 \$haystack of function strpos expects string, mixed given\.$#' + identifier: argument.type + count: 3 + path: php/WP_Mock/EventManager.php + + - + message: '#^Parameter \#1 \$name of class WP_Mock\\HookedCallback constructor expects string, mixed given\.$#' + identifier: argument.type + count: 1 + path: php/WP_Mock/EventManager.php + + - + message: '#^Parameter \#1 \$type of method WP_Mock\\HookedCallback\:\:setType\(\) expects string, mixed given\.$#' + identifier: argument.type count: 1 path: php/WP_Mock/EventManager.php - - message: "#^Cannot call method send\\(\\) on mixed\\.$#" + message: '#^Parameter \#2 \$offset of function array_splice expects int, int\|string\|false given\.$#' + identifier: argument.type + count: 1 + path: php/WP_Mock/EventManager.php + + - + message: '#^Part \$name \(mixed\) of encapsed string cannot be cast to string\.$#' + identifier: encapsedStringPart.nonString + count: 1 + path: php/WP_Mock/EventManager.php + + - + message: '#^Part \$type \(mixed\) of encapsed string cannot be cast to string\.$#' + identifier: encapsedStringPart.nonString + count: 1 + path: php/WP_Mock/EventManager.php + + - + message: '#^Property WP_Mock\\EventManager\:\:\$actions type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: php/WP_Mock/EventManager.php + + - + message: '#^Property WP_Mock\\EventManager\:\:\$callbacks has no type specified\.$#' + identifier: missingType.property + count: 1 + path: php/WP_Mock/EventManager.php + + - + message: '#^Property WP_Mock\\EventManager\:\:\$expected type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: php/WP_Mock/EventManager.php + + - + message: '#^Property WP_Mock\\EventManager\:\:\$filters type has no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue + count: 1 + path: php/WP_Mock/EventManager.php + + - + message: '#^Cannot call method send\(\) on mixed\.$#' + identifier: method.nonObject count: 1 path: php/WP_Mock/Filter.php - - message: "#^Method WP_Mock\\\\Filter\\:\\:apply\\(\\) has parameter \\$args with no value type specified in iterable type array\\.$#" + message: '#^Method WP_Mock\\Filter\:\:apply\(\) has parameter \$args with no value type specified in iterable type array\.$#' + identifier: missingType.iterableValue count: 1 path: php/WP_Mock/Filter.php - - message: "#^Method WP_Mock\\\\Filter\\:\\:apply\\(\\) throws checked exception PHPUnit\\\\Framework\\\\ExpectationFailedException but it's missing from the PHPDoc @throws tag\\.$#" + message: '#^Method WP_Mock\\Filter\:\:apply\(\) throws checked exception PHPUnit\\Framework\\ExpectationFailedException but it''s missing from the PHPDoc @throws tag\.$#' + identifier: missingType.checkedException count: 2 path: php/WP_Mock/Filter.php - - message: "#^Method WP_Mock\\\\Filter\\:\\:new_responder\\(\\) has no return type specified\\.$#" + message: '#^Method WP_Mock\\Filter_Responder\:\:reply\(\) has no return type specified\.$#' + identifier: missingType.return count: 1 path: php/WP_Mock/Filter.php - - message: "#^Method WP_Mock\\\\Filter_Responder\\:\\:reply\\(\\) has no return type specified\\.$#" + message: '#^Method WP_Mock\\Filter_Responder\:\:reply\(\) has parameter \$value with no type specified\.$#' + identifier: missingType.parameter count: 1 path: php/WP_Mock/Filter.php - - message: "#^Method WP_Mock\\\\Filter_Responder\\:\\:reply\\(\\) has parameter \\$value with no type specified\\.$#" + message: '#^Method WP_Mock\\Filter_Responder\:\:send\(\) has no return type specified\.$#' + identifier: missingType.return count: 1 path: php/WP_Mock/Filter.php - - message: "#^Method WP_Mock\\\\Filter_Responder\\:\\:send\\(\\) has no return type specified\\.$#" + message: '#^Parameter \#1 \$callback of function call_user_func_array expects callable\(\)\: mixed, array\{mixed, ''send''\} given\.$#' + identifier: argument.type count: 1 path: php/WP_Mock/Filter.php - - message: "#^Parameter \\#1 \\$function of function call_user_func_array expects callable\\(\\)\\: mixed, array\\{mixed, 'send'\\} given\\.$#" + message: '#^Call to an undefined method Mockery\\CompositeExpectation\|Mockery\\Expectation\:\:atLeast\(\)\.$#' + identifier: method.notFound count: 1 - path: php/WP_Mock/Filter.php + path: php/WP_Mock/Functions.php - - message: "#^Cannot access offset string on mixed\\.$#" - count: 4 + message: '#^Call to an undefined method Mockery\\CompositeExpectation\|Mockery\\Expectation\:\:atMost\(\)\.$#' + identifier: method.notFound + count: 1 + path: php/WP_Mock/Functions.php + + - + message: '#^Method WP_Mock\\Functions\:\:replaceFunction\(\) throws checked exception PHPUnit\\Framework\\ExpectationFailedException but it''s missing from the PHPDoc @throws tag\.$#' + identifier: missingType.checkedException + count: 1 + path: php/WP_Mock/Functions.php + + - + message: '#^Method WP_Mock\\Functions\:\:setExpectedTimes\(\) throws checked exception InvalidArgumentException but it''s missing from the PHPDoc @throws tag\.$#' + identifier: missingType.checkedException + count: 1 + path: php/WP_Mock/Functions.php + + - + message: '#^Parameter \#1 \$expectation of method WP_Mock\\Functions\:\:setExpectedReturn\(\) expects Mockery\\Expectation, Mockery\\CompositeExpectation\|Mockery\\Expectation given\.$#' + identifier: argument.type + count: 1 + path: php/WP_Mock/Functions.php + + - + message: '#^Cannot access offset string on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 3 + path: php/WP_Mock/Hook.php + + - + message: '#^Method WP_Mock\\Hook\:\:new_responder\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 path: php/WP_Mock/Hook.php - - message: "#^Method WP_Mock\\\\Hook\\:\\:new_responder\\(\\) has no return type specified\\.$#" + message: '#^Method WP_Mock\\Hook\:\:with\(\) should return WP_Mock\\Action_Responder\|WP_Mock\\Filter_Responder\|WP_Mock\\HookedCallbackResponder but returns mixed\.$#' + identifier: return.type count: 1 path: php/WP_Mock/Hook.php - - message: "#^Cannot access offset 0 on callable\\(\\)\\: mixed\\.$#" + message: '#^Cannot access offset 0 on callable\(\)\: mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible count: 1 path: php/WP_Mock/HookedCallback.php - - message: "#^Cannot access offset 1 on callable\\(\\)\\: mixed\\.$#" + message: '#^Cannot access offset 1 on callable\(\)\: mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible count: 1 path: php/WP_Mock/HookedCallback.php - - message: "#^Cannot access offset mixed on mixed\\.$#" - count: 5 + message: '#^Cannot access offset mixed on mixed\.$#' + identifier: offsetAccess.nonOffsetAccessible + count: 2 + path: php/WP_Mock/HookedCallback.php + + - + message: '#^Cannot call method react\(\) on mixed\.$#' + identifier: method.nonObject + count: 1 + path: php/WP_Mock/HookedCallback.php + + - + message: '#^Method WP_Mock\\HookedCallback\:\:new_responder\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: php/WP_Mock/HookedCallback.php + + - + message: '#^Method WP_Mock\\HookedCallback\:\:react\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 path: php/WP_Mock/HookedCallback.php - - message: "#^Cannot call method react\\(\\) on mixed\\.$#" + message: '#^Method WP_Mock\\HookedCallback\:\:react\(\) has parameter \$argument_count with no type specified\.$#' + identifier: missingType.parameter count: 1 path: php/WP_Mock/HookedCallback.php - - message: "#^Method WP_Mock\\\\HookedCallback\\:\\:new_responder\\(\\) has no return type specified\\.$#" + message: '#^Method WP_Mock\\HookedCallback\:\:react\(\) has parameter \$callback with no type specified\.$#' + identifier: missingType.parameter count: 1 path: php/WP_Mock/HookedCallback.php - - message: "#^Method WP_Mock\\\\HookedCallback\\:\\:react\\(\\) has no return type specified\\.$#" + message: '#^Method WP_Mock\\HookedCallback\:\:react\(\) has parameter \$priority with no type specified\.$#' + identifier: missingType.parameter count: 1 path: php/WP_Mock/HookedCallback.php - - message: "#^Method WP_Mock\\\\HookedCallback\\:\\:react\\(\\) has parameter \\$argument_count with no type specified\\.$#" + message: '#^Method WP_Mock\\HookedCallback\:\:react\(\) throws checked exception Mockery\\Exception but it''s missing from the PHPDoc @throws tag\.$#' + identifier: missingType.checkedException count: 1 path: php/WP_Mock/HookedCallback.php - - message: "#^Method WP_Mock\\\\HookedCallback\\:\\:react\\(\\) has parameter \\$callback with no type specified\\.$#" + message: '#^Method WP_Mock\\HookedCallback\:\:react\(\) throws checked exception PHPUnit\\Framework\\ExpectationFailedException but it''s missing from the PHPDoc @throws tag\.$#' + identifier: missingType.checkedException count: 1 path: php/WP_Mock/HookedCallback.php - - message: "#^Method WP_Mock\\\\HookedCallback\\:\\:react\\(\\) has parameter \\$priority with no type specified\\.$#" + message: '#^Method WP_Mock\\HookedCallback\:\:react\(\) throws checked exception ReflectionException but it''s missing from the PHPDoc @throws tag\.$#' + identifier: missingType.checkedException count: 1 path: php/WP_Mock/HookedCallback.php - - message: "#^Method WP_Mock\\\\HookedCallback\\:\\:react\\(\\) throws checked exception Mockery\\\\Exception but it's missing from the PHPDoc @throws tag\\.$#" + message: '#^Method WP_Mock\\HookedCallback\:\:setType\(\) has no return type specified\.$#' + identifier: missingType.return count: 1 path: php/WP_Mock/HookedCallback.php - - message: "#^Method WP_Mock\\\\HookedCallback\\:\\:react\\(\\) throws checked exception PHPUnit\\\\Framework\\\\ExpectationFailedException but it's missing from the PHPDoc @throws tag\\.$#" + message: '#^Method WP_Mock\\HookedCallbackResponder\:\:perform\(\) has no return type specified\.$#' + identifier: missingType.return count: 1 path: php/WP_Mock/HookedCallback.php - - message: "#^Method WP_Mock\\\\HookedCallback\\:\\:react\\(\\) throws checked exception ReflectionException but it's missing from the PHPDoc @throws tag\\.$#" + message: '#^Method WP_Mock\\HookedCallbackResponder\:\:perform\(\) has parameter \$callable with no type specified\.$#' + identifier: missingType.parameter count: 1 path: php/WP_Mock/HookedCallback.php - - message: "#^Method WP_Mock\\\\HookedCallback\\:\\:setType\\(\\) has no return type specified\\.$#" + message: '#^Method WP_Mock\\HookedCallbackResponder\:\:react\(\) has no return type specified\.$#' + identifier: missingType.return count: 1 path: php/WP_Mock/HookedCallback.php - - message: "#^Method WP_Mock\\\\HookedCallbackResponder\\:\\:perform\\(\\) has no return type specified\\.$#" + message: '#^Parameter \#1 \$callback of method WP_Mock\\HookedCallback\:\:callback_to_string\(\) expects callable\(\)\: mixed, mixed given\.$#' + identifier: argument.type count: 1 path: php/WP_Mock/HookedCallback.php - - message: "#^Method WP_Mock\\\\HookedCallbackResponder\\:\\:perform\\(\\) has parameter \\$callable with no type specified\\.$#" + message: '#^Parameter \#1 \$expected of class WP_Mock\\Matcher\\AnyInstance constructor expects object\|string\|null, mixed given\.$#' + identifier: argument.type count: 1 path: php/WP_Mock/HookedCallback.php - - message: "#^Method WP_Mock\\\\HookedCallbackResponder\\:\\:react\\(\\) has no return type specified\\.$#" + message: '#^Parameter \#1 \$object of function get_class expects object, mixed given\.$#' + identifier: argument.type count: 1 path: php/WP_Mock/HookedCallback.php - - message: "#^Property WP_Mock\\\\HookedCallback\\:\\:\\$callback has no type specified\\.$#" + message: '#^Parameter \#2 \.\.\.\$values of function sprintf expects bool\|float\|int\|string\|null, mixed given\.$#' + identifier: argument.type count: 1 path: php/WP_Mock/HookedCallback.php - - message: "#^Property WP_Mock\\\\HookedCallback\\:\\:\\$type has no type specified\\.$#" + message: '#^Part \$method \(mixed\) of encapsed string cannot be cast to string\.$#' + identifier: encapsedStringPart.nonString count: 1 path: php/WP_Mock/HookedCallback.php - - message: "#^Method WP_Mock\\\\InvokedFilterValue\\:\\:__invoke\\(\\) has no return type specified\\.$#" + message: '#^Property WP_Mock\\HookedCallback\:\:\$callback has no type specified\.$#' + identifier: missingType.property + count: 1 + path: php/WP_Mock/HookedCallback.php + + - + message: '#^Property WP_Mock\\HookedCallback\:\:\$type has no type specified\.$#' + identifier: missingType.property + count: 1 + path: php/WP_Mock/HookedCallback.php + + - + message: '#^Property WP_Mock\\HookedCallbackResponder\:\:\$callable \(callable\(\)\: mixed\) does not accept mixed\.$#' + identifier: assign.propertyType + count: 1 + path: php/WP_Mock/HookedCallback.php + + - + message: '#^Method WP_Mock\\InvokedFilterValue\:\:__invoke\(\) has no return type specified\.$#' + identifier: missingType.return + count: 1 + path: php/WP_Mock/InvokedFilterValue.php + + - + message: '#^Binary operation "\." between mixed and string results in an error\.$#' + identifier: binaryOp.invalid + count: 2 + path: php/WP_Mock/Tools/TestCase.php + + - + message: '#^Call to method PHPUnit\\Framework\\Assert\:\:assertIsArray\(\) with array\{\} will always evaluate to true\.$#' + identifier: method.alreadyNarrowedType + count: 1 + path: tests/Unit/WP_Mock/FunctionsTest.php + + - + message: '#^Call to an undefined method Mockery\\ExpectationInterface\|Mockery\\HigherOrderMessage\:\:with\(\)\.$#' + identifier: method.notFound + count: 1 + path: tests/Unit/WP_Mock/Matcher/FuzzyObjectTest.php + + - + message: '#^Cannot call method andReturnTrue\(\) on mixed\.$#' + identifier: method.nonObject + count: 1 + path: tests/Unit/WP_Mock/Matcher/FuzzyObjectTest.php + + - + message: '#^Cannot call method getInaccessibleMethod\(\) on mixed\.$#' + identifier: method.nonObject count: 1 - path: php/WP_Mock/InvokedFilterValue.php \ No newline at end of file + path: tests/Unit/WP_Mock/Traits/AccessInaccessibleClassMembersTraitTest.php + + - + message: '#^Cannot call method getInaccessibleProperty\(\) on mixed\.$#' + identifier: method.nonObject + count: 1 + path: tests/Unit/WP_Mock/Traits/AccessInaccessibleClassMembersTraitTest.php + + - + message: '#^Cannot call method getInaccessiblePropertyValue\(\) on mixed\.$#' + identifier: method.nonObject + count: 1 + path: tests/Unit/WP_Mock/Traits/AccessInaccessibleClassMembersTraitTest.php + + - + message: '#^Cannot call method getName\(\) on mixed\.$#' + identifier: method.nonObject + count: 2 + path: tests/Unit/WP_Mock/Traits/AccessInaccessibleClassMembersTraitTest.php + + - + message: '#^Cannot call method getValue\(\) on mixed\.$#' + identifier: method.nonObject + count: 3 + path: tests/Unit/WP_Mock/Traits/AccessInaccessibleClassMembersTraitTest.php + + - + message: '#^Cannot call method invoke\(\) on mixed\.$#' + identifier: method.nonObject + count: 1 + path: tests/Unit/WP_Mock/Traits/AccessInaccessibleClassMembersTraitTest.php + + - + message: '#^Cannot call method invokeInaccessibleMethod\(\) on mixed\.$#' + identifier: method.nonObject + count: 1 + path: tests/Unit/WP_Mock/Traits/AccessInaccessibleClassMembersTraitTest.php + + - + message: '#^Cannot call method setInaccessibleProperty\(\) on mixed\.$#' + identifier: method.nonObject + count: 1 + path: tests/Unit/WP_Mock/Traits/AccessInaccessibleClassMembersTraitTest.php + + - + message: '#^Cannot call method setInaccessiblePropertyValue\(\) on mixed\.$#' + identifier: method.nonObject + count: 1 + path: tests/Unit/WP_Mock/Traits/AccessInaccessibleClassMembersTraitTest.php + + - + message: '#^Parameter \#1 \$object of function get_class expects object, mixed given\.$#' + identifier: argument.type + count: 1 + path: tests/Unit/WP_Mock/Traits/AccessInaccessibleClassMembersTraitTest.php + + - + message: '#^Call to an undefined method WP_Mock\\Action_Responder\|WP_Mock\\Filter_Responder\|WP_Mock\\HookedCallbackResponder\:\:reply\(\)\.$#' + identifier: method.notFound + count: 5 + path: tests/Unit/WP_MockTest.php + + - + message: '#^PHPDoc tag @throws with type InvalidArgumentException\|PHPUnit\\Framework\\Exception\|SebastianBergmann\\RecursionContext\\InvalidArgumentException is not subtype of Throwable$#' + identifier: throws.notThrowable + count: 2 + path: tests/Unit/WP_MockTest.php + + - + message: '#^PHPDoc tag @throws with type Mockery\\Exception\|PHPUnit\\Framework\\Exception\|SebastianBergmann\\RecursionContext\\InvalidArgumentException is not subtype of Throwable$#' + identifier: throws.notThrowable + count: 1 + path: tests/Unit/WP_MockTest.php + + - + message: '#^PHPDoc tag @throws with type PHPUnit\\Framework\\Exception\|SebastianBergmann\\RecursionContext\\InvalidArgumentException is not subtype of Throwable$#' + identifier: throws.notThrowable + count: 4 + path: tests/Unit/WP_MockTest.php + + - + message: '#^PHPDoc tag @throws with type PHPUnit\\Framework\\ExpectationFailedException\|SebastianBergmann\\RecursionContext\\InvalidArgumentException is not subtype of Throwable$#' + identifier: throws.notThrowable + count: 3 + path: tests/Unit/WP_MockTest.php \ No newline at end of file diff --git a/phpstan.neon b/phpstan.neon index 7700f11..41e0036 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -5,6 +5,11 @@ parameters: reportUnmatchedIgnoredErrors: false treatPhpDocTypesAsCertain: false exceptions: + # PHPUnit's mock-creation infrastructure throws these; they are test-runner concerns, + # not exceptions a test documents/handles, so don't require them in @throws tags. + uncheckedExceptionClasses: + - 'PHPUnit\Framework\MockObject\Exception' + - 'PHPUnit\Framework\InvalidArgumentException' check: missingCheckedExceptionInThrows: true tooWideThrowType: true diff --git a/tests/Unit/WP_Mock/DeprecatedMethodListenerTest.php b/tests/Unit/WP_Mock/DeprecatedMethodListenerTest.php index 2f90257..1122e55 100644 --- a/tests/Unit/WP_Mock/DeprecatedMethodListenerTest.php +++ b/tests/Unit/WP_Mock/DeprecatedMethodListenerTest.php @@ -171,7 +171,7 @@ public function testCanConvertArgumentsToScalarValue($arg, $expected): void $result = $method->invokeArgs($instance, [$arg]); - if (is_object($arg) && is_string($expected) && is_string($result)) { + if (is_object($arg) && is_string($expected) && '' !== $expected && is_string($result)) { $this->assertStringStartsWith($expected, $result); } else { $this->assertSame($expected, $result); @@ -244,7 +244,7 @@ public function deprecatedMethod(array $args = []): string * @see DeprecatedMethodListener::$deprecatedCalls * * @param DeprecatedMethodListener $listener - * @return array}> + * @return array * @throws ReflectionException */ protected function getDeprecatedMethodCalls(DeprecatedMethodListener $listener): array From dc80714454087ea51bee505cc9412d20b9d17489 Mon Sep 17 00:00:00 2001 From: Fulvio Notarstefano Date: Tue, 23 Jun 2026 18:35:27 +0900 Subject: [PATCH 12/16] docs: point the 2.0.0 changelog compare link at the 2.0.0 tag Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01K8HGMxCuSjUX6p4PQL7t5Y --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ec6f86..69a727d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [2.0.0](https://github.com/10up/wp_mock/compare/1.1.1...trunk) - Unreleased +## [2.0.0](https://github.com/10up/wp_mock/compare/1.1.1...2.0.0) - Unreleased WP_Mock now supports PHPUnit 9.6, 10, 11, 12 and 13 from a single codebase. The minimum PHP version is unchanged (7.4); Composer installs the highest PHPUnit version your PHP allows. Most projects can upgrade by bumping only `10up/wp_mock` — see [UPGRADE.md](https://github.com/10up/wp_mock/blob/trunk/UPGRADE.md). From 53328419b1b7a925f9e9b3538db7d5930caeb382 Mon Sep 17 00:00:00 2001 From: Fulvio Notarstefano Date: Tue, 23 Jun 2026 19:02:47 +0900 Subject: [PATCH 13/16] fix(ci): named deprecation-test fixture + raise patchwork floor for prefer-lowest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two failures surfaced by the PR's CI matrix: - testCanHandleDeprecatedMethodCallThroughWpMock failed on PHP <= 8.1 (the PHPUnit 9 and 10 legs): an anonymous class name embeds a null byte, which trigger_error() truncates, dropping the method name from the captured deprecation message (PHP 8.2+ stringifies anonymous classes differently, so 8.2/8.3/8.4 passed). Replaced the anonymous fixture with a named class (WpMockWithDeprecatedMethod) whose __METHOD__ is stable and null-byte-free on every PHP version. - The prefer-lowest leg fatal-errored inside patchwork 2.1.0 (references a nonexistent PHPUnit\TextUI\TestSuiteObject). Raised antecedent/patchwork to ^2.1.27 — the version origin already ships in its lock, verified free of that reference. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01K8HGMxCuSjUX6p4PQL7t5Y --- composer.json | 2 +- composer.lock | 2 +- .../WP_Mock/DeprecatedMethodListenerTest.php | 51 +++++++++++-------- 3 files changed, 33 insertions(+), 22 deletions(-) diff --git a/composer.json b/composer.json index 69ab56c..3f90cde 100644 --- a/composer.json +++ b/composer.json @@ -7,7 +7,7 @@ "php": ">=7.4", "phpunit/phpunit": "^9.6 || ^10 || ^11 || ^12 || ^13", "mockery/mockery": "^1.6.12", - "antecedent/patchwork": "^2.1" + "antecedent/patchwork": "^2.1.27" }, "require-dev": { "behat/behat": "^v3.11.0", diff --git a/composer.lock b/composer.lock index a42df82..b6638f2 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "6e8e14f416aba6dd568c922410d26017", + "content-hash": "4789f3fd85ff5cad37bafce75dcf6bc7", "packages": [ { "name": "antecedent/patchwork", diff --git a/tests/Unit/WP_Mock/DeprecatedMethodListenerTest.php b/tests/Unit/WP_Mock/DeprecatedMethodListenerTest.php index 1122e55..aab91aa 100644 --- a/tests/Unit/WP_Mock/DeprecatedMethodListenerTest.php +++ b/tests/Unit/WP_Mock/DeprecatedMethodListenerTest.php @@ -205,26 +205,10 @@ public function testCanHandleDeprecatedMethodCallThroughWpMock(): void { $deprecatedMethodListener = new DeprecatedMethodListener(); - $instance = new class ($deprecatedMethodListener) extends WP_Mock { - /** - * @param DeprecatedMethodListener $deprecatedMethodListener - */ - public function __construct(DeprecatedMethodListener $deprecatedMethodListener) - { - static::$deprecatedMethodListener = $deprecatedMethodListener; - } - - /** - * @param array $args - * @return string - */ - public function deprecatedMethod(array $args = []): string - { - static::getDeprecatedMethodListener()->logDeprecatedCall(__METHOD__, $args); - - return 'test'; - } - }; + // Use a named fixture (defined below) rather than an anonymous class: anonymous class + // names embed a null byte that trigger_error() truncates on PHP <= 8.1, which would drop + // the method name from the deprecation message. + $instance = new WpMockWithDeprecatedMethod($deprecatedMethodListener); $result = null; @@ -257,3 +241,30 @@ protected function getDeprecatedMethodCalls(DeprecatedMethodListener $listener): return is_array($value) ? $value : []; } } + +/** + * Named WP_Mock subclass with a deprecated method, used by + * {@see DeprecatedMethodListenerTest::testCanHandleDeprecatedMethodCallThroughWpMock()}. + * + * Intentionally a named (not anonymous) class so `__METHOD__` is stable and free of the null byte + * that anonymous class names embed — which `trigger_error()` truncates on PHP <= 8.1, dropping the + * method name from the captured deprecation message. + */ +final class WpMockWithDeprecatedMethod extends WP_Mock +{ + public function __construct(DeprecatedMethodListener $deprecatedMethodListener) + { + static::$deprecatedMethodListener = $deprecatedMethodListener; + } + + /** + * @param array $args + * @return string + */ + public function deprecatedMethod(array $args = []): string + { + static::getDeprecatedMethodListener()->logDeprecatedCall(__METHOD__, $args); + + return 'test'; + } +} From 7b053ab225471c41de572f39812bc8242e12acd4 Mon Sep 17 00:00:00 2001 From: Fulvio Notarstefano Date: Tue, 23 Jun 2026 19:13:26 +0900 Subject: [PATCH 14/16] ci: measure coverage on the PHPUnit 9 leg (consistent with origin) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit origin measured Coveralls coverage on the PHP 8.1 leg, which there ran PHPUnit 9. In this branch 8.1 runs PHPUnit 10, and PHPUnit 10 reports less coverage than 9 for the process-isolated (@runInSeparateProcess) tests that exercise much of WP_Mock.php — which showed up as a false ~7% coverage "decrease" on the PR. Move the single coverage upload to the PHP 8.0 leg (PHPUnit 9, phpunit9.xml.dist), matching origin's coverage engine and config so the number is comparable. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01K8HGMxCuSjUX6p4PQL7t5Y --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b4e99a1..70432ca 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -75,15 +75,15 @@ jobs: # Tests should be run randomly to catch any test dependency issue. - name: Execute tests with PHPUnit - if: ${{ matrix.php != '8.1' }} + if: ${{ matrix.php != '8.0' }} run: vendor/bin/phpunit -c ${{ matrix.config }} --order-by=random - name: Execute tests with PHPUnit and output code coverage report - if: ${{ matrix.php == '8.1' }} + if: ${{ matrix.php == '8.0' }} run: vendor/bin/phpunit -c ${{ matrix.config }} --order-by=random --coverage-clover=./clover.xml --coverage-text - name: Upload test coverage results to Coveralls - if: ${{ matrix.php == '8.1' }} + if: ${{ matrix.php == '8.0' }} env: COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: vendor/bin/php-coveralls --coverage_clover="./clover.xml" --json_path="./coveralls-upload.json" -v From 3bc10a57393f5fe979c1aea5e30941531064dfa3 Mon Sep 17 00:00:00 2001 From: Fulvio Notarstefano Date: Wed, 24 Jun 2026 12:21:15 +0900 Subject: [PATCH 15/16] feat!: drop PHPUnit 9, raise floor to PHP 8.1 / PHPUnit 10 Per maintainer feedback, scope WP_Mock 2.0 as a clean modern major rather than a 9-13 bridge. The floor is now PHP 8.1 + PHPUnit 10; the supported range is PHPUnit 10, 11, 12 and 13 from one codebase. PHP 7.4/8.0 and PHPUnit 9 users stay on 1.x. This lets the harness shed every 9.x compatibility shim: - Tests are attributes-only: removed the dual @dataProvider / @runInSeparateProcess / @preserveGlobalState doc-annotations (the #[...] attributes already existed) and converted 152 method-level @covers to class-level #[CoversClass]. The two trait tests and the global-function-mock test are intentionally bare (#[CoversTrait] needs PHPUnit 11+, above the floor; #[CoversFunction] on conditionally-defined global mocks is fragile under failOnWarning). - Deleted phpunit9.xml.dist; phpunit.xml.dist is the only runner config. - Dropped the dead PHPUnit 9 branch from WP_MockTestCase::isRunningInIsolation(). - Removed the now-unused direct sebastian/comparator dev dependency (PHPUnit pulls it transitively). - IsEqualHtml::matches() now types $other as mixed (PHP 8.1 floor). CI matrix drops the 7.4, 8.0 and 7.4-prefer-lowest legs; it now runs 8.1 -> PU10 (coverage), 8.2 -> PU11, 8.3 -> PU12, 8.4 -> PU13, plus an 8.1 prefer-lowest leg. The single Coveralls upload moves to the 8.1/PU10 leg. Docs (README, installation, CHANGELOG, UPGRADE) lead with the PHP 8.1 / PHPUnit 10 floor as the headline breaking change. Local: 175 tests pass on PHPUnit 13 with failOnDeprecation on; PHPStan clean (baseline unchanged); phpcs clean (8.1+); Behat 40/40. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01K8HGMxCuSjUX6p4PQL7t5Y --- .github/workflows/ci.yml | 29 +++---- CHANGELOG.md | 8 +- README.md | 4 +- UPGRADE.md | 18 ++-- composer.json | 5 +- composer.lock | 34 ++++---- docs/general/configuration.md | 4 +- docs/general/installation.md | 6 +- php/WP_Mock/DeprecatedMethodListener.php | 6 +- php/WP_Mock/Tools/Constraints/IsEqualHtml.php | 4 +- phpcs.xml | 2 +- phpunit9.xml.dist | 41 ---------- tests/Integration/WP_MockTest.php | 65 ++------------- tests/Unit/WP_Mock/API/FunctionMocksTest.php | 31 +------ .../WP_Mock/DeprecatedMethodListenerTest.php | 24 +----- tests/Unit/WP_Mock/Functions/HandlerTest.php | 32 +------- .../WP_Mock/Functions/ReturnSequenceTest.php | 10 +-- tests/Unit/WP_Mock/FunctionsTest.php | 40 +-------- tests/Unit/WP_Mock/HookTest.php | 8 +- .../Unit/WP_Mock/Matcher/AnyInstanceTest.php | 21 +---- .../Unit/WP_Mock/Matcher/FuzzyObjectTest.php | 17 +--- .../Tools/Constraints/ExpectationsMetTest.php | 13 +-- .../Tools/Constraints/IsEqualHtmlTest.php | 12 --- tests/Unit/WP_Mock/Tools/TestCaseTest.php | 38 --------- ...ccessInaccessibleClassMembersTraitTest.php | 16 +--- .../Traits/MockWordPressObjectsTraitTest.php | 8 +- tests/Unit/WP_MockTest.php | 82 +------------------ tests/WP_MockTestCase.php | 10 +-- 28 files changed, 84 insertions(+), 504 deletions(-) delete mode 100644 phpunit9.xml.dist diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 70432ca..54414bf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,20 +29,13 @@ jobs: - php: '8.1' os: 'ubuntu-latest' config: 'phpunit.xml.dist' + coverage: true label: 'PHP 8.1 · PHPUnit 10' - - php: '8.0' - os: 'ubuntu-24.04' - config: 'phpunit9.xml.dist' - label: 'PHP 8.0 · PHPUnit 9' - - php: '7.4' - os: 'ubuntu-24.04' - config: 'phpunit9.xml.dist' - label: 'PHP 7.4 · PHPUnit 9' - - php: '7.4' - os: 'ubuntu-24.04' - config: 'phpunit9.xml.dist' + - php: '8.1' + os: 'ubuntu-latest' + config: 'phpunit.xml.dist' composer_flags: '--prefer-lowest' - label: 'PHP 7.4 · PHPUnit 9 (lowest deps)' + label: 'PHP 8.1 · PHPUnit 10 (lowest deps)' fail-fast: false env: PHP_VERSION: ${{ matrix.php }} @@ -68,29 +61,29 @@ jobs: ${{ runner.os }}-php${{ matrix.php }}-composer- # Resolve dependencies per PHP version: a single committed lock cannot satisfy - # every supported PHP/PHPUnit pair (PHPUnit 9–13 span PHP 7.4–8.4), so each leg - # updates to the highest PHPUnit its PHP allows (Composer picks 9/10/11/12/13). + # every supported PHP/PHPUnit pair (PHPUnit 10–13 span PHP 8.1–8.4), so each leg + # updates to the highest PHPUnit its PHP allows (Composer picks 10/11/12/13). - name: Install dependencies run: composer update ${{ matrix.composer_flags }} --no-ansi --no-interaction --no-scripts --no-progress --prefer-dist # Tests should be run randomly to catch any test dependency issue. - name: Execute tests with PHPUnit - if: ${{ matrix.php != '8.0' }} + if: ${{ !matrix.coverage }} run: vendor/bin/phpunit -c ${{ matrix.config }} --order-by=random - name: Execute tests with PHPUnit and output code coverage report - if: ${{ matrix.php == '8.0' }} + if: ${{ matrix.coverage }} run: vendor/bin/phpunit -c ${{ matrix.config }} --order-by=random --coverage-clover=./clover.xml --coverage-text - name: Upload test coverage results to Coveralls - if: ${{ matrix.php == '8.0' }} + if: ${{ matrix.coverage }} env: COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: vendor/bin/php-coveralls --coverage_clover="./clover.xml" --json_path="./coveralls-upload.json" -v php-compatibility: runs-on: ubuntu-latest - name: PHP compatibility (7.4+) + name: PHP compatibility (8.1+) steps: - uses: actions/checkout@v4 diff --git a/CHANGELOG.md b/CHANGELOG.md index 69a727d..8e57578 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,18 +6,18 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## [2.0.0](https://github.com/10up/wp_mock/compare/1.1.1...2.0.0) - Unreleased -WP_Mock now supports PHPUnit 9.6, 10, 11, 12 and 13 from a single codebase. The minimum PHP version is unchanged (7.4); Composer installs the highest PHPUnit version your PHP allows. Most projects can upgrade by bumping only `10up/wp_mock` — see [UPGRADE.md](https://github.com/10up/wp_mock/blob/trunk/UPGRADE.md). +WP_Mock 2.0 modernizes the test harness for current PHPUnit. It now **requires PHP 8.1+ and PHPUnit 10+**, and supports **PHPUnit 10, 11, 12 and 13** from a single codebase; Composer installs the highest PHPUnit version your PHP allows. The public assertion API is unchanged, so most projects on a supported PHP/PHPUnit upgrade by bumping only `10up/wp_mock` — see [UPGRADE.md](https://github.com/10up/wp_mock/blob/trunk/UPGRADE.md). ### Added -- Support for PHPUnit 10, 11, 12 and 13 (in addition to 9.6) - `TestCase::assertOutputEqualsHtml(string $expectedHtml, callable $callback)` for whitespace-insensitive assertions on echoed HTML output ### Changed -- **BREAKING:** deprecated WP_Mock methods now emit a native `E_USER_DEPRECATED` notice (via `trigger_error()`) instead of forcing a test to be marked "risky". To fail tests on these, set `failOnDeprecation="true"` (PHPUnit 10+) or `convertDeprecationsToExceptions="true"` (PHPUnit 9) in your configuration. +- **BREAKING:** raised the minimum PHP version to **8.1** (was 7.4) and dropped support for **PHPUnit 9**. Projects on PHP 7.4/8.0, or pinned to PHPUnit 9, should stay on WP_Mock 1.x. +- **BREAKING:** deprecated WP_Mock methods now emit a native `E_USER_DEPRECATED` notice (via `trigger_error()`) instead of forcing a test to be marked "risky". Set `failOnDeprecation="true"` in your PHPUnit configuration to fail tests on these. - **BREAKING:** removed the implicit output filtering and the `@stripTabsAndNewlinesFromOutput` annotation — including the `TestCase::expectOutputString()` override — because PHPUnit 10 removed `setOutputCallback()` and made `expectOutputString()` `final`. Use `assertOutputEqualsHtml()` (or PHPUnit's native `expectOutputString()` for exact matches). - `IsEqualHtml` now implements `matches()`/`toString()` and takes a single constructor argument +- Internal test metadata moved to PHP 8 attributes (`#[DataProvider]`, `#[CoversClass]`, …); doc-comment annotations were removed (they no longer work on PHPUnit 12+) - Raised minimum dependencies: `mockery/mockery ^1.6.12`, PHPStan tooling to `^2` -- Test runner configuration is split into `phpunit.xml.dist` (PHPUnit 10+) and `phpunit9.xml.dist` (PHPUnit 9) ### Removed - The abandoned `sempro/phpunit-pretty-print` dev dependency (use `--testdox`) diff --git a/README.md b/README.md index 1ffe370..73d8e8e 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ > WP_Mock is an API mocking framework, built and maintained by [10up](https://10up.com) and [GoDaddy](https://godaddy.com) for the purpose of making it possible to properly unit test within a WordPress project. -![Support Level][support-level-image] ![PHP 7.4+][php-image] [![Coverage Status][coveralls-image]][coveralls-url] [![Packagist][packagist-image]][packagist-url] [![BSD-3-Clause License][license-image]][license-url] +![Support Level][support-level-image] ![PHP 8.1+][php-image] [![Coverage Status][coveralls-image]][coveralls-url] [![Packagist][packagist-image]][packagist-url] [![BSD-3-Clause License][license-image]][license-url] ## Installation @@ -39,7 +39,7 @@ A special thanks to all [WP_Mock contributors](https://github.com/10up/wp_mock/b [support-level-image]: https://img.shields.io/badge/support-active-green.svg -[php-image]: https://img.shields.io/badge/php-7.4%2B-green.svg +[php-image]: https://img.shields.io/badge/php-8.1%2B-green.svg [packagist-image]: https://img.shields.io/packagist/dt/10up/wp_mock.svg [packagist-url]: https://packagist.org/packages/10up/wp_mock [coveralls-image]: https://coveralls.io/repos/github/10up/wp_mock/badge.svg?branch=trunk diff --git a/UPGRADE.md b/UPGRADE.md index 818077f..d39b34d 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -2,26 +2,27 @@ ## 1.x → 2.0 -WP_Mock 2.0 adds support for **PHPUnit 10, 11, 12 and 13** alongside the existing PHPUnit 9.6, from a single codebase. The **minimum PHP version is unchanged (7.4)**. +WP_Mock 2.0 modernizes the test harness for current PHPUnit. The headline change is a higher floor: + +> **WP_Mock 2.0 requires PHP 8.1+ and PHPUnit 10+.** +> If your project runs on **PHP 7.4 or 8.0**, or is pinned to **PHPUnit 9**, stay on WP_Mock **1.x** — it continues to work. WP_Mock 2.0 supports PHPUnit 10, 11, 12 and 13 from a single codebase. Composer installs the highest PHPUnit version compatible with your PHP: | Your PHP | PHPUnit installed | |----------|-------------------| -| 7.4 | 9 | -| 8.0 | 9 | | 8.1 | 10 | | 8.2 | 11 | | 8.3 | 12 | | 8.4 | 13 | -For most projects, upgrading is just: +For most projects on a supported PHP/PHPUnit, upgrading is just: ```shell composer require --dev 10up/wp_mock:^2.0 ``` -You do **not** have to change your PHPUnit version to adopt 2.0 — if you stay on PHPUnit 9, WP_Mock 2.0 still works. The public assertion API (`assertConditionsMet()`, `assertHooksAdded()`, `assertActionsCalled()`, `assertEqualsHtml()`, `mockStaticMethod()`, …) is unchanged. +The public assertion API (`assertConditionsMet()`, `assertHooksAdded()`, `assertActionsCalled()`, `assertEqualsHtml()`, `mockStaticMethod()`, …) is unchanged. There are two behavior changes to be aware of. @@ -29,12 +30,7 @@ There are two behavior changes to be aware of. If you (or your tooling) relied on WP_Mock marking a test **risky** when a deprecated WP_Mock method was called, that signal is now a native PHP **deprecation** (`E_USER_DEPRECATED`) surfaced through PHPUnit's own deprecation reporting. -To make these fail your test suite, enable strict deprecation handling in your PHPUnit configuration: - -- **PHPUnit 10+:** `failOnDeprecation="true"` -- **PHPUnit 9:** `convertDeprecationsToExceptions="true"` - -With default configuration, the deprecation is reported but does not fail the run. +To make these fail your test suite, set `failOnDeprecation="true"` on the root `` element of your configuration. With default configuration, the deprecation is reported but does not fail the run. ### 2. Output assertions diff --git a/composer.json b/composer.json index 3f90cde..57cff23 100644 --- a/composer.json +++ b/composer.json @@ -4,14 +4,13 @@ "license": "BSD-3-Clause", "prefer-stable": true, "require": { - "php": ">=7.4", - "phpunit/phpunit": "^9.6 || ^10 || ^11 || ^12 || ^13", + "php": ">=8.1", + "phpunit/phpunit": "^10 || ^11 || ^12 || ^13", "mockery/mockery": "^1.6.12", "antecedent/patchwork": "^2.1.27" }, "require-dev": { "behat/behat": "^v3.11.0", - "sebastian/comparator": ">=4.0.8", "php-coveralls/php-coveralls": "^v2.7", "phpstan/phpstan": "^2.0", "phpstan/phpstan-phpunit": "^2.0", diff --git a/composer.lock b/composer.lock index b6638f2..cbec9dd 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "4789f3fd85ff5cad37bafce75dcf6bc7", + "content-hash": "320f46c0020de661db877e853eabd0e4", "packages": [ { "name": "antecedent/patchwork", @@ -2960,26 +2960,26 @@ }, { "name": "guzzlehttp/guzzle", - "version": "7.12.1", + "version": "7.12.3", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "d34627490fbc03bf5c5d7cfed81f2faa19519425" + "reference": "9aa17bcdd777ee31df9fc83c337ca4ca2340def3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/d34627490fbc03bf5c5d7cfed81f2faa19519425", - "reference": "d34627490fbc03bf5c5d7cfed81f2faa19519425", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/9aa17bcdd777ee31df9fc83c337ca4ca2340def3", + "reference": "9aa17bcdd777ee31df9fc83c337ca4ca2340def3", "shasum": "" }, "require": { "ext-json": "*", "guzzlehttp/promises": "^2.5", - "guzzlehttp/psr7": "^2.12.1", + "guzzlehttp/psr7": "^2.12.3", "php": "^7.2.5 || ^8.0", "psr/http-client": "^1.0", "symfony/deprecation-contracts": "^2.5 || ^3.0", - "symfony/polyfill-php80": "^1.24" + "symfony/polyfill-php80": "^1.25" }, "provide": { "psr/http-client-implementation": "1.0" @@ -3068,7 +3068,7 @@ ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.12.1" + "source": "https://github.com/guzzle/guzzle/tree/7.12.3" }, "funding": [ { @@ -3084,7 +3084,7 @@ "type": "tidelift" } ], - "time": "2026-06-18T14:12:49+00:00" + "time": "2026-06-23T15:29:02+00:00" }, { "name": "guzzlehttp/promises", @@ -3172,16 +3172,16 @@ }, { "name": "guzzlehttp/psr7", - "version": "2.12.1", + "version": "2.12.3", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "172ef2f4e9824c1e058b7f30be8ae25a02c0f2b7" + "reference": "7ec62dc3f44aa218487dbed81a9bf9bc647be55d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/172ef2f4e9824c1e058b7f30be8ae25a02c0f2b7", - "reference": "172ef2f4e9824c1e058b7f30be8ae25a02c0f2b7", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/7ec62dc3f44aa218487dbed81a9bf9bc647be55d", + "reference": "7ec62dc3f44aa218487dbed81a9bf9bc647be55d", "shasum": "" }, "require": { @@ -3190,7 +3190,7 @@ "psr/http-message": "^1.1 || ^2.0", "ralouphie/getallheaders": "^3.0", "symfony/deprecation-contracts": "^2.5 || ^3.0", - "symfony/polyfill-php80": "^1.24" + "symfony/polyfill-php80": "^1.25" }, "provide": { "psr/http-factory-implementation": "1.0", @@ -3271,7 +3271,7 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/2.12.1" + "source": "https://github.com/guzzle/psr7/tree/2.12.3" }, "funding": [ { @@ -3287,7 +3287,7 @@ "type": "tidelift" } ], - "time": "2026-06-18T09:49:37+00:00" + "time": "2026-06-23T15:21:08+00:00" }, { "name": "php-coveralls/php-coveralls", @@ -6687,7 +6687,7 @@ "prefer-stable": true, "prefer-lowest": false, "platform": { - "php": ">=7.4" + "php": ">=8.1" }, "platform-dev": {}, "plugin-api-version": "2.9.0" diff --git a/docs/general/configuration.md b/docs/general/configuration.md index f76a90f..2992e44 100644 --- a/docs/general/configuration.md +++ b/docs/general/configuration.md @@ -30,13 +30,13 @@ The bootstrap file can do a few things: ## Configure PHPUnit with WP_Mock -You can run PHPUnit using a `--bootstrap` flag to include your bootstrap configuration while executing your tests (see [PHPUnit documentation](https://docs.phpunit.de/en/9.5/textui.html?highlight=--bootstrap#command-line-options)): +You can run PHPUnit using a `--bootstrap` flag to include your bootstrap configuration while executing your tests (see [PHPUnit documentation](https://docs.phpunit.de/en/10.5/textui.html#command-line-options)): ```shell ./vendor/bin/phpunit --bootstrap /path/to/bootstrap.php ``` -A more convenient way though would be to add the following to the phpunit.xml configuration file (see [PHPUnit documentation](https://docs.phpunit.de/en/9.5/configuration.html)): +A more convenient way though would be to add the following to the phpunit.xml configuration file (see [PHPUnit documentation](https://docs.phpunit.de/en/10.5/configuration.html)): ```shell bootstrap="/path/to/bootstrap.php" diff --git a/docs/general/installation.md b/docs/general/installation.md index 413a98c..c496f7e 100644 --- a/docs/general/installation.md +++ b/docs/general/installation.md @@ -2,7 +2,7 @@ ## Requirements -* PHP 7.4+ +* PHP 8.1+ * Composer 2.0+ ## Install WP_Mock @@ -17,11 +17,11 @@ composer require --dev 10up/wp_mock WP_Mock needs the following dependencies to work: -* PHPUnit ^9.6 || ^10 || ^11 || ^12 || ^13 (BSD 3-Clause license) +* PHPUnit ^10 || ^11 || ^12 || ^13 (BSD 3-Clause license) * Mockery ^1.6.12 (BSD 3-Clause license) * Patchwork ^2.1 (MIT license) -They will be installed for you by Composer. WP_Mock supports PHPUnit 9 through 13 from a single codebase; Composer installs the highest PHPUnit version your PHP version allows (for example PHP 7.4 resolves PHPUnit 9, while PHP 8.4 resolves PHPUnit 13). +They will be installed for you by Composer. WP_Mock supports PHPUnit 10 through 13 from a single codebase; Composer installs the highest PHPUnit version your PHP version allows (for example PHP 8.1 resolves PHPUnit 10, while PHP 8.4 resolves PHPUnit 13). Next, you will need to configure PHPUnit first before enabling WP_Mock. [Consult PHPUnit documentation](https://phpunit.de/documentation.html) for this step. diff --git a/php/WP_Mock/DeprecatedMethodListener.php b/php/WP_Mock/DeprecatedMethodListener.php index e17b853..81dbc70 100644 --- a/php/WP_Mock/DeprecatedMethodListener.php +++ b/php/WP_Mock/DeprecatedMethodListener.php @@ -6,10 +6,8 @@ * Internal handler for deprecated method calls. * * Flags usage of deprecated WP_Mock methods by emitting an {@see E_USER_DEPRECATED} notice, - * which PHPUnit captures and attributes to the running test natively across all supported - * versions: - * - PHPUnit 9.x: surfaces (and, with `convertDeprecationsToExceptions="true"`, fails the test). - * - PHPUnit 10+: reported per test; fails the suite when `failOnDeprecation="true"`. + * which PHPUnit captures and attributes to the running test natively: it is reported per test + * and fails the suite when `failOnDeprecation="true"` is set in the PHPUnit configuration. * * To flag a method as deprecated, call the following from within the deprecated method's logic: * diff --git a/php/WP_Mock/Tools/Constraints/IsEqualHtml.php b/php/WP_Mock/Tools/Constraints/IsEqualHtml.php index 9c0843a..8b13fb3 100644 --- a/php/WP_Mock/Tools/Constraints/IsEqualHtml.php +++ b/php/WP_Mock/Tools/Constraints/IsEqualHtml.php @@ -28,10 +28,10 @@ public function __construct(string $value) /** * Evaluates whether $other equals the expected HTML, ignoring insignificant whitespace. * - * @param mixed $other value to evaluate (untyped for PHP 7.4 compatibility; the parent declares `mixed` on PHPUnit 10+) + * @param mixed $other value to evaluate * @return bool */ - public function matches($other): bool + public function matches(mixed $other): bool { $actual = is_scalar($other) ? (string) $other : ''; diff --git a/phpcs.xml b/phpcs.xml index 4a8e898..5b46ceb 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -4,5 +4,5 @@ ./tests - + \ No newline at end of file diff --git a/phpunit9.xml.dist b/phpunit9.xml.dist deleted file mode 100644 index 45f900f..0000000 --- a/phpunit9.xml.dist +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - ./tests/Unit - - - ./tests/Integration - - - - - - ./php - - - - diff --git a/tests/Integration/WP_MockTest.php b/tests/Integration/WP_MockTest.php index cb87ceb..f5c28b9 100644 --- a/tests/Integration/WP_MockTest.php +++ b/tests/Integration/WP_MockTest.php @@ -4,16 +4,19 @@ use Exception; use Generator; +use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\PreserveGlobalState; use PHPUnit\Framework\Attributes\RunInSeparateProcess; use PHPUnit\Framework\ExpectationFailedException; use WP_Mock; +use WP_Mock\Functions; +use WP_Mock\Functions\Handler; use WP_Mock\Tests\WP_MockTestCase; -/** - * @covers \WP_Mock - */ +#[CoversClass(WP_Mock::class)] +#[CoversClass(Functions::class)] +#[CoversClass(Handler::class)] class WP_MockTest extends WP_MockTestCase { /** @var string[] */ @@ -55,13 +58,6 @@ protected function setUp(): void } /** - * @covers \WP_Mock::bootstrap() - * @covers \WP_Mock\Functions::__construct() - * @covers \WP_Mock\Functions::flush() - * - * @runInSeparateProcess - * @preserveGlobalState disabled - * * @return void * @throws Exception */ @@ -81,12 +77,6 @@ public function testCommonFunctionsAreDefined(): void } /** - * @covers \WP_Mock::userFunction() - * @covers \WP_Mock\Functions::__construct() - * @covers \WP_Mock\Functions::flush() - * - * @dataProvider providerCommonFunctionsDefaultFunctionality - * * @param callable&string $function * @param string $action echo or return * @return void @@ -129,12 +119,6 @@ public static function providerCommonFunctionsDefaultFunctionality(): array } /** - * @covers \WP_Mock::activateStrictMode() - * @covers \WP_Mock::bootstrap() - * - * @runInSeparateProcess - * @preserveGlobalState disabled - * * @return void */ #[RunInSeparateProcess] @@ -152,11 +136,6 @@ public function testDefaultFailsInStrictMode(): void } /** - * @covers \WP_Mock::userFunction() - * @covers \WP_Mock\Functions::register() - * @covers \WP_Mock\Functions::generateFunction() - * @covers \WP_Mock\Functions::setUpMock() - * * @return void * @throws Exception */ @@ -172,11 +151,6 @@ public function testMockingOverridesDefaults(): void } /** - * @covers \WP_Mock::userFunction() - * @covers \WP_Mock\Functions::register() - * @covers \WP_Mock\Functions::generateFunction() - * @covers \WP_Mock\Functions::setUpMock() - * * @return void * @throws Exception */ @@ -189,18 +163,6 @@ public function testBotchedMocksStillOverridesDefault(): void } /** - * @covers \WP_Mock::userFunction() - * @covers \WP_Mock\Functions::register() - * @covers \WP_Mock\Functions::generateFunction() - * @covers \WP_Mock\Functions::setUpMock() - * @covers \WP_Mock\Functions::setExpectedTimes() - * @covers \WP_Mock\Functions::setExpectedArgs() - * @covers \WP_Mock\Functions::setExpectedReturn() - * @covers \WP_Mock\Functions::parseExpectedReturn() - * @covers \WP_Mock\Functions\Handler::registerHandler() - * - * @dataProvider providerUserFunctionExpectationArgs - * * @param array $expectationArgs * @param array $expectedResults * @return void @@ -274,9 +236,6 @@ public static function providerUserFunctionExpectationArgs(): Generator } /** - * @covers \WP_Mock::passthruFunction() - * @covers \WP_Mock\Functions::register() - * * @return void * @throws Exception */ @@ -290,9 +249,6 @@ public function testCanMockPassthruFunction(): void } /** - * @covers \WP_Mock::echoFunction() - * @covers \WP_Mock\Functions::register() - * * @return void * @throws Exception */ @@ -308,11 +264,6 @@ public function testCanMockEchoFunction(): void } /** - * @covers \WP_Mock::expectActionAdded() - * @covers \WP_Mock::expectFilterAdded() - * @covers \WP_Mock::expectHookAdded() - * @covers \WP_Mock::assertHooksAdded() - * * @return void */ public function testCanExpectHooksAdded() : void @@ -327,10 +278,6 @@ public function testCanExpectHooksAdded() : void } /** - * @covers \WP_Mock::expectActionNotAdded() - * @covers \WP_Mock::expectFilterNotAdded() - * @covers \WP_Mock::expectHookNotAdded() - * * @return void * @throws Exception */ diff --git a/tests/Unit/WP_Mock/API/FunctionMocksTest.php b/tests/Unit/WP_Mock/API/FunctionMocksTest.php index 77a3d31..6bb3896 100644 --- a/tests/Unit/WP_Mock/API/FunctionMocksTest.php +++ b/tests/Unit/WP_Mock/API/FunctionMocksTest.php @@ -8,30 +8,11 @@ use WP_Mock; use WP_Mock\Tests\WP_MockTestCase; -/** - * @covers \WP_Mock - * @covers \WP_Mock\Functions\Handler - */ +// No coverage metadata: this exercises globally-defined function mocks (esc_url(), __(), …), +// not a single class, so it contributes whole-suite coverage rather than per-class attribution. final class FunctionMocksTest extends WP_MockTestCase { /** - * @covers \__() - * @covers \_n() - * @covers \_x() - * @covers \esc_attr() - * @covers \esc_attr__() - * @covers \esc_attr_x() - * @covers \esc_html() - * @covers \esc_html__() - * @covers \esc_js() - * @covers \esc_textarea() - * @covers \esc_url() - * @covers \esc_url_raw() - * @covers \WP_Mock\Functions\Handler::handlePredefinedReturnFunction() - * - * @preserveGlobalState disabled - * @runInSeparateProcess - * * @return void */ #[RunInSeparateProcess] @@ -68,14 +49,6 @@ public function testPredefinedReturnFunctions(): void } /** - * @covers \_e() - * @covers \esc_attr_e() - * @covers \esc_html_e() - * @covers \WP_Mock\Functions\Handler::handlePredefinedEchoFunction() - * - * @preserveGlobalState disabled - * @runInSeparateProcess - * * @return void * @throws Exception */ diff --git a/tests/Unit/WP_Mock/DeprecatedMethodListenerTest.php b/tests/Unit/WP_Mock/DeprecatedMethodListenerTest.php index aab91aa..072a772 100644 --- a/tests/Unit/WP_Mock/DeprecatedMethodListenerTest.php +++ b/tests/Unit/WP_Mock/DeprecatedMethodListenerTest.php @@ -14,9 +14,7 @@ use WP_Mock\DeprecatedMethodListener; use WP_Mock\Tests\WP_MockTestCase; -/** - * @covers \WP_Mock\DeprecatedMethodListener - */ +#[CoversClass(WP_Mock::class)] #[CoversClass(DeprecatedMethodListener::class)] final class DeprecatedMethodListenerTest extends WP_MockTestCase { @@ -50,9 +48,8 @@ protected function tearDown(): void /** * Captures the {@see E_USER_DEPRECATED} messages emitted while running $callback. * - * A local error handler intercepts the notices so neither PHPUnit 9's - * `convertDeprecationsToExceptions` nor PHPUnit 10+'s `failOnDeprecation` - * interferes with the assertions. + * A local error handler intercepts the notices so that PHPUnit's + * `failOnDeprecation` does not interfere with the assertions. * * @param callable $callback * @return string[] the captured deprecation messages, in order @@ -77,8 +74,6 @@ protected function captureDeprecations(callable $callback): array } /** - * @covers \WP_Mock\DeprecatedMethodListener::setTestName() - * * @return void * @throws ReflectionException|Exception */ @@ -95,8 +90,6 @@ public function testCanSetTestName(): void } /** - * @covers \WP_Mock\DeprecatedMethodListener::logDeprecatedCall() - * * @return void * @throws ReflectionException|Exception */ @@ -119,9 +112,6 @@ public function testLogDeprecatedCallRecordsAndTriggersDeprecation(): void } /** - * @covers \WP_Mock\DeprecatedMethodListener::logDeprecatedCall() - * @covers \WP_Mock\DeprecatedMethodListener::buildMessage() - * * @return void * @throws Exception */ @@ -138,8 +128,6 @@ public function testDeprecationMessageIncludesTestNameAndArgs(): void } /** - * @covers \WP_Mock\DeprecatedMethodListener::reset() - * * @return void * @throws Exception */ @@ -154,9 +142,6 @@ public function testCanResetDeprecatedCallsLog(): void } /** - * @covers \WP_Mock\DeprecatedMethodListener::toScalar() - * @dataProvider providerConvertsArgumentsToScalarValue - * * @param mixed $arg * @param string|bool|null|float|int $expected * @return void @@ -195,9 +180,6 @@ public static function providerConvertsArgumentsToScalarValue(): Generator } /** - * @covers \WP_Mock\DeprecatedMethodListener::logDeprecatedCall() - * @covers \WP_Mock::getDeprecatedMethodListener() - * * @return void * @throws Exception */ diff --git a/tests/Unit/WP_Mock/Functions/HandlerTest.php b/tests/Unit/WP_Mock/Functions/HandlerTest.php index 3389295..41009cc 100644 --- a/tests/Unit/WP_Mock/Functions/HandlerTest.php +++ b/tests/Unit/WP_Mock/Functions/HandlerTest.php @@ -3,6 +3,7 @@ namespace Unit\WP_Mock\Functions; use Exception; +use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\PreserveGlobalState; use PHPUnit\Framework\Attributes\RunInSeparateProcess; use ReflectionProperty; @@ -10,14 +11,10 @@ use WP_Mock\Functions\Handler; use WP_Mock\Tests\WP_MockTestCase; -/** - * @covers \WP_Mock\Functions\Handler - */ +#[CoversClass(Handler::class)] final class HandlerTest extends WP_MockTestCase { /** - * @covers \WP_Mock\Functions\Handler::registerHandler() - * * @return void * @throws Exception */ @@ -45,11 +42,6 @@ public function testCanRegisterHandler(): void } /** - * @covers \WP_Mock\Functions\Handler::handleFunction() - * - * @preserveGlobalState disabled - * @runInSeparateProcess - * * @return void * @throws Exception */ @@ -70,11 +62,6 @@ public function testCanHandleFunction(): void } /** - * @covers \WP_Mock\Functions\Handler::handlerExists() - * - * @preserveGlobalState disabled - * @runInSeparateProcess - * * @return void * @throws Exception */ @@ -95,11 +82,6 @@ public function testCanDetermineHandlerExists(): void } /** - * @covers \WP_Mock\Functions\Handler::cleanup() - * - * @preserveGlobalState disabled - * @runInSeparateProcess - * * @return void * @throws Exception */ @@ -122,11 +104,6 @@ public function testCanCleanup(): void } /** - * @covers \WP_Mock\Functions\Handler::handlePredefinedReturnFunction() - * - * @preserveGlobalState disabled - * @runInSeparateProcess - * * @return void * @throws Exception */ @@ -145,11 +122,6 @@ public function testCanHandlePredefinedReturnFunction(): void } /** - * @covers \WP_Mock\Functions\Handler::handlePredefinedEchoFunction() - * - * @preserveGlobalState disabled - * @runInSeparateProcess - * * @return void * @throws Exception */ diff --git a/tests/Unit/WP_Mock/Functions/ReturnSequenceTest.php b/tests/Unit/WP_Mock/Functions/ReturnSequenceTest.php index 5172a9a..169304c 100644 --- a/tests/Unit/WP_Mock/Functions/ReturnSequenceTest.php +++ b/tests/Unit/WP_Mock/Functions/ReturnSequenceTest.php @@ -3,18 +3,15 @@ namespace WP_Mock\Tests\Unit\WP_Mock\Functions; use Exception; +use PHPUnit\Framework\Attributes\CoversClass; use ReflectionProperty; use WP_Mock\Functions\ReturnSequence; use WP_Mock\Tests\WP_MockTestCase; -/** - * @covers \WP_Mock\Functions\ReturnSequence - */ +#[CoversClass(ReturnSequence::class)] final class ReturnSequenceTest extends WP_MockTestCase { /** - * @covers \WP_Mock\Functions\ReturnSequence::__construct() - * * @return void * @throws Exception */ @@ -28,9 +25,6 @@ public function testConstructor(): void } /** - * @covers \WP_Mock\Functions\ReturnSequence::getReturnValues() - * @covers \WP_Mock\Functions\ReturnSequence::setReturnValues() - * * @return void * @throws Exception */ diff --git a/tests/Unit/WP_Mock/FunctionsTest.php b/tests/Unit/WP_Mock/FunctionsTest.php index 58602dd..670bfa2 100644 --- a/tests/Unit/WP_Mock/FunctionsTest.php +++ b/tests/Unit/WP_Mock/FunctionsTest.php @@ -9,6 +9,7 @@ use Mockery\CountValidator\Exact; use Mockery\Expectation; use Mockery\Mock; +use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\PreserveGlobalState; use PHPUnit\Framework\Attributes\RunInSeparateProcess; @@ -19,17 +20,10 @@ use WP_Mock\Functions\Handler; use WP_Mock\Tests\WP_MockTestCase; -/** - * @covers \WP_Mock\Functions - */ +#[CoversClass(Functions::class)] final class FunctionsTest extends WP_MockTestCase { /** - * @covers \WP_Mock\Functions::__construct() - * - * @runInSeparateProcess - * @preserveGlobalState disabled - * * @return void * @throws ReflectionException|Exception */ @@ -85,11 +79,6 @@ public function testCanInitialize(): void } /** - * @covers \WP_Mock\Functions::register() - * - * @preserveGlobalState disabled - * @runInSeparateProcess - * * @return void * @throws Exception */ @@ -125,9 +114,6 @@ public function testCanRegister(): void } /** - * @covers \WP_Mock\Functions::setUpMock() - * @dataProvider providerCanSetUpMock - * * @param array $expectationArgs * @return void * @throws Exception @@ -188,9 +174,6 @@ public static function providerCanSetUpMock(): Generator } /** - * @covers \WP_Mock\Functions::generateFunction() - * @dataProvider providerCanGenerateFunction - * * @param bool $willCreate * @param bool $willReplace * @return void @@ -238,12 +221,6 @@ public static function providerCanGenerateFunction(): Generator } /** - * @covers \WP_Mock\Functions::createFunction() - * @dataProvider providerCanCreateFunction - * - * @runInSeparateProcess - * @preserveGlobalState disabled - * * @param string $functionName * @param string[] $functionsList * @param bool $functionWillExist @@ -286,8 +263,6 @@ public static function providerCanCreateFunction(): Generator } /** - * @covers \WP_Mock\Functions::replaceFunction() - * * @return void * @throws ReflectionException|Exception */ @@ -307,8 +282,6 @@ public function testCanReplaceFunction(): void } /** - * @covers \WP_Mock\Functions::sanitizeFunctionName() - * * @return void * @throws ReflectionException|Exception */ @@ -324,9 +297,6 @@ public function testCanSanitizeFunctionName(): void } /** - * @covers \WP_Mock\Functions::validateFunctionName() - * @dataProvider providerCanValidateFunction - * * @param string $functionName * @param bool $validates * @return void @@ -357,9 +327,6 @@ public static function providerCanValidateFunction(): Generator } /** - * @covers \WP_Mock\Functions::anyOf() - * @dataProvider providerMatchAnyTypes - * * @param bool $expected * @param mixed $matchedValue * @param mixed...$typesToMatch @@ -386,9 +353,6 @@ public static function providerMatchAnyTypes(): Generator } /** - * @covers \WP_Mock\Functions::type() - * @dataProvider providerMatchTypes - * * @param string $typeToMatch * @param mixed $matchedValue * @return void diff --git a/tests/Unit/WP_Mock/HookTest.php b/tests/Unit/WP_Mock/HookTest.php index 1a2e880..8a78e14 100644 --- a/tests/Unit/WP_Mock/HookTest.php +++ b/tests/Unit/WP_Mock/HookTest.php @@ -7,6 +7,7 @@ use Exception; use Mockery; use PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations; +use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use ReflectionException; @@ -14,18 +15,13 @@ use WP_Mock\Hook; use WP_Mock\Traits\AccessInaccessibleClassMembersTrait; -/** - * @covers \WP_Mock\Hook - */ +#[CoversClass(Hook::class)] #[AllowMockObjectsWithoutExpectations] final class HookTest extends TestCase { use AccessInaccessibleClassMembersTrait; /** - * @covers \WP_Mock\Hook::safe_offset() - * @dataProvider providerSafeOffset - * * @param mixed $value * @param string $expected * @return void diff --git a/tests/Unit/WP_Mock/Matcher/AnyInstanceTest.php b/tests/Unit/WP_Mock/Matcher/AnyInstanceTest.php index 049a99a..aa7edf6 100644 --- a/tests/Unit/WP_Mock/Matcher/AnyInstanceTest.php +++ b/tests/Unit/WP_Mock/Matcher/AnyInstanceTest.php @@ -3,20 +3,17 @@ namespace WP_Mock\Tests\Unit\WP_Mock\Matcher; use Exception; +use PHPUnit\Framework\Attributes\CoversClass; use ReflectionException; use WP_Mock\Matcher\AnyInstance; use WP_Mock\Tests\Mocks\SampleClass; use WP_Mock\Tests\Mocks\SampleSubClass; use WP_Mock\Tests\WP_MockTestCase; -/** - * @covers \WP_Mock\Matcher\AnyInstance - */ +#[CoversClass(AnyInstance::class)] class AnyInstanceTest extends WP_MockTestCase { /** - * @covers \WP_Mock\Matcher\AnyInstance::match() - * * @return void * @throws ReflectionException|Exception */ @@ -32,8 +29,6 @@ public function testExactClassInstanceMatchesTrue(): void } /** - * @covers \WP_Mock\Matcher\AnyInstance::match() - * * @return void * @throws ReflectionException|Exception */ @@ -49,8 +44,6 @@ public function testExactClassStringMatchesTrue(): void } /** - * @covers \WP_Mock\Matcher\AnyInstance::match() - * * @return void * @throws ReflectionException|Exception */ @@ -66,8 +59,6 @@ public function testSubClassMatchesTrue(): void } /** - * @covers \WP_Mock\Matcher\AnyInstance::match() - * * @return void * @throws ReflectionException|Exception */ @@ -83,8 +74,6 @@ public function testWrongClassMatchesFalse(): void } /** - * @covers \WP_Mock\Matcher\AnyInstance::match() - * * @return void * @throws ReflectionException|Exception */ @@ -101,8 +90,6 @@ public function testClosureMatchesFalse(): void } /** - * @covers \WP_Mock\Matcher\AnyInstance::match() - * * @return void * @throws ReflectionException|Exception */ @@ -118,8 +105,6 @@ public function testStringFunctionMatchesFalse(): void } /** - * @covers \WP_Mock\Matcher\AnyInstance::__toString() - * * @return void * @throws Exception */ @@ -133,8 +118,6 @@ public function testToString(): void } /** - * @covers \WP_Mock\Matcher\AnyInstance::__construct() - * * @return void * @throws Exception */ diff --git a/tests/Unit/WP_Mock/Matcher/FuzzyObjectTest.php b/tests/Unit/WP_Mock/Matcher/FuzzyObjectTest.php index cc9b04d..094536c 100644 --- a/tests/Unit/WP_Mock/Matcher/FuzzyObjectTest.php +++ b/tests/Unit/WP_Mock/Matcher/FuzzyObjectTest.php @@ -6,6 +6,7 @@ use Generator; use Mockery; use Mockery\Exception as MockeryException; +use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use ReflectionException; @@ -15,15 +16,10 @@ use WP_Mock\Tests\Mocks\SampleClassTwo; use WP_Mock\Tests\Mocks\SampleSubClass; -/** - * @covers \WP_Mock\Matcher\FuzzyObject - */ +#[CoversClass(FuzzyObject::class)] final class FuzzyObjectTest extends TestCase { /** - * @covers \WP_Mock\Matcher\FuzzyObject::__construct() - * @dataProvider providerCanConstruct - * * @param object|array|mixed $expected * @param bool $shouldThrowException * @return void @@ -81,9 +77,6 @@ public static function providerCanConstruct(): Generator } /** - * @covers \WP_Mock\Matcher\FuzzyObject::match() - * @dataProvider providerCanMatch - * * @param mixed $testClass * @param object $expectedClass * @param bool $expectedResult @@ -181,9 +174,6 @@ public static function providerCanMatch(): Generator } /** - * @covers \WP_Mock\Matcher\FuzzyObject::haveCommonAncestor() - * @dataProvider providerCanDetermineHaveCommonAncestor - * * @param object|mixed $object1 * @param object|mixed $object2 * @param bool $expectedResult @@ -236,9 +226,6 @@ public static function providerCanDetermineHaveCommonAncestor(): Generator } /** - * @covers \WP_Mock\Matcher\FuzzyObject::__toString() - * @dataProvider providerToString - * * @param object|mixed $object * @param string $expectedResult * @return void diff --git a/tests/Unit/WP_Mock/Tools/Constraints/ExpectationsMetTest.php b/tests/Unit/WP_Mock/Tools/Constraints/ExpectationsMetTest.php index 00d4c10..071f856 100644 --- a/tests/Unit/WP_Mock/Tools/Constraints/ExpectationsMetTest.php +++ b/tests/Unit/WP_Mock/Tools/Constraints/ExpectationsMetTest.php @@ -3,20 +3,17 @@ namespace WP_Mock\Tests\Unit\WP_Mock\Tools\Constraints; use Exception; +use PHPUnit\Framework\Attributes\CoversClass; use ReflectionException; use ReflectionMethod; use ReflectionProperty; use WP_Mock\Tests\WP_MockTestCase; use WP_Mock\Tools\Constraints\ExpectationsMet; -/** - * @covers \WP_Mock\Tools\Constraints\ExpectationsMet - */ +#[CoversClass(ExpectationsMet::class)] final class ExpectationsMetTest extends WP_MockTestCase { /** - * @covers \WP_Mock\Tools\Constraints\ExpectationsMet::matches() - * * @return void * @throws Exception */ @@ -28,8 +25,6 @@ public function testMatches(): void } /** - * @covers \WP_Mock\Tools\Constraints\ExpectationsMet::toString() - * * @return void * @throws Exception */ @@ -39,8 +34,6 @@ public function testCanConvertToString(): void } /** - * @covers \WP_Mock\Tools\Constraints\ExpectationsMet::failureDescription() - * * @return void * @throws ReflectionException|Exception */ @@ -54,8 +47,6 @@ public function testCanGetFailureDescription(): void } /** - * @covers \WP_Mock\Tools\Constraints\ExpectationsMet::additionalFailureDescription() - * * @return void * @throws ReflectionException|Exception */ diff --git a/tests/Unit/WP_Mock/Tools/Constraints/IsEqualHtmlTest.php b/tests/Unit/WP_Mock/Tools/Constraints/IsEqualHtmlTest.php index 18935db..0edb731 100644 --- a/tests/Unit/WP_Mock/Tools/Constraints/IsEqualHtmlTest.php +++ b/tests/Unit/WP_Mock/Tools/Constraints/IsEqualHtmlTest.php @@ -12,15 +12,10 @@ use WP_Mock\Tests\WP_MockTestCase; use WP_Mock\Tools\Constraints\IsEqualHtml; -/** - * @covers \WP_Mock\Tools\Constraints\IsEqualHtml - */ #[CoversClass(IsEqualHtml::class)] final class IsEqualHtmlTest extends WP_MockTestCase { /** - * @covers \WP_Mock\Tools\Constraints\IsEqualHtml::__construct() - * * @return void * @throws ReflectionException|Exception */ @@ -35,8 +30,6 @@ public function testConstructor(): void } /** - * @covers \WP_Mock\Tools\Constraints\IsEqualHtml::clean() - * * @return void * @throws ReflectionException|Exception */ @@ -51,9 +44,6 @@ public function testCanClean(): void } /** - * @covers \WP_Mock\Tools\Constraints\IsEqualHtml::matches() - * @dataProvider providerMatches - * * @param string $value * @param string $otherValue * @param bool $expected @@ -91,8 +81,6 @@ public static function providerMatches(): Generator } /** - * @covers \WP_Mock\Tools\Constraints\IsEqualHtml::toString() - * * @return void * @throws Exception */ diff --git a/tests/Unit/WP_Mock/Tools/TestCaseTest.php b/tests/Unit/WP_Mock/Tools/TestCaseTest.php index 4f138a7..3a867c0 100644 --- a/tests/Unit/WP_Mock/Tools/TestCaseTest.php +++ b/tests/Unit/WP_Mock/Tools/TestCaseTest.php @@ -18,16 +18,11 @@ use WP_Mock\Tests\WP_MockTestCase; use WP_Mock\Tools\TestCase; -/** - * @covers \WP_Mock\Tools\TestCase - */ #[CoversClass(TestCase::class)] #[AllowMockObjectsWithoutExpectations] final class TestCaseTest extends WP_MockTestCase { /** - * @covers \WP_Mock\Tools\TestCase::setUp() - * * @return void * @throws Exception */ @@ -50,11 +45,6 @@ public function testCanSetUpTests(): void } /** - * @covers \WP_Mock\Tools\TestCase::tearDown() - * - * @runInSeparateProcess - * @preserveGlobalState disabled - * * @return void * @throws Exception|ReflectionException */ @@ -80,8 +70,6 @@ public function testCanTearDownTests(): void } /** - * @covers \WP_Mock\Tools\TestCase::cleanGlobals() - * * @return void * @throws ReflectionException|Exception */ @@ -102,12 +90,6 @@ public function testCanCleanGlobals(): void } /** - * @covers \WP_Mock\Tools\TestCase::assertActionsCalled() - * @dataProvider providerAssertActionsCalled - * - * @runInSeparateProcess - * @preserveGlobalState disabled - * * @param bool $throwsException * @return void * @throws Exception @@ -141,12 +123,6 @@ public static function providerAssertActionsCalled(): Generator } /** - * @covers \WP_Mock\Tools\TestCase::assertHooksAdded() - * @dataProvider providerAssertHooksAdded - * - * @runInSeparateProcess - * @preserveGlobalState disabled - * * @param bool $throwsException * @return void * @throws Exception @@ -180,8 +156,6 @@ public static function providerAssertHooksAdded(): Generator } /** - * @covers \WP_Mock\Tools\TestCase::assertCurrentConditionsMet() - * * @return void * @throws Exception */ @@ -197,8 +171,6 @@ public function testCanAssertCurrentTestConditionsWereMet(): void } /** - * @covers \WP_Mock\Tools\TestCase::assertConditionsMet() - * * @return void */ public function testCanAssertTestConditionsWereMet(): void @@ -210,8 +182,6 @@ public function testCanAssertTestConditionsWereMet(): void } /** - * @covers \WP_Mock\Tools\TestCase::assertEqualsHtml() - * * @return void * @throws Exception */ @@ -227,8 +197,6 @@ public function testCanAssertEqualsHtml(): void } /** - * @covers \WP_Mock\Tools\TestCase::assertOutputEqualsHtml() - * * @return void * @throws Exception */ @@ -250,12 +218,6 @@ public function testCanAssertOutputEqualsHtml(): void } /** - * @covers \WP_Mock\Tools\TestCase::mockStaticMethod() - * @dataProvider providerMockStaticMethod - * - * @runInSeparateProcess - * @preserveGlobalState disabled - * * @param bool $usingPatchwork * @param bool $invalidMethod * @return void diff --git a/tests/Unit/WP_Mock/Traits/AccessInaccessibleClassMembersTraitTest.php b/tests/Unit/WP_Mock/Traits/AccessInaccessibleClassMembersTraitTest.php index 0a9d6b2..08265ee 100644 --- a/tests/Unit/WP_Mock/Traits/AccessInaccessibleClassMembersTraitTest.php +++ b/tests/Unit/WP_Mock/Traits/AccessInaccessibleClassMembersTraitTest.php @@ -7,14 +7,10 @@ use ReflectionException; use WP_Mock\Traits\AccessInaccessibleClassMembersTrait; -/** - * @covers \WP_Mock\Traits\AccessInaccessibleClassMembersTrait - */ +// No coverage metadata: #[CoversTrait] requires PHPUnit 11+, but the supported floor is PHPUnit 10. final class AccessInaccessibleClassMembersTraitTest extends TestCase { /** - * @covers \WP_Mock\Traits\AccessInaccessibleClassMembersTrait::getInaccessibleProperty() - * * @return void * @throws ReflectionException|Exception */ @@ -28,8 +24,6 @@ public function testCanGetInaccessibleProperty(): void } /** - * @covers \WP_Mock\Traits\AccessInaccessibleClassMembersTrait::setInaccessibleProperty() - * * @return void * @throws ReflectionException|Exception */ @@ -42,8 +36,6 @@ public function testCanSetInaccessibleProperty(): void } /** - * @covers \WP_Mock\Traits\AccessInaccessibleClassMembersTrait::setInaccessiblePropertyValue() - * * @return void * @throws ReflectionException|Exception */ @@ -56,8 +48,6 @@ public function testCanSetInaccessiblePropertyValue() : void } /** - * @covers \WP_Mock\Traits\AccessInaccessibleClassMembersTrait::getInaccessiblePropertyValue() - * * @return void * @throws ReflectionException|Exception */ @@ -69,8 +59,6 @@ public function testCanGetInaccessiblePropertyValue(): void } /** - * @covers \WP_Mock\Traits\AccessInaccessibleClassMembersTrait::getInaccessibleMethod() - * * @return void * @throws ReflectionException|Exception */ @@ -84,8 +72,6 @@ public function testCanGetInaccessibleMethod(): void } /** - * @covers \WP_Mock\Traits\AccessInaccessibleClassMembersTrait::invokeInaccessibleMethod() - * * @return void * @throws ReflectionException|Exception */ diff --git a/tests/Unit/WP_Mock/Traits/MockWordPressObjectsTraitTest.php b/tests/Unit/WP_Mock/Traits/MockWordPressObjectsTraitTest.php index 6990a5d..a72467d 100644 --- a/tests/Unit/WP_Mock/Traits/MockWordPressObjectsTraitTest.php +++ b/tests/Unit/WP_Mock/Traits/MockWordPressObjectsTraitTest.php @@ -10,14 +10,10 @@ use WP_Mock\Traits\MockWordPressObjectsTrait; use WP_Post; -/** - * @covers \WP_Mock\Traits\MockWordPressObjectsTrait - */ +// No coverage metadata: #[CoversTrait] requires PHPUnit 11+, but the supported floor is PHPUnit 10. final class MockWordPressObjectsTraitTest extends WP_MockTestCase { /** - * @covers \WP_Mock\Traits\MockWordPressObjectsTrait::mockPost() - * * @return void * @throws ReflectionException|Exception */ @@ -60,8 +56,6 @@ public function testCanMockWordPressPost(): void } /** - * @covers \WP_Mock\Traits\MockWordPressObjectsTrait::mockWp() - * * @return void * @throws ReflectionException|Exception */ diff --git a/tests/Unit/WP_MockTest.php b/tests/Unit/WP_MockTest.php index 7e632a3..0c8543a 100644 --- a/tests/Unit/WP_MockTest.php +++ b/tests/Unit/WP_MockTest.php @@ -7,6 +7,7 @@ use stdClass; use Generator; use PHPUnit\Framework\Exception; +use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\PreserveGlobalState; use PHPUnit\Framework\Attributes\RunInSeparateProcess; @@ -19,17 +20,10 @@ use PHPUnit\Framework\ExpectationFailedException; use SebastianBergmann\RecursionContext\InvalidArgumentException; -/** - * @covers \WP_Mock - */ +#[CoversClass(WP_Mock::class)] class WP_MockTest extends WP_MockTestCase { /** - * @covers \WP_Mock::strictMode() - * - * @runInSeparateProcess - * @preserveGlobalState disabled - * * @return void * @throws ExpectationFailedException|InvalidArgumentException */ @@ -41,11 +35,6 @@ public function testStrictModeOffByDefault(): void } /** - * @covers \WP_Mock::activateStrictMode() - * - * @runInSeparateProcess - * @preserveGlobalState disabled - * * @return void * @throws ExpectationFailedException|InvalidArgumentException */ @@ -59,11 +48,6 @@ public function testActivateStrictModeTurnsStrictModeOn(): void } /** - * @covers \WP_Mock::strictMode() - * - * @runInSeparateProcess - * @preserveGlobalState disabled - * * @return void * @throws ExpectationFailedException|InvalidArgumentException */ @@ -78,11 +62,6 @@ public function testActivateStrictModeDoesNotWorkAfterBootstrap(): void } /** - * @covers \WP_Mock::userFunction() - * - * @runInSeparateProcess - * @preserveGlobalState disabled - * * @return void * @throws Exception|InvalidArgumentException|\InvalidArgumentException */ @@ -99,11 +78,6 @@ public function testUserFunctionReturnsExpectationContract(): void } /** - * @covers \WP_Mock::assertHooksAdded() - * - * @runInSeparateProcess - * @preserveGlobalState disabled - * * @return void */ #[RunInSeparateProcess] @@ -127,11 +101,6 @@ public function testAssertHooksAddedForFiltersAndActionsPasses(): void } /** - * @covers \WP_Mock::assertHooksAdded() - * - * @runInSeparateProcess - * @preserveGlobalState disabled - * * @return void */ #[RunInSeparateProcess] @@ -156,11 +125,6 @@ public function testAssertHooksAddedForFiltersAndActionsFails(): void } /** - * @covers \WP_Mock::assertActionsCalled() - * - * @runInSeparateProcess - * @preserveGlobalState disabled - * * @return void */ #[RunInSeparateProcess] @@ -178,11 +142,6 @@ public function testAssertActionsCalledPasses(): void } /** - * @covers \WP_Mock::assertActionsCalled() - * - * @runInSeparateProcess - * @preserveGlobalState disabled - * * @return void */ #[RunInSeparateProcess] @@ -204,11 +163,6 @@ public function testAssertActionsCalledFails(): void } /** - * @covers \WP_Mock::assertFiltersCalled() - * - * @runInSeparateProcess - * @preserveGlobalState disabled - * * @return void */ #[RunInSeparateProcess] @@ -227,11 +181,6 @@ public function testAssertFiltersCalledPasses(): void } /** - * @covers \WP_Mock::assertFiltersCalled() - * - * @runInSeparateProcess - * @preserveGlobalState disabled - * * @return void */ #[RunInSeparateProcess] @@ -248,11 +197,6 @@ public function testAssertFiltersCalledFails(): void } /** - * @covers \WP_Mock::assertFiltersCalled() - * - * @runInSeparateProcess - * @preserveGlobalState disabled - * * @return void */ #[RunInSeparateProcess] @@ -271,8 +215,6 @@ public function testAssertFiltersPassesWithTypes(): void } /** - * @covers \WP_Mock::alias() - * * @return void * @throws Exception|InvalidArgumentException|\InvalidArgumentException */ @@ -292,9 +234,6 @@ public function testCanAliasFunction(): void } /** - * @covers \WP_Mock::fuzzyObject() - * @dataProvider providerFuzzyObject - * * @param array|object|mixed $object * @param string $expected * @return void @@ -325,8 +264,6 @@ public static function providerFuzzyObject(): Generator } /** - * @covers \WP_Mock::getDeprecatedMethodListener() - * * @return void * @throws Exception|InvalidArgumentException */ @@ -340,11 +277,6 @@ public function testCanGetDeprecatedMethodListener(): void } /** - * @covers \WP_Mock::onFilter() - * - * @runInSeparateProcess - * @preserveGlobalState disabled - * * @return void * @throws Exception|InvalidArgumentException */ @@ -367,11 +299,6 @@ public function testOnFilterPasses(): void } /** - * @covers \WP_Mock::onFilter() - * - * @runInSeparateProcess - * @preserveGlobalState disabled - * * @return void * @throws Exception|InvalidArgumentException */ @@ -394,11 +321,6 @@ public function testOnFilterPassesWithAnyArgs(): void } /** - * @covers \WP_Mock::onFilter() - * - * @runInSeparateProcess - * @preserveGlobalState disabled - * * @return void * @throws Exception|InvalidArgumentException */ diff --git a/tests/WP_MockTestCase.php b/tests/WP_MockTestCase.php index 801c1ee..9baa6d3 100644 --- a/tests/WP_MockTestCase.php +++ b/tests/WP_MockTestCase.php @@ -53,19 +53,13 @@ protected function assertConditionsMet(): void /** * Determines whether the current test is running in process isolation. * - * Cross-version replacement for PHPUnit's {@see \PHPUnit\Framework\TestCase::isInIsolation()}, - * which was public in PHPUnit 9 but removed in PHPUnit 10+ (where the state lives in a private - * `$inIsolation` property). + * PHPUnit 10+ removed the public `isInIsolation()` method; the state now lives in a private + * `$inIsolation` property on {@see \PHPUnit\Framework\TestCase}, which we read by reflection. * * @return bool */ protected function isRunningInIsolation(): bool { - if (method_exists($this, 'isInIsolation')) { - /** @phpstan-ignore-next-line method exists on PHPUnit 9 only */ - return (bool) $this->isInIsolation(); - } - try { $property = new \ReflectionProperty(TestCase::class, 'inIsolation'); $property->setAccessible(true); From 221736aeed1df0fd343e37fa91b47c2e17ab7663 Mon Sep 17 00:00:00 2001 From: Fulvio Notarstefano Date: Wed, 24 Jun 2026 12:26:20 +0900 Subject: [PATCH 16/16] refactor(tests): shape getDeprecatedMethodCalls() return type Address review feedback: the helper reads DeprecatedMethodListener::$deprecatedCalls (typed array}>) but declared the broad array. Mirror the source shape so the [$method, $args] pairs are clear at call sites and static analysis stays useful. The reflected value is narrowed with a @var (it arrives as mixed from ReflectionProperty::getValue()), so PHPStan level-max stays clean. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01K8HGMxCuSjUX6p4PQL7t5Y --- tests/Unit/WP_Mock/DeprecatedMethodListenerTest.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/Unit/WP_Mock/DeprecatedMethodListenerTest.php b/tests/Unit/WP_Mock/DeprecatedMethodListenerTest.php index 072a772..8f30b95 100644 --- a/tests/Unit/WP_Mock/DeprecatedMethodListenerTest.php +++ b/tests/Unit/WP_Mock/DeprecatedMethodListenerTest.php @@ -210,7 +210,7 @@ public function testCanHandleDeprecatedMethodCallThroughWpMock(): void * @see DeprecatedMethodListener::$deprecatedCalls * * @param DeprecatedMethodListener $listener - * @return array + * @return array}> the logged [$method, $args] pairs * @throws ReflectionException */ protected function getDeprecatedMethodCalls(DeprecatedMethodListener $listener): array @@ -220,7 +220,12 @@ protected function getDeprecatedMethodCalls(DeprecatedMethodListener $listener): $value = $property->getValue($listener); - return is_array($value) ? $value : []; + if (! is_array($value)) { + return []; + } + + /** @var array}> $value */ + return $value; } }