123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363 |
- <?php declare(strict_types=1);
- /*
- * This file is part of PHPUnit.
- *
- * This file is modified to replace ReflectionParameter::getClass() usage,
- * which is deprecated in PHP 8.
- *
- * When the test suite is updated for compatibility with PHPUnit 9.x,
- * this override can be removed.
- *
- * (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.
- */
- namespace PHPUnit\Framework\MockObject;
- use ReflectionClass;
- use ReflectionException;
- use ReflectionMethod;
- use Text_Template;
- final class MockMethod
- {
- /**
- * @var Text_Template[]
- */
- private static $templates = [];
- /**
- * @var string
- */
- private $className;
- /**
- * @var string
- */
- private $methodName;
- /**
- * @var bool
- */
- private $cloneArguments;
- /**
- * @var string string
- */
- private $modifier;
- /**
- * @var string
- */
- private $argumentsForDeclaration;
- /**
- * @var string
- */
- private $argumentsForCall;
- /**
- * @var string
- */
- private $returnType;
- /**
- * @var string
- */
- private $reference;
- /**
- * @var bool
- */
- private $callOriginalMethod;
- /**
- * @var bool
- */
- private $static;
- /**
- * @var ?string
- */
- private $deprecation;
- /**
- * @var bool
- */
- private $allowsReturnNull;
- public static function fromReflection(ReflectionMethod $method, bool $callOriginalMethod, bool $cloneArguments): self
- {
- if ($method->isPrivate()) {
- $modifier = 'private';
- } elseif ($method->isProtected()) {
- $modifier = 'protected';
- } else {
- $modifier = 'public';
- }
- if ($method->isStatic()) {
- $modifier .= ' static';
- }
- if ($method->returnsReference()) {
- $reference = '&';
- } else {
- $reference = '';
- }
- if ($method->hasReturnType()) {
- $returnType = $method->getReturnType()->getName();
- } else {
- $returnType = '';
- }
- $docComment = $method->getDocComment();
- if (\is_string($docComment)
- && \preg_match('#\*[ \t]*+@deprecated[ \t]*+(.*?)\r?+\n[ \t]*+\*(?:[ \t]*+@|/$)#s', $docComment, $deprecation)
- ) {
- $deprecation = \trim(\preg_replace('#[ \t]*\r?\n[ \t]*+\*[ \t]*+#', ' ', $deprecation[1]));
- } else {
- $deprecation = null;
- }
- return new self(
- $method->getDeclaringClass()->getName(),
- $method->getName(),
- $cloneArguments,
- $modifier,
- self::getMethodParameters($method),
- self::getMethodParameters($method, true),
- $returnType,
- $reference,
- $callOriginalMethod,
- $method->isStatic(),
- $deprecation,
- $method->hasReturnType() && $method->getReturnType()->allowsNull()
- );
- }
- public static function fromName(string $fullClassName, string $methodName, bool $cloneArguments): self
- {
- return new self(
- $fullClassName,
- $methodName,
- $cloneArguments,
- 'public',
- '',
- '',
- '',
- '',
- false,
- false,
- null,
- false
- );
- }
- public function __construct(string $className, string $methodName, bool $cloneArguments, string $modifier, string $argumentsForDeclaration, string $argumentsForCall, string $returnType, string $reference, bool $callOriginalMethod, bool $static, ?string $deprecation, bool $allowsReturnNull)
- {
- $this->className = $className;
- $this->methodName = $methodName;
- $this->cloneArguments = $cloneArguments;
- $this->modifier = $modifier;
- $this->argumentsForDeclaration = $argumentsForDeclaration;
- $this->argumentsForCall = $argumentsForCall;
- $this->returnType = $returnType;
- $this->reference = $reference;
- $this->callOriginalMethod = $callOriginalMethod;
- $this->static = $static;
- $this->deprecation = $deprecation;
- $this->allowsReturnNull = $allowsReturnNull;
- }
- public function getName(): string
- {
- return $this->methodName;
- }
- /**
- * @throws \ReflectionException
- * @throws \PHPUnit\Framework\MockObject\RuntimeException
- * @throws \InvalidArgumentException
- */
- public function generateCode(): string
- {
- if ($this->static) {
- $templateFile = 'mocked_static_method.tpl';
- } elseif ($this->returnType === 'void') {
- $templateFile = \sprintf(
- '%s_method_void.tpl',
- $this->callOriginalMethod ? 'proxied' : 'mocked'
- );
- } else {
- $templateFile = \sprintf(
- '%s_method.tpl',
- $this->callOriginalMethod ? 'proxied' : 'mocked'
- );
- }
- $returnType = $this->returnType;
- // @see https://bugs.php.net/bug.php?id=70722
- if ($returnType === 'self') {
- $returnType = $this->className;
- }
- // @see https://github.com/sebastianbergmann/phpunit-mock-objects/issues/406
- if ($returnType === 'parent') {
- $reflector = new ReflectionClass($this->className);
- $parentClass = $reflector->getParentClass();
- if ($parentClass === false) {
- throw new RuntimeException(
- \sprintf(
- 'Cannot mock %s::%s because "parent" return type declaration is used but %s does not have a parent class',
- $this->className,
- $this->methodName,
- $this->className
- )
- );
- }
- $returnType = $parentClass->getName();
- }
- $deprecation = $this->deprecation;
- if (null !== $this->deprecation) {
- $deprecation = "The $this->className::$this->methodName method is deprecated ($this->deprecation).";
- $deprecationTemplate = $this->getTemplate('deprecation.tpl');
- $deprecationTemplate->setVar([
- 'deprecation' => \var_export($deprecation, true),
- ]);
- $deprecation = $deprecationTemplate->render();
- }
- $template = $this->getTemplate($templateFile);
- $template->setVar(
- [
- 'arguments_decl' => $this->argumentsForDeclaration,
- 'arguments_call' => $this->argumentsForCall,
- 'return_delim' => $returnType ? ': ' : '',
- 'return_type' => $this->allowsReturnNull ? '?' . $returnType : $returnType,
- 'arguments_count' => !empty($this->argumentsForCall) ? \substr_count($this->argumentsForCall, ',') + 1 : 0,
- 'class_name' => $this->className,
- 'method_name' => $this->methodName,
- 'modifier' => $this->modifier,
- 'reference' => $this->reference,
- 'clone_arguments' => $this->cloneArguments ? 'true' : 'false',
- 'deprecation' => $deprecation,
- ]
- );
- return $template->render();
- }
- private function getTemplate(string $template): Text_Template
- {
- $filename = __DIR__ . \DIRECTORY_SEPARATOR . 'Generator' . \DIRECTORY_SEPARATOR . $template;
- if (!isset(self::$templates[$filename])) {
- self::$templates[$filename] = new Text_Template($filename);
- }
- return self::$templates[$filename];
- }
- /**
- * Returns the parameters of a function or method.
- *
- * @throws RuntimeException
- */
- private static function getMethodParameters(ReflectionMethod $method, bool $forCall = false): string
- {
- $parameters = [];
- 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 ($parameter->isVariadic()) {
- if ($forCall) {
- continue;
- }
- $name = '...' . $name;
- }
- $nullable = '';
- $default = '';
- $reference = '';
- $typeDeclaration = '';
- if (!$forCall) {
- if ($parameter->hasType() && $parameter->allowsNull()) {
- $nullable = '?';
- }
- if ($parameter->hasType() && $parameter->getType()->getName() !== 'self') {
- $typeDeclaration = $parameter->getType()->getName() . ' ';
- } else {
- try {
- $class = $parameter->getType() && !$parameter->getType()->isBuiltin()
- ? new ReflectionClass($parameter->getType()->getName())
- : null;
- } catch (ReflectionException $e) {
- throw new 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 (!$parameter->isVariadic()) {
- if ($parameter->isDefaultValueAvailable()) {
- try {
- $value = \var_export($parameter->getDefaultValue(), true);
- } catch (\ReflectionException $e) {
- throw new RuntimeException(
- $e->getMessage(),
- (int) $e->getCode(),
- $e
- );
- }
- $default = ' = ' . $value;
- } elseif ($parameter->isOptional()) {
- $default = ' = null';
- }
- }
- }
- if ($parameter->isPassedByReference()) {
- $reference = '&';
- }
- $parameters[] = $nullable . $typeDeclaration . $reference . $name . $default;
- }
- return \implode(', ', $parameters);
- }
- }
|