Skip to content

Commit 3d1f278

Browse files
committed
rename global to built-in
1 parent 5d60822 commit 3d1f278

1 file changed

Lines changed: 92 additions & 0 deletions

File tree

src/BuiltInFunctionMock.php

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
<?php
2+
3+
namespace MintyPHP\Mocking;
4+
5+
use Exception;
6+
use PHPUnit\Framework\ExpectationFailedException;
7+
use PHPUnit\Framework\TestCase;
8+
use Throwable;
9+
10+
class BuiltInFunctionMock
11+
{
12+
// Static properties
13+
/** @var array<string,BuiltInFunctionMock> */
14+
public static array $mocks = [];
15+
16+
// Instance properties
17+
/** @var string */
18+
private string $affectedNamespace;
19+
/** @var TestCase */
20+
private TestCase $testCase;
21+
/** @var array<int,array{function:string,arguments:array<int,mixed>,returns:mixed,exception:?Throwable}> $expectations*/
22+
private array $expectations;
23+
24+
/**
25+
* Register a static mock for the given class name.
26+
* @param string $affectedNamespace The namespace where the built-in function is used and should be mocked
27+
* @param TestCase $testCase The PHPUnit test case
28+
*/
29+
public function __construct(string $affectedNamespace, TestCase $testCase)
30+
{
31+
$this->affectedNamespace = $affectedNamespace;
32+
$this->testCase = $testCase;
33+
$this->expectations = [];
34+
self::$mocks[$this->affectedNamespace] = $this;
35+
}
36+
37+
/**
38+
* Expect a with specific body (exact match).
39+
* @param string $function The function name
40+
* @param array<int,mixed> $arguments The arguments to expect
41+
* @param mixed $returns The return value if not void
42+
* @param ?Throwable $exception An optional exception to throw
43+
*/
44+
public function expect(string $function, array $arguments, mixed $returns = null, ?Throwable $exception = null): void
45+
{
46+
$namespace = $this->affectedNamespace;
47+
if (!function_exists("$namespace\\$function")) {
48+
eval("namespace $namespace { function $function() { return \\MintyPHP\\Mocking\\BuiltInFunctionMock::handleFunctionCall('$namespace','$function',func_get_args()); } }");
49+
}
50+
$this->expectations[] = [
51+
'function' => $function,
52+
'arguments' => $arguments,
53+
'returns' => $returns,
54+
'exception' => $exception,
55+
];
56+
}
57+
58+
/** Assert that all expectations were met. */
59+
public function assertExpectationsMet(): void
60+
{
61+
if (!empty($this->expectations)) {
62+
$this->testCase->fail(sprintf('Not all expectations met for %s, %d remaining', $this->affectedNamespace, count($this->expectations)));
63+
}
64+
}
65+
66+
/**
67+
* Handle a static call to a mocked class.
68+
* @param string $namespace The namespace the function is called from
69+
* @param string $function The built-in function name that is called
70+
* @param array<int,mixed> $arguments The arguments passed to the function
71+
* @return mixed The return value
72+
* @throws Exception If no mock is registered or expectation fails
73+
* @throws ExpectationFailedException If expectation fails
74+
*/
75+
public static function handleFunctionCall(string $namespace, string $function, array $arguments): mixed
76+
{
77+
if (!isset(self::$mocks[$namespace])) {
78+
throw new Exception(sprintf('No mock registered for function: %s', $namespace));
79+
}
80+
$mock = self::$mocks[$namespace];
81+
if (empty($mock->expectations)) {
82+
$mock->testCase->fail(sprintf('No expectations left for %s', $function));
83+
}
84+
$expected = array_shift($mock->expectations);
85+
$mock->testCase->assertEquals($expected['function'], $function, 'Unexpected function called');
86+
$mock->testCase->assertEquals($expected['arguments'], $arguments, sprintf('Arguments mismatch for %s', $function));
87+
if ($expected['exception'] !== null) {
88+
throw $expected['exception'];
89+
}
90+
return $expected['returns'];
91+
}
92+
}

0 commit comments

Comments
 (0)