PageRenderTime 85ms CodeModel.GetById 31ms app.highlight 47ms RepoModel.GetById 1ms app.codeStats 0ms

/library/Mockery/Mock.php

http://github.com/padraic/mockery
PHP | 968 lines | 522 code | 120 blank | 326 comment | 72 complexity | 6e2c75227df8f9bf14c2c9648714a771 MD5 | raw file
  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 padraic@php.net so we can send you a copy immediately.
 14 *
 15 * @category   Mockery
 16 * @package    Mockery
 17 * @copyright  Copyright (c) 2010 Pádraic Brady (http://blog.astrumfutura.com)
 18 * @license    http://github.com/padraic/mockery/blob/master/LICENSE New BSD License
 19 */
 20
 21namespace Mockery;
 22
 23use Mockery\HigherOrderMessage;
 24use Mockery\MockInterface;
 25use Mockery\LegacyMockInterface;
 26use Mockery\ExpectsHigherOrderMessage;
 27use Mockery\Exception\BadMethodCallException;
 28
 29class Mock implements MockInterface
 30{
 31    /**
 32     * Stores an array of all expectation directors for this mock
 33     *
 34     * @var array
 35     */
 36    protected $_mockery_expectations = array();
 37
 38    /**
 39     * Stores an initial number of expectations that can be manipulated
 40     * while using the getter method.
 41     *
 42     * @var int
 43     */
 44    protected $_mockery_expectations_count = 0;
 45
 46    /**
 47     * Flag to indicate whether we can ignore method calls missing from our
 48     * expectations
 49     *
 50     * @var bool
 51     */
 52    protected $_mockery_ignoreMissing = false;
 53
 54    /**
 55     * Flag to indicate whether we can defer method calls missing from our
 56     * expectations
 57     *
 58     * @var bool
 59     */
 60    protected $_mockery_deferMissing = false;
 61
 62    /**
 63     * Flag to indicate whether this mock was verified
 64     *
 65     * @var bool
 66     */
 67    protected $_mockery_verified = false;
 68
 69    /**
 70     * Given name of the mock
 71     *
 72     * @var string
 73     */
 74    protected $_mockery_name = null;
 75
 76    /**
 77     * Order number of allocation
 78     *
 79     * @var int
 80     */
 81    protected $_mockery_allocatedOrder = 0;
 82
 83    /**
 84     * Current ordered number
 85     *
 86     * @var int
 87     */
 88    protected $_mockery_currentOrder = 0;
 89
 90    /**
 91     * Ordered groups
 92     *
 93     * @var array
 94     */
 95    protected $_mockery_groups = array();
 96
 97    /**
 98     * Mock container containing this mock object
 99     *
100     * @var \Mockery\Container
101     */
102    protected $_mockery_container = null;
103
104    /**
105     * Instance of a core object on which methods are called in the event
106     * it has been set, and an expectation for one of the object's methods
107     * does not exist. This implements a simple partial mock proxy system.
108     *
109     * @var object
110     */
111    protected $_mockery_partial = null;
112
113    /**
114     * Flag to indicate we should ignore all expectations temporarily. Used
115     * mainly to prevent expectation matching when in the middle of a mock
116     * object recording session.
117     *
118     * @var bool
119     */
120    protected $_mockery_disableExpectationMatching = false;
121
122    /**
123     * Stores all stubbed public methods separate from any on-object public
124     * properties that may exist.
125     *
126     * @var array
127     */
128    protected $_mockery_mockableProperties = array();
129
130    /**
131     * @var array
132     */
133    protected $_mockery_mockableMethods = array();
134
135    /**
136     * Just a local cache for this mock's target's methods
137     *
138     * @var \ReflectionMethod[]
139     */
140    protected static $_mockery_methods;
141
142    protected $_mockery_allowMockingProtectedMethods = false;
143
144    protected $_mockery_receivedMethodCalls;
145
146    /**
147     * If shouldIgnoreMissing is called, this value will be returned on all calls to missing methods
148     * @var mixed
149     */
150    protected $_mockery_defaultReturnValue = null;
151
152    /**
153     * Tracks internally all the bad method call exceptions that happened during runtime
154     *
155     * @var array
156     */
157    protected $_mockery_thrownExceptions = [];
158
159    protected $_mockery_instanceMock = true;
160
161    /**
162     * We want to avoid constructors since class is copied to Generator.php
163     * for inclusion on extending class definitions.
164     *
165     * @param \Mockery\Container $container
166     * @param object $partialObject
167     * @param bool $instanceMock
168     * @return void
169     */
170    public function mockery_init(\Mockery\Container $container = null, $partialObject = null, $instanceMock = true)
171    {
172        if (is_null($container)) {
173            $container = new \Mockery\Container();
174        }
175        $this->_mockery_container = $container;
176        if (!is_null($partialObject)) {
177            $this->_mockery_partial = $partialObject;
178        }
179
180        if (!\Mockery::getConfiguration()->mockingNonExistentMethodsAllowed()) {
181            foreach ($this->mockery_getMethods() as $method) {
182                if ($method->isPublic()) {
183                    $this->_mockery_mockableMethods[] = $method->getName();
184                }
185            }
186        }
187
188        $this->_mockery_instanceMock = $instanceMock;
189    }
190
191    /**
192     * Set expected method calls
193     *
194     * @param mixed ...$methodNames one or many methods that are expected to be called in this mock
195     *
196     * @return \Mockery\ExpectationInterface|\Mockery\Expectation|\Mockery\HigherOrderMessage
197     */
198    public function shouldReceive(...$methodNames)
199    {
200        if (count($methodNames) === 0) {
201            return new HigherOrderMessage($this, "shouldReceive");
202        }
203
204        foreach ($methodNames as $method) {
205            if ("" == $method) {
206                throw new \InvalidArgumentException("Received empty method name");
207            }
208        }
209
210        $self = $this;
211        $allowMockingProtectedMethods = $this->_mockery_allowMockingProtectedMethods;
212
213        $lastExpectation = \Mockery::parseShouldReturnArgs(
214            $this,
215            $methodNames,
216            function ($method) use ($self, $allowMockingProtectedMethods) {
217                $rm = $self->mockery_getMethod($method);
218                if ($rm) {
219                    if ($rm->isPrivate()) {
220                        throw new \InvalidArgumentException("$method() cannot be mocked as it is a private method");
221                    }
222                    if (!$allowMockingProtectedMethods && $rm->isProtected()) {
223                        throw new \InvalidArgumentException("$method() cannot be mocked as it is a protected method and mocking protected methods is not enabled for the currently used mock object. Use shouldAllowMockingProtectedMethods() to enable mocking of protected methods.");
224                    }
225                }
226
227                $director = $self->mockery_getExpectationsFor($method);
228                if (!$director) {
229                    $director = new \Mockery\ExpectationDirector($method, $self);
230                    $self->mockery_setExpectationsFor($method, $director);
231                }
232                $expectation = new \Mockery\Expectation($self, $method);
233                $director->addExpectation($expectation);
234                return $expectation;
235            }
236        );
237        return $lastExpectation;
238    }
239
240    // start method allows
241    /**
242     * @param mixed $something  String method name or map of method => return
243     * @return self|\Mockery\ExpectationInterface|\Mockery\Expectation|\Mockery\HigherOrderMessage
244     */
245    public function allows($something = [])
246    {
247        if (is_string($something)) {
248            return $this->shouldReceive($something);
249        }
250
251        if (empty($something)) {
252            return $this->shouldReceive();
253        }
254
255        foreach ($something as $method => $returnValue) {
256            $this->shouldReceive($method)->andReturn($returnValue);
257        }
258
259        return $this;
260    }
261    // end method allows
262
263    // start method expects
264    /**
265    /**
266     * @param mixed $something  String method name (optional)
267     * @return \Mockery\ExpectationInterface|\Mockery\Expectation|ExpectsHigherOrderMessage
268     */
269    public function expects($something = null)
270    {
271        if (is_string($something)) {
272            return $this->shouldReceive($something)->once();
273        }
274
275        return new ExpectsHigherOrderMessage($this);
276    }
277    // end method expects
278
279    /**
280     * Shortcut method for setting an expectation that a method should not be called.
281     *
282     * @param array ...$methodNames one or many methods that are expected not to be called in this mock
283     * @return \Mockery\ExpectationInterface|\Mockery\Expectation|\Mockery\HigherOrderMessage
284     */
285    public function shouldNotReceive(...$methodNames)
286    {
287        if (count($methodNames) === 0) {
288            return new HigherOrderMessage($this, "shouldNotReceive");
289        }
290
291        $expectation = call_user_func_array(array($this, 'shouldReceive'), $methodNames);
292        $expectation->never();
293        return $expectation;
294    }
295
296    /**
297     * Allows additional methods to be mocked that do not explicitly exist on mocked class
298     * @param String $method name of the method to be mocked
299     * @return Mock
300     */
301    public function shouldAllowMockingMethod($method)
302    {
303        $this->_mockery_mockableMethods[] = $method;
304        return $this;
305    }
306
307    /**
308     * Set mock to ignore unexpected methods and return Undefined class
309     * @param mixed $returnValue the default return value for calls to missing functions on this mock
310     * @return Mock
311     */
312    public function shouldIgnoreMissing($returnValue = null)
313    {
314        $this->_mockery_ignoreMissing = true;
315        $this->_mockery_defaultReturnValue = $returnValue;
316        return $this;
317    }
318
319    public function asUndefined()
320    {
321        $this->_mockery_ignoreMissing = true;
322        $this->_mockery_defaultReturnValue = new \Mockery\Undefined();
323        return $this;
324    }
325
326    /**
327     * @return Mock
328     */
329    public function shouldAllowMockingProtectedMethods()
330    {
331        if (!\Mockery::getConfiguration()->mockingNonExistentMethodsAllowed()) {
332            foreach ($this->mockery_getMethods() as $method) {
333                if ($method->isProtected()) {
334                    $this->_mockery_mockableMethods[] = $method->getName();
335                }
336            }
337        }
338
339        $this->_mockery_allowMockingProtectedMethods = true;
340        return $this;
341    }
342
343
344    /**
345     * Set mock to defer unexpected methods to it's parent
346     *
347     * This is particularly useless for this class, as it doesn't have a parent,
348     * but included for completeness
349     *
350     * @deprecated 2.0.0 Please use makePartial() instead
351     *
352     * @return Mock
353     */
354    public function shouldDeferMissing()
355    {
356        return $this->makePartial();
357    }
358
359    /**
360     * Set mock to defer unexpected methods to it's parent
361     *
362     * It was an alias for shouldDeferMissing(), which will be removed
363     * in 2.0.0.
364     *
365     * @return Mock
366     */
367    public function makePartial()
368    {
369        $this->_mockery_deferMissing = true;
370        return $this;
371    }
372
373    /**
374     * In the event shouldReceive() accepting one or more methods/returns,
375     * this method will switch them from normal expectations to default
376     * expectations
377     *
378     * @return self
379     */
380    public function byDefault()
381    {
382        foreach ($this->_mockery_expectations as $director) {
383            $exps = $director->getExpectations();
384            foreach ($exps as $exp) {
385                $exp->byDefault();
386            }
387        }
388        return $this;
389    }
390
391    /**
392     * Capture calls to this mock
393     */
394    public function __call($method, array $args)
395    {
396        return $this->_mockery_handleMethodCall($method, $args);
397    }
398
399    public static function __callStatic($method, array $args)
400    {
401        return self::_mockery_handleStaticMethodCall($method, $args);
402    }
403
404    /**
405     * Forward calls to this magic method to the __call method
406     */
407    public function __toString()
408    {
409        return $this->__call('__toString', array());
410    }
411
412    /**
413     * Iterate across all expectation directors and validate each
414     *
415     * @throws \Mockery\CountValidator\Exception
416     * @return void
417     */
418    public function mockery_verify()
419    {
420        if ($this->_mockery_verified) {
421            return;
422        }
423        if (isset($this->_mockery_ignoreVerification)
424            && $this->_mockery_ignoreVerification == true) {
425            return;
426        }
427        $this->_mockery_verified = true;
428        foreach ($this->_mockery_expectations as $director) {
429            $director->verify();
430        }
431    }
432
433    /**
434     * Gets a list of exceptions thrown by this mock
435     *
436     * @return array
437     */
438    public function mockery_thrownExceptions()
439    {
440        return $this->_mockery_thrownExceptions;
441    }
442
443    /**
444     * Tear down tasks for this mock
445     *
446     * @return void
447     */
448    public function mockery_teardown()
449    {
450    }
451
452    /**
453     * Fetch the next available allocation order number
454     *
455     * @return int
456     */
457    public function mockery_allocateOrder()
458    {
459        $this->_mockery_allocatedOrder += 1;
460        return $this->_mockery_allocatedOrder;
461    }
462
463    /**
464     * Set ordering for a group
465     *
466     * @param mixed $group
467     * @param int $order
468     */
469    public function mockery_setGroup($group, $order)
470    {
471        $this->_mockery_groups[$group] = $order;
472    }
473
474    /**
475     * Fetch array of ordered groups
476     *
477     * @return array
478     */
479    public function mockery_getGroups()
480    {
481        return $this->_mockery_groups;
482    }
483
484    /**
485     * Set current ordered number
486     *
487     * @param int $order
488     */
489    public function mockery_setCurrentOrder($order)
490    {
491        $this->_mockery_currentOrder = $order;
492        return $this->_mockery_currentOrder;
493    }
494
495    /**
496     * Get current ordered number
497     *
498     * @return int
499     */
500    public function mockery_getCurrentOrder()
501    {
502        return $this->_mockery_currentOrder;
503    }
504
505    /**
506     * Validate the current mock's ordering
507     *
508     * @param string $method
509     * @param int $order
510     * @throws \Mockery\Exception
511     * @return void
512     */
513    public function mockery_validateOrder($method, $order)
514    {
515        if ($order < $this->_mockery_currentOrder) {
516            $exception = new \Mockery\Exception\InvalidOrderException(
517                'Method ' . __CLASS__ . '::' . $method . '()'
518                . ' called out of order: expected order '
519                . $order . ', was ' . $this->_mockery_currentOrder
520            );
521            $exception->setMock($this)
522                ->setMethodName($method)
523                ->setExpectedOrder($order)
524                ->setActualOrder($this->_mockery_currentOrder);
525            throw $exception;
526        }
527        $this->mockery_setCurrentOrder($order);
528    }
529
530    /**
531     * Gets the count of expectations for this mock
532     *
533     * @return int
534     */
535    public function mockery_getExpectationCount()
536    {
537        $count = $this->_mockery_expectations_count;
538        foreach ($this->_mockery_expectations as $director) {
539            $count += $director->getExpectationCount();
540        }
541        return $count;
542    }
543
544    /**
545     * Return the expectations director for the given method
546     *
547     * @var string $method
548     * @return \Mockery\ExpectationDirector|null
549     */
550    public function mockery_setExpectationsFor($method, \Mockery\ExpectationDirector $director)
551    {
552        $this->_mockery_expectations[$method] = $director;
553    }
554
555    /**
556     * Return the expectations director for the given method
557     *
558     * @var string $method
559     * @return \Mockery\ExpectationDirector|null
560     */
561    public function mockery_getExpectationsFor($method)
562    {
563        if (isset($this->_mockery_expectations[$method])) {
564            return $this->_mockery_expectations[$method];
565        }
566    }
567
568    /**
569     * Find an expectation matching the given method and arguments
570     *
571     * @var string $method
572     * @var array $args
573     * @return \Mockery\Expectation|null
574     */
575    public function mockery_findExpectation($method, array $args)
576    {
577        if (!isset($this->_mockery_expectations[$method])) {
578            return null;
579        }
580        $director = $this->_mockery_expectations[$method];
581
582        return $director->findExpectation($args);
583    }
584
585    /**
586     * Return the container for this mock
587     *
588     * @return \Mockery\Container
589     */
590    public function mockery_getContainer()
591    {
592        return $this->_mockery_container;
593    }
594
595    /**
596     * Return the name for this mock
597     *
598     * @return string
599     */
600    public function mockery_getName()
601    {
602        return __CLASS__;
603    }
604
605    /**
606     * @return array
607     */
608    public function mockery_getMockableProperties()
609    {
610        return $this->_mockery_mockableProperties;
611    }
612
613    public function __isset($name)
614    {
615        if (false === stripos($name, '_mockery_') && method_exists(get_parent_class($this), '__isset')) {
616            return call_user_func('parent::__isset', $name);
617        }
618
619        return false;
620    }
621
622    public function mockery_getExpectations()
623    {
624        return $this->_mockery_expectations;
625    }
626
627    /**
628     * Calls a parent class method and returns the result. Used in a passthru
629     * expectation where a real return value is required while still taking
630     * advantage of expectation matching and call count verification.
631     *
632     * @param string $name
633     * @param array $args
634     * @return mixed
635     */
636    public function mockery_callSubjectMethod($name, array $args)
637    {
638        if (!method_exists($this, $name) && method_exists(get_parent_class($this), '__call')) {
639            return call_user_func('parent::__call', $name, $args);
640        }
641        return call_user_func_array('parent::' . $name, $args);
642    }
643
644    /**
645     * @return string[]
646     */
647    public function mockery_getMockableMethods()
648    {
649        return $this->_mockery_mockableMethods;
650    }
651
652    /**
653     * @return bool
654     */
655    public function mockery_isAnonymous()
656    {
657        $rfc = new \ReflectionClass($this);
658
659        // HHVM has a Stringish interface
660        $interfaces = array_filter($rfc->getInterfaces(), function ($i) {
661            return $i->getName() !== "Stringish";
662        });
663        $onlyImplementsMock = 2 == count($interfaces);
664
665        return (false === $rfc->getParentClass()) && $onlyImplementsMock;
666    }
667
668    public function mockery_isInstance()
669    {
670        return $this->_mockery_instanceMock;
671    }
672
673    public function __wakeup()
674    {
675        /**
676         * This does not add __wakeup method support. It's a blind method and any
677         * expected __wakeup work will NOT be performed. It merely cuts off
678         * annoying errors where a __wakeup exists but is not essential when
679         * mocking
680         */
681    }
682
683    public function __destruct()
684    {
685        /**
686         * Overrides real class destructor in case if class was created without original constructor
687         */
688    }
689
690    public function mockery_getMethod($name)
691    {
692        foreach ($this->mockery_getMethods() as $method) {
693            if ($method->getName() == $name) {
694                return $method;
695            }
696        }
697
698        return null;
699    }
700
701    /**
702     * @param string $name Method name.
703     *
704     * @return mixed Generated return value based on the declared return value of the named method.
705     */
706    public function mockery_returnValueForMethod($name)
707    {
708        if (version_compare(PHP_VERSION, '7.0.0-dev') < 0) {
709            return;
710        }
711
712        $rm = $this->mockery_getMethod($name);
713        if (!$rm || !$rm->hasReturnType()) {
714            return;
715        }
716
717        $returnType = $rm->getReturnType();
718
719        // Default return value for methods with nullable type is null
720        if ($returnType->allowsNull()) {
721            return null;
722        }
723
724        $type = PHP_VERSION_ID >= 70100 ? $returnType->getName() : (string) $returnType;
725        switch ($type) {
726            case '':       return;
727            case 'string': return '';
728            case 'int':    return 0;
729            case 'float':  return 0.0;
730            case 'bool':   return false;
731            case 'array':  return [];
732
733            case 'callable':
734            case 'Closure':
735                return function () {
736                };
737
738            case 'Traversable':
739            case 'Generator':
740                // Remove eval() when minimum version >=5.5
741                $generator = eval('return function () { yield; };');
742                return $generator();
743
744            case 'self':
745                return \Mockery::mock($rm->getDeclaringClass()->getName());
746
747            case 'void':
748                return null;
749
750            case 'object':
751                if (version_compare(PHP_VERSION, '7.2.0-dev') >= 0) {
752                    return \Mockery::mock();
753                }
754
755            default:
756                return \Mockery::mock($type);
757        }
758    }
759
760    public function shouldHaveReceived($method = null, $args = null)
761    {
762        if ($method === null) {
763            return new HigherOrderMessage($this, "shouldHaveReceived");
764        }
765
766        $expectation = new \Mockery\VerificationExpectation($this, $method);
767        if (null !== $args) {
768            $expectation->withArgs($args);
769        }
770        $expectation->atLeast()->once();
771        $director = new \Mockery\VerificationDirector($this->_mockery_getReceivedMethodCalls(), $expectation);
772        $this->_mockery_expectations_count++;
773        $director->verify();
774        return $director;
775    }
776
777    public function shouldHaveBeenCalled()
778    {
779        return $this->shouldHaveReceived("__invoke");
780    }
781
782    public function shouldNotHaveReceived($method = null, $args = null)
783    {
784        if ($method === null) {
785            return new HigherOrderMessage($this, "shouldNotHaveReceived");
786        }
787
788        $expectation = new \Mockery\VerificationExpectation($this, $method);
789        if (null !== $args) {
790            $expectation->withArgs($args);
791        }
792        $expectation->never();
793        $director = new \Mockery\VerificationDirector($this->_mockery_getReceivedMethodCalls(), $expectation);
794        $this->_mockery_expectations_count++;
795        $director->verify();
796        return null;
797    }
798
799    public function shouldNotHaveBeenCalled(array $args = null)
800    {
801        return $this->shouldNotHaveReceived("__invoke", $args);
802    }
803
804    protected static function _mockery_handleStaticMethodCall($method, array $args)
805    {
806        $associatedRealObject = \Mockery::fetchMock(__CLASS__);
807        try {
808            return $associatedRealObject->__call($method, $args);
809        } catch (BadMethodCallException $e) {
810            throw new BadMethodCallException(
811                'Static method ' . $associatedRealObject->mockery_getName() . '::' . $method
812                . '() does not exist on this mock object',
813                null,
814                $e
815            );
816        }
817    }
818
819    protected function _mockery_getReceivedMethodCalls()
820    {
821        return $this->_mockery_receivedMethodCalls ?: $this->_mockery_receivedMethodCalls = new \Mockery\ReceivedMethodCalls();
822    }
823
824    /**
825     * Called when an instance Mock was created and its constructor is getting called
826     *
827     * @see \Mockery\Generator\StringManipulation\Pass\InstanceMockPass
828     * @param array $args
829     */
830    protected function _mockery_constructorCalled(array $args)
831    {
832        if (!isset($this->_mockery_expectations['__construct']) /* _mockery_handleMethodCall runs the other checks */) {
833            return;
834        }
835        $this->_mockery_handleMethodCall('__construct', $args);
836    }
837
838    protected function _mockery_findExpectedMethodHandler($method)
839    {
840        if (isset($this->_mockery_expectations[$method])) {
841            return $this->_mockery_expectations[$method];
842        }
843
844        $lowerCasedMockeryExpectations = array_change_key_case($this->_mockery_expectations, CASE_LOWER);
845        $lowerCasedMethod = strtolower($method);
846
847        if (isset($lowerCasedMockeryExpectations[$lowerCasedMethod])) {
848            return $lowerCasedMockeryExpectations[$lowerCasedMethod];
849        }
850
851        return null;
852    }
853
854    protected function _mockery_handleMethodCall($method, array $args)
855    {
856        $this->_mockery_getReceivedMethodCalls()->push(new \Mockery\MethodCall($method, $args));
857
858        $rm = $this->mockery_getMethod($method);
859        if ($rm && $rm->isProtected() && !$this->_mockery_allowMockingProtectedMethods) {
860            if ($rm->isAbstract()) {
861                return;
862            }
863
864            try {
865                $prototype = $rm->getPrototype();
866                if ($prototype->isAbstract()) {
867                    return;
868                }
869            } catch (\ReflectionException $re) {
870                // noop - there is no hasPrototype method
871            }
872
873            return call_user_func_array("parent::$method", $args);
874        }
875
876        $handler = $this->_mockery_findExpectedMethodHandler($method);
877
878        if ($handler !== null && !$this->_mockery_disableExpectationMatching) {
879            try {
880                return $handler->call($args);
881            } catch (\Mockery\Exception\NoMatchingExpectationException $e) {
882                if (!$this->_mockery_ignoreMissing && !$this->_mockery_deferMissing) {
883                    throw $e;
884                }
885            }
886        }
887
888        if (!is_null($this->_mockery_partial) &&
889            (method_exists($this->_mockery_partial, $method) || method_exists($this->_mockery_partial, '__call'))
890        ) {
891            return call_user_func_array(array($this->_mockery_partial, $method), $args);
892        } elseif ($this->_mockery_deferMissing && is_callable("parent::$method")
893            && (!$this->hasMethodOverloadingInParentClass() || method_exists(get_parent_class($this), $method))) {
894            return call_user_func_array("parent::$method", $args);
895        } elseif ($this->_mockery_deferMissing && method_exists(get_parent_class($this), '__call')) {
896            return call_user_func('parent::__call', $method, $args);
897        } elseif ($method == '__toString') {
898            // __toString is special because we force its addition to the class API regardless of the
899            // original implementation.  Thus, we should always return a string rather than honor
900            // _mockery_ignoreMissing and break the API with an error.
901            return sprintf("%s#%s", __CLASS__, spl_object_hash($this));
902        } elseif ($this->_mockery_ignoreMissing) {
903            if (\Mockery::getConfiguration()->mockingNonExistentMethodsAllowed() || (method_exists($this->_mockery_partial, $method) || is_callable("parent::$method"))) {
904                if ($this->_mockery_defaultReturnValue instanceof \Mockery\Undefined) {
905                    return call_user_func_array(array($this->_mockery_defaultReturnValue, $method), $args);
906                } elseif (null === $this->_mockery_defaultReturnValue) {
907                    return $this->mockery_returnValueForMethod($method);
908                }
909
910                return $this->_mockery_defaultReturnValue;
911            }
912        }
913
914        $message = 'Method ' . __CLASS__ . '::' . $method .
915            '() does not exist on this mock object';
916
917        if (!is_null($rm)) {
918            $message = 'Received ' . __CLASS__ .
919                '::' . $method . '(), but no expectations were specified';
920        }
921
922        $bmce = new BadMethodCallException($message);
923        $this->_mockery_thrownExceptions[] = $bmce;
924        throw $bmce;
925    }
926
927    /**
928     * Uses reflection to get the list of all
929     * methods within the current mock object
930     *
931     * @return array
932     */
933    protected function mockery_getMethods()
934    {
935        if (static::$_mockery_methods && \Mockery::getConfiguration()->reflectionCacheEnabled()) {
936            return static::$_mockery_methods;
937        }
938
939        if (isset($this->_mockery_partial)) {
940            $reflected = new \ReflectionObject($this->_mockery_partial);
941        } else {
942            $reflected = new \ReflectionClass($this);
943        }
944
945        return static::$_mockery_methods = $reflected->getMethods();
946    }
947
948    private function hasMethodOverloadingInParentClass()
949    {
950        // if there's __call any name would be callable
951        return is_callable('parent::aFunctionNameThatNoOneWouldEverUseInRealLife12345');
952    }
953
954    /**
955     * @return array
956     */
957    private function getNonPublicMethods()
958    {
959        return array_map(
960            function ($method) {
961                return $method->getName();
962            },
963            array_filter($this->mockery_getMethods(), function ($method) {
964                return !$method->isPublic();
965            })
966        );
967    }
968}