Completed
Pull Request — master (#363)
by Olivier
01:26
created

MethodProphecy::shouldNotBeenCalled()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
3
/*
4
 * This file is part of the Prophecy.
5
 * (c) Konstantin Kudryashov <[email protected]>
6
 *     Marcello Duarte <[email protected]>
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 <[email protected]>
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);
0 ignored issues
show
Bug introduced by
It seems like $arguments defined by parameter $arguments on line 47 can also be of type object<Prophecy\Argument\ArgumentsWildcard>; however, Prophecy\Exception\Doubl...xception::__construct() does only seem to accept null|object<Prophecy\Exc...rgumentsWildcard>|array, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
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 View Code Duplication
    public function will($promise)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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 View Code Duplication
    public function should($prediction)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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
     * Sets call times prediction to the prophecy.
283
     *
284
     * @see \Prophecy\Prediction\CallTimesPrediction
285
     *
286
     * @return $this
287
     */
288
    public function shouldBeCalledOnce()
289
    {
290
        return $this->shouldBeCalledTimes(1);
291
    }
292
293
    /**
294
     * Sets call times prediction to the prophecy.
295
     *
296
     * @see \Prophecy\Prediction\CallTimesPrediction
297
     *
298
     * @return $this
299
     */
300
    public function shouldBeCalledTwice()
301
    {
302
        return $this->shouldBeCalledTimes(2);
303
    }
304
305
    /**
306
     * Checks provided prediction immediately.
307
     *
308
     * @param callable|Prediction\PredictionInterface $prediction
309
     *
310
     * @return $this
311
     *
312
     * @throws \Prophecy\Exception\InvalidArgumentException
313
     */
314
    public function shouldHave($prediction)
315
    {
316
        if (is_callable($prediction)) {
317
            $prediction = new Prediction\CallbackPrediction($prediction);
318
        }
319
320
        if (!$prediction instanceof Prediction\PredictionInterface) {
321
            throw new InvalidArgumentException(sprintf(
322
                'Expected callable or instance of PredictionInterface, but got %s.',
323
                gettype($prediction)
324
            ));
325
        }
326
327
        if (null === $this->promise && !$this->voidReturnType) {
328
            $this->willReturn();
329
        }
330
331
        $calls = $this->getObjectProphecy()->findProphecyMethodCalls(
332
            $this->getMethodName(),
333
            $this->getArgumentsWildcard()
334
        );
335
336
        try {
337
            $prediction->check($calls, $this->getObjectProphecy(), $this);
338
            $this->checkedPredictions[] = $prediction;
339
        } catch (\Exception $e) {
340
            $this->checkedPredictions[] = $prediction;
341
342
            throw $e;
343
        }
344
345
        return $this;
346
    }
347
348
    /**
349
     * Checks call prediction.
350
     *
351
     * @see \Prophecy\Prediction\CallPrediction
352
     *
353
     * @return $this
354
     */
355
    public function shouldHaveBeenCalled()
356
    {
357
        return $this->shouldHave(new Prediction\CallPrediction);
358
    }
359
360
    /**
361
     * Checks no calls prediction.
362
     *
363
     * @see \Prophecy\Prediction\NoCallsPrediction
364
     *
365
     * @return $this
366
     */
367
    public function shouldNotHaveBeenCalled()
368
    {
369
        return $this->shouldHave(new Prediction\NoCallsPrediction);
370
    }
371
372
    /**
373
     * Checks no calls prediction.
374
     *
375
     * @see \Prophecy\Prediction\NoCallsPrediction
376
     * @deprecated
377
     *
378
     * @return $this
379
     */
380
    public function shouldNotBeenCalled()
381
    {
382
        return $this->shouldNotHaveBeenCalled();
383
    }
384
385
    /**
386
     * Checks call times prediction.
387
     *
388
     * @see \Prophecy\Prediction\CallTimesPrediction
389
     *
390
     * @param int $count
391
     *
392
     * @return $this
393
     */
394
    public function shouldHaveBeenCalledTimes($count)
395
    {
396
        return $this->shouldHave(new Prediction\CallTimesPrediction($count));
397
    }
398
399
    /**
400
     * Checks call times prediction.
401
     *
402
     * @see \Prophecy\Prediction\CallTimesPrediction
403
     *
404
     * @return $this
405
     */
406
    public function shouldHaveBeenCalledOnce()
407
    {
408
        return $this->shouldHaveBeenCalledTimes(1);
409
    }
410
411
    /**
412
     * Checks call times prediction.
413
     *
414
     * @see \Prophecy\Prediction\CallTimesPrediction
415
     *
416
     * @return $this
417
     */
418
    public function shouldHaveBeenCalledTwice()
419
    {
420
        return $this->shouldHaveBeenCalledTimes(2);
421
    }
422
423
    /**
424
     * Checks currently registered [with should(...)] prediction.
425
     */
426
    public function checkPrediction()
427
    {
428
        if (null === $this->prediction) {
429
            return;
430
        }
431
432
        $this->shouldHave($this->prediction);
433
    }
434
435
    /**
436
     * Returns currently registered promise.
437
     *
438
     * @return null|Promise\PromiseInterface
439
     */
440
    public function getPromise()
441
    {
442
        return $this->promise;
443
    }
444
445
    /**
446
     * Returns currently registered prediction.
447
     *
448
     * @return null|Prediction\PredictionInterface
449
     */
450
    public function getPrediction()
451
    {
452
        return $this->prediction;
453
    }
454
455
    /**
456
     * Returns predictions that were checked on this object.
457
     *
458
     * @return Prediction\PredictionInterface[]
459
     */
460
    public function getCheckedPredictions()
461
    {
462
        return $this->checkedPredictions;
463
    }
464
465
    /**
466
     * Returns object prophecy this method prophecy is tied to.
467
     *
468
     * @return ObjectProphecy
469
     */
470
    public function getObjectProphecy()
471
    {
472
        return $this->objectProphecy;
473
    }
474
475
    /**
476
     * Returns method name.
477
     *
478
     * @return string
479
     */
480
    public function getMethodName()
481
    {
482
        return $this->methodName;
483
    }
484
485
    /**
486
     * Returns arguments wildcard.
487
     *
488
     * @return Argument\ArgumentsWildcard
489
     */
490
    public function getArgumentsWildcard()
491
    {
492
        return $this->argumentsWildcard;
493
    }
494
495
    /**
496
     * @return bool
497
     */
498
    public function hasReturnVoid()
499
    {
500
        return $this->voidReturnType;
501
    }
502
503
    private function bindToObjectProphecy()
504
    {
505
        if ($this->bound) {
506
            return;
507
        }
508
509
        $this->getObjectProphecy()->addMethodProphecy($this);
510
        $this->bound = true;
511
    }
512
}
513