5c0ede9a66d79939e7319198578fb911bf11bdae
[yaffs-website] / vendor / phpspec / prophecy / src / Prophecy / Prophecy / MethodProphecy.php
1 <?php
2
3 /*
4  * This file is part of the Prophecy.
5  * (c) Konstantin Kudryashov <ever.zet@gmail.com>
6  *     Marcello Duarte <marcello.duarte@gmail.com>
7  *
8  * For the full copyright and license information, please view the LICENSE
9  * file that was distributed with this source code.
10  */
11
12 namespace Prophecy\Prophecy;
13
14 use Prophecy\Argument;
15 use Prophecy\Prophet;
16 use Prophecy\Promise;
17 use Prophecy\Prediction;
18 use Prophecy\Exception\Doubler\MethodNotFoundException;
19 use Prophecy\Exception\InvalidArgumentException;
20 use Prophecy\Exception\Prophecy\MethodProphecyException;
21
22 /**
23  * Method prophecy.
24  *
25  * @author Konstantin Kudryashov <ever.zet@gmail.com>
26  */
27 class MethodProphecy
28 {
29     private $objectProphecy;
30     private $methodName;
31     private $argumentsWildcard;
32     private $promise;
33     private $prediction;
34     private $checkedPredictions = array();
35     private $bound = false;
36     private $voidReturnType = false;
37
38     /**
39      * Initializes method prophecy.
40      *
41      * @param ObjectProphecy                        $objectProphecy
42      * @param string                                $methodName
43      * @param null|Argument\ArgumentsWildcard|array $arguments
44      *
45      * @throws \Prophecy\Exception\Doubler\MethodNotFoundException If method not found
46      */
47     public function __construct(ObjectProphecy $objectProphecy, $methodName, $arguments = null)
48     {
49         $double = $objectProphecy->reveal();
50         if (!method_exists($double, $methodName)) {
51             throw new MethodNotFoundException(sprintf(
52                 'Method `%s::%s()` is not defined.', get_class($double), $methodName
53             ), get_class($double), $methodName, $arguments);
54         }
55
56         $this->objectProphecy = $objectProphecy;
57         $this->methodName     = $methodName;
58
59         $reflectedMethod = new \ReflectionMethod($double, $methodName);
60         if ($reflectedMethod->isFinal()) {
61             throw new MethodProphecyException(sprintf(
62                 "Can not add prophecy for a method `%s::%s()`\n".
63                 "as it is a final method.",
64                 get_class($double),
65                 $methodName
66             ), $this);
67         }
68
69         if (null !== $arguments) {
70             $this->withArguments($arguments);
71         }
72
73         if (version_compare(PHP_VERSION, '7.0', '>=') && true === $reflectedMethod->hasReturnType()) {
74             $type = (string) $reflectedMethod->getReturnType();
75
76             if ('void' === $type) {
77                 $this->voidReturnType = true;
78                 return;
79             }
80
81             $this->will(function () use ($type) {
82                 switch ($type) {
83                     case 'string': return '';
84                     case 'float':  return 0.0;
85                     case 'int':    return 0;
86                     case 'bool':   return false;
87                     case 'array':  return array();
88
89                     case 'callable':
90                     case 'Closure':
91                         return function () {};
92
93                     case 'Traversable':
94                     case 'Generator':
95                         // Remove eval() when minimum version >=5.5
96                         /** @var callable $generator */
97                         $generator = eval('return function () { yield; };');
98                         return $generator();
99
100                     default:
101                         $prophet = new Prophet;
102                         return $prophet->prophesize($type)->reveal();
103                 }
104             });
105         }
106     }
107
108     /**
109      * Sets argument wildcard.
110      *
111      * @param array|Argument\ArgumentsWildcard $arguments
112      *
113      * @return $this
114      *
115      * @throws \Prophecy\Exception\InvalidArgumentException
116      */
117     public function withArguments($arguments)
118     {
119         if (is_array($arguments)) {
120             $arguments = new Argument\ArgumentsWildcard($arguments);
121         }
122
123         if (!$arguments instanceof Argument\ArgumentsWildcard) {
124             throw new InvalidArgumentException(sprintf(
125                 "Either an array or an instance of ArgumentsWildcard expected as\n".
126                 'a `MethodProphecy::withArguments()` argument, but got %s.',
127                 gettype($arguments)
128             ));
129         }
130
131         $this->argumentsWildcard = $arguments;
132
133         return $this;
134     }
135
136     /**
137      * Sets custom promise to the prophecy.
138      *
139      * @param callable|Promise\PromiseInterface $promise
140      *
141      * @return $this
142      *
143      * @throws \Prophecy\Exception\InvalidArgumentException
144      */
145     public function will($promise)
146     {
147         if (is_callable($promise)) {
148             $promise = new Promise\CallbackPromise($promise);
149         }
150
151         if (!$promise instanceof Promise\PromiseInterface) {
152             throw new InvalidArgumentException(sprintf(
153                 'Expected callable or instance of PromiseInterface, but got %s.',
154                 gettype($promise)
155             ));
156         }
157
158         $this->bindToObjectProphecy();
159         $this->promise = $promise;
160
161         return $this;
162     }
163
164     /**
165      * Sets return promise to the prophecy.
166      *
167      * @see Prophecy\Promise\ReturnPromise
168      *
169      * @return $this
170      */
171     public function willReturn()
172     {
173         if ($this->voidReturnType) {
174             throw new MethodProphecyException(
175                 "The method \"$this->methodName\" has a void return type, and so cannot return anything",
176                 $this
177             );
178         }
179
180         return $this->will(new Promise\ReturnPromise(func_get_args()));
181     }
182
183     /**
184      * Sets return argument promise to the prophecy.
185      *
186      * @param int $index The zero-indexed number of the argument to return
187      *
188      * @see Prophecy\Promise\ReturnArgumentPromise
189      *
190      * @return $this
191      */
192     public function willReturnArgument($index = 0)
193     {
194         if ($this->voidReturnType) {
195             throw new MethodProphecyException("The method \"$this->methodName\" has a void return type", $this);
196         }
197
198         return $this->will(new Promise\ReturnArgumentPromise($index));
199     }
200
201     /**
202      * Sets throw promise to the prophecy.
203      *
204      * @see Prophecy\Promise\ThrowPromise
205      *
206      * @param string|\Exception $exception Exception class or instance
207      *
208      * @return $this
209      */
210     public function willThrow($exception)
211     {
212         return $this->will(new Promise\ThrowPromise($exception));
213     }
214
215     /**
216      * Sets custom prediction to the prophecy.
217      *
218      * @param callable|Prediction\PredictionInterface $prediction
219      *
220      * @return $this
221      *
222      * @throws \Prophecy\Exception\InvalidArgumentException
223      */
224     public function should($prediction)
225     {
226         if (is_callable($prediction)) {
227             $prediction = new Prediction\CallbackPrediction($prediction);
228         }
229
230         if (!$prediction instanceof Prediction\PredictionInterface) {
231             throw new InvalidArgumentException(sprintf(
232                 'Expected callable or instance of PredictionInterface, but got %s.',
233                 gettype($prediction)
234             ));
235         }
236
237         $this->bindToObjectProphecy();
238         $this->prediction = $prediction;
239
240         return $this;
241     }
242
243     /**
244      * Sets call prediction to the prophecy.
245      *
246      * @see Prophecy\Prediction\CallPrediction
247      *
248      * @return $this
249      */
250     public function shouldBeCalled()
251     {
252         return $this->should(new Prediction\CallPrediction);
253     }
254
255     /**
256      * Sets no calls prediction to the prophecy.
257      *
258      * @see Prophecy\Prediction\NoCallsPrediction
259      *
260      * @return $this
261      */
262     public function shouldNotBeCalled()
263     {
264         return $this->should(new Prediction\NoCallsPrediction);
265     }
266
267     /**
268      * Sets call times prediction to the prophecy.
269      *
270      * @see Prophecy\Prediction\CallTimesPrediction
271      *
272      * @param $count
273      *
274      * @return $this
275      */
276     public function shouldBeCalledTimes($count)
277     {
278         return $this->should(new Prediction\CallTimesPrediction($count));
279     }
280
281     /**
282      * Checks provided prediction immediately.
283      *
284      * @param callable|Prediction\PredictionInterface $prediction
285      *
286      * @return $this
287      *
288      * @throws \Prophecy\Exception\InvalidArgumentException
289      */
290     public function shouldHave($prediction)
291     {
292         if (is_callable($prediction)) {
293             $prediction = new Prediction\CallbackPrediction($prediction);
294         }
295
296         if (!$prediction instanceof Prediction\PredictionInterface) {
297             throw new InvalidArgumentException(sprintf(
298                 'Expected callable or instance of PredictionInterface, but got %s.',
299                 gettype($prediction)
300             ));
301         }
302
303         if (null === $this->promise && !$this->voidReturnType) {
304             $this->willReturn();
305         }
306
307         $calls = $this->getObjectProphecy()->findProphecyMethodCalls(
308             $this->getMethodName(),
309             $this->getArgumentsWildcard()
310         );
311
312         try {
313             $prediction->check($calls, $this->getObjectProphecy(), $this);
314             $this->checkedPredictions[] = $prediction;
315         } catch (\Exception $e) {
316             $this->checkedPredictions[] = $prediction;
317
318             throw $e;
319         }
320
321         return $this;
322     }
323
324     /**
325      * Checks call prediction.
326      *
327      * @see Prophecy\Prediction\CallPrediction
328      *
329      * @return $this
330      */
331     public function shouldHaveBeenCalled()
332     {
333         return $this->shouldHave(new Prediction\CallPrediction);
334     }
335
336     /**
337      * Checks no calls prediction.
338      *
339      * @see Prophecy\Prediction\NoCallsPrediction
340      *
341      * @return $this
342      */
343     public function shouldNotHaveBeenCalled()
344     {
345         return $this->shouldHave(new Prediction\NoCallsPrediction);
346     }
347
348     /**
349      * Checks no calls prediction.
350      *
351      * @see Prophecy\Prediction\NoCallsPrediction
352      * @deprecated
353      *
354      * @return $this
355      */
356     public function shouldNotBeenCalled()
357     {
358         return $this->shouldNotHaveBeenCalled();
359     }
360
361     /**
362      * Checks call times prediction.
363      *
364      * @see Prophecy\Prediction\CallTimesPrediction
365      *
366      * @param int $count
367      *
368      * @return $this
369      */
370     public function shouldHaveBeenCalledTimes($count)
371     {
372         return $this->shouldHave(new Prediction\CallTimesPrediction($count));
373     }
374
375     /**
376      * Checks currently registered [with should(...)] prediction.
377      */
378     public function checkPrediction()
379     {
380         if (null === $this->prediction) {
381             return;
382         }
383
384         $this->shouldHave($this->prediction);
385     }
386
387     /**
388      * Returns currently registered promise.
389      *
390      * @return null|Promise\PromiseInterface
391      */
392     public function getPromise()
393     {
394         return $this->promise;
395     }
396
397     /**
398      * Returns currently registered prediction.
399      *
400      * @return null|Prediction\PredictionInterface
401      */
402     public function getPrediction()
403     {
404         return $this->prediction;
405     }
406
407     /**
408      * Returns predictions that were checked on this object.
409      *
410      * @return Prediction\PredictionInterface[]
411      */
412     public function getCheckedPredictions()
413     {
414         return $this->checkedPredictions;
415     }
416
417     /**
418      * Returns object prophecy this method prophecy is tied to.
419      *
420      * @return ObjectProphecy
421      */
422     public function getObjectProphecy()
423     {
424         return $this->objectProphecy;
425     }
426
427     /**
428      * Returns method name.
429      *
430      * @return string
431      */
432     public function getMethodName()
433     {
434         return $this->methodName;
435     }
436
437     /**
438      * Returns arguments wildcard.
439      *
440      * @return Argument\ArgumentsWildcard
441      */
442     public function getArgumentsWildcard()
443     {
444         return $this->argumentsWildcard;
445     }
446
447     /**
448      * @return bool
449      */
450     public function hasReturnVoid()
451     {
452         return $this->voidReturnType;
453     }
454
455     private function bindToObjectProphecy()
456     {
457         if ($this->bound) {
458             return;
459         }
460
461         $this->getObjectProphecy()->addMethodProphecy($this);
462         $this->bound = true;
463     }
464 }