Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
Total | |
0.00% |
0 / 1 |
|
20.83% |
5 / 24 |
CRAP | |
34.51% |
78 / 226 |
Container | |
0.00% |
0 / 1 |
|
20.83% |
5 / 24 |
2212.68 | |
34.51% |
78 / 226 |
__construct | |
100.00% |
1 / 1 |
3 | |
100.00% |
3 / 3 |
|||
mock | |
0.00% |
0 / 1 |
398.43 | |
37.04% |
40 / 108 |
|||
instanceMock | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
getLoader | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
getGenerator | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
getKeyOfDemeterMockFor | |
0.00% |
0 / 1 |
12 | |
0.00% |
0 / 8 |
|||
getMocks | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
mockery_teardown | |
0.00% |
0 / 1 |
2.26 | |
60.00% |
3 / 5 |
|||
mockery_verify | |
100.00% |
1 / 1 |
2 | |
100.00% |
4 / 4 |
|||
mockery_close | |
100.00% |
1 / 1 |
2 | |
100.00% |
5 / 5 |
|||
mockery_allocateOrder | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
mockery_setGroup | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
mockery_getGroups | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
mockery_setCurrentOrder | |
0.00% |
0 / 1 |
1.12 | |
50.00% |
1 / 2 |
|||
mockery_getCurrentOrder | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
mockery_validateOrder | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 12 |
|||
mockery_getExpectationCount | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 5 |
|||
rememberMock | |
0.00% |
0 / 1 |
2.03 | |
80.00% |
4 / 5 |
|||
self | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 3 |
|||
fetchMock | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 2 |
|||
_getInstance | |
0.00% |
0 / 1 |
33.02 | |
43.33% |
13 / 30 |
|||
declareClass | |
0.00% |
0 / 1 |
12 | |
0.00% |
0 / 4 |
|||
anonymous function | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
checkForNamedMockClashes | |
0.00% |
0 / 1 |
10.75 | |
25.00% |
3 / 12 |
<?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; | |
use Mockery\Generator\Generator; | |
use Mockery\Generator\MockConfigurationBuilder; | |
use Mockery\Loader\Loader as LoaderInterface; | |
class Container | |
{ | |
const BLOCKS = \Mockery::BLOCKS; | |
/** | |
* Store of mock objects | |
* | |
* @var array | |
*/ | |
protected $_mocks = array(); | |
/** | |
* Order number of allocation | |
* | |
* @var int | |
*/ | |
protected $_allocatedOrder = 0; | |
/** | |
* Current ordered number | |
* | |
* @var int | |
*/ | |
protected $_currentOrder = 0; | |
/** | |
* Ordered groups | |
* | |
* @var array | |
*/ | |
protected $_groups = array(); | |
/** | |
* @var Generator\Generator | |
*/ | |
protected $_generator; | |
/** | |
* @var LoaderInterface | |
*/ | |
protected $_loader; | |
/** | |
* @var array | |
*/ | |
protected $_namedMocks = array(); | |
public function __construct(Generator $generator = null, LoaderInterface $loader = null) | |
{ | |
$this->_generator = $generator ?: \Mockery::getDefaultGenerator(); | |
$this->_loader = $loader ?: \Mockery::getDefaultLoader(); | |
} | |
/** | |
* Generates a new mock object for this container | |
* | |
* I apologies in advance for this. A God Method just fits the API which | |
* doesn't require differentiating between classes, interfaces, abstracts, | |
* names or partials - just so long as it's something that can be mocked. | |
* I'll refactor it one day so it's easier to follow. | |
* | |
* @throws Exception\RuntimeException | |
* @throws Exception | |
* @return \Mockery\Mock | |
*/ | |
public function mock() | |
{ | |
$expectationClosure = null; | |
$quickdefs = array(); | |
$constructorArgs = null; | |
$blocks = array(); | |
$args = func_get_args(); | |
if (count($args) > 1) { | |
$finalArg = end($args); | |
reset($args); | |
if (is_callable($finalArg) && is_object($finalArg)) { | |
$expectationClosure = array_pop($args); | |
} | |
} | |
$builder = new MockConfigurationBuilder(); | |
foreach ($args as $k => $arg) { | |
if ($arg instanceof MockConfigurationBuilder) { | |
$builder = $arg; | |
unset($args[$k]); | |
} | |
} | |
reset($args); | |
$builder->setParameterOverrides(\Mockery::getConfiguration()->getInternalClassMethodParamMaps()); | |
while (count($args) > 0) { | |
$arg = current($args); | |
// check for multiple interfaces | |
if (is_string($arg) && strpos($arg, ',') && !strpos($arg, ']')) { | |
$interfaces = explode(',', str_replace(' ', '', $arg)); | |
foreach ($interfaces as $i) { | |
if (!interface_exists($i, true) && !class_exists($i, true)) { | |
throw new \Mockery\Exception( | |
'Class name follows the format for defining multiple' | |
. ' interfaces, however one or more of the interfaces' | |
. ' do not exist or are not included, or the base class' | |
. ' (which you may omit from the mock definition) does not exist' | |
); | |
} | |
} | |
$builder->addTargets($interfaces); | |
array_shift($args); | |
continue; | |
} elseif (is_string($arg) && substr($arg, 0, 6) == 'alias:') { | |
$name = array_shift($args); | |
$name = str_replace('alias:', '', $name); | |
$builder->addTarget('stdClass'); | |
$builder->setName($name); | |
continue; | |
} elseif (is_string($arg) && substr($arg, 0, 9) == 'overload:') { | |
$name = array_shift($args); | |
$name = str_replace('overload:', '', $name); | |
$builder->setInstanceMock(true); | |
$builder->addTarget('stdClass'); | |
$builder->setName($name); | |
continue; | |
} elseif (is_string($arg) && substr($arg, strlen($arg)-1, 1) == ']') { | |
$parts = explode('[', $arg); | |
if (!class_exists($parts[0], true) && !interface_exists($parts[0], true)) { | |
throw new \Mockery\Exception('Can only create a partial mock from' | |
. ' an existing class or interface'); | |
} | |
$class = $parts[0]; | |
$parts[1] = str_replace(' ','', $parts[1]); | |
$partialMethods = explode(',', strtolower(rtrim($parts[1], ']'))); | |
$builder->addTarget($class); | |
$builder->setWhiteListedMethods($partialMethods); | |
array_shift($args); | |
continue; | |
} elseif (is_string($arg) && (class_exists($arg, true) || interface_exists($arg, true))) { | |
$class = array_shift($args); | |
$builder->addTarget($class); | |
continue; | |
} elseif (is_string($arg)) { | |
$class = array_shift($args); | |
$builder->addTarget($class); | |
continue; | |
} elseif (is_object($arg)) { | |
$partial = array_shift($args); | |
$builder->addTarget($partial); | |
continue; | |
} elseif (is_array($arg) && !empty($arg) && array_keys($arg) !== range(0, count($arg) - 1)) { | |
// if associative array | |
if(array_key_exists(self::BLOCKS, $arg)) $blocks = $arg[self::BLOCKS]; unset($arg[self::BLOCKS]); | |
$quickdefs = array_shift($args); | |
continue; | |
} elseif (is_array($arg)) { | |
$constructorArgs = array_shift($args); | |
continue; | |
} | |
throw new \Mockery\Exception( | |
'Unable to parse arguments sent to ' | |
. get_class($this) . '::mock()' | |
); | |
} | |
$builder->addBlackListedMethods($blocks); | |
if (!is_null($constructorArgs)) { | |
$builder->addBlackListedMethod("__construct"); // we need to pass through | |
} | |
if (!empty($partialMethods) && $constructorArgs === null) { | |
$constructorArgs = array(); | |
} | |
$config = $builder->getMockConfiguration(); | |
$this->checkForNamedMockClashes($config); | |
$def = $this->getGenerator()->generate($config); | |
if (class_exists($def->getClassName(), $attemptAutoload = false)) { | |
$rfc = new \ReflectionClass($def->getClassName()); | |
if (!$rfc->implementsInterface("Mockery\MockInterface")) { | |
throw new \Mockery\Exception\RuntimeException("Could not load mock {$def->getClassName()}, class already exists"); | |
} | |
} | |
$this->getLoader()->load($def); | |
$mock = $this->_getInstance($def->getClassName(), $constructorArgs); | |
$mock->mockery_init($this, $config->getTargetObject()); | |
if (!empty($quickdefs)) { | |
$mock->shouldReceive($quickdefs)->byDefault(); | |
} | |
if (!empty($expectationClosure)) { | |
$expectationClosure($mock); | |
} | |
$this->rememberMock($mock); | |
return $mock; | |
} | |
public function instanceMock() | |
{ | |
} | |
public function getLoader() | |
{ | |
return $this->_loader; | |
} | |
public function getGenerator() | |
{ | |
return $this->_generator; | |
} | |
/** | |
* @param string $method | |
* @return string|null | |
*/ | |
public function getKeyOfDemeterMockFor($method) | |
{ | |
$keys = array_keys($this->_mocks); | |
$match = preg_grep("/__demeter_{$method}$/", $keys); | |
if (count($match) == 1) { | |
$res = array_values($match); | |
if (count($res) > 0) { | |
return $res[0]; | |
} | |
} | |
return null; | |
} | |
/** | |
* @return array | |
*/ | |
public function getMocks() | |
{ | |
return $this->_mocks; | |
} | |
/** | |
* Tear down tasks for this container | |
* | |
* @throws \Exception | |
* @return void | |
*/ | |
public function mockery_teardown() | |
{ | |
try { | |
$this->mockery_verify(); | |
} catch (\Exception $e) { | |
$this->mockery_close(); | |
throw $e; | |
} | |
} | |
/** | |
* Verify the container mocks | |
* | |
* @return void | |
*/ | |
public function mockery_verify() | |
{ | |
foreach($this->_mocks as $mock) { | |
$mock->mockery_verify(); | |
} | |
} | |
/** | |
* Reset the container to its original state | |
* | |
* @return void | |
*/ | |
public function mockery_close() | |
{ | |
foreach($this->_mocks as $mock) { | |
$mock->mockery_teardown(); | |
} | |
$this->_mocks = array(); | |
} | |
/** | |
* Fetch the next available allocation order number | |
* | |
* @return int | |
*/ | |
public function mockery_allocateOrder() | |
{ | |
$this->_allocatedOrder += 1; | |
return $this->_allocatedOrder; | |
} | |
/** | |
* Set ordering for a group | |
* | |
* @param mixed $group | |
* @param int $order | |
*/ | |
public function mockery_setGroup($group, $order) | |
{ | |
$this->_groups[$group] = $order; | |
} | |
/** | |
* Fetch array of ordered groups | |
* | |
* @return array | |
*/ | |
public function mockery_getGroups() | |
{ | |
return $this->_groups; | |
} | |
/** | |
* Set current ordered number | |
* | |
* @param int $order | |
* @return int The current order number that was set | |
*/ | |
public function mockery_setCurrentOrder($order) | |
{ | |
$this->_currentOrder = $order; | |
return $this->_currentOrder; | |
} | |
/** | |
* Get current ordered number | |
* | |
* @return int | |
*/ | |
public function mockery_getCurrentOrder() | |
{ | |
return $this->_currentOrder; | |
} | |
/** | |
* Validate the current mock's ordering | |
* | |
* @param string $method | |
* @param int $order | |
* @throws \Mockery\Exception | |
* @return void | |
*/ | |
public function mockery_validateOrder($method, $order, \Mockery\MockInterface $mock) | |
{ | |
if ($order < $this->_currentOrder) { | |
$exception = new \Mockery\Exception\InvalidOrderException( | |
'Method ' . $method . ' called out of order: expected order ' | |
. $order . ', was ' . $this->_currentOrder | |
); | |
$exception->setMock($mock) | |
->setMethodName($method) | |
->setExpectedOrder($order) | |
->setActualOrder($this->_currentOrder); | |
throw $exception; | |
} | |
$this->mockery_setCurrentOrder($order); | |
} | |
/** | |
* Gets the count of expectations on the mocks | |
* | |
* @return int | |
*/ | |
public function mockery_getExpectationCount() | |
{ | |
$count = 0; | |
foreach($this->_mocks as $mock) { | |
$count += $mock->mockery_getExpectationCount(); | |
} | |
return $count; | |
} | |
/** | |
* Store a mock and set its container reference | |
* | |
* @param \Mockery\Mock | |
* @return \Mockery\Mock | |
*/ | |
public function rememberMock(\Mockery\MockInterface $mock) | |
{ | |
if (!isset($this->_mocks[get_class($mock)])) { | |
$this->_mocks[get_class($mock)] = $mock; | |
} else { | |
/** | |
* This condition triggers for an instance mock where origin mock | |
* is already remembered | |
*/ | |
$this->_mocks[] = $mock; | |
} | |
return $mock; | |
} | |
/** | |
* Retrieve the last remembered mock object, which is the same as saying | |
* retrieve the current mock being programmed where you have yet to call | |
* mock() to change it - thus why the method name is "self" since it will be | |
* be used during the programming of the same mock. | |
* | |
* @return \Mockery\Mock | |
*/ | |
public function self() | |
{ | |
$mocks = array_values($this->_mocks); | |
$index = count($mocks) - 1; | |
return $mocks[$index]; | |
} | |
/** | |
* Return a specific remembered mock according to the array index it | |
* was stored to in this container instance | |
* | |
* @return \Mockery\Mock | |
*/ | |
public function fetchMock($reference) | |
{ | |
if (isset($this->_mocks[$reference])) return $this->_mocks[$reference]; | |
} | |
protected function _getInstance($mockName, $constructorArgs = null) | |
{ | |
$r = new \ReflectionClass($mockName); | |
if (null === $r->getConstructor()) { | |
$return = new $mockName; | |
return $return; | |
} | |
if ($constructorArgs !== null) { | |
return $r->newInstanceArgs($constructorArgs); | |
} | |
$isInternal = $r->isInternal(); | |
$child = $r; | |
while (!$isInternal && $parent = $child->getParentClass()) { | |
$isInternal = $parent->isInternal(); | |
$child = $parent; | |
} | |
try { | |
if (version_compare(PHP_VERSION, '5.4') < 0 || $isInternal) { | |
$return = unserialize(sprintf( | |
'%s:%d:"%s":0:{}', | |
// see https://github.com/sebastianbergmann/phpunit-mock-objects/pull/176/files | |
(version_compare(PHP_VERSION, '5.4', '>') && $r->implementsInterface('Serializable') ? 'C' : 'O'), | |
strlen($mockName), | |
$mockName) | |
); | |
} else { | |
$return = $r->newInstanceWithoutConstructor(); | |
} | |
} catch (\Exception $ex) { | |
$internalMockName = $mockName . '_Internal'; | |
if (!class_exists($internalMockName)) { | |
eval("class $internalMockName extends $mockName {" . | |
'public function __construct() {}' . | |
'}'); | |
} | |
$return = new $internalMockName(); | |
} | |
return $return; | |
} | |
/** | |
* Takes a class name and declares it | |
* | |
* @param string $fqcn | |
*/ | |
public function declareClass($fqcn) | |
{ | |
if (false !== strpos($fqcn, '/')) { | |
throw new \Mockery\Exception( | |
'Class name contains a forward slash instead of backslash needed ' | |
. 'when employing namespaces' | |
); | |
} | |
if (false !== strpos($fqcn, "\\")) { | |
$parts = array_filter(explode("\\", $fqcn), function ($part) { | |
return $part !== ""; | |
}); | |
$cl = array_pop($parts); | |
$ns = implode("\\", $parts); | |
eval(" namespace $ns { class $cl {} }"); | |
} else { | |
eval(" class $fqcn {} "); | |
} | |
} | |
protected function checkForNamedMockClashes($config) | |
{ | |
$name = $config->getName(); | |
if (!$name) { | |
return; | |
} | |
$hash = $config->getHash(); | |
if (isset($this->_namedMocks[$name])) { | |
if ($hash !== $this->_namedMocks[$name]) { | |
throw new \Mockery\Exception( | |
"The mock named '$name' has been already defined with a different mock configuration" | |
); | |
} | |
} | |
$this->_namedMocks[$name] = $hash; | |
} | |
} |