/vendor/mockery/mockery/library/Mockery/Expectation.php
PHP | 757 lines | 352 code | 66 blank | 339 comment | 43 complexity | 4d488931882f870fbdfe982cd2b7c898 MD5 | raw file
- <?php
- /**
- * Mockery
- *
- * LICENSE
- *
- * This source file is subject to the new BSD license that is bundled
- * with this package in the file LICENSE.txt.
- * It is also available through the world-wide-web at this URL:
- * http://github.com/padraic/mockery/blob/master/LICENSE
- * If you did not receive a copy of the license and are unable to
- * obtain it through the world-wide-web, please send an email
- * to padraic@php.net so we can send you a copy immediately.
- *
- * @category Mockery
- * @package Mockery
- * @copyright Copyright (c) 2010-2014 Pádraic Brady (http://blog.astrumfutura.com)
- * @license http://github.com/padraic/mockery/blob/master/LICENSE New BSD License
- */
- namespace Mockery;
- class Expectation implements ExpectationInterface
- {
- /**
- * Mock object to which this expectation belongs
- *
- * @var object
- */
- protected $_mock = null;
- /**
- * Method name
- *
- * @var string
- */
- protected $_name = null;
- /**
- * Arguments expected by this expectation
- *
- * @var array
- */
- protected $_expectedArgs = array();
- /**
- * Count validator store
- *
- * @var array
- */
- protected $_countValidators = array();
- /**
- * The count validator class to use
- *
- * @var string
- */
- protected $_countValidatorClass = 'Mockery\CountValidator\Exact';
- /**
- * Actual count of calls to this expectation
- *
- * @var int
- */
- protected $_actualCount = 0;
- /**
- * Value to return from this expectation
- *
- * @var mixed
- */
- protected $_returnValue = null;
- /**
- * Array of return values as a queue for multiple return sequence
- *
- * @var array
- */
- protected $_returnQueue = array();
- /**
- * Array of closures executed with given arguments to generate a result
- * to be returned
- *
- * @var array
- */
- protected $_closureQueue = array();
- /**
- * Array of values to be set when this expectation matches
- *
- * @var array
- */
- protected $_setQueue = array();
- /**
- * Integer representing the call order of this expectation
- *
- * @var int
- */
- protected $_orderNumber = null;
- /**
- * Integer representing the call order of this expectation on a global basis
- *
- * @var int
- */
- protected $_globalOrderNumber = null;
- /**
- * Flag indicating that an exception is expected to be throw (not returned)
- *
- * @var bool
- */
- protected $_throw = false;
- /**
- * Flag indicating whether the order of calling is determined locally or
- * globally
- *
- * @var bool
- */
- protected $_globally = false;
- /**
- * Flag indicating we expect no arguments
- *
- * @var bool
- */
- protected $_noArgsExpectation = false;
- /**
- * Flag indicating if the return value should be obtained from the original
- * class method instead of returning predefined values from the return queue
- *
- * @var bool
- */
- protected $_passthru = false;
- /**
- * Constructor
- *
- * @param \Mockery\MockInterface $mock
- * @param string $name
- */
- public function __construct(\Mockery\MockInterface $mock, $name)
- {
- $this->_mock = $mock;
- $this->_name = $name;
- }
- /**
- * Return a string with the method name and arguments formatted
- *
- * @param string $name Name of the expected method
- * @param array $args List of arguments to the method
- * @return string
- */
- public function __toString()
- {
- return \Mockery::formatArgs($this->_name, $this->_expectedArgs);
- }
- /**
- * Verify the current call, i.e. that the given arguments match those
- * of this expectation
- *
- * @param array $args
- * @return mixed
- */
- public function verifyCall(array $args)
- {
- $this->validateOrder();
- $this->_actualCount++;
- if (true === $this->_passthru) {
- return $this->_mock->mockery_callSubjectMethod($this->_name, $args);
- }
- $return = $this->_getReturnValue($args);
- if ($return instanceof \Exception && $this->_throw === true) {
- throw $return;
- }
- $this->_setValues();
- return $return;
- }
- /**
- * Sets public properties with queued values to the mock object
- *
- * @param array $args
- * @return mixed
- */
- protected function _setValues()
- {
- foreach ($this->_setQueue as $name => &$values) {
- if (count($values) > 0) {
- $value = array_shift($values);
- $this->_mock->{$name} = $value;
- }
- }
- }
- /**
- * Fetch the return value for the matching args
- *
- * @param array $args
- * @return mixed
- */
- protected function _getReturnValue(array $args)
- {
- if (count($this->_closureQueue) > 1) {
- return call_user_func_array(array_shift($this->_closureQueue), $args);
- } elseif (count($this->_closureQueue) > 0) {
- return call_user_func_array(current($this->_closureQueue), $args);
- } elseif (count($this->_returnQueue) > 1) {
- return array_shift($this->_returnQueue);
- } elseif (count($this->_returnQueue) > 0) {
- return current($this->_returnQueue);
- }
- $rm = $this->_mock->mockery_getMethod($this->_name);
- if ($rm && version_compare(PHP_VERSION, '7.0.0-dev') >= 0 && $rm->hasReturnType()) {
- $type = (string) $rm->getReturnType();
- switch ($type) {
- case '': return;
- case 'string': return '';
- case 'int': return 0;
- case 'float': return 0.0;
- case 'bool': return false;
- case 'array': return array();
- case 'callable':
- case 'Closure':
- return function () {};
- case 'Traversable':
- case 'Generator':
- // Remove eval() when minimum version >=5.5
- $generator = eval('return function () { yield; };');
- return $generator();
- default:
- return \Mockery::mock($type);
- }
- }
- }
- /**
- * Checks if this expectation is eligible for additional calls
- *
- * @return bool
- */
- public function isEligible()
- {
- foreach ($this->_countValidators as $validator) {
- if (!$validator->isEligible($this->_actualCount)) {
- return false;
- }
- }
- return true;
- }
- /**
- * Check if there is a constraint on call count
- *
- * @return bool
- */
- public function isCallCountConstrained()
- {
- return (count($this->_countValidators) > 0);
- }
- /**
- * Verify call order
- *
- * @return void
- */
- public function validateOrder()
- {
- if ($this->_orderNumber) {
- $this->_mock->mockery_validateOrder((string) $this, $this->_orderNumber, $this->_mock);
- }
- if ($this->_globalOrderNumber) {
- $this->_mock->mockery_getContainer()
- ->mockery_validateOrder((string) $this, $this->_globalOrderNumber, $this->_mock);
- }
- }
- /**
- * Verify this expectation
- *
- * @return bool
- */
- public function verify()
- {
- foreach ($this->_countValidators as $validator) {
- $validator->validate($this->_actualCount);
- }
- }
- /**
- * Check if passed arguments match an argument expectation
- *
- * @param array $args
- * @return bool
- */
- public function matchArgs(array $args)
- {
- if (empty($this->_expectedArgs) && !$this->_noArgsExpectation) {
- return true;
- }
- if (count($args) !== count($this->_expectedArgs)) {
- return false;
- }
- $argCount = count($args);
- for ($i=0; $i<$argCount; $i++) {
- $param =& $args[$i];
- if (!$this->_matchArg($this->_expectedArgs[$i], $param)) {
- return false;
- }
- }
- return true;
- }
- /**
- * Check if passed argument matches an argument expectation
- *
- * @param array $args
- * @return bool
- */
- protected function _matchArg($expected, &$actual)
- {
- if ($expected === $actual) {
- return true;
- }
- if (!is_object($expected) && !is_object($actual) && $expected == $actual) {
- return true;
- }
- if (is_string($expected) && !is_array($actual) && !is_object($actual)) {
- # push/pop an error handler here to to make sure no error/exception thrown if $expected is not a regex
- set_error_handler(function () {});
- $result = preg_match($expected, (string) $actual);
- restore_error_handler();
- if ($result) {
- return true;
- }
- }
- if (is_string($expected) && is_object($actual)) {
- $result = $actual instanceof $expected;
- if ($result) {
- return true;
- }
- }
- if ($expected instanceof \Mockery\Matcher\MatcherAbstract) {
- return $expected->match($actual);
- }
- if (is_a($expected, '\Hamcrest\Matcher') || is_a($expected, '\Hamcrest_Matcher')) {
- return $expected->matches($actual);
- }
- return false;
- }
- /**
- * Expected argument setter for the expectation
- *
- * @param mixed ...
- * @return self
- */
- public function with()
- {
- return $this->withArgs(func_get_args());
- }
- /**
- * Expected arguments for the expectation passed as an array
- *
- * @param array $args
- * @return self
- */
- public function withArgs(array $args)
- {
- if (empty($args)) {
- return $this->withNoArgs();
- }
- $this->_expectedArgs = $args;
- $this->_noArgsExpectation = false;
- return $this;
- }
- /**
- * Set with() as no arguments expected
- *
- * @return self
- */
- public function withNoArgs()
- {
- $this->_noArgsExpectation = true;
- $this->_expectedArgs = null;
- return $this;
- }
- /**
- * Set expectation that any arguments are acceptable
- *
- * @return self
- */
- public function withAnyArgs()
- {
- $this->_expectedArgs = array();
- return $this;
- }
- /**
- * Set a return value, or sequential queue of return values
- *
- * @param mixed ...
- * @return self
- */
- public function andReturn()
- {
- $this->_returnQueue = func_get_args();
- return $this;
- }
- /**
- * Return this mock, like a fluent interface
- *
- * @return self
- */
- public function andReturnSelf()
- {
- return $this->andReturn($this->_mock);
- }
- /**
- * Set a sequential queue of return values with an array
- *
- * @param array $values
- * @return self
- */
- public function andReturnValues(array $values)
- {
- call_user_func_array(array($this, 'andReturn'), $values);
- return $this;
- }
- /**
- * Set a closure or sequence of closures with which to generate return
- * values. The arguments passed to the expected method are passed to the
- * closures as parameters.
- *
- * @param callable ...
- * @return self
- */
- public function andReturnUsing()
- {
- $this->_closureQueue = func_get_args();
- return $this;
- }
- /**
- * Return a self-returning black hole object.
- *
- * @return self
- */
- public function andReturnUndefined()
- {
- $this->andReturn(new \Mockery\Undefined);
- return $this;
- }
- /**
- * Return null. This is merely a language construct for Mock describing.
- *
- * @return self
- */
- public function andReturnNull()
- {
- return $this;
- }
- /**
- * Set Exception class and arguments to that class to be thrown
- *
- * @param string $exception
- * @param string $message
- * @param int $code
- * @param Exception $previous
- * @return self
- */
- public function andThrow($exception, $message = '', $code = 0, \Exception $previous = null)
- {
- $this->_throw = true;
- if (is_object($exception)) {
- $this->andReturn($exception);
- } else {
- $this->andReturn(new $exception($message, $code, $previous));
- }
- return $this;
- }
- /**
- * Set Exception classes to be thrown
- *
- * @param array $exceptions
- * @return self
- */
- public function andThrowExceptions(array $exceptions)
- {
- $this->_throw = true;
- foreach ($exceptions as $exception) {
- if (!is_object($exception)) {
- throw new Exception('You must pass an array of exception objects to andThrowExceptions');
- }
- }
- return $this->andReturnValues($exceptions);
- }
- /**
- * Register values to be set to a public property each time this expectation occurs
- *
- * @param string $name
- * @param mixed $value
- * @return self
- */
- public function andSet($name, $value)
- {
- $values = func_get_args();
- array_shift($values);
- $this->_setQueue[$name] = $values;
- return $this;
- }
- /**
- * Alias to andSet(). Allows the natural English construct
- * - set('foo', 'bar')->andReturn('bar')
- *
- * @param string $name
- * @param mixed $value
- * @return self
- */
- public function set($name, $value)
- {
- return call_user_func_array(array($this, 'andSet'), func_get_args());
- }
- /**
- * Indicates this expectation should occur zero or more times
- *
- * @return self
- */
- public function zeroOrMoreTimes()
- {
- $this->atLeast()->never();
- }
- /**
- * Indicates the number of times this expectation should occur
- *
- * @param int $limit
- * @return self
- */
- public function times($limit = null)
- {
- if (is_null($limit)) {
- return $this;
- }
- $this->_countValidators[] = new $this->_countValidatorClass($this, $limit);
- $this->_countValidatorClass = 'Mockery\CountValidator\Exact';
- return $this;
- }
- /**
- * Indicates that this expectation is never expected to be called
- *
- * @return self
- */
- public function never()
- {
- return $this->times(0);
- }
- /**
- * Indicates that this expectation is expected exactly once
- *
- * @return self
- */
- public function once()
- {
- return $this->times(1);
- }
- /**
- * Indicates that this expectation is expected exactly twice
- *
- * @return self
- */
- public function twice()
- {
- return $this->times(2);
- }
- /**
- * Sets next count validator to the AtLeast instance
- *
- * @return self
- */
- public function atLeast()
- {
- $this->_countValidatorClass = 'Mockery\CountValidator\AtLeast';
- return $this;
- }
- /**
- * Sets next count validator to the AtMost instance
- *
- * @return self
- */
- public function atMost()
- {
- $this->_countValidatorClass = 'Mockery\CountValidator\AtMost';
- return $this;
- }
- /**
- * Shorthand for setting minimum and maximum constraints on call counts
- *
- * @param int $minimum
- * @param int $maximum
- */
- public function between($minimum, $maximum)
- {
- return $this->atLeast()->times($minimum)->atMost()->times($maximum);
- }
- /**
- * Indicates that this expectation must be called in a specific given order
- *
- * @param string $group Name of the ordered group
- * @return self
- */
- public function ordered($group = null)
- {
- if ($this->_globally) {
- $this->_globalOrderNumber = $this->_defineOrdered($group, $this->_mock->mockery_getContainer());
- } else {
- $this->_orderNumber = $this->_defineOrdered($group, $this->_mock);
- }
- $this->_globally = false;
- return $this;
- }
- /**
- * Indicates call order should apply globally
- *
- * @return self
- */
- public function globally()
- {
- $this->_globally = true;
- return $this;
- }
- /**
- * Setup the ordering tracking on the mock or mock container
- *
- * @param string $group
- * @param object $ordering
- * @return int
- */
- protected function _defineOrdered($group, $ordering)
- {
- $groups = $ordering->mockery_getGroups();
- if (is_null($group)) {
- $result = $ordering->mockery_allocateOrder();
- } elseif (isset($groups[$group])) {
- $result = $groups[$group];
- } else {
- $result = $ordering->mockery_allocateOrder();
- $ordering->mockery_setGroup($group, $result);
- }
- return $result;
- }
- /**
- * Return order number
- *
- * @return int
- */
- public function getOrderNumber()
- {
- return $this->_orderNumber;
- }
- /**
- * Mark this expectation as being a default
- *
- * @return self
- */
- public function byDefault()
- {
- $director = $this->_mock->mockery_getExpectationsFor($this->_name);
- if (!empty($director)) {
- $director->makeExpectationDefault($this);
- }
- return $this;
- }
- /**
- * Return the parent mock of the expectation
- *
- * @return \Mockery\MockInterface
- */
- public function getMock()
- {
- return $this->_mock;
- }
- /**
- * Flag this expectation as calling the original class method with the
- * any provided arguments instead of using a return value queue.
- *
- * @return self
- */
- public function passthru()
- {
- if ($this->_mock instanceof Mock) {
- throw new Exception(
- 'Mock Objects not created from a loaded/existing class are '
- . 'incapable of passing method calls through to a parent class'
- );
- }
- $this->_passthru = true;
- return $this;
- }
- /**
- * Cloning logic
- *
- */
- public function __clone()
- {
- $newValidators = array();
- $countValidators = $this->_countValidators;
- foreach ($countValidators as $validator) {
- $newValidators[] = clone $validator;
- }
- $this->_countValidators = $newValidators;
- }
- public function getName()
- {
- return $this->_name;
- }
- }