--- /dev/null
+<?php
+/*
+ * This file is part of the PHPUnit_MockObject package.
+ *
+ * (c) Sebastian Bergmann <sebastian@phpunit.de>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Doctrine\Instantiator\Instantiator;
+use Doctrine\Instantiator\Exception\InvalidArgumentException as InstantiatorInvalidArgumentException;
+use Doctrine\Instantiator\Exception\UnexpectedValueException as InstantiatorUnexpectedValueException;
+
+if (!function_exists('trait_exists')) {
+ function trait_exists($traitname, $autoload = true)
+ {
+ return false;
+ }
+}
+
+/**
+ * Mock Object Code Generator
+ *
+ * @since Class available since Release 1.0.0
+ */
+class PHPUnit_Framework_MockObject_Generator
+{
+ /**
+ * @var array
+ */
+ private static $cache = array();
+
+ /**
+ * @var array
+ */
+ protected $blacklistedMethodNames = array(
+ '__CLASS__' => true,
+ '__DIR__' => true,
+ '__FILE__' => true,
+ '__FUNCTION__' => true,
+ '__LINE__' => true,
+ '__METHOD__' => true,
+ '__NAMESPACE__' => true,
+ '__TRAIT__' => true,
+ '__clone' => true,
+ '__halt_compiler' => true,
+ 'abstract' => true,
+ 'and' => true,
+ 'array' => true,
+ 'as' => true,
+ 'break' => true,
+ 'callable' => true,
+ 'case' => true,
+ 'catch' => true,
+ 'class' => true,
+ 'clone' => true,
+ 'const' => true,
+ 'continue' => true,
+ 'declare' => true,
+ 'default' => true,
+ 'die' => true,
+ 'do' => true,
+ 'echo' => true,
+ 'else' => true,
+ 'elseif' => true,
+ 'empty' => true,
+ 'enddeclare' => true,
+ 'endfor' => true,
+ 'endforeach' => true,
+ 'endif' => true,
+ 'endswitch' => true,
+ 'endwhile' => true,
+ 'eval' => true,
+ 'exit' => true,
+ 'expects' => true,
+ 'extends' => true,
+ 'final' => true,
+ 'for' => true,
+ 'foreach' => true,
+ 'function' => true,
+ 'global' => true,
+ 'goto' => true,
+ 'if' => true,
+ 'implements' => true,
+ 'include' => true,
+ 'include_once' => true,
+ 'instanceof' => true,
+ 'insteadof' => true,
+ 'interface' => true,
+ 'isset' => true,
+ 'list' => true,
+ 'namespace' => true,
+ 'new' => true,
+ 'or' => true,
+ 'print' => true,
+ 'private' => true,
+ 'protected' => true,
+ 'public' => true,
+ 'require' => true,
+ 'require_once' => true,
+ 'return' => true,
+ 'static' => true,
+ 'switch' => true,
+ 'throw' => true,
+ 'trait' => true,
+ 'try' => true,
+ 'unset' => true,
+ 'use' => true,
+ 'var' => true,
+ 'while' => true,
+ 'xor' => true
+ );
+
+ /**
+ * Returns a mock object for the specified class.
+ *
+ * @param array|string $type
+ * @param array $methods
+ * @param array $arguments
+ * @param string $mockClassName
+ * @param bool $callOriginalConstructor
+ * @param bool $callOriginalClone
+ * @param bool $callAutoload
+ * @param bool $cloneArguments
+ * @param bool $callOriginalMethods
+ * @param object $proxyTarget
+ * @return object
+ * @throws InvalidArgumentException
+ * @throws PHPUnit_Framework_Exception
+ * @throws PHPUnit_Framework_MockObject_RuntimeException
+ * @since Method available since Release 1.0.0
+ */
+ public function getMock($type, $methods = array(), array $arguments = array(), $mockClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true, $cloneArguments = true, $callOriginalMethods = false, $proxyTarget = null)
+ {
+ if (!is_array($type) && !is_string($type)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'array or string');
+ }
+
+ if (!is_string($mockClassName)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(4, 'string');
+ }
+
+ if (!is_array($methods) && !is_null($methods)) {
+ throw new InvalidArgumentException;
+ }
+
+ if ($type === 'Traversable' || $type === '\\Traversable') {
+ $type = 'Iterator';
+ }
+
+ if (is_array($type)) {
+ $type = array_unique(array_map(
+ function ($type) {
+ if ($type === 'Traversable' ||
+ $type === '\\Traversable' ||
+ $type === '\\Iterator') {
+ return 'Iterator';
+ }
+
+ return $type;
+ },
+ $type
+ ));
+ }
+
+ if (null !== $methods) {
+ foreach ($methods as $method) {
+ if (!preg_match('~[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*~', $method)) {
+ throw new PHPUnit_Framework_Exception(
+ sprintf(
+ 'Cannot stub or mock method with invalid name "%s"',
+ $method
+ )
+ );
+ }
+ }
+
+ if ($methods != array_unique($methods)) {
+ throw new PHPUnit_Framework_MockObject_RuntimeException(
+ sprintf(
+ 'Cannot stub or mock using a method list that contains duplicates: "%s"',
+ implode(', ', $methods)
+ )
+ );
+ }
+ }
+
+ if ($mockClassName != '' && class_exists($mockClassName, false)) {
+ $reflect = new ReflectionClass($mockClassName);
+
+ if (!$reflect->implementsInterface('PHPUnit_Framework_MockObject_MockObject')) {
+ throw new PHPUnit_Framework_MockObject_RuntimeException(
+ sprintf(
+ 'Class "%s" already exists.',
+ $mockClassName
+ )
+ );
+ }
+ }
+
+ $mock = $this->generate(
+ $type,
+ $methods,
+ $mockClassName,
+ $callOriginalClone,
+ $callAutoload,
+ $cloneArguments,
+ $callOriginalMethods
+ );
+
+ return $this->getObject(
+ $mock['code'],
+ $mock['mockClassName'],
+ $type,
+ $callOriginalConstructor,
+ $callAutoload,
+ $arguments,
+ $callOriginalMethods,
+ $proxyTarget
+ );
+ }
+
+ /**
+ * @param string $code
+ * @param string $className
+ * @param array|string $type
+ * @param bool $callOriginalConstructor
+ * @param bool $callAutoload
+ * @param array $arguments
+ * @param bool $callOriginalMethods
+ * @param object $proxyTarget
+ * @return object
+ */
+ protected function getObject($code, $className, $type = '', $callOriginalConstructor = false, $callAutoload = false, array $arguments = array(), $callOriginalMethods = false, $proxyTarget = null)
+ {
+ $this->evalClass($code, $className);
+
+ if ($callOriginalConstructor &&
+ is_string($type) &&
+ !interface_exists($type, $callAutoload)) {
+ if (count($arguments) == 0) {
+ $object = new $className;
+ } else {
+ $class = new ReflectionClass($className);
+ $object = $class->newInstanceArgs($arguments);
+ }
+ } else {
+ try {
+ $instantiator = new Instantiator;
+ $object = $instantiator->instantiate($className);
+ } catch (InstantiatorUnexpectedValueException $exception) {
+ if ($exception->getPrevious()) {
+ $exception = $exception->getPrevious();
+ }
+
+ throw new PHPUnit_Framework_MockObject_RuntimeException(
+ $exception->getMessage()
+ );
+ } catch (InstantiatorInvalidArgumentException $exception) {
+ throw new PHPUnit_Framework_MockObject_RuntimeException(
+ $exception->getMessage()
+ );
+ }
+ }
+
+ if ($callOriginalMethods) {
+ if (!is_object($proxyTarget)) {
+ if (count($arguments) == 0) {
+ $proxyTarget = new $type;
+ } else {
+ $class = new ReflectionClass($type);
+ $proxyTarget = $class->newInstanceArgs($arguments);
+ }
+ }
+
+ $object->__phpunit_setOriginalObject($proxyTarget);
+ }
+
+ return $object;
+ }
+
+ /**
+ * @param string $code
+ * @param string $className
+ */
+ protected function evalClass($code, $className)
+ {
+ if (!class_exists($className, false)) {
+ eval($code);
+ }
+ }
+
+ /**
+ * Returns a mock object for the specified abstract class with all abstract
+ * methods of the class mocked. Concrete methods to mock can be specified with
+ * the last parameter
+ *
+ * @param string $originalClassName
+ * @param array $arguments
+ * @param string $mockClassName
+ * @param bool $callOriginalConstructor
+ * @param bool $callOriginalClone
+ * @param bool $callAutoload
+ * @param array $mockedMethods
+ * @param bool $cloneArguments
+ * @return object
+ * @since Method available since Release 1.0.0
+ * @throws PHPUnit_Framework_MockObject_RuntimeException
+ * @throws PHPUnit_Framework_Exception
+ */
+ public function getMockForAbstractClass($originalClassName, array $arguments = array(), $mockClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true, $mockedMethods = array(), $cloneArguments = true)
+ {
+ if (!is_string($originalClassName)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
+ }
+
+ if (!is_string($mockClassName)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(3, 'string');
+ }
+
+ if (class_exists($originalClassName, $callAutoload) ||
+ interface_exists($originalClassName, $callAutoload)) {
+ $reflector = new ReflectionClass($originalClassName);
+ $methods = $mockedMethods;
+
+ foreach ($reflector->getMethods() as $method) {
+ if ($method->isAbstract() && !in_array($method->getName(), $methods)) {
+ $methods[] = $method->getName();
+ }
+ }
+
+ if (empty($methods)) {
+ $methods = null;
+ }
+
+ return $this->getMock(
+ $originalClassName,
+ $methods,
+ $arguments,
+ $mockClassName,
+ $callOriginalConstructor,
+ $callOriginalClone,
+ $callAutoload,
+ $cloneArguments
+ );
+ } else {
+ throw new PHPUnit_Framework_MockObject_RuntimeException(
+ sprintf('Class "%s" does not exist.', $originalClassName)
+ );
+ }
+ }
+
+ /**
+ * Returns a mock object for the specified trait with all abstract methods
+ * of the trait mocked. Concrete methods to mock can be specified with the
+ * `$mockedMethods` parameter.
+ *
+ * @param string $traitName
+ * @param array $arguments
+ * @param string $mockClassName
+ * @param bool $callOriginalConstructor
+ * @param bool $callOriginalClone
+ * @param bool $callAutoload
+ * @param array $mockedMethods
+ * @param bool $cloneArguments
+ * @return object
+ * @since Method available since Release 1.2.3
+ * @throws PHPUnit_Framework_MockObject_RuntimeException
+ * @throws PHPUnit_Framework_Exception
+ */
+ public function getMockForTrait($traitName, array $arguments = array(), $mockClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true, $mockedMethods = array(), $cloneArguments = true)
+ {
+ if (!is_string($traitName)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
+ }
+
+ if (!is_string($mockClassName)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(3, 'string');
+ }
+
+ if (!trait_exists($traitName, $callAutoload)) {
+ throw new PHPUnit_Framework_MockObject_RuntimeException(
+ sprintf(
+ 'Trait "%s" does not exist.',
+ $traitName
+ )
+ );
+ }
+
+ $className = $this->generateClassName(
+ $traitName,
+ '',
+ 'Trait_'
+ );
+
+ $templateDir = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'Generator' .
+ DIRECTORY_SEPARATOR;
+ $classTemplate = new Text_Template(
+ $templateDir . 'trait_class.tpl'
+ );
+
+ $classTemplate->setVar(
+ array(
+ 'prologue' => 'abstract ',
+ 'class_name' => $className['className'],
+ 'trait_name' => $traitName
+ )
+ );
+
+ $this->evalClass(
+ $classTemplate->render(),
+ $className['className']
+ );
+
+ return $this->getMockForAbstractClass($className['className'], $arguments, $mockClassName, $callOriginalConstructor, $callOriginalClone, $callAutoload, $mockedMethods, $cloneArguments);
+ }
+
+ /**
+ * Returns an object for the specified trait.
+ *
+ * @param string $traitName
+ * @param array $arguments
+ * @param string $traitClassName
+ * @param bool $callOriginalConstructor
+ * @param bool $callOriginalClone
+ * @param bool $callAutoload
+ * @return object
+ * @since Method available since Release 1.1.0
+ * @throws PHPUnit_Framework_MockObject_RuntimeException
+ * @throws PHPUnit_Framework_Exception
+ */
+ public function getObjectForTrait($traitName, array $arguments = array(), $traitClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true)
+ {
+ if (!is_string($traitName)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
+ }
+
+ if (!is_string($traitClassName)) {
+ throw PHPUnit_Util_InvalidArgumentHelper::factory(3, 'string');
+ }
+
+ if (!trait_exists($traitName, $callAutoload)) {
+ throw new PHPUnit_Framework_MockObject_RuntimeException(
+ sprintf(
+ 'Trait "%s" does not exist.',
+ $traitName
+ )
+ );
+ }
+
+ $className = $this->generateClassName(
+ $traitName,
+ $traitClassName,
+ 'Trait_'
+ );
+
+ $templateDir = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'Generator' .
+ DIRECTORY_SEPARATOR;
+ $classTemplate = new Text_Template(
+ $templateDir . 'trait_class.tpl'
+ );
+
+ $classTemplate->setVar(
+ array(
+ 'prologue' => '',
+ 'class_name' => $className['className'],
+ 'trait_name' => $traitName
+ )
+ );
+
+ return $this->getObject(
+ $classTemplate->render(),
+ $className['className']
+ );
+ }
+
+ /**
+ * @param array|string $type
+ * @param array $methods
+ * @param string $mockClassName
+ * @param bool $callOriginalClone
+ * @param bool $callAutoload
+ * @param bool $cloneArguments
+ * @param bool $callOriginalMethods
+ * @return array
+ */
+ public function generate($type, array $methods = null, $mockClassName = '', $callOriginalClone = true, $callAutoload = true, $cloneArguments = true, $callOriginalMethods = false)
+ {
+ if (is_array($type)) {
+ sort($type);
+ }
+
+ if ($mockClassName == '') {
+ $key = md5(
+ is_array($type) ? implode('_', $type) : $type .
+ serialize($methods) .
+ serialize($callOriginalClone) .
+ serialize($cloneArguments) .
+ serialize($callOriginalMethods)
+ );
+
+ if (isset(self::$cache[$key])) {
+ return self::$cache[$key];
+ }
+ }
+
+ $mock = $this->generateMock(
+ $type,
+ $methods,
+ $mockClassName,
+ $callOriginalClone,
+ $callAutoload,
+ $cloneArguments,
+ $callOriginalMethods
+ );
+
+ if (isset($key)) {
+ self::$cache[$key] = $mock;
+ }
+
+ return $mock;
+ }
+
+ /**
+ * @param string $wsdlFile
+ * @param string $className
+ * @param array $methods
+ * @param array $options
+ * @return string
+ * @throws PHPUnit_Framework_MockObject_RuntimeException
+ */
+ public function generateClassFromWsdl($wsdlFile, $className, array $methods = array(), array $options = array())
+ {
+ if (!extension_loaded('soap')) {
+ throw new PHPUnit_Framework_MockObject_RuntimeException(
+ 'The SOAP extension is required to generate a mock object from WSDL.'
+ );
+ }
+
+ $options = array_merge($options, array('cache_wsdl' => WSDL_CACHE_NONE));
+ $client = new SoapClient($wsdlFile, $options);
+ $_methods = array_unique($client->__getFunctions());
+ unset($client);
+
+ sort($_methods);
+
+ $templateDir = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'Generator' . DIRECTORY_SEPARATOR;
+ $methodTemplate = new Text_Template($templateDir . 'wsdl_method.tpl');
+ $methodsBuffer = '';
+
+ foreach ($_methods as $method) {
+ $nameStart = strpos($method, ' ') + 1;
+ $nameEnd = strpos($method, '(');
+ $name = substr($method, $nameStart, $nameEnd - $nameStart);
+
+ if (empty($methods) || in_array($name, $methods)) {
+ $args = explode(
+ ',',
+ substr(
+ $method,
+ $nameEnd + 1,
+ strpos($method, ')') - $nameEnd - 1
+ )
+ );
+ $numArgs = count($args);
+
+ for ($i = 0; $i < $numArgs; $i++) {
+ $args[$i] = substr($args[$i], strpos($args[$i], '$'));
+ }
+
+ $methodTemplate->setVar(
+ array(
+ 'method_name' => $name,
+ 'arguments' => implode(', ', $args)
+ )
+ );
+
+ $methodsBuffer .= $methodTemplate->render();
+ }
+ }
+
+ $optionsBuffer = 'array(';
+
+ foreach ($options as $key => $value) {
+ $optionsBuffer .= $key . ' => ' . $value;
+ }
+
+ $optionsBuffer .= ')';
+
+ $classTemplate = new Text_Template($templateDir . 'wsdl_class.tpl');
+ $namespace = '';
+
+ if (strpos($className, '\\') !== false) {
+ $parts = explode('\\', $className);
+ $className = array_pop($parts);
+ $namespace = 'namespace ' . implode('\\', $parts) . ';' . "\n\n";
+ }
+
+ $classTemplate->setVar(
+ array(
+ 'namespace' => $namespace,
+ 'class_name' => $className,
+ 'wsdl' => $wsdlFile,
+ 'options' => $optionsBuffer,
+ 'methods' => $methodsBuffer
+ )
+ );
+
+ return $classTemplate->render();
+ }
+
+ /**
+ * @param array|string $type
+ * @param array|null $methods
+ * @param string $mockClassName
+ * @param bool $callOriginalClone
+ * @param bool $callAutoload
+ * @param bool $cloneArguments
+ * @param bool $callOriginalMethods
+ * @return array
+ * @throws PHPUnit_Framework_Exception
+ */
+ protected function generateMock($type, $methods, $mockClassName, $callOriginalClone, $callAutoload, $cloneArguments, $callOriginalMethods)
+ {
+ $templateDir = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'Generator' .
+ DIRECTORY_SEPARATOR;
+ $classTemplate = new Text_Template(
+ $templateDir . 'mocked_class.tpl'
+ );
+
+ $additionalInterfaces = array();
+ $cloneTemplate = '';
+ $isClass = false;
+ $isInterface = false;
+
+ $mockClassName = $this->generateClassName(
+ $type,
+ $mockClassName,
+ 'Mock_'
+ );
+
+ if (is_array($type)) {
+ foreach ($type as $_type) {
+ if (!interface_exists($_type, $callAutoload)) {
+ throw new PHPUnit_Framework_Exception(
+ sprintf(
+ 'Interface "%s" does not exist.',
+ $_type
+ )
+ );
+ }
+
+ $additionalInterfaces[] = $_type;
+
+ foreach ($this->getClassMethods($_type) as $method) {
+ if (in_array($method, $methods)) {
+ throw new PHPUnit_Framework_Exception(
+ sprintf(
+ 'Duplicate method "%s" not allowed.',
+ $method
+ )
+ );
+ }
+
+ $methods[] = $method;
+ }
+ }
+ }
+
+ if (class_exists($mockClassName['fullClassName'], $callAutoload)) {
+ $isClass = true;
+ } else {
+ if (interface_exists($mockClassName['fullClassName'], $callAutoload)) {
+ $isInterface = true;
+ }
+ }
+
+ if (!class_exists($mockClassName['fullClassName'], $callAutoload) &&
+ !interface_exists($mockClassName['fullClassName'], $callAutoload)) {
+ $prologue = 'class ' . $mockClassName['originalClassName'] . "\n{\n}\n\n";
+
+ if (!empty($mockClassName['namespaceName'])) {
+ $prologue = 'namespace ' . $mockClassName['namespaceName'] .
+ " {\n\n" . $prologue . "}\n\n" .
+ "namespace {\n\n";
+
+ $epilogue = "\n\n}";
+ }
+
+ $cloneTemplate = new Text_Template(
+ $templateDir . 'mocked_clone.tpl'
+ );
+ } else {
+ $class = new ReflectionClass($mockClassName['fullClassName']);
+
+ if ($class->isFinal()) {
+ throw new PHPUnit_Framework_Exception(
+ sprintf(
+ 'Class "%s" is declared "final" and cannot be mocked.',
+ $mockClassName['fullClassName']
+ )
+ );
+ }
+
+ if ($class->hasMethod('__clone')) {
+ $cloneMethod = $class->getMethod('__clone');
+
+ if (!$cloneMethod->isFinal()) {
+ if ($callOriginalClone && !$isInterface) {
+ $cloneTemplate = new Text_Template(
+ $templateDir . 'unmocked_clone.tpl'
+ );
+ } else {
+ $cloneTemplate = new Text_Template(
+ $templateDir . 'mocked_clone.tpl'
+ );
+ }
+ }
+ } else {
+ $cloneTemplate = new Text_Template(
+ $templateDir . 'mocked_clone.tpl'
+ );
+ }
+ }
+
+ if (is_object($cloneTemplate)) {
+ $cloneTemplate = $cloneTemplate->render();
+ }
+
+ if (is_array($methods) && empty($methods) &&
+ ($isClass || $isInterface)) {
+ $methods = $this->getClassMethods($mockClassName['fullClassName']);
+ }
+
+ if (!is_array($methods)) {
+ $methods = array();
+ }
+
+ $mockedMethods = '';
+
+ if (isset($class)) {
+ // https://github.com/sebastianbergmann/phpunit-mock-objects/issues/103
+ if ($isInterface && $class->implementsInterface('Traversable') &&
+ !$class->implementsInterface('Iterator') &&
+ !$class->implementsInterface('IteratorAggregate')) {
+ $additionalInterfaces[] = 'Iterator';
+ $methods = array_merge($methods, $this->getClassMethods('Iterator'));
+ }
+
+ foreach ($methods as $methodName) {
+ try {
+ $method = $class->getMethod($methodName);
+
+ if ($this->canMockMethod($method)) {
+ $mockedMethods .= $this->generateMockedMethodDefinitionFromExisting(
+ $templateDir,
+ $method,
+ $cloneArguments,
+ $callOriginalMethods
+ );
+ }
+ } catch (ReflectionException $e) {
+ $mockedMethods .= $this->generateMockedMethodDefinition(
+ $templateDir,
+ $mockClassName['fullClassName'],
+ $methodName,
+ $cloneArguments
+ );
+ }
+ }
+ } else {
+ foreach ($methods as $methodName) {
+ $mockedMethods .= $this->generateMockedMethodDefinition(
+ $templateDir,
+ $mockClassName['fullClassName'],
+ $methodName,
+ $cloneArguments
+ );
+ }
+ }
+
+ $method = '';
+
+ if (!in_array('method', $methods)) {
+ $methodTemplate = new Text_Template(
+ $templateDir . 'mocked_class_method.tpl'
+ );
+
+ $method = $methodTemplate->render();
+ }
+
+ $classTemplate->setVar(
+ array(
+ 'prologue' => isset($prologue) ? $prologue : '',
+ 'epilogue' => isset($epilogue) ? $epilogue : '',
+ 'class_declaration' => $this->generateMockClassDeclaration(
+ $mockClassName,
+ $isInterface,
+ $additionalInterfaces
+ ),
+ 'clone' => $cloneTemplate,
+ 'mock_class_name' => $mockClassName['className'],
+ 'mocked_methods' => $mockedMethods,
+ 'method' => $method
+ )
+ );
+
+ return array(
+ 'code' => $classTemplate->render(),
+ 'mockClassName' => $mockClassName['className']
+ );
+ }
+
+ /**
+ * @param array|string $type
+ * @param string $className
+ * @param string $prefix
+ * @return array
+ */
+ protected function generateClassName($type, $className, $prefix)
+ {
+ if (is_array($type)) {
+ $type = implode('_', $type);
+ }
+
+ if ($type[0] == '\\') {
+ $type = substr($type, 1);
+ }
+
+ $classNameParts = explode('\\', $type);
+
+ if (count($classNameParts) > 1) {
+ $type = array_pop($classNameParts);
+ $namespaceName = implode('\\', $classNameParts);
+ $fullClassName = $namespaceName . '\\' . $type;
+ } else {
+ $namespaceName = '';
+ $fullClassName = $type;
+ }
+
+ if ($className == '') {
+ do {
+ $className = $prefix . $type . '_' .
+ substr(md5(microtime()), 0, 8);
+ } while (class_exists($className, false));
+ }
+
+ return array(
+ 'className' => $className,
+ 'originalClassName' => $type,
+ 'fullClassName' => $fullClassName,
+ 'namespaceName' => $namespaceName
+ );
+ }
+
+ /**
+ * @param array $mockClassName
+ * @param bool $isInterface
+ * @param array $additionalInterfaces
+ * @return array
+ */
+ protected function generateMockClassDeclaration(array $mockClassName, $isInterface, array $additionalInterfaces = array())
+ {
+ $buffer = 'class ';
+
+ $additionalInterfaces[] = 'PHPUnit_Framework_MockObject_MockObject';
+ $interfaces = implode(', ', $additionalInterfaces);
+
+ if ($isInterface) {
+ $buffer .= sprintf(
+ '%s implements %s',
+ $mockClassName['className'],
+ $interfaces
+ );
+
+ if (!in_array($mockClassName['originalClassName'], $additionalInterfaces)) {
+ $buffer .= ', ';
+
+ if (!empty($mockClassName['namespaceName'])) {
+ $buffer .= $mockClassName['namespaceName'] . '\\';
+ }
+
+ $buffer .= $mockClassName['originalClassName'];
+ }
+ } else {
+ $buffer .= sprintf(
+ '%s extends %s%s implements %s',
+ $mockClassName['className'],
+ !empty($mockClassName['namespaceName']) ? $mockClassName['namespaceName'] . '\\' : '',
+ $mockClassName['originalClassName'],
+ $interfaces
+ );
+ }
+
+ return $buffer;
+ }
+
+ /**
+ * @param string $templateDir
+ * @param ReflectionMethod $method
+ * @param bool $cloneArguments
+ * @param bool $callOriginalMethods
+ * @return string
+ */
+ protected function generateMockedMethodDefinitionFromExisting($templateDir, ReflectionMethod $method, $cloneArguments, $callOriginalMethods)
+ {
+ if ($method->isPrivate()) {
+ $modifier = 'private';
+ } elseif ($method->isProtected()) {
+ $modifier = 'protected';
+ } else {
+ $modifier = 'public';
+ }
+
+ if ($method->isStatic()) {
+ $modifier .= ' static';
+ }
+
+ if ($method->returnsReference()) {
+ $reference = '&';
+ } else {
+ $reference = '';
+ }
+
+ return $this->generateMockedMethodDefinition(
+ $templateDir,
+ $method->getDeclaringClass()->getName(),
+ $method->getName(),
+ $cloneArguments,
+ $modifier,
+ $this->getMethodParameters($method),
+ $this->getMethodParameters($method, true),
+ $reference,
+ $callOriginalMethods,
+ $method->isStatic()
+ );
+ }
+
+ /**
+ * @param string $templateDir
+ * @param string $className
+ * @param string $methodName
+ * @param bool $cloneArguments
+ * @param string $modifier
+ * @param string $arguments_decl
+ * @param string $arguments_call
+ * @param string $reference
+ * @param bool $callOriginalMethods
+ * @param bool $static
+ * @return string
+ */
+ protected function generateMockedMethodDefinition($templateDir, $className, $methodName, $cloneArguments = true, $modifier = 'public', $arguments_decl = '', $arguments_call = '', $reference = '', $callOriginalMethods = false, $static = false)
+ {
+ if ($static) {
+ $templateFile = 'mocked_static_method.tpl';
+ } else {
+ $templateFile = sprintf(
+ '%s_method.tpl',
+ $callOriginalMethods ? 'proxied' : 'mocked'
+ );
+ }
+
+ $template = new Text_Template($templateDir . $templateFile);
+
+ $template->setVar(
+ array(
+ 'arguments_decl' => $arguments_decl,
+ 'arguments_call' => $arguments_call,
+ 'arguments_count' => !empty($arguments_call) ? count(explode(',', $arguments_call)) : 0,
+ 'class_name' => $className,
+ 'method_name' => $methodName,
+ 'modifier' => $modifier,
+ 'reference' => $reference,
+ 'clone_arguments' => $cloneArguments ? 'TRUE' : 'FALSE'
+ )
+ );
+
+ return $template->render();
+ }
+
+ /**
+ * @param ReflectionMethod $method
+ * @return bool
+ */
+ protected function canMockMethod(ReflectionMethod $method)
+ {
+ if ($method->isConstructor() ||
+ $method->isFinal() ||
+ $method->isPrivate() ||
+ isset($this->blacklistedMethodNames[$method->getName()])) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Returns the parameters of a function or method.
+ *
+ * @param ReflectionMethod $method
+ * @param bool $forCall
+ * @return string
+ * @throws PHPUnit_Framework_MockObject_RuntimeException
+ * @since Method available since Release 2.0.0
+ */
+ protected function getMethodParameters(ReflectionMethod $method, $forCall = false)
+ {
+ $parameters = array();
+
+ foreach ($method->getParameters() as $i => $parameter) {
+ $name = '$' . $parameter->getName();
+
+ /* Note: PHP extensions may use empty names for reference arguments
+ * or "..." for methods taking a variable number of arguments.
+ */
+ if ($name === '$' || $name === '$...') {
+ $name = '$arg' . $i;
+ }
+
+ if ($this->isVariadic($parameter)) {
+ if ($forCall) {
+ continue;
+ } else {
+ $name = '...' . $name;
+ }
+ }
+
+ $default = '';
+ $reference = '';
+ $typeDeclaration = '';
+
+ if (!$forCall) {
+ if ($this->hasType($parameter)) {
+ $typeDeclaration = (string) $parameter->getType() . ' ';
+ } elseif ($parameter->isArray()) {
+ $typeDeclaration = 'array ';
+ } elseif ((defined('HHVM_VERSION') || version_compare(PHP_VERSION, '5.4.0', '>='))
+ && $parameter->isCallable()) {
+ $typeDeclaration = 'callable ';
+ } else {
+ try {
+ $class = $parameter->getClass();
+ } catch (ReflectionException $e) {
+ throw new PHPUnit_Framework_MockObject_RuntimeException(
+ sprintf(
+ 'Cannot mock %s::%s() because a class or ' .
+ 'interface used in the signature is not loaded',
+ $method->getDeclaringClass()->getName(),
+ $method->getName()
+ ),
+ 0,
+ $e
+ );
+ }
+
+ if ($class !== null) {
+ $typeDeclaration = $class->getName() . ' ';
+ }
+ }
+
+ if (!$this->isVariadic($parameter)) {
+ if ($parameter->isDefaultValueAvailable()) {
+ $value = $parameter->getDefaultValue();
+ $default = ' = ' . var_export($value, true);
+ } elseif ($parameter->isOptional()) {
+ $default = ' = null';
+ }
+ }
+ }
+
+ if ($parameter->isPassedByReference()) {
+ $reference = '&';
+ }
+
+ $parameters[] = $typeDeclaration . $reference . $name . $default;
+ }
+
+ return implode(', ', $parameters);
+ }
+
+ /**
+ * @param ReflectionParameter $parameter
+ * @return bool
+ * @since Method available since Release 2.2.1
+ */
+ private function isVariadic(ReflectionParameter $parameter)
+ {
+ return method_exists('ReflectionParameter', 'isVariadic') && $parameter->isVariadic();
+ }
+
+ /**
+ * @param ReflectionParameter $parameter
+ * @return bool
+ * @since Method available since Release 2.3.4
+ */
+ private function hasType(ReflectionParameter $parameter)
+ {
+ return method_exists('ReflectionParameter', 'hasType') && $parameter->hasType();
+ }
+
+ /**
+ * @param string $className
+ * @return array
+ * @since Method available since Release 2.3.2
+ */
+ private function getClassMethods($className)
+ {
+ $class = new ReflectionClass($className);
+ $methods = array();
+
+ foreach ($class->getMethods() as $method) {
+ if (($method->isPublic() || $method->isAbstract()) && !in_array($method->getName(), $methods)) {
+ $methods[] = $method->getName();
+ }
+ }
+
+ return $methods;
+ }
+}