Skip to content

Commit 6b0c3a2

Browse files
Closes #6498
1 parent 75a22aa commit 6b0c3a2

File tree

7 files changed

+160
-87
lines changed

7 files changed

+160
-87
lines changed

src/Framework/MockObject/Runtime/AbstractInvocationImplementation.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ final public function willThrowException(Throwable $exception): InvocationStubbe
218218

219219
final public function seal(): void
220220
{
221-
$this->invocationHandler->seal($this->invocationHandler->isMockObject());
221+
$this->invocationHandler->seal();
222222
}
223223

224224
/**

src/Framework/MockObject/Runtime/Api/Method.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
namespace PHPUnit\Framework\MockObject;
1111

1212
use PHPUnit\Framework\Constraint\Constraint;
13-
use PHPUnit\Framework\MockObject\Rule\AnyInvokedCount;
1413
use PHPUnit\Framework\MockObject\Runtime\PropertyHook;
1514

1615
/**
@@ -26,7 +25,7 @@ public function method(Constraint|PropertyHook|string $constraint): InvocationSt
2625
{
2726
return $this
2827
->__phpunit_getInvocationHandler()
29-
->expects(new AnyInvokedCount)
28+
->configureStub()
3029
->method($constraint);
3130
}
3231
}

src/Framework/MockObject/Runtime/Api/MockObjectApi.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
*/
1010
namespace PHPUnit\Framework\MockObject;
1111

12+
use function assert;
1213
use PHPUnit\Framework\MockObject\Rule\InvocationOrder;
1314

1415
/**
@@ -42,6 +43,10 @@ abstract public function __phpunit_unsetInvocationMocker(): void;
4243

4344
public function expects(InvocationOrder $matcher): InvocationMocker
4445
{
45-
return $this->__phpunit_getInvocationHandler()->expects($matcher);
46+
$handler = $this->__phpunit_getInvocationHandler();
47+
48+
assert($handler instanceof MockObjectInvocationHandler);
49+
50+
return $handler->expects($matcher);
4651
}
4752
}

src/Framework/MockObject/Runtime/Api/TestDoubleState.php

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,17 @@ public function invocationHandler(): InvocationHandler
4040
return $this->invocationHandler;
4141
}
4242

43-
$this->invocationHandler = new InvocationHandler(
44-
$this->configurableMethods,
45-
$this->generateReturnValues,
46-
$this->isMockObject,
47-
);
43+
if ($this->isMockObject) {
44+
$this->invocationHandler = new MockObjectInvocationHandler(
45+
$this->configurableMethods,
46+
$this->generateReturnValues,
47+
);
48+
} else {
49+
$this->invocationHandler = new TestStubInvocationHandler(
50+
$this->configurableMethods,
51+
$this->generateReturnValues,
52+
);
53+
}
4854

4955
return $this->invocationHandler;
5056
}

src/Framework/MockObject/Runtime/InvocationHandler.php

Lines changed: 23 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -10,23 +10,23 @@
1010
namespace PHPUnit\Framework\MockObject;
1111

1212
use function array_any;
13-
use function array_unique;
14-
use function array_values;
15-
use function in_array;
1613
use function strtolower;
1714
use Exception;
18-
use PHPUnit\Framework\MockObject\Rule\InvocationOrder;
19-
use PHPUnit\Framework\MockObject\Rule\InvokedCount;
20-
use PHPUnit\Framework\MockObject\Rule\MethodName;
15+
use PHPUnit\Framework\MockObject\Rule\AnyInvokedCount;
2116
use Throwable;
2217

2318
/**
2419
* @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit
2520
*
2621
* @internal This class is not covered by the backward compatibility promise for PHPUnit
2722
*/
28-
final class InvocationHandler
23+
abstract class InvocationHandler
2924
{
25+
/**
26+
* @var list<ConfigurableMethod>
27+
*/
28+
protected readonly array $configurableMethods;
29+
3030
/**
3131
* @var list<Matcher>
3232
*/
@@ -36,28 +36,16 @@ final class InvocationHandler
3636
* @var array<non-empty-string, Matcher>
3737
*/
3838
private array $matcherMap = [];
39-
40-
/**
41-
* @var list<ConfigurableMethod>
42-
*/
43-
private readonly array $configurableMethods;
4439
private readonly bool $returnValueGeneration;
45-
private readonly bool $isMockObject;
4640
private bool $sealed = false;
4741

4842
/**
4943
* @param list<ConfigurableMethod> $configurableMethods
5044
*/
51-
public function __construct(array $configurableMethods, bool $returnValueGeneration, bool $isMockObject = false)
45+
public function __construct(array $configurableMethods, bool $returnValueGeneration)
5246
{
5347
$this->configurableMethods = $configurableMethods;
5448
$this->returnValueGeneration = $returnValueGeneration;
55-
$this->isMockObject = $isMockObject;
56-
}
57-
58-
public function isMockObject(): bool
59-
{
60-
return $this->isMockObject;
6149
}
6250

6351
public function hasMatchers(): bool
@@ -98,22 +86,15 @@ public function registerMatcher(string $id, Matcher $matcher): void
9886
/**
9987
* @throws TestDoubleSealedException
10088
*/
101-
public function expects(InvocationOrder $rule): InvocationMocker|InvocationStubber
89+
public function configureStub(): InvocationStubber
10290
{
103-
if ($this->sealed) {
91+
if ($this->isSealed()) {
10492
throw new TestDoubleSealedException;
10593
}
10694

107-
$matcher = new Matcher($rule);
108-
$this->addMatcher($matcher);
95+
$matcher = new Matcher(new AnyInvokedCount);
10996

110-
if ($this->isMockObject) {
111-
return new InvocationMockerImplementation(
112-
$this,
113-
$matcher,
114-
...$this->configurableMethods,
115-
);
116-
}
97+
$this->addMatcher($matcher);
11798

11899
return new InvocationStubberImplementation(
119100
$this,
@@ -176,64 +157,28 @@ public function verify(): void
176157
}
177158
}
178159

179-
public function seal(bool $isMockObject): void
180-
{
181-
if ($this->sealed) {
182-
return;
183-
}
184-
185-
$this->sealed = true;
186-
187-
if (!$isMockObject) {
188-
return;
189-
}
190-
191-
$configuredMethods = $this->configuredMethodNames();
192-
193-
foreach ($this->configurableMethods as $method) {
194-
if (!in_array($method->name(), $configuredMethods, true)) {
195-
$matcher = new Matcher(new InvokedCount(0));
196-
197-
$matcher->setMethodNameRule(new MethodName($method->name()));
198-
199-
$this->addMatcher($matcher);
200-
}
201-
}
202-
}
203-
204160
public function isSealed(): bool
205161
{
206162
return $this->sealed;
207163
}
208164

209-
private function addMatcher(Matcher $matcher): void
165+
abstract public function seal(): void;
166+
167+
protected function addMatcher(Matcher $matcher): void
210168
{
211169
$this->matchers[] = $matcher;
212170
}
213171

172+
protected function markSealed(): void
173+
{
174+
$this->sealed = true;
175+
}
176+
214177
/**
215-
* Returns the list of method names that have been configured with expectations.
216-
* Only considers exact string matches for method names.
217-
* Methods with any() expectation are considered configured.
218-
*
219-
* @return list<non-empty-string>
178+
* @return list<Matcher>
220179
*/
221-
private function configuredMethodNames(): array
180+
protected function matchers(): array
222181
{
223-
$names = [];
224-
225-
foreach ($this->matchers as $matcher) {
226-
if (!$matcher->hasMethodNameRule()) {
227-
continue;
228-
}
229-
230-
foreach ($this->configurableMethods as $method) {
231-
if ($matcher->methodNameRule()->matchesName($method->name())) {
232-
$names[] = $method->name();
233-
}
234-
}
235-
}
236-
237-
return array_values(array_unique($names));
182+
return $this->matchers;
238183
}
239184
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
<?php declare(strict_types=1);
2+
/*
3+
* This file is part of PHPUnit.
4+
*
5+
* (c) Sebastian Bergmann <sebastian@phpunit.de>
6+
*
7+
* For the full copyright and license information, please view the LICENSE
8+
* file that was distributed with this source code.
9+
*/
10+
namespace PHPUnit\Framework\MockObject;
11+
12+
use function array_unique;
13+
use function array_values;
14+
use function in_array;
15+
use PHPUnit\Framework\MockObject\Rule\InvocationOrder;
16+
use PHPUnit\Framework\MockObject\Rule\InvokedCount;
17+
use PHPUnit\Framework\MockObject\Rule\MethodName;
18+
19+
/**
20+
* @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit
21+
*
22+
* @internal This class is not covered by the backward compatibility promise for PHPUnit
23+
*/
24+
final class MockObjectInvocationHandler extends InvocationHandler
25+
{
26+
/**
27+
* @throws TestDoubleSealedException
28+
*/
29+
public function expects(InvocationOrder $rule): InvocationMocker
30+
{
31+
if ($this->isSealed()) {
32+
throw new TestDoubleSealedException;
33+
}
34+
35+
$matcher = new Matcher($rule);
36+
$this->addMatcher($matcher);
37+
38+
return new InvocationMockerImplementation(
39+
$this,
40+
$matcher,
41+
...$this->configurableMethods,
42+
);
43+
}
44+
45+
public function seal(): void
46+
{
47+
if ($this->isSealed()) {
48+
return;
49+
}
50+
51+
$this->markSealed();
52+
53+
$configuredMethods = $this->configuredMethodNames();
54+
55+
foreach ($this->configurableMethods as $method) {
56+
if (!in_array($method->name(), $configuredMethods, true)) {
57+
$matcher = new Matcher(new InvokedCount(0));
58+
59+
$matcher->setMethodNameRule(new MethodName($method->name()));
60+
61+
$this->addMatcher($matcher);
62+
}
63+
}
64+
}
65+
66+
/**
67+
* Returns the list of method names that have been configured with expectations.
68+
* Only considers exact string matches for method names.
69+
* Methods with any() expectation are considered configured.
70+
*
71+
* @return list<non-empty-string>
72+
*/
73+
private function configuredMethodNames(): array
74+
{
75+
$names = [];
76+
77+
foreach ($this->matchers() as $matcher) {
78+
if (!$matcher->hasMethodNameRule()) {
79+
continue;
80+
}
81+
82+
foreach ($this->configurableMethods as $method) {
83+
if ($matcher->methodNameRule()->matchesName($method->name())) {
84+
$names[] = $method->name();
85+
}
86+
}
87+
}
88+
89+
return array_values(array_unique($names));
90+
}
91+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php declare(strict_types=1);
2+
/*
3+
* This file is part of PHPUnit.
4+
*
5+
* (c) Sebastian Bergmann <sebastian@phpunit.de>
6+
*
7+
* For the full copyright and license information, please view the LICENSE
8+
* file that was distributed with this source code.
9+
*/
10+
namespace PHPUnit\Framework\MockObject;
11+
12+
/**
13+
* @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit
14+
*
15+
* @internal This class is not covered by the backward compatibility promise for PHPUnit
16+
*/
17+
final class TestStubInvocationHandler extends InvocationHandler
18+
{
19+
public function seal(): void
20+
{
21+
if ($this->isSealed()) {
22+
return;
23+
}
24+
25+
$this->markSealed();
26+
}
27+
}

0 commit comments

Comments
 (0)