From 2c8b6352cbb664cb99651b9786a08a3da887392d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20Djupsj=C3=B6backa?= Date: Thu, 6 Mar 2025 21:01:48 +0200 Subject: [PATCH 1/8] Support PHPUnit 12 by avoiding removed methods getMockForAbstractClass() and addMethods() --- classes/PHPMock.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/classes/PHPMock.php b/classes/PHPMock.php index 543db17..1aef131 100644 --- a/classes/PHPMock.php +++ b/classes/PHPMock.php @@ -66,10 +66,10 @@ public function getFunctionMock($namespace, $name) $delegateBuilder->build($name); $builder = $this->getMockBuilder($delegateBuilder->getFullyQualifiedClassName()); - if (is_callable([$builder, 'addMethods'])) { - $builder->addMethods([$name]); + if (is_callable([$builder, 'onlyMethods'])) { + $builder->onlyMethods(['delegate', $name]); } - $mock = $builder->getMockForAbstractClass(); + $mock = $builder->getMock(); $this->addMatcher($mock, $name); $functionMockBuilder = new MockBuilder(); From 6524ed645ef6a7b091e47d50597c25eef4dd843d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Bundyra?= Date: Fri, 7 Mar 2025 11:59:40 +0000 Subject: [PATCH 2/8] Update composer.json - PHPUnit 12.x support --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 9218934..126c2a7 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,7 @@ }, "require": { "php": ">=7", - "phpunit/phpunit": "^6 || ^7 || ^8 || ^9 || ^10.0.17 || ^11", + "phpunit/phpunit": "^6 || ^7 || ^8 || ^9 || ^10.0.17 || ^11 || ^12", "php-mock/php-mock-integration": "^2.3" }, "require-dev": { From 6a35727550b7d0a7d85c6b2c6a83fd413f08c07e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Bundyra?= Date: Fri, 7 Mar 2025 12:07:09 +0000 Subject: [PATCH 3/8] Update workflow - probably needs more exclusions --- .github/workflows/tests.yml | 109 ++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 29053d3..e5d071b 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,6 +13,7 @@ jobs: strategy: matrix: php-version: + - '8.4' - '8.3' - '8.2' - '8.1' @@ -23,6 +24,10 @@ jobs: - '7.1' - '7.0' phpunit-version: + - '12.0.0' + - '11.5.0' + - '11.4.0' + - '11.3.0' - '11.2.0' - '11.1.0' - '11.0.0' @@ -58,6 +63,52 @@ jobs: - '6.0.0' exclude: + # PHP 8.4 Exclusions + - php-version: '8.4' + phpunit-version: '9.4.0' + - php-version: '8.4' + phpunit-version: '9.3.0' + - php-version: '8.4' + phpunit-version: '9.2.0' + - php-version: '8.4' + phpunit-version: '9.1.0' + - php-version: '8.4' + phpunit-version: '9.0.0' + - php-version: '8.4' + phpunit-version: '8.4.0' + - php-version: '8.4' + phpunit-version: '8.3.0' + - php-version: '8.4' + phpunit-version: '8.2.0' + - php-version: '8.4' + phpunit-version: '8.1.0' + - php-version: '8.4' + phpunit-version: '8.0.0' + - php-version: '8.4' + phpunit-version: '7.5.0' + - php-version: '8.4' + phpunit-version: '7.4.0' + - php-version: '8.4' + phpunit-version: '7.3.0' + - php-version: '8.4' + phpunit-version: '7.2.0' + - php-version: '8.4' + phpunit-version: '7.1.0' + - php-version: '8.4' + phpunit-version: '7.0.0' + - php-version: '8.4' + phpunit-version: '6.5.0' + - php-version: '8.4' + phpunit-version: '6.4.0' + - php-version: '8.4' + phpunit-version: '6.3.0' + - php-version: '8.4' + phpunit-version: '6.2.0' + - php-version: '8.4' + phpunit-version: '6.1.0' + - php-version: '8.4' + phpunit-version: '6.0.0' + # PHP 8.3 Exclusions - php-version: '8.3' phpunit-version: '9.4.0' @@ -105,6 +156,8 @@ jobs: phpunit-version: '6.0.0' # PHP 8.2 Exclusions + - php-version: '8.2' + phpunit-version: '12.0.0' - php-version: '8.2' phpunit-version: '9.4.0' - php-version: '8.2' @@ -151,6 +204,14 @@ jobs: phpunit-version: '6.0.0' # PHP 8.1 Exclusions + - php-version: '8.1' + phpunit-version: '12.0.0' + - php-version: '8.1' + phpunit-version: '11.5.0' + - php-version: '8.1' + phpunit-version: '11.4.0' + - php-version: '8.1' + phpunit-version: '11.3.0' - php-version: '8.1' phpunit-version: '11.2.0' - php-version: '8.1' @@ -203,6 +264,14 @@ jobs: phpunit-version: '6.0.0' # PHP 8.0 Exclusions + - php-version: '8.0' + phpunit-version: '12.0.0' + - php-version: '8.0' + phpunit-version: '11.5.0' + - php-version: '8.0' + phpunit-version: '11.4.0' + - php-version: '8.0' + phpunit-version: '11.3.0' - php-version: '8.0' phpunit-version: '11.2.0' - php-version: '8.0' @@ -263,6 +332,14 @@ jobs: phpunit-version: '6.0.0' # PHP 7.4 Exclusions + - php-version: '7.4' + phpunit-version: '12.0.0' + - php-version: '7.4' + phpunit-version: '11.5.0' + - php-version: '7.4' + phpunit-version: '11.4.0' + - php-version: '7.4' + phpunit-version: '11.3.0' - php-version: '7.4' phpunit-version: '11.2.0' - php-version: '7.4' @@ -309,6 +386,14 @@ jobs: phpunit-version: '6.0.0' # PHP 7.3 Exclusions + - php-version: '7.3' + phpunit-version: '12.0.0' + - php-version: '7.3' + phpunit-version: '11.5.0' + - php-version: '7.3' + phpunit-version: '11.4.0' + - php-version: '7.3' + phpunit-version: '11.3.0' - php-version: '7.3' phpunit-version: '11.2.0' - php-version: '7.3' @@ -329,6 +414,14 @@ jobs: phpunit-version: '10.0.0' # PHP 7.2 Exclusions + - php-version: '7.2' + phpunit-version: '12.0.0' + - php-version: '7.2' + phpunit-version: '11.5.0' + - php-version: '7.2' + phpunit-version: '11.4.0' + - php-version: '7.2' + phpunit-version: '11.3.0' - php-version: '7.2' phpunit-version: '11.2.0' - php-version: '7.2' @@ -363,6 +456,14 @@ jobs: phpunit-version: '9.0.0' # PHP 7.1 Exclusions + - php-version: '7.1' + phpunit-version: '12.0.0' + - php-version: '7.1' + phpunit-version: '11.5.0' + - php-version: '7.1' + phpunit-version: '11.4.0' + - php-version: '7.1' + phpunit-version: '11.3.0' - php-version: '7.1' phpunit-version: '11.2.0' - php-version: '7.1' @@ -409,6 +510,14 @@ jobs: phpunit-version: '8.0.0' # PHP 7.0 Exclusions + - php-version: '7.0' + phpunit-version: '12.0.0' + - php-version: '7.0' + phpunit-version: '11.5.0' + - php-version: '7.0' + phpunit-version: '11.4.0' + - php-version: '7.0' + phpunit-version: '11.3.0' - php-version: '7.0' phpunit-version: '11.2.0' - php-version: '7.0' From 7496e33470d591aefbaa81befe99fe0ec9623feb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20Djupsj=C3=B6backa?= Date: Fri, 7 Mar 2025 16:35:38 +0200 Subject: [PATCH 4/8] Use fresh php-mock-integration beta version --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 126c2a7..14b27bd 100644 --- a/composer.json +++ b/composer.json @@ -20,7 +20,7 @@ "require": { "php": ">=7", "phpunit/phpunit": "^6 || ^7 || ^8 || ^9 || ^10.0.17 || ^11 || ^12", - "php-mock/php-mock-integration": "^2.3" + "php-mock/php-mock-integration": "^2.5.0-beta1" }, "require-dev": { "mockery/mockery": "^1.3.6" From f17d4a9d2a90f01ea64b916a99fe5218de765146 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20Djupsj=C3=B6backa?= Date: Fri, 7 Mar 2025 19:15:06 +0200 Subject: [PATCH 5/8] Handle PHPUnit internal changes between version 11 and 12 --- autoload.php | 5 + .../DefaultArgumentRemoverReturnTypes120.php | 128 ++++++++++++++++++ classes/MockObjectProxyReturnTypes120.php | 100 ++++++++++++++ classes/PHPMock.php | 5 +- tests/MockObjectProxyTest.php | 2 + 5 files changed, 238 insertions(+), 2 deletions(-) create mode 100644 classes/DefaultArgumentRemoverReturnTypes120.php create mode 100644 classes/MockObjectProxyReturnTypes120.php diff --git a/autoload.php b/autoload.php index e59f8ca..f567a6a 100644 --- a/autoload.php +++ b/autoload.php @@ -90,6 +90,11 @@ class_alias( } if ($hasVersion + && version_compare(\PHPUnit\Runner\Version::id(), '12.0.0') >= 0 +) { + class_alias(\phpmock\phpunit\DefaultArgumentRemoverReturnTypes120::class, \phpmock\phpunit\DefaultArgumentRemover::class); + class_alias(\phpmock\phpunit\MockObjectProxyReturnTypes120::class, \phpmock\phpunit\MockObjectProxy::class); +} elseif ($hasVersion && version_compare(\PHPUnit\Runner\Version::id(), '10.0.0') >= 0 ) { class_alias(\phpmock\phpunit\DefaultArgumentRemoverReturnTypes100::class, \phpmock\phpunit\DefaultArgumentRemover::class); diff --git a/classes/DefaultArgumentRemoverReturnTypes120.php b/classes/DefaultArgumentRemoverReturnTypes120.php new file mode 100644 index 0000000..8cce8fa --- /dev/null +++ b/classes/DefaultArgumentRemoverReturnTypes120.php @@ -0,0 +1,128 @@ + + * @link bitcoin:1335STSwu9hST4vcMRppEPgENMHD2r1REK Donations + * @license http://www.wtfpl.net/txt/copying/ WTFPL + * @internal + */ +class DefaultArgumentRemoverReturnTypes120 extends InvocationOrder +{ + /** + * @SuppressWarnings(PHPMD) + */ + public function invokedDo(Invocation $invocation): void + { + } + + /** + * @SuppressWarnings(PHPMD) + */ + public function matches(Invocation $invocation) : bool + { + $iClass = class_exists(Invocation::class); + + if ($iClass + || $invocation instanceof Invocation\StaticInvocation + ) { + $this->removeDefaultArguments( + $invocation, + $iClass ? Invocation::class : Invocation\StaticInvocation::class + ); + } elseif (!$this->shouldRemoveDefaultArgumentsWithReflection($invocation)) { + MockFunctionGenerator::removeDefaultArguments($invocation->parameters); + } + + return false; + } + + public function verify() : void + { + } + + /** + * This method is not defined in the interface, but used in + * PHPUnit_Framework_MockObject_InvocationMocker::hasMatchers(). + * + * @return boolean + * @see \PHPUnit_Framework_MockObject_InvocationMocker::hasMatchers() + */ + public function hasMatchers() + { + return false; + } + + public function toString() : string + { + return __CLASS__; + } + + /** + * Remove default arguments from StaticInvocation or its children (hack) + * + * @SuppressWarnings(PHPMD) + */ + private function removeDefaultArguments(Invocation $invocation, string $class) + { + if ($this->shouldRemoveDefaultArgumentsWithReflection($invocation)) { + return; + } + + $remover = function () { + MockFunctionGenerator::removeDefaultArguments($this->parameters); + }; + + $remover->bindTo($invocation, $class)(); + } + + /** + * Alternative to remove default arguments from StaticInvocation or its children (hack) + * + * @SuppressWarnings(PHPMD.StaticAccess) + */ + public static function removeDefaultArgumentsWithReflection(Invocation $invocation): Invocation + { + if (!(new self())->shouldRemoveDefaultArgumentsWithReflection($invocation)) { + return $invocation; + } + + $reflection = new ReflectionClass($invocation); + + $reflectionReturnType = $reflection->getProperty('returnType'); + $reflectionReturnType->setAccessible(true); + + $reflectionIsOptional = $reflection->getProperty('isReturnTypeNullable'); + $reflectionIsOptional->setAccessible(true); + + $returnType = $reflectionReturnType->getValue($invocation); + + if ($reflectionIsOptional->getValue($invocation)) { + $returnType = '?' . $returnType; + } + + $parameters = $invocation->parameters(); + MockFunctionGenerator::removeDefaultArguments($parameters); + + return new Invocation( + $invocation->className(), + $invocation->methodName(), + $parameters, + $returnType, + $invocation->object() + ); + } + + protected function shouldRemoveDefaultArgumentsWithReflection(Invocation $invocation) + { + return method_exists($invocation, 'parameters'); + } +} diff --git a/classes/MockObjectProxyReturnTypes120.php b/classes/MockObjectProxyReturnTypes120.php new file mode 100644 index 0000000..f30503e --- /dev/null +++ b/classes/MockObjectProxyReturnTypes120.php @@ -0,0 +1,100 @@ + + * @link bitcoin:1335STSwu9hST4vcMRppEPgENMHD2r1REK Donations + * @license http://www.wtfpl.net/txt/copying/ WTFPL + * @internal + */ +class MockObjectProxyReturnTypes120 implements MockObject +{ + /** + * @var MockObject $mockObject The mock object. + */ + private $mockObject; + + /** + * Inject the subject. + * + * @param MockObject $mockObject The subject. + */ + public function __construct(MockObject $mockObject) + { + $this->mockObject = $mockObject; + } + + /** + * @SuppressWarnings(PHPMD) + */ + // @codingStandardsIgnoreStart + public function __phpunit_getInvocationHandler(): InvocationHandler + { + return $this->mockObject->__phpunit_getInvocationHandler(); + } + + /** + * @SuppressWarnings(PHPMD) + */ + // @codingStandardsIgnoreStart + public function __phpunit_setOriginalObject(object $originalObject) : void + { + // @codingStandardsIgnoreEnd + $this->mockObject->__phpunit_setOriginalObject($originalObject); + } + + /** + * @SuppressWarnings(PHPMD) + */ + // @codingStandardsIgnoreStart + public function __phpunit_verify(bool $unsetInvocationMocker = true) : void + { + // @codingStandardsIgnoreEnd + $this->mockObject->__phpunit_verify($unsetInvocationMocker); + } + + public function expects(InvocationOrder $matcher) : BuilderInvocationMocker + { + return $this->mockObject->expects($matcher)->method(MockDelegateFunctionBuilder::METHOD); + } + + public function method(Constraint|PropertyHook|string $constraint): InvocationStubber + { + return $this->mockObject->method($constraint); + } + + /** + * This method is not part of the contract but was found in + * PHPUnit's mocked_class.tpl.dist. + * + * @SuppressWarnings(PHPMD) + */ + // @codingStandardsIgnoreStart + public function __phpunit_hasMatchers() : bool + { + // @codingStandardsIgnoreEnd + return $this->mockObject->__phpunit_hasMatchers(); + } + + /** + * @SuppressWarnings(PHPMD) + */ + // @codingStandardsIgnoreStart + public function __phpunit_setReturnValueGeneration(bool $returnValueGeneration) : void + { + // @codingStandardsIgnoreEnd + $this->mockObject->__phpunit_setReturnValueGeneration($returnValueGeneration); + } +} diff --git a/classes/PHPMock.php b/classes/PHPMock.php index 1aef131..3db89af 100644 --- a/classes/PHPMock.php +++ b/classes/PHPMock.php @@ -199,8 +199,9 @@ private function prepareCustomTemplates() } $mockMethodClasses = [ - 'PHPUnit\\Framework\\MockObject\\Generator\\MockMethod', - 'PHPUnit\\Framework\\MockObject\\MockMethod', + 'PHPUnit\\Framework\\MockObject\\Generator\\DoubledMethod', // PHPUnit 12 + 'PHPUnit\\Framework\\MockObject\\Generator\\MockMethod', // PHPUnit 10-11 + 'PHPUnit\\Framework\\MockObject\\MockMethod', // PHPUnit 9 ]; foreach ($mockMethodClasses as $mockMethodClass) { diff --git a/tests/MockObjectProxyTest.php b/tests/MockObjectProxyTest.php index 482e052..e9f35e4 100644 --- a/tests/MockObjectProxyTest.php +++ b/tests/MockObjectProxyTest.php @@ -4,6 +4,7 @@ use Mockery; use phpmock\integration\MockDelegateFunctionBuilder; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\MockObject\Builder\InvocationMocker; use PHPUnit\Framework\MockObject\ConfigurableMethod; use PHPUnit\Framework\MockObject\InvocationHandler; @@ -137,6 +138,7 @@ public function testHasMatcher() * @test * @dataProvider provideTestProxiedMethods */ + #[DataProvider('provideTestProxiedMethods')] public function testProxiedMethods($method, array $arguments = [], $expected = null) { $mock = Mockery::mock(MockObject::class); From 646a3b2428aeccf562d6db175c0fb05b2965fc44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Viktor=20Djupsj=C3=B6backa?= Date: Fri, 7 Mar 2025 20:05:25 +0200 Subject: [PATCH 6/8] Try to cater for PHPUnit <= 8.2, use constant instead of hardcoded method name --- classes/PHPMock.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/classes/PHPMock.php b/classes/PHPMock.php index 3db89af..4575504 100644 --- a/classes/PHPMock.php +++ b/classes/PHPMock.php @@ -67,7 +67,9 @@ public function getFunctionMock($namespace, $name) $builder = $this->getMockBuilder($delegateBuilder->getFullyQualifiedClassName()); if (is_callable([$builder, 'onlyMethods'])) { - $builder->onlyMethods(['delegate', $name]); + $builder->onlyMethods([MockDelegateFunctionBuilder::METHOD, $name]); + } elseif (is_callable([$builder, 'setMethods'])) { + $builder->setMethods([MockDelegateFunctionBuilder::METHOD, $name]); } $mock = $builder->getMock(); $this->addMatcher($mock, $name); From 004c723373b9cdb54b00b49bfb02f7fafa9f899a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Bundyra?= Date: Sat, 8 Mar 2025 19:29:13 +0000 Subject: [PATCH 7/8] Trying 3.0.0-beta1 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 14b27bd..f05cb66 100644 --- a/composer.json +++ b/composer.json @@ -20,7 +20,7 @@ "require": { "php": ">=7", "phpunit/phpunit": "^6 || ^7 || ^8 || ^9 || ^10.0.17 || ^11 || ^12", - "php-mock/php-mock-integration": "^2.5.0-beta1" + "php-mock/php-mock-integration": "^3.0.0-beta1" }, "require-dev": { "mockery/mockery": "^1.3.6" From 51f2e679696bc8efc161450241002d4ed917dc7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Bundyra?= Date: Sat, 8 Mar 2025 19:43:16 +0000 Subject: [PATCH 8/8] Use stable ^3.0 for php-mock-integration --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index f05cb66..702ebe8 100644 --- a/composer.json +++ b/composer.json @@ -20,7 +20,7 @@ "require": { "php": ">=7", "phpunit/phpunit": "^6 || ^7 || ^8 || ^9 || ^10.0.17 || ^11 || ^12", - "php-mock/php-mock-integration": "^3.0.0-beta1" + "php-mock/php-mock-integration": "^3.0" }, "require-dev": { "mockery/mockery": "^1.3.6"