Mercurial > hg > isophonics-drupal-site
diff vendor/phpunit/phpunit-mock-objects/src/Framework/MockObject/Generator.php @ 0:4c8ae668cc8c
Initial import (non-working)
author | Chris Cannam |
---|---|
date | Wed, 29 Nov 2017 16:09:58 +0000 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vendor/phpunit/phpunit-mock-objects/src/Framework/MockObject/Generator.php Wed Nov 29 16:09:58 2017 +0000 @@ -0,0 +1,1120 @@ +<?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; + } +}