From 52d3928dfd3319bddca2389ec07119f855aeaf01 Mon Sep 17 00:00:00 2001 From: Brian Henry Date: Thu, 15 Jan 2026 17:25:49 -0800 Subject: [PATCH 1/6] Add PHP 8.0 named parameters support --- php/WP_Mock/Functions.php | 28 ++++++++++++++++++++++------ tests/Integration/WP_MockTest.php | 29 +++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 6 deletions(-) diff --git a/php/WP_Mock/Functions.php b/php/WP_Mock/Functions.php index ddf5cf6..e7755a7 100644 --- a/php/WP_Mock/Functions.php +++ b/php/WP_Mock/Functions.php @@ -97,7 +97,8 @@ public function flush(): void */ public function register(string $function, array $args = []) { - $this->generateFunction($function); + $functionArgs = isset($args['args']) ? $args['args'] : []; + $this->generateFunction($function, $functionArgs); if (empty($this->mockedFunctions[$function])) { /** @phpstan-ignore-next-line */ @@ -138,7 +139,10 @@ protected function setUpMock($mock, string $functionName, array $args = []) // set the expected arguments the function should be called with if (isset($args['args'])) { - $this->setExpectedArgs($expectation, $args['args']); + $this->setExpectedArgs( + $expectation, + is_array($args['args']) ? array_values($args['args']): $args['args'] + ); } // set the expected return value based on a passed argument or return values for each call in order @@ -272,25 +276,27 @@ protected function setExpectedReturn(&$expectation, $return) * The declared function is namespace-aware. * * @param string $functionName function name + * @param string $functionArgs function arguments * @return void * @throws InvalidArgumentException */ - protected function generateFunction(string $functionName): void + protected function generateFunction(string $functionName, array $functionArgs = []): void { $functionName = $this->sanitizeFunctionName($functionName); $this->validateFunctionName($functionName); - $this->createFunction($functionName) or $this->replaceFunction($functionName); + $this->createFunction($functionName, $functionArgs) or $this->replaceFunction($functionName); } /** * Creates a function using eval. * * @param string $functionName function name + * @param array $functionArgs function arguments, required only when calling mocked functions using named parameters * @return bool true if this function created the mock, false otherwise */ - protected function createFunction(string $functionName): bool + protected function createFunction(string $functionName, array $functionArgs = []): bool { if (in_array($functionName, self::$userMockedFunctions, true)) { return true; @@ -304,9 +310,19 @@ protected function createFunction(string $functionName): bool $name = array_pop($parts); $namespace = empty($parts) ? '' : 'namespace '.implode('\\', $parts).';'.PHP_EOL; + $functionNamedParameters = ''; + + $has_named_parameters = array_reduce( array_keys($functionArgs), function ($carry, $arg) { + return $carry && !is_int($arg); + }, true); + + if($has_named_parameters){ + $functionNamedParameters = implode(', ', array_map(fn($name) => '$' . $name, array_keys($functionArgs))); + } + $declaration = <<assertConditionsMet(); } + + /** + * Fix "Error : Unknown named parameter $transient" when passing named parameters in the argument list. + * + * @covers \WP_Mock::userFunction() + * @see WP_Mock\Functions::createFunction() + * + * @throws Exception + */ + public function testCanMockNamedParameters(): void { + + if(!version_compare(PHP_VERSION, '8.0', '>=')) { + $this->markTestSkipped('PHP 8.0 required for named parameters.'); + } + + WP_Mock::userFunction( + 'get_transient', + array( + 'times' => 1, + 'args' => array( + 'transient' => 'my-transient-name', + ), + 'return' => 'the-mocked-transient-value', + ) + ); + + /** @phpstan-ignore-next-line function "exists" */ + $this->assertEquals('the-mocked-transient-value', get_transient(transient: 'my-transient-name')); + } } From 79eb61a2f6e1fabc4da90add859a5e2a24d5f484 Mon Sep 17 00:00:00 2001 From: Brian Henry Date: Thu, 15 Jan 2026 17:36:41 -0800 Subject: [PATCH 2/6] Use `[]` array syntax --- tests/Integration/WP_MockTest.php | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/tests/Integration/WP_MockTest.php b/tests/Integration/WP_MockTest.php index f5e8c4c..fad4bb4 100644 --- a/tests/Integration/WP_MockTest.php +++ b/tests/Integration/WP_MockTest.php @@ -350,16 +350,13 @@ public function testCanMockNamedParameters(): void { $this->markTestSkipped('PHP 8.0 required for named parameters.'); } - WP_Mock::userFunction( - 'get_transient', - array( - 'times' => 1, - 'args' => array( - 'transient' => 'my-transient-name', - ), - 'return' => 'the-mocked-transient-value', - ) - ); + WP_Mock::userFunction('get_transient', [ + 'times' => 1, + 'args' => [ + 'transient' => 'my-transient-name', + ], + 'return' => 'the-mocked-transient-value', + ]); /** @phpstan-ignore-next-line function "exists" */ $this->assertEquals('the-mocked-transient-value', get_transient(transient: 'my-transient-name')); From d39cac068357023cf9d136538fa43fbaa2c03530 Mon Sep 17 00:00:00 2001 From: Brian Henry Date: Thu, 15 Jan 2026 17:41:16 -0800 Subject: [PATCH 3/6] Add `is_array()` check --- php/WP_Mock/Functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/php/WP_Mock/Functions.php b/php/WP_Mock/Functions.php index e7755a7..3e73424 100644 --- a/php/WP_Mock/Functions.php +++ b/php/WP_Mock/Functions.php @@ -97,7 +97,7 @@ public function flush(): void */ public function register(string $function, array $args = []) { - $functionArgs = isset($args['args']) ? $args['args'] : []; + $functionArgs = isset($args['args']) && is_array($args['args']) ? $args['args'] : []; $this->generateFunction($function, $functionArgs); if (empty($this->mockedFunctions[$function])) { From b179741a512f3658fbe544f8c4e3614e222e8773 Mon Sep 17 00:00:00 2001 From: Brian Henry Date: Thu, 15 Jan 2026 17:47:35 -0800 Subject: [PATCH 4/6] Add named parameters example --- docs/usage/using-wp-mock.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/usage/using-wp-mock.md b/docs/usage/using-wp-mock.md index 304a031..29fed4a 100644 --- a/docs/usage/using-wp-mock.md +++ b/docs/usage/using-wp-mock.md @@ -74,6 +74,16 @@ For example, the invocation below will set the expectation that the `get_permali WP_Mock::userFunction('get_permalink')->once()->with(42)->andReturn('https://example.com/foo'); ``` +To mock functions that are called using named parameters, e.g. `get_permalink(post: 42)`, you must pass the arguments as an associative array: + +```php +WP_Mock::userFunction('get_permalink', [ + 'args' => [ + 'post' => '42', + ], +])->once()->andReturn('https://example.com/foo'); +``` + ## Using expectations in arguments You can also pass an associative array of arguments to the second parameter of `WP_Mock::userFunction()` to set expectations about the function's arguments, the number of times it should be called, and what it should return. From 553b551c78953a0d54bb233bbac65654f29fce4e Mon Sep 17 00:00:00 2001 From: Brian Henry Date: Thu, 15 Jan 2026 17:54:59 -0800 Subject: [PATCH 5/6] Add array shapes --- php/WP_Mock/Functions.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/php/WP_Mock/Functions.php b/php/WP_Mock/Functions.php index 3e73424..c69c076 100644 --- a/php/WP_Mock/Functions.php +++ b/php/WP_Mock/Functions.php @@ -276,7 +276,7 @@ protected function setExpectedReturn(&$expectation, $return) * The declared function is namespace-aware. * * @param string $functionName function name - * @param string $functionArgs function arguments + * @param array $functionArgs function arguments * @return void * @throws InvalidArgumentException */ @@ -293,7 +293,7 @@ protected function generateFunction(string $functionName, array $functionArgs = * Creates a function using eval. * * @param string $functionName function name - * @param array $functionArgs function arguments, required only when calling mocked functions using named parameters + * @param array $functionArgs function arguments, required only when calling mocked functions using named parameters * @return bool true if this function created the mock, false otherwise */ protected function createFunction(string $functionName, array $functionArgs = []): bool From 8fe92ebc081058cc50431aea41050aeaf4d1b4e2 Mon Sep 17 00:00:00 2001 From: Brian Henry Date: Thu, 15 Jan 2026 17:55:23 -0800 Subject: [PATCH 6/6] Use `eval()` to hide code that fails on PHP 7.4 --- tests/Integration/WP_MockTest.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/Integration/WP_MockTest.php b/tests/Integration/WP_MockTest.php index fad4bb4..54916c3 100644 --- a/tests/Integration/WP_MockTest.php +++ b/tests/Integration/WP_MockTest.php @@ -358,7 +358,13 @@ public function testCanMockNamedParameters(): void { 'return' => 'the-mocked-transient-value', ]); + // Without this, tests fail on PHP 7.4. + // PHP Fatal error: Uncaught ParseError: syntax error, unexpected ':', expecting ')' in :362 + $hidePhp8CodeFromOlderVersions = <<<'PHP' + return get_transient(transient: 'my-transient-name'); + PHP; + /** @phpstan-ignore-next-line function "exists" */ - $this->assertEquals('the-mocked-transient-value', get_transient(transient: 'my-transient-name')); + $this->assertEquals('the-mocked-transient-value', eval($hidePhp8CodeFromOlderVersions)); } }