1 <?php
2 /*
3  * This file is part of the PHPUnit_MockObject package.
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 
11 /**
12  * Main matcher which defines a full expectation using method, parameter and
13  * invocation matchers.
14  * This matcher encapsulates all the other matchers and allows the builder to
15  * set the specific matchers when the appropriate methods are called (once(),
16  * where() etc.).
17  *
18  * All properties are public so that they can easily be accessed by the builder.
19  *
20  * @since Class available since Release 1.0.0
21  */
22 class PHPUnit_Framework_MockObject_Matcher implements PHPUnit_Framework_MockObject_Matcher_Invocation
23 {
24     /**
25      * @var PHPUnit_Framework_MockObject_Matcher_Invocation
26      */
27     public $invocationMatcher;
28 
29     /**
30      * @var mixed
31      */
32     public $afterMatchBuilderId = null;
33 
34     /**
35      * @var bool
36      */
37     public $afterMatchBuilderIsInvoked = false;
38 
39     /**
40      * @var PHPUnit_Framework_MockObject_Matcher_MethodName
41      */
42     public $methodNameMatcher = null;
43 
44     /**
45      * @var PHPUnit_Framework_MockObject_Matcher_Parameters
46      */
47     public $parametersMatcher = null;
48 
49     /**
50      * @var PHPUnit_Framework_MockObject_Stub
51      */
52     public $stub = null;
53 
54     /**
55      * @param PHPUnit_Framework_MockObject_Matcher_Invocation $invocationMatcher
56      */
57     public function __construct(PHPUnit_Framework_MockObject_Matcher_Invocation $invocationMatcher)
58     {
59         $this->invocationMatcher = $invocationMatcher;
60     }
61 
62     /**
63      * @return string
64      */
65     public function toString()
66     {
67         $list = [];
68 
69         if ($this->invocationMatcher !== null) {
70             $list[] = $this->invocationMatcher->toString();
71         }
72 
73         if ($this->methodNameMatcher !== null) {
74             $list[] = 'where ' . $this->methodNameMatcher->toString();
75         }
76 
77         if ($this->parametersMatcher !== null) {
78             $list[] = 'and ' . $this->parametersMatcher->toString();
79         }
80 
81         if ($this->afterMatchBuilderId !== null) {
82             $list[] = 'after ' . $this->afterMatchBuilderId;
83         }
84 
85         if ($this->stub !== null) {
86             $list[] = 'will ' . $this->stub->toString();
87         }
88 
89         return implode(' ', $list);
90     }
91 
92     /**
93      * @param PHPUnit_Framework_MockObject_Invocation $invocation
94      *
95      * @return mixed
96      */
97     public function invoked(PHPUnit_Framework_MockObject_Invocation $invocation)
98     {
99         if ($this->invocationMatcher === null) {
100             throw new PHPUnit_Framework_MockObject_RuntimeException(
101                 'No invocation matcher is set'
102             );
103         }
104 
105         if ($this->methodNameMatcher === null) {
106             throw new PHPUnit_Framework_MockObject_RuntimeException('No method matcher is set');
107         }
108 
109         if ($this->afterMatchBuilderId !== null) {
110             $builder = $invocation->object
111                                   ->__phpunit_getInvocationMocker()
112                                   ->lookupId($this->afterMatchBuilderId);
113 
114             if (!$builder) {
115                 throw new PHPUnit_Framework_MockObject_RuntimeException(
116                     sprintf(
117                         'No builder found for match builder identification <%s>',
118                         $this->afterMatchBuilderId
119                     )
120                 );
121             }
122 
123             $matcher = $builder->getMatcher();
124 
125             if ($matcher && $matcher->invocationMatcher->hasBeenInvoked()) {
126                 $this->afterMatchBuilderIsInvoked = true;
127             }
128         }
129 
130         $this->invocationMatcher->invoked($invocation);
131 
132         try {
133             if ($this->parametersMatcher !== null &&
134                 !$this->parametersMatcher->matches($invocation)) {
135                 $this->parametersMatcher->verify();
136             }
137         } catch (PHPUnit_Framework_ExpectationFailedException $e) {
138             throw new PHPUnit_Framework_ExpectationFailedException(
139                 sprintf(
140                     "Expectation failed for %s when %s\n%s",
141                     $this->methodNameMatcher->toString(),
142                     $this->invocationMatcher->toString(),
143                     $e->getMessage()
144                 ),
145                 $e->getComparisonFailure()
146             );
147         }
148 
149         if ($this->stub) {
150             return $this->stub->invoke($invocation);
151         }
152 
153         return $invocation->generateReturnValue();
154     }
155 
156     /**
157      * @param PHPUnit_Framework_MockObject_Invocation $invocation
158      *
159      * @return bool
160      */
161     public function matches(PHPUnit_Framework_MockObject_Invocation $invocation)
162     {
163         if ($this->afterMatchBuilderId !== null) {
164             $builder = $invocation->object
165                                   ->__phpunit_getInvocationMocker()
166                                   ->lookupId($this->afterMatchBuilderId);
167 
168             if (!$builder) {
169                 throw new PHPUnit_Framework_MockObject_RuntimeException(
170                     sprintf(
171                         'No builder found for match builder identification <%s>',
172                         $this->afterMatchBuilderId
173                     )
174                 );
175             }
176 
177             $matcher = $builder->getMatcher();
178 
179             if (!$matcher) {
180                 return false;
181             }
182 
183             if (!$matcher->invocationMatcher->hasBeenInvoked()) {
184                 return false;
185             }
186         }
187 
188         if ($this->invocationMatcher === null) {
189             throw new PHPUnit_Framework_MockObject_RuntimeException(
190                 'No invocation matcher is set'
191             );
192         }
193 
194         if ($this->methodNameMatcher === null) {
195             throw new PHPUnit_Framework_MockObject_RuntimeException('No method matcher is set');
196         }
197 
198         if (!$this->invocationMatcher->matches($invocation)) {
199             return false;
200         }
201 
202         try {
203             if (!$this->methodNameMatcher->matches($invocation)) {
204                 return false;
205             }
206         } catch (PHPUnit_Framework_ExpectationFailedException $e) {
207             throw new PHPUnit_Framework_ExpectationFailedException(
208                 sprintf(
209                     "Expectation failed for %s when %s\n%s",
210                     $this->methodNameMatcher->toString(),
211                     $this->invocationMatcher->toString(),
212                     $e->getMessage()
213                 ),
214                 $e->getComparisonFailure()
215             );
216         }
217 
218         return true;
219     }
220 
221     /**
222      * @throws PHPUnit_Framework_MockObject_RuntimeException
223      * @throws PHPUnit_Framework_ExpectationFailedException
224      */
225     public function verify()
226     {
227         if ($this->invocationMatcher === null) {
228             throw new PHPUnit_Framework_MockObject_RuntimeException(
229                 'No invocation matcher is set'
230             );
231         }
232 
233         if ($this->methodNameMatcher === null) {
234             throw new PHPUnit_Framework_MockObject_RuntimeException('No method matcher is set');
235         }
236 
237         try {
238             $this->invocationMatcher->verify();
239 
240             if ($this->parametersMatcher === null) {
241                 $this->parametersMatcher = new PHPUnit_Framework_MockObject_Matcher_AnyParameters;
242             }
243 
244             $invocationIsAny   = $this->invocationMatcher instanceof PHPUnit_Framework_MockObject_Matcher_AnyInvokedCount;
245             $invocationIsNever = $this->invocationMatcher instanceof PHPUnit_Framework_MockObject_Matcher_InvokedCount && $this->invocationMatcher->isNever();
246 
247             if (!$invocationIsAny && !$invocationIsNever) {
248                 $this->parametersMatcher->verify();
249             }
250         } catch (PHPUnit_Framework_ExpectationFailedException $e) {
251             throw new PHPUnit_Framework_ExpectationFailedException(
252                 sprintf(
253                     "Expectation failed for %s when %s.\n%s",
254                     $this->methodNameMatcher->toString(),
255                     $this->invocationMatcher->toString(),
256                     PHPUnit_Framework_TestFailure::exceptionToString($e)
257                 )
258             );
259         }
260     }
261 
262     /**
263      * @since Method available since Release 1.2.4
264      */
265     public function hasMatchers()
266     {
267         if ($this->invocationMatcher !== null &&
268             !$this->invocationMatcher instanceof PHPUnit_Framework_MockObject_Matcher_AnyInvokedCount) {
269             return true;
270         }
271 
272         return false;
273     }
274 }
275