Chris@14
|
1 <?php
|
Chris@14
|
2 /*
|
Chris@14
|
3 * This file is part of the phpunit-mock-objects package.
|
Chris@14
|
4 *
|
Chris@14
|
5 * (c) Sebastian Bergmann <sebastian@phpunit.de>
|
Chris@14
|
6 *
|
Chris@14
|
7 * For the full copyright and license information, please view the LICENSE
|
Chris@14
|
8 * file that was distributed with this source code.
|
Chris@14
|
9 */
|
Chris@14
|
10 namespace PHPUnit\Framework\MockObject;
|
Chris@14
|
11
|
Chris@14
|
12 use PHPUnit\Framework\ExpectationFailedException;
|
Chris@14
|
13 use PHPUnit\Framework\MockObject\Matcher\AnyInvokedCount;
|
Chris@14
|
14 use PHPUnit\Framework\MockObject\Matcher\AnyParameters;
|
Chris@14
|
15 use PHPUnit\Framework\MockObject\Matcher\Invocation as MatcherInvocation;
|
Chris@14
|
16 use PHPUnit\Framework\MockObject\Matcher\InvokedCount;
|
Chris@14
|
17 use PHPUnit\Framework\MockObject\Matcher\MethodName;
|
Chris@14
|
18 use PHPUnit\Framework\MockObject\Matcher\Parameters;
|
Chris@14
|
19 use PHPUnit\Framework\TestFailure;
|
Chris@14
|
20
|
Chris@14
|
21 /**
|
Chris@14
|
22 * Main matcher which defines a full expectation using method, parameter and
|
Chris@14
|
23 * invocation matchers.
|
Chris@14
|
24 * This matcher encapsulates all the other matchers and allows the builder to
|
Chris@14
|
25 * set the specific matchers when the appropriate methods are called (once(),
|
Chris@14
|
26 * where() etc.).
|
Chris@14
|
27 *
|
Chris@14
|
28 * All properties are public so that they can easily be accessed by the builder.
|
Chris@14
|
29 */
|
Chris@14
|
30 class Matcher implements MatcherInvocation
|
Chris@14
|
31 {
|
Chris@14
|
32 /**
|
Chris@14
|
33 * @var MatcherInvocation
|
Chris@14
|
34 */
|
Chris@14
|
35 private $invocationMatcher;
|
Chris@14
|
36
|
Chris@14
|
37 /**
|
Chris@14
|
38 * @var mixed
|
Chris@14
|
39 */
|
Chris@14
|
40 private $afterMatchBuilderId = null;
|
Chris@14
|
41
|
Chris@14
|
42 /**
|
Chris@14
|
43 * @var bool
|
Chris@14
|
44 */
|
Chris@14
|
45 private $afterMatchBuilderIsInvoked = false;
|
Chris@14
|
46
|
Chris@14
|
47 /**
|
Chris@14
|
48 * @var MethodName
|
Chris@14
|
49 */
|
Chris@14
|
50 private $methodNameMatcher = null;
|
Chris@14
|
51
|
Chris@14
|
52 /**
|
Chris@14
|
53 * @var Parameters
|
Chris@14
|
54 */
|
Chris@14
|
55 private $parametersMatcher = null;
|
Chris@14
|
56
|
Chris@14
|
57 /**
|
Chris@14
|
58 * @var Stub
|
Chris@14
|
59 */
|
Chris@14
|
60 private $stub = null;
|
Chris@14
|
61
|
Chris@14
|
62 /**
|
Chris@14
|
63 * @param MatcherInvocation $invocationMatcher
|
Chris@14
|
64 */
|
Chris@14
|
65 public function __construct(MatcherInvocation $invocationMatcher)
|
Chris@14
|
66 {
|
Chris@14
|
67 $this->invocationMatcher = $invocationMatcher;
|
Chris@14
|
68 }
|
Chris@14
|
69
|
Chris@14
|
70 public function hasMatchers(): bool
|
Chris@14
|
71 {
|
Chris@14
|
72 return $this->invocationMatcher !== null && !$this->invocationMatcher instanceof AnyInvokedCount;
|
Chris@14
|
73 }
|
Chris@14
|
74
|
Chris@14
|
75 public function hasMethodNameMatcher(): bool
|
Chris@14
|
76 {
|
Chris@14
|
77 return $this->methodNameMatcher !== null;
|
Chris@14
|
78 }
|
Chris@14
|
79
|
Chris@14
|
80 public function getMethodNameMatcher(): MethodName
|
Chris@14
|
81 {
|
Chris@14
|
82 return $this->methodNameMatcher;
|
Chris@14
|
83 }
|
Chris@14
|
84
|
Chris@14
|
85 public function setMethodNameMatcher(MethodName $matcher)
|
Chris@14
|
86 {
|
Chris@14
|
87 $this->methodNameMatcher = $matcher;
|
Chris@14
|
88 }
|
Chris@14
|
89
|
Chris@14
|
90 public function hasParametersMatcher(): bool
|
Chris@14
|
91 {
|
Chris@14
|
92 return $this->parametersMatcher !== null;
|
Chris@14
|
93 }
|
Chris@14
|
94
|
Chris@14
|
95 public function getParametersMatcher(): Parameters
|
Chris@14
|
96 {
|
Chris@14
|
97 return $this->parametersMatcher;
|
Chris@14
|
98 }
|
Chris@14
|
99
|
Chris@14
|
100 public function setParametersMatcher($matcher)
|
Chris@14
|
101 {
|
Chris@14
|
102 $this->parametersMatcher = $matcher;
|
Chris@14
|
103 }
|
Chris@14
|
104
|
Chris@14
|
105 public function setStub($stub)
|
Chris@14
|
106 {
|
Chris@14
|
107 $this->stub = $stub;
|
Chris@14
|
108 }
|
Chris@14
|
109
|
Chris@14
|
110 public function setAfterMatchBuilderId($id)
|
Chris@14
|
111 {
|
Chris@14
|
112 $this->afterMatchBuilderId = $id;
|
Chris@14
|
113 }
|
Chris@14
|
114
|
Chris@14
|
115 /**
|
Chris@14
|
116 * @param Invocation $invocation
|
Chris@14
|
117 *
|
Chris@14
|
118 * @return mixed
|
Chris@14
|
119 *
|
Chris@14
|
120 * @throws \Exception
|
Chris@14
|
121 * @throws RuntimeException
|
Chris@14
|
122 * @throws ExpectationFailedException
|
Chris@14
|
123 */
|
Chris@14
|
124 public function invoked(Invocation $invocation)
|
Chris@14
|
125 {
|
Chris@14
|
126 if ($this->invocationMatcher === null) {
|
Chris@14
|
127 throw new RuntimeException(
|
Chris@14
|
128 'No invocation matcher is set'
|
Chris@14
|
129 );
|
Chris@14
|
130 }
|
Chris@14
|
131
|
Chris@14
|
132 if ($this->methodNameMatcher === null) {
|
Chris@14
|
133 throw new RuntimeException('No method matcher is set');
|
Chris@14
|
134 }
|
Chris@14
|
135
|
Chris@14
|
136 if ($this->afterMatchBuilderId !== null) {
|
Chris@14
|
137 $builder = $invocation->getObject()
|
Chris@14
|
138 ->__phpunit_getInvocationMocker()
|
Chris@14
|
139 ->lookupId($this->afterMatchBuilderId);
|
Chris@14
|
140
|
Chris@14
|
141 if (!$builder) {
|
Chris@14
|
142 throw new RuntimeException(
|
Chris@14
|
143 \sprintf(
|
Chris@14
|
144 'No builder found for match builder identification <%s>',
|
Chris@14
|
145 $this->afterMatchBuilderId
|
Chris@14
|
146 )
|
Chris@14
|
147 );
|
Chris@14
|
148 }
|
Chris@14
|
149
|
Chris@14
|
150 $matcher = $builder->getMatcher();
|
Chris@14
|
151
|
Chris@14
|
152 if ($matcher && $matcher->invocationMatcher->hasBeenInvoked()) {
|
Chris@14
|
153 $this->afterMatchBuilderIsInvoked = true;
|
Chris@14
|
154 }
|
Chris@14
|
155 }
|
Chris@14
|
156
|
Chris@14
|
157 $this->invocationMatcher->invoked($invocation);
|
Chris@14
|
158
|
Chris@14
|
159 try {
|
Chris@14
|
160 if ($this->parametersMatcher !== null &&
|
Chris@14
|
161 !$this->parametersMatcher->matches($invocation)) {
|
Chris@14
|
162 $this->parametersMatcher->verify();
|
Chris@14
|
163 }
|
Chris@14
|
164 } catch (ExpectationFailedException $e) {
|
Chris@14
|
165 throw new ExpectationFailedException(
|
Chris@14
|
166 \sprintf(
|
Chris@14
|
167 "Expectation failed for %s when %s\n%s",
|
Chris@14
|
168 $this->methodNameMatcher->toString(),
|
Chris@14
|
169 $this->invocationMatcher->toString(),
|
Chris@14
|
170 $e->getMessage()
|
Chris@14
|
171 ),
|
Chris@14
|
172 $e->getComparisonFailure()
|
Chris@14
|
173 );
|
Chris@14
|
174 }
|
Chris@14
|
175
|
Chris@14
|
176 if ($this->stub) {
|
Chris@14
|
177 return $this->stub->invoke($invocation);
|
Chris@14
|
178 }
|
Chris@14
|
179
|
Chris@14
|
180 return $invocation->generateReturnValue();
|
Chris@14
|
181 }
|
Chris@14
|
182
|
Chris@14
|
183 /**
|
Chris@14
|
184 * @param Invocation $invocation
|
Chris@14
|
185 *
|
Chris@14
|
186 * @return bool
|
Chris@14
|
187 *
|
Chris@14
|
188 * @throws RuntimeException
|
Chris@14
|
189 * @throws ExpectationFailedException
|
Chris@14
|
190 */
|
Chris@14
|
191 public function matches(Invocation $invocation)
|
Chris@14
|
192 {
|
Chris@14
|
193 if ($this->afterMatchBuilderId !== null) {
|
Chris@14
|
194 $builder = $invocation->getObject()
|
Chris@14
|
195 ->__phpunit_getInvocationMocker()
|
Chris@14
|
196 ->lookupId($this->afterMatchBuilderId);
|
Chris@14
|
197
|
Chris@14
|
198 if (!$builder) {
|
Chris@14
|
199 throw new RuntimeException(
|
Chris@14
|
200 \sprintf(
|
Chris@14
|
201 'No builder found for match builder identification <%s>',
|
Chris@14
|
202 $this->afterMatchBuilderId
|
Chris@14
|
203 )
|
Chris@14
|
204 );
|
Chris@14
|
205 }
|
Chris@14
|
206
|
Chris@14
|
207 $matcher = $builder->getMatcher();
|
Chris@14
|
208
|
Chris@14
|
209 if (!$matcher) {
|
Chris@14
|
210 return false;
|
Chris@14
|
211 }
|
Chris@14
|
212
|
Chris@14
|
213 if (!$matcher->invocationMatcher->hasBeenInvoked()) {
|
Chris@14
|
214 return false;
|
Chris@14
|
215 }
|
Chris@14
|
216 }
|
Chris@14
|
217
|
Chris@14
|
218 if ($this->invocationMatcher === null) {
|
Chris@14
|
219 throw new RuntimeException(
|
Chris@14
|
220 'No invocation matcher is set'
|
Chris@14
|
221 );
|
Chris@14
|
222 }
|
Chris@14
|
223
|
Chris@14
|
224 if ($this->methodNameMatcher === null) {
|
Chris@14
|
225 throw new RuntimeException('No method matcher is set');
|
Chris@14
|
226 }
|
Chris@14
|
227
|
Chris@14
|
228 if (!$this->invocationMatcher->matches($invocation)) {
|
Chris@14
|
229 return false;
|
Chris@14
|
230 }
|
Chris@14
|
231
|
Chris@14
|
232 try {
|
Chris@14
|
233 if (!$this->methodNameMatcher->matches($invocation)) {
|
Chris@14
|
234 return false;
|
Chris@14
|
235 }
|
Chris@14
|
236 } catch (ExpectationFailedException $e) {
|
Chris@14
|
237 throw new ExpectationFailedException(
|
Chris@14
|
238 \sprintf(
|
Chris@14
|
239 "Expectation failed for %s when %s\n%s",
|
Chris@14
|
240 $this->methodNameMatcher->toString(),
|
Chris@14
|
241 $this->invocationMatcher->toString(),
|
Chris@14
|
242 $e->getMessage()
|
Chris@14
|
243 ),
|
Chris@14
|
244 $e->getComparisonFailure()
|
Chris@14
|
245 );
|
Chris@14
|
246 }
|
Chris@14
|
247
|
Chris@14
|
248 return true;
|
Chris@14
|
249 }
|
Chris@14
|
250
|
Chris@14
|
251 /**
|
Chris@14
|
252 * @throws RuntimeException
|
Chris@14
|
253 * @throws ExpectationFailedException
|
Chris@14
|
254 */
|
Chris@14
|
255 public function verify()
|
Chris@14
|
256 {
|
Chris@14
|
257 if ($this->invocationMatcher === null) {
|
Chris@14
|
258 throw new RuntimeException(
|
Chris@14
|
259 'No invocation matcher is set'
|
Chris@14
|
260 );
|
Chris@14
|
261 }
|
Chris@14
|
262
|
Chris@14
|
263 if ($this->methodNameMatcher === null) {
|
Chris@14
|
264 throw new RuntimeException('No method matcher is set');
|
Chris@14
|
265 }
|
Chris@14
|
266
|
Chris@14
|
267 try {
|
Chris@14
|
268 $this->invocationMatcher->verify();
|
Chris@14
|
269
|
Chris@14
|
270 if ($this->parametersMatcher === null) {
|
Chris@14
|
271 $this->parametersMatcher = new AnyParameters;
|
Chris@14
|
272 }
|
Chris@14
|
273
|
Chris@14
|
274 $invocationIsAny = $this->invocationMatcher instanceof AnyInvokedCount;
|
Chris@14
|
275 $invocationIsNever = $this->invocationMatcher instanceof InvokedCount && $this->invocationMatcher->isNever();
|
Chris@14
|
276
|
Chris@14
|
277 if (!$invocationIsAny && !$invocationIsNever) {
|
Chris@14
|
278 $this->parametersMatcher->verify();
|
Chris@14
|
279 }
|
Chris@14
|
280 } catch (ExpectationFailedException $e) {
|
Chris@14
|
281 throw new ExpectationFailedException(
|
Chris@14
|
282 \sprintf(
|
Chris@14
|
283 "Expectation failed for %s when %s.\n%s",
|
Chris@14
|
284 $this->methodNameMatcher->toString(),
|
Chris@14
|
285 $this->invocationMatcher->toString(),
|
Chris@14
|
286 TestFailure::exceptionToString($e)
|
Chris@14
|
287 )
|
Chris@14
|
288 );
|
Chris@14
|
289 }
|
Chris@14
|
290 }
|
Chris@14
|
291
|
Chris@14
|
292 /**
|
Chris@14
|
293 * @return string
|
Chris@14
|
294 */
|
Chris@14
|
295 public function toString()
|
Chris@14
|
296 {
|
Chris@14
|
297 $list = [];
|
Chris@14
|
298
|
Chris@14
|
299 if ($this->invocationMatcher !== null) {
|
Chris@14
|
300 $list[] = $this->invocationMatcher->toString();
|
Chris@14
|
301 }
|
Chris@14
|
302
|
Chris@14
|
303 if ($this->methodNameMatcher !== null) {
|
Chris@14
|
304 $list[] = 'where ' . $this->methodNameMatcher->toString();
|
Chris@14
|
305 }
|
Chris@14
|
306
|
Chris@14
|
307 if ($this->parametersMatcher !== null) {
|
Chris@14
|
308 $list[] = 'and ' . $this->parametersMatcher->toString();
|
Chris@14
|
309 }
|
Chris@14
|
310
|
Chris@14
|
311 if ($this->afterMatchBuilderId !== null) {
|
Chris@14
|
312 $list[] = 'after ' . $this->afterMatchBuilderId;
|
Chris@14
|
313 }
|
Chris@14
|
314
|
Chris@14
|
315 if ($this->stub !== null) {
|
Chris@14
|
316 $list[] = 'will ' . $this->stub->toString();
|
Chris@14
|
317 }
|
Chris@14
|
318
|
Chris@14
|
319 return \implode(' ', $list);
|
Chris@14
|
320 }
|
Chris@14
|
321 }
|