Completed
Push — master ( b95514...65ac07 )
by Dave
02:35
created

Expectation::never()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
/**
3
 * Mockery
4
 *
5
 * LICENSE
6
 *
7
 * This source file is subject to the new BSD license that is bundled
8
 * with this package in the file LICENSE.txt.
9
 * It is also available through the world-wide-web at this URL:
10
 * http://github.com/padraic/mockery/blob/master/LICENSE
11
 * If you did not receive a copy of the license and are unable to
12
 * obtain it through the world-wide-web, please send an email
13
 * to [email protected] so we can send you a copy immediately.
14
 *
15
 * @category   Mockery
16
 * @package    Mockery
17
 * @copyright  Copyright (c) 2010-2014 Pádraic Brady (http://blog.astrumfutura.com)
18
 * @license    http://github.com/padraic/mockery/blob/master/LICENSE New BSD License
19
 */
20
21
namespace Mockery;
22
23
class Expectation implements ExpectationInterface
24
{
25
    /**
26
     * Mock object to which this expectation belongs
27
     *
28
     * @var object
29
     */
30
    protected $_mock = null;
31
32
    /**
33
     * Method name
34
     *
35
     * @var string
36
     */
37
    protected $_name = null;
38
39
    /**
40
     * Arguments expected by this expectation
41
     *
42
     * @var array
43
     */
44
    protected $_expectedArgs = array();
45
46
    /**
47
     * Count validator store
48
     *
49
     * @var array
50
     */
51
    protected $_countValidators = array();
52
53
    /**
54
     * The count validator class to use
55
     *
56
     * @var string
57
     */
58
    protected $_countValidatorClass = 'Mockery\CountValidator\Exact';
59
60
    /**
61
     * Actual count of calls to this expectation
62
     *
63
     * @var int
64
     */
65
    protected $_actualCount = 0;
66
67
    /**
68
     * Value to return from this expectation
69
     *
70
     * @var mixed
71
     */
72
    protected $_returnValue = null;
73
74
    /**
75
     * Array of return values as a queue for multiple return sequence
76
     *
77
     * @var array
78
     */
79
    protected $_returnQueue = array();
80
81
    /**
82
     * Array of closures executed with given arguments to generate a result
83
     * to be returned
84
     *
85
     * @var array
86
     */
87
    protected $_closureQueue = array();
88
89
    /**
90
     * Array of values to be set when this expectation matches
91
     *
92
     * @var array
93
     */
94
    protected $_setQueue = array();
95
96
    /**
97
     * Integer representing the call order of this expectation
98
     *
99
     * @var int
100
     */
101
    protected $_orderNumber = null;
102
103
    /**
104
     * Integer representing the call order of this expectation on a global basis
105
     *
106
     * @var int
107
     */
108
    protected $_globalOrderNumber = null;
109
110
    /**
111
     * Flag indicating that an exception is expected to be throw (not returned)
112
     *
113
     * @var bool
114
     */
115
    protected $_throw = false;
116
117
    /**
118
     * Flag indicating whether the order of calling is determined locally or
119
     * globally
120
     *
121
     * @var bool
122
     */
123
    protected $_globally = false;
124
125
    /**
126
     * Flag indicating we expect no arguments
127
     *
128
     * @var bool
129
     */
130
    protected $_noArgsExpectation = false;
131
132
    /**
133
     * Flag indicating if the return value should be obtained from the original
134
     * class method instead of returning predefined values from the return queue
135
     *
136
     * @var bool
137
     */
138
    protected $_passthru = false;
139
140
    /**
141
     * Constructor
142
     *
143
     * @param \Mockery\MockInterface $mock
144
     * @param string $name
145
     */
146 304
    public function __construct(\Mockery\MockInterface $mock, $name)
147
    {
148 304
        $this->_mock = $mock;
149 304
        $this->_name = $name;
150 304
    }
151
152
    /**
153
     * Return a string with the method name and arguments formatted
154
     *
155
     * @param string $name Name of the expected method
156
     * @param array $args List of arguments to the method
157
     * @return string
158
     */
159 45
    public function __toString()
160
    {
161 45
        return \Mockery::formatArgs($this->_name, $this->_expectedArgs);
162
    }
163
164
    /**
165
     * Verify the current call, i.e. that the given arguments match those
166
     * of this expectation
167
     *
168
     * @param array $args
169
     * @return mixed
170
     */
171 233
    public function verifyCall(array $args)
172
    {
173 233
        $this->validateOrder();
174 233
        $this->_actualCount++;
175 233
        if (true === $this->_passthru) {
176 3
            return $this->_mock->mockery_callSubjectMethod($this->_name, $args);
177
        }
178 231
        $return = $this->_getReturnValue($args);
179 231
        if ($return instanceof \Exception && $this->_throw === true) {
180 7
            throw $return;
181
        }
182 225
        $this->_setValues();
183 225
        return $return;
184
    }
185
186
    /**
187
     * Sets public properties with queued values to the mock object
188
     *
189
     * @param array $args
190
     * @return mixed
191
     */
192 225
    protected function _setValues()
193
    {
194 225
        foreach ($this->_setQueue as $name => &$values) {
195 8
            if (count($values) > 0) {
196 8
                $value = array_shift($values);
197 8
                $this->_mock->{$name} = $value;
198 8
            }
199 225
        }
200 225
    }
201
202
    /**
203
     * Fetch the return value for the matching args
204
     *
205
     * @param array $args
206
     * @return mixed
207
     */
208 231
    protected function _getReturnValue(array $args)
209
    {
210 231
        if (count($this->_closureQueue) > 1) {
211
            return call_user_func_array(array_shift($this->_closureQueue), $args);
212 231
        } elseif (count($this->_closureQueue) > 0) {
213 1
            return call_user_func_array(current($this->_closureQueue), $args);
214 230
        } elseif (count($this->_returnQueue) > 1) {
215 5
            return array_shift($this->_returnQueue);
216 229
        } elseif (count($this->_returnQueue) > 0) {
217 95
            return current($this->_returnQueue);
218
        }
219
220 135
        return $this->_mock->mockery_returnValueForMethod($this->_name);
221
    }
222
223
    /**
224
     * Checks if this expectation is eligible for additional calls
225
     *
226
     * @return bool
227
     */
228 274
    public function isEligible()
229
    {
230 274
        foreach ($this->_countValidators as $validator) {
231 139
            if (!$validator->isEligible($this->_actualCount)) {
232 23
                return false;
233
            }
234 271
        }
235 271
        return true;
236
    }
237
238
    /**
239
     * Check if there is a constraint on call count
240
     *
241
     * @return bool
242
     */
243
    public function isCallCountConstrained()
244
    {
245
        return (count($this->_countValidators) > 0);
246
    }
247
248
    /**
249
     * Verify call order
250
     *
251
     * @return void
252
     */
253 233
    public function validateOrder()
254
    {
255 233
        if ($this->_orderNumber) {
256 17
            $this->_mock->mockery_validateOrder((string) $this, $this->_orderNumber, $this->_mock);
257 17
        }
258 233
        if ($this->_globalOrderNumber) {
259 1
            $this->_mock->mockery_getContainer()
260 1
                ->mockery_validateOrder((string) $this, $this->_globalOrderNumber, $this->_mock);
261 1
        }
262 233
    }
263
264
    /**
265
     * Verify this expectation
266
     *
267
     * @return bool
268
     */
269 142
    public function verify()
270
    {
271 142
        foreach ($this->_countValidators as $validator) {
272 115
            $validator->validate($this->_actualCount);
273 126
        }
274 124
    }
275
276
    /**
277
     * Check if passed arguments match an argument expectation
278
     *
279
     * @param array $args
280
     * @return bool
281
     */
282 279
    public function matchArgs(array $args)
283
    {
284 279
        if (empty($this->_expectedArgs) && !$this->_noArgsExpectation) {
285 159
            return true;
286
        }
287 123
        $expectedArgsCount = count($this->_expectedArgs);
288 123
        if ($expectedArgsCount === 1 && ($this->_expectedArgs[0] instanceof \Mockery\Matcher\MultiArgumentClosure)) {
289 6
            if ($this->_matchArg($this->_expectedArgs[0], $args)) {
290 4
                return true;
291
            }
292 2
            return false;
293
        }
294 117
        if (count($args) !== $expectedArgsCount) {
295 8
            return false;
296
        }
297 111
        $argCount = count($args);
298 111
        for ($i=0; $i<$argCount; $i++) {
299 108
            $param =& $args[$i];
300 108
            if (!$this->_matchArg($this->_expectedArgs[$i], $param)) {
301 47
                return false;
302
            }
303 70
        }
304
305 73
        return true;
306
    }
307
308
    /**
309
     * Check if passed argument matches an argument expectation
310
     *
311
     * @param array $args
312
     * @return bool
313
     */
314 114
    protected function _matchArg($expected, &$actual)
315
    {
316 114
        if ($expected === $actual) {
317 31
            return true;
318
        }
319 95
        if (!is_object($expected) && !is_object($actual) && $expected == $actual) {
320 1
            return true;
321
        }
322 94
        if (is_string($expected) && !is_array($actual) && !is_object($actual)) {
323
            # push/pop an error handler here to to make sure no error/exception thrown if $expected is not a regex
324
            set_error_handler(function () {});
325 6
            $result = preg_match($expected, (string) $actual);
326 6
            restore_error_handler();
327
328 6
            if ($result) {
329 3
                return true;
330
            }
331 3
        }
332 93
        if (is_string($expected) && is_object($actual)) {
333 1
            $result = $actual instanceof $expected;
334 1
            if ($result) {
335 1
                return true;
336
            }
337
        }
338 92
        if ($expected instanceof \Mockery\Matcher\MatcherAbstract) {
339 70
            return $expected->match($actual);
340
        }
341 22
        if ($expected instanceof \Hamcrest\Matcher || $expected instanceof \Hamcrest_Matcher) {
0 ignored issues
show
Bug introduced by
The class Hamcrest_Matcher does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
342 3
            return $expected->matches($actual);
343
        }
344 19
        return false;
345
    }
346
347
    /**
348
     * Expected argument setter for the expectation
349
     *
350
     * @param mixed ...
351
     * @return self
352
     */
353 139
    public function with()
354
    {
355 139
        return $this->withArgs(func_get_args());
356
    }
357
358
    /**
359
     * Expected arguments for the expectation passed as an array
360
     *
361
     * @param array|\Closure $argsOrClosure
362
     * @return self
363
     */
364 151
    public function withArgs($argsOrClosure)
365
    {
366 151
        if (is_array($argsOrClosure)) {
367 144
            if (empty($argsOrClosure)) {
368 6
                return $this->withNoArgs();
369
            }
370 141
            $this->_expectedArgs = $argsOrClosure;
371 148
        } elseif (is_object($argsOrClosure) && ($argsOrClosure instanceof \Closure)) {
372 6
            $this->_expectedArgs = [new \Mockery\Matcher\MultiArgumentClosure($argsOrClosure)];
373 6
        } else {
374 1
            throw new \InvalidArgumentException(sprintf('Call to %s with an invalid argument (%s), only array and '.
375 1
                'closure are allowed', __METHOD__, $argsOrClosure));
376
        }
377 147
        $this->_noArgsExpectation = false;
378 147
        return $this;
379
    }
380
381
    /**
382
     * Set with() as no arguments expected
383
     *
384
     * @return self
385
     */
386 10
    public function withNoArgs()
387
    {
388 10
        $this->_noArgsExpectation = true;
389 10
        $this->_expectedArgs = null;
390 10
        return $this;
391
    }
392
393
    /**
394
     * Set expectation that any arguments are acceptable
395
     *
396
     * @return self
397
     */
398 2
    public function withAnyArgs()
399
    {
400 2
        $this->_expectedArgs = array();
401 2
        return $this;
402
    }
403
404
    /**
405
     * Set a return value, or sequential queue of return values
406
     *
407
     * @param mixed ...
408
     * @return self
409
     */
410 110
    public function andReturn()
411
    {
412 110
        $this->_returnQueue = func_get_args();
413 110
        return $this;
414
    }
415
416
    /**
417
     * Return this mock, like a fluent interface
418
     *
419
     * @return self
420
     */
421 1
    public function andReturnSelf()
422
    {
423 1
        return $this->andReturn($this->_mock);
424
    }
425
426
    /**
427
     * Set a sequential queue of return values with an array
428
     *
429
     * @param array $values
430
     * @return self
431
     */
432 2
    public function andReturnValues(array $values)
433
    {
434 2
        call_user_func_array(array($this, 'andReturn'), $values);
435 2
        return $this;
436
    }
437
438
    /**
439
     * Set a closure or sequence of closures with which to generate return
440
     * values. The arguments passed to the expected method are passed to the
441
     * closures as parameters.
442
     *
443
     * @param callable ...
444
     * @return self
445
     */
446 1
    public function andReturnUsing()
447
    {
448 1
        $this->_closureQueue = func_get_args();
449 1
        return $this;
450
    }
451
452
    /**
453
     * Return a self-returning black hole object.
454
     *
455
     * @return self
456
     */
457 1
    public function andReturnUndefined()
458
    {
459 1
        $this->andReturn(new \Mockery\Undefined);
460 1
        return $this;
461
    }
462
463
    /**
464
     * Return null. This is merely a language construct for Mock describing.
465
     *
466
     * @return self
467
     */
468 1
    public function andReturnNull()
469
    {
470 1
        return $this;
471
    }
472
473 1
    public function andReturnFalse()
474
    {
475 1
        return $this->andReturn(false);
476
    }
477
478 1
    public function andReturnTrue()
479
    {
480 1
        return $this->andReturn(true);
481
    }
482
483
    /**
484
     * Set Exception class and arguments to that class to be thrown
485
     *
486
     * @param string|\Exception $exception
487
     * @param string $message
488
     * @param int $code
489
     * @param \Exception $previous
490
     * @return self
491
     */
492 6
    public function andThrow($exception, $message = '', $code = 0, \Exception $previous = null)
493
    {
494 6
        $this->_throw = true;
495 6
        if (is_object($exception)) {
496 2
            $this->andReturn($exception);
497 2
        } else {
498 4
            $this->andReturn(new $exception($message, $code, $previous));
499
        }
500 6
        return $this;
501
    }
502
503
    /**
504
     * Set Exception classes to be thrown
505
     *
506
     * @param array $exceptions
507
     * @return self
508
     */
509 2
    public function andThrowExceptions(array $exceptions)
510
    {
511 2
        $this->_throw = true;
512 2
        foreach ($exceptions as $exception) {
513 2
            if (!is_object($exception)) {
514 1
                throw new Exception('You must pass an array of exception objects to andThrowExceptions');
515
            }
516 1
        }
517 1
        return $this->andReturnValues($exceptions);
518
    }
519
520
    /**
521
     * Register values to be set to a public property each time this expectation occurs
522
     *
523
     * @param string $name
524
     * @param mixed $value
525
     * @return self
526
     */
527 8
    public function andSet($name, $value)
528
    {
529 8
        $values = func_get_args();
530 8
        array_shift($values);
531 8
        $this->_setQueue[$name] = $values;
532 8
        return $this;
533
    }
534
535
    /**
536
     * Alias to andSet(). Allows the natural English construct
537
     * - set('foo', 'bar')->andReturn('bar')
538
     *
539
     * @param string $name
540
     * @param mixed $value
541
     * @return self
542
     */
543 3
    public function set($name, $value)
544
    {
545 3
        return call_user_func_array(array($this, 'andSet'), func_get_args());
546
    }
547
548
    /**
549
     * Indicates this expectation should occur zero or more times
550
     *
551
     * @return self
552
     */
553 2
    public function zeroOrMoreTimes()
554
    {
555 2
        $this->atLeast()->never();
556 2
    }
557
558
    /**
559
     * Indicates the number of times this expectation should occur
560
     *
561
     * @param int $limit
562
     * @throws InvalidArgumentException
563
     * @return self
564
     */
565 155
    public function times($limit = null)
566
    {
567 155
        if (is_null($limit)) {
568
            return $this;
569
        }
570 155
        if (!is_int($limit)) {
571 1
            throw new \InvalidArgumentException('The passed Times limit should be an integer value');
572
        }
573 154
        $this->_countValidators[] = new $this->_countValidatorClass($this, $limit);
574 154
        $this->_countValidatorClass = 'Mockery\CountValidator\Exact';
575 154
        return $this;
576
    }
577
578
    /**
579
     * Indicates that this expectation is never expected to be called
580
     *
581
     * @return self
582
     */
583 37
    public function never()
584
    {
585 37
        return $this->times(0);
586
    }
587
588
    /**
589
     * Indicates that this expectation is expected exactly once
590
     *
591
     * @return self
592
     */
593 106
    public function once()
594
    {
595 106
        return $this->times(1);
596
    }
597
598
    /**
599
     * Indicates that this expectation is expected exactly twice
600
     *
601
     * @return self
602
     */
603 18
    public function twice()
604
    {
605 18
        return $this->times(2);
606
    }
607
608
    /**
609
     * Sets next count validator to the AtLeast instance
610
     *
611
     * @return self
612
     */
613 13
    public function atLeast()
614
    {
615 13
        $this->_countValidatorClass = 'Mockery\CountValidator\AtLeast';
616 13
        return $this;
617
    }
618
619
    /**
620
     * Sets next count validator to the AtMost instance
621
     *
622
     * @return self
623
     */
624 7
    public function atMost()
625
    {
626 7
        $this->_countValidatorClass = 'Mockery\CountValidator\AtMost';
627 7
        return $this;
628
    }
629
630
    /**
631
     * Shorthand for setting minimum and maximum constraints on call counts
632
     *
633
     * @param int $minimum
634
     * @param int $maximum
635
     */
636
    public function between($minimum, $maximum)
637
    {
638
        return $this->atLeast()->times($minimum)->atMost()->times($maximum);
639
    }
640
641
    /**
642
     * Indicates that this expectation must be called in a specific given order
643
     *
644
     * @param string $group Name of the ordered group
645
     * @return self
646
     */
647 20
    public function ordered($group = null)
648
    {
649 20
        if ($this->_globally) {
650 1
            $this->_globalOrderNumber = $this->_defineOrdered($group, $this->_mock->mockery_getContainer());
651 1
        } else {
652 19
            $this->_orderNumber = $this->_defineOrdered($group, $this->_mock);
653
        }
654 20
        $this->_globally = false;
655 20
        return $this;
656
    }
657
658
    /**
659
     * Indicates call order should apply globally
660
     *
661
     * @return self
662
     */
663 1
    public function globally()
664
    {
665 1
        $this->_globally = true;
666 1
        return $this;
667
    }
668
669
    /**
670
     * Setup the ordering tracking on the mock or mock container
671
     *
672
     * @param string $group
673
     * @param object $ordering
674
     * @return int
675
     */
676 20
    protected function _defineOrdered($group, $ordering)
677
    {
678 20
        $groups = $ordering->mockery_getGroups();
679 20
        if (is_null($group)) {
680 19
            $result = $ordering->mockery_allocateOrder();
681 20
        } elseif (isset($groups[$group])) {
682 2
            $result = $groups[$group];
683 2
        } else {
684 4
            $result = $ordering->mockery_allocateOrder();
685 4
            $ordering->mockery_setGroup($group, $result);
686
        }
687 20
        return $result;
688
    }
689
690
    /**
691
     * Return order number
692
     *
693
     * @return int
694
     */
695 1
    public function getOrderNumber()
696
    {
697 1
        return $this->_orderNumber;
698
    }
699
700
    /**
701
     * Mark this expectation as being a default
702
     *
703
     * @return self
704
     */
705 24
    public function byDefault()
706
    {
707 24
        $director = $this->_mock->mockery_getExpectationsFor($this->_name);
708 24
        if (!empty($director)) {
709 24
            $director->makeExpectationDefault($this);
710 23
        }
711 23
        return $this;
712
    }
713
714
    /**
715
     * Return the parent mock of the expectation
716
     *
717
     * @return \Mockery\MockInterface
718
     */
719 28
    public function getMock()
720
    {
721 28
        return $this->_mock;
722
    }
723
724
    /**
725
     * Flag this expectation as calling the original class method with the
726
     * any provided arguments instead of using a return value queue.
727
     *
728
     * @return self
729
     */
730 3
    public function passthru()
731
    {
732 3
        if ($this->_mock instanceof Mock) {
733
            throw new Exception(
734
                'Mock Objects not created from a loaded/existing class are '
735
                . 'incapable of passing method calls through to a parent class'
736
            );
737
        }
738 3
        $this->_passthru = true;
739 3
        return $this;
740
    }
741
742
    /**
743
     * Cloning logic
744
     *
745
     */
746 8
    public function __clone()
747
    {
748 8
        $newValidators = array();
749 8
        $countValidators = $this->_countValidators;
750 8
        foreach ($countValidators as $validator) {
751 5
            $newValidators[] = clone $validator;
752 8
        }
753 8
        $this->_countValidators = $newValidators;
754 8
    }
755
756 5
    public function getName()
757
    {
758 5
        return $this->_name;
759
    }
760
}
761