Blame | Letzte Änderung | Log anzeigen | RSS feed
<?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 Pádraic Brady (http://blog.astrumfutura.com)* @license http://github.com/padraic/mockery/blob/master/LICENSE New BSD License*/use Mockery\ClosureWrapper;use Mockery\ExpectationInterface;use Mockery\Generator\CachingGenerator;use Mockery\Generator\Generator;use Mockery\Generator\MockConfigurationBuilder;use Mockery\Generator\MockNameBuilder;use Mockery\Generator\StringManipulationGenerator;use Mockery\Loader\EvalLoader;use Mockery\Loader\Loader;use Mockery\Matcher\MatcherAbstract;use Mockery\Reflector;class Mockery{const BLOCKS = 'Mockery_Forward_Blocks';/*** Global container to hold all mocks for the current unit test running.** @var \Mockery\Container|null*/protected static $_container = null;/*** Global configuration handler containing configuration options.** @var \Mockery\Configuration*/protected static $_config = null;/*** @var \Mockery\Generator\Generator*/protected static $_generator;/*** @var \Mockery\Loader\Loader*/protected static $_loader;/*** @var array*/private static $_filesToCleanUp = [];/*** Defines the global helper functions** @return void*/public static function globalHelpers(){require_once __DIR__ . '/helpers.php';}/*** @return array** @deprecated since 1.3.2 and will be removed in 2.0.*/public static function builtInTypes(){return array('array','bool','callable','float','int','iterable','object','self','string','void',);}/*** @param string $type* @return bool** @deprecated since 1.3.2 and will be removed in 2.0.*/public static function isBuiltInType($type){return in_array($type, \Mockery::builtInTypes());}/*** Static shortcut to \Mockery\Container::mock().** @param mixed ...$args** @return \Mockery\MockInterface|\Mockery\LegacyMockInterface*/public static function mock(...$args){return call_user_func_array(array(self::getContainer(), 'mock'), $args);}/*** Static and semantic shortcut for getting a mock from the container* and applying the spy's expected behavior into it.** @param mixed ...$args** @return \Mockery\MockInterface|\Mockery\LegacyMockInterface*/public static function spy(...$args){if (count($args) && $args[0] instanceof \Closure) {$args[0] = new ClosureWrapper($args[0]);}return call_user_func_array(array(self::getContainer(), 'mock'), $args)->shouldIgnoreMissing();}/*** Static and Semantic shortcut to \Mockery\Container::mock().** @param mixed ...$args** @return \Mockery\MockInterface|\Mockery\LegacyMockInterface*/public static function instanceMock(...$args){return call_user_func_array(array(self::getContainer(), 'mock'), $args);}/*** Static shortcut to \Mockery\Container::mock(), first argument names the mock.** @param mixed ...$args** @return \Mockery\MockInterface|\Mockery\LegacyMockInterface*/public static function namedMock(...$args){$name = array_shift($args);$builder = new MockConfigurationBuilder();$builder->setName($name);array_unshift($args, $builder);return call_user_func_array(array(self::getContainer(), 'mock'), $args);}/*** Static shortcut to \Mockery\Container::self().** @throws LogicException** @return \Mockery\MockInterface|\Mockery\LegacyMockInterface*/public static function self(){if (is_null(self::$_container)) {throw new \LogicException('You have not declared any mocks yet');}return self::$_container->self();}/*** Static shortcut to closing up and verifying all mocks in the global* container, and resetting the container static variable to null.** @return void*/public static function close(){foreach (self::$_filesToCleanUp as $fileName) {@unlink($fileName);}self::$_filesToCleanUp = [];if (is_null(self::$_container)) {return;}$container = self::$_container;self::$_container = null;$container->mockery_teardown();$container->mockery_close();}/*** Static fetching of a mock associated with a name or explicit class poser.** @param string $name** @return \Mockery\Mock*/public static function fetchMock($name){return self::getContainer()->fetchMock($name);}/*** Lazy loader and getter for* the container property.** @return Mockery\Container*/public static function getContainer(){if (is_null(self::$_container)) {self::$_container = new Mockery\Container(self::getGenerator(), self::getLoader());}return self::$_container;}/*** Setter for the $_generator static property.** @param \Mockery\Generator\Generator $generator*/public static function setGenerator(Generator $generator){self::$_generator = $generator;}/*** Lazy loader method and getter for* the generator property.** @return Generator*/public static function getGenerator(){if (is_null(self::$_generator)) {self::$_generator = self::getDefaultGenerator();}return self::$_generator;}/*** Creates and returns a default generator* used inside this class.** @return CachingGenerator*/public static function getDefaultGenerator(){return new CachingGenerator(StringManipulationGenerator::withDefaultPasses());}/*** Setter for the $_loader static property.** @param Loader $loader*/public static function setLoader(Loader $loader){self::$_loader = $loader;}/*** Lazy loader method and getter for* the $_loader property.** @return Loader*/public static function getLoader(){if (is_null(self::$_loader)) {self::$_loader = self::getDefaultLoader();}return self::$_loader;}/*** Gets an EvalLoader to be used as default.** @return EvalLoader*/public static function getDefaultLoader(){return new EvalLoader();}/*** Set the container.** @param \Mockery\Container $container** @return \Mockery\Container*/public static function setContainer(Mockery\Container $container){return self::$_container = $container;}/*** Reset the container to null.** @return void*/public static function resetContainer(){self::$_container = null;}/*** Return instance of ANY matcher.** @return \Mockery\Matcher\Any*/public static function any(){return new \Mockery\Matcher\Any();}/*** Return instance of AndAnyOtherArgs matcher.** An alternative name to `andAnyOtherArgs` so* the API stays closer to `any` as well.** @return \Mockery\Matcher\AndAnyOtherArgs*/public static function andAnyOthers(){return new \Mockery\Matcher\AndAnyOtherArgs();}/*** Return instance of AndAnyOtherArgs matcher.** @return \Mockery\Matcher\AndAnyOtherArgs*/public static function andAnyOtherArgs(){return new \Mockery\Matcher\AndAnyOtherArgs();}/*** Return instance of TYPE matcher.** @param mixed $expected** @return \Mockery\Matcher\Type*/public static function type($expected){return new \Mockery\Matcher\Type($expected);}/*** Return instance of DUCKTYPE matcher.** @param array ...$args** @return \Mockery\Matcher\Ducktype*/public static function ducktype(...$args){return new \Mockery\Matcher\Ducktype($args);}/*** Return instance of SUBSET matcher.** @param array $part* @param bool $strict - (Optional) True for strict comparison, false for loose** @return \Mockery\Matcher\Subset*/public static function subset(array $part, $strict = true){return new \Mockery\Matcher\Subset($part, $strict);}/*** Return instance of CONTAINS matcher.** @param mixed $args** @return \Mockery\Matcher\Contains*/public static function contains(...$args){return new \Mockery\Matcher\Contains($args);}/*** Return instance of HASKEY matcher.** @param mixed $key** @return \Mockery\Matcher\HasKey*/public static function hasKey($key){return new \Mockery\Matcher\HasKey($key);}/*** Return instance of HASVALUE matcher.** @param mixed $val** @return \Mockery\Matcher\HasValue*/public static function hasValue($val){return new \Mockery\Matcher\HasValue($val);}/*** Return instance of CLOSURE matcher.** @param $reference** @return \Mockery\Matcher\Closure*/public static function capture(&$reference){$closure = function ($argument) use (&$reference) {$reference = $argument;return true;};return new \Mockery\Matcher\Closure($closure);}/*** Return instance of CLOSURE matcher.** @param mixed $closure** @return \Mockery\Matcher\Closure*/public static function on($closure){return new \Mockery\Matcher\Closure($closure);}/*** Return instance of MUSTBE matcher.** @param mixed $expected** @return \Mockery\Matcher\MustBe*/public static function mustBe($expected){return new \Mockery\Matcher\MustBe($expected);}/*** Return instance of NOT matcher.** @param mixed $expected** @return \Mockery\Matcher\Not*/public static function not($expected){return new \Mockery\Matcher\Not($expected);}/*** Return instance of ANYOF matcher.** @param array ...$args** @return \Mockery\Matcher\AnyOf*/public static function anyOf(...$args){return new \Mockery\Matcher\AnyOf($args);}/*** Return instance of NOTANYOF matcher.** @param array ...$args** @return \Mockery\Matcher\NotAnyOf*/public static function notAnyOf(...$args){return new \Mockery\Matcher\NotAnyOf($args);}/*** Return instance of PATTERN matcher.** @param mixed $expected** @return \Mockery\Matcher\Pattern*/public static function pattern($expected){return new \Mockery\Matcher\Pattern($expected);}/*** Lazy loader and Getter for the global* configuration container.** @return \Mockery\Configuration*/public static function getConfiguration(){if (is_null(self::$_config)) {self::$_config = new \Mockery\Configuration();}return self::$_config;}/*** Utility method to format method name and arguments into a string.** @param string $method* @param array $arguments** @return string*/public static function formatArgs($method, array $arguments = null){if (is_null($arguments)) {return $method . '()';}$formattedArguments = array();foreach ($arguments as $argument) {$formattedArguments[] = self::formatArgument($argument);}return $method . '(' . implode(', ', $formattedArguments) . ')';}/*** Gets the string representation* of any passed argument.** @param mixed $argument* @param int $depth** @return mixed*/private static function formatArgument($argument, $depth = 0){if ($argument instanceof MatcherAbstract) {return (string) $argument;}if (is_object($argument)) {return 'object(' . get_class($argument) . ')';}if (is_int($argument) || is_float($argument)) {return $argument;}if (is_array($argument)) {if ($depth === 1) {$argument = '[...]';} else {$sample = array();foreach ($argument as $key => $value) {$key = is_int($key) ? $key : "'$key'";$value = self::formatArgument($value, $depth + 1);$sample[] = "$key => $value";}$argument = "[" . implode(", ", $sample) . "]";}return ((strlen($argument) > 1000) ? substr($argument, 0, 1000) . '...]' : $argument);}if (is_bool($argument)) {return $argument ? 'true' : 'false';}if (is_resource($argument)) {return 'resource(...)';}if (is_null($argument)) {return 'NULL';}return "'" . (string) $argument . "'";}/*** Utility function to format objects to printable arrays.** @param array $objects** @return string*/public static function formatObjects(array $objects = null){static $formatting;if ($formatting) {return '[Recursion]';}if (is_null($objects)) {return '';}$objects = array_filter($objects, 'is_object');if (empty($objects)) {return '';}$formatting = true;$parts = array();foreach ($objects as $object) {$parts[get_class($object)] = self::objectToArray($object);}$formatting = false;return 'Objects: ( ' . var_export($parts, true) . ')';}/*** Utility function to turn public properties and public get* and is* method values into an array.** @param object $object* @param int $nesting** @return array*/private static function objectToArray($object, $nesting = 3){if ($nesting == 0) {return array('...');}$defaultFormatter = function ($object, $nesting) {return array('properties' => self::extractInstancePublicProperties($object, $nesting));};$class = get_class($object);$formatter = self::getConfiguration()->getObjectFormatter($class, $defaultFormatter);$array = array('class' => $class,'identity' => '#' . md5(spl_object_hash($object)));$array = array_merge($array, $formatter($object, $nesting));return $array;}/*** Returns all public instance properties.** @param mixed $object* @param int $nesting** @return array*/private static function extractInstancePublicProperties($object, $nesting){$reflection = new \ReflectionClass(get_class($object));$properties = $reflection->getProperties(\ReflectionProperty::IS_PUBLIC);$cleanedProperties = array();foreach ($properties as $publicProperty) {if (!$publicProperty->isStatic()) {$name = $publicProperty->getName();try {$cleanedProperties[$name] = self::cleanupNesting($object->$name, $nesting);} catch (\Exception $exception) {$cleanedProperties[$name] = $exception->getMessage();}}}return $cleanedProperties;}/*** Utility method used for recursively generating* an object or array representation.** @param mixed $argument* @param int $nesting** @return mixed*/private static function cleanupNesting($argument, $nesting){if (is_object($argument)) {$object = self::objectToArray($argument, $nesting - 1);$object['class'] = get_class($argument);return $object;}if (is_array($argument)) {return self::cleanupArray($argument, $nesting - 1);}return $argument;}/*** Utility method for recursively* gerating a representation* of the given array.** @param array $argument* @param int $nesting** @return mixed*/private static function cleanupArray($argument, $nesting = 3){if ($nesting == 0) {return '...';}foreach ($argument as $key => $value) {if (is_array($value)) {$argument[$key] = self::cleanupArray($value, $nesting - 1);} elseif (is_object($value)) {$argument[$key] = self::objectToArray($value, $nesting - 1);}}return $argument;}/*** Utility function to parse shouldReceive() arguments and generate* expectations from such as needed.** @param Mockery\LegacyMockInterface $mock* @param array ...$args* @param callable $add* @return \Mockery\CompositeExpectation*/public static function parseShouldReturnArgs(\Mockery\LegacyMockInterface $mock, $args, $add){$composite = new \Mockery\CompositeExpectation();foreach ($args as $arg) {if (is_array($arg)) {foreach ($arg as $k => $v) {$expectation = self::buildDemeterChain($mock, $k, $add)->andReturn($v);$composite->add($expectation);}} elseif (is_string($arg)) {$expectation = self::buildDemeterChain($mock, $arg, $add);$composite->add($expectation);}}return $composite;}/*** Sets up expectations on the members of the CompositeExpectation and* builds up any demeter chain that was passed to shouldReceive.** @param \Mockery\LegacyMockInterface $mock* @param string $arg* @param callable $add* @throws Mockery\Exception* @return \Mockery\ExpectationInterface*/protected static function buildDemeterChain(\Mockery\LegacyMockInterface $mock, $arg, $add){/** @var Mockery\Container $container */$container = $mock->mockery_getContainer();$methodNames = explode('->', $arg);reset($methodNames);if (!\Mockery::getConfiguration()->mockingNonExistentMethodsAllowed()&& !$mock->mockery_isAnonymous()&& !in_array(current($methodNames), $mock->mockery_getMockableMethods())) {throw new \Mockery\Exception('Mockery\'s configuration currently forbids mocking the method '. current($methodNames) . ' as it does not exist on the class or object '. 'being mocked');}/** @var ExpectationInterface|null $expectations */$expectations = null;/** @var Callable $nextExp */$nextExp = function ($method) use ($add) {return $add($method);};$parent = get_class($mock);while (true) {$method = array_shift($methodNames);$expectations = $mock->mockery_getExpectationsFor($method);if (is_null($expectations) || self::noMoreElementsInChain($methodNames)) {$expectations = $nextExp($method);if (self::noMoreElementsInChain($methodNames)) {break;}$mock = self::getNewDemeterMock($container, $parent, $method, $expectations);} else {$demeterMockKey = $container->getKeyOfDemeterMockFor($method, $parent);if ($demeterMockKey) {$mock = self::getExistingDemeterMock($container, $demeterMockKey);}}$parent .= '->' . $method;$nextExp = function ($n) use ($mock) {return $mock->shouldReceive($n);};}return $expectations;}/*** Gets a new demeter configured* mock from the container.** @param \Mockery\Container $container* @param string $parent* @param string $method* @param Mockery\ExpectationInterface $exp** @return \Mockery\Mock*/private static function getNewDemeterMock(Mockery\Container $container,$parent,$method,Mockery\ExpectationInterface $exp) {$newMockName = 'demeter_' . md5($parent) . '_' . $method;$parRef = null;$parRefMethod = null;$parRefMethodRetType = null;$parentMock = $exp->getMock();if ($parentMock !== null) {$parRef = new ReflectionObject($parentMock);}if ($parRef !== null && $parRef->hasMethod($method)) {$parRefMethod = $parRef->getMethod($method);$parRefMethodRetType = Reflector::getReturnType($parRefMethod, true);if ($parRefMethodRetType !== null && $parRefMethodRetType !== 'mixed') {$nameBuilder = new MockNameBuilder();$nameBuilder->addPart('\\' . $newMockName);$mock = self::namedMock($nameBuilder->build(), $parRefMethodRetType);$exp->andReturn($mock);return $mock;}}$mock = $container->mock($newMockName);$exp->andReturn($mock);return $mock;}/*** Gets an specific demeter mock from* the ones kept by the container.** @param \Mockery\Container $container* @param string $demeterMockKey** @return mixed*/private static function getExistingDemeterMock(Mockery\Container $container,$demeterMockKey) {$mocks = $container->getMocks();$mock = $mocks[$demeterMockKey];return $mock;}/*** Checks if the passed array representing a demeter* chain with the method names is empty.** @param array $methodNames** @return bool*/private static function noMoreElementsInChain(array $methodNames){return empty($methodNames);}public static function declareClass($fqn){return static::declareType($fqn, "class");}public static function declareInterface($fqn){return static::declareType($fqn, "interface");}private static function declareType($fqn, $type){$targetCode = "<?php ";$shortName = $fqn;if (strpos($fqn, "\\")) {$parts = explode("\\", $fqn);$shortName = trim(array_pop($parts));$namespace = implode("\\", $parts);$targetCode.= "namespace $namespace;\n";}$targetCode.= "$type $shortName {} ";/** We could eval here, but it doesn't play well with the way* PHPUnit tries to backup global state and the require definition* loader*/$tmpfname = tempnam(sys_get_temp_dir(), "Mockery");file_put_contents($tmpfname, $targetCode);require $tmpfname;\Mockery::registerFileForCleanUp($tmpfname);}/*** Register a file to be deleted on tearDown.** @param string $fileName*/public static function registerFileForCleanUp($fileName){self::$_filesToCleanUp[] = $fileName;}}