OSDN Git Service

Добавление типов оборудования
[invent/invent.git] / vendor / phpunit / phpunit / src / Framework / MockObject / MockMethod.php
1 <?php declare(strict_types=1);
2 /*
3  * This file is part of PHPUnit.
4  *
5  * (c) Sebastian Bergmann <sebastian@phpunit.de>
6  *
7  * For the full copyright and license information, please view the LICENSE
8  * file that was distributed with this source code.
9  */
10 namespace PHPUnit\Framework\MockObject;
11
12 use SebastianBergmann\Type\ObjectType;
13 use SebastianBergmann\Type\Type;
14 use SebastianBergmann\Type\UnknownType;
15 use SebastianBergmann\Type\VoidType;
16
17 /**
18  * @internal This class is not covered by the backward compatibility promise for PHPUnit
19  */
20 final class MockMethod
21 {
22     /**
23      * @var \Text_Template[]
24      */
25     private static $templates = [];
26
27     /**
28      * @var string
29      */
30     private $className;
31
32     /**
33      * @var string
34      */
35     private $methodName;
36
37     /**
38      * @var bool
39      */
40     private $cloneArguments;
41
42     /**
43      * @var string string
44      */
45     private $modifier;
46
47     /**
48      * @var string
49      */
50     private $argumentsForDeclaration;
51
52     /**
53      * @var string
54      */
55     private $argumentsForCall;
56
57     /**
58      * @var Type
59      */
60     private $returnType;
61
62     /**
63      * @var string
64      */
65     private $reference;
66
67     /**
68      * @var bool
69      */
70     private $callOriginalMethod;
71
72     /**
73      * @var bool
74      */
75     private $static;
76
77     /**
78      * @var ?string
79      */
80     private $deprecation;
81
82     /**
83      * @var bool
84      */
85     private $allowsReturnNull;
86
87     /**
88      * @throws RuntimeException
89      */
90     public static function fromReflection(\ReflectionMethod $method, bool $callOriginalMethod, bool $cloneArguments): self
91     {
92         if ($method->isPrivate()) {
93             $modifier = 'private';
94         } elseif ($method->isProtected()) {
95             $modifier = 'protected';
96         } else {
97             $modifier = 'public';
98         }
99
100         if ($method->isStatic()) {
101             $modifier .= ' static';
102         }
103
104         if ($method->returnsReference()) {
105             $reference = '&';
106         } else {
107             $reference = '';
108         }
109
110         $docComment = $method->getDocComment();
111
112         if (\is_string($docComment) &&
113             \preg_match('#\*[ \t]*+@deprecated[ \t]*+(.*?)\r?+\n[ \t]*+\*(?:[ \t]*+@|/$)#s', $docComment, $deprecation)) {
114             $deprecation = \trim(\preg_replace('#[ \t]*\r?\n[ \t]*+\*[ \t]*+#', ' ', $deprecation[1]));
115         } else {
116             $deprecation = null;
117         }
118
119         return new self(
120             $method->getDeclaringClass()->getName(),
121             $method->getName(),
122             $cloneArguments,
123             $modifier,
124             self::getMethodParameters($method),
125             self::getMethodParameters($method, true),
126             self::deriveReturnType($method),
127             $reference,
128             $callOriginalMethod,
129             $method->isStatic(),
130             $deprecation,
131             $method->hasReturnType() && $method->getReturnType()->allowsNull()
132         );
133     }
134
135     public static function fromName(string $fullClassName, string $methodName, bool $cloneArguments): self
136     {
137         return new self(
138             $fullClassName,
139             $methodName,
140             $cloneArguments,
141             'public',
142             '',
143             '',
144             new UnknownType,
145             '',
146             false,
147             false,
148             null,
149             false
150         );
151     }
152
153     public function __construct(string $className, string $methodName, bool $cloneArguments, string $modifier, string $argumentsForDeclaration, string $argumentsForCall, Type $returnType, string $reference, bool $callOriginalMethod, bool $static, ?string $deprecation, bool $allowsReturnNull)
154     {
155         $this->className               = $className;
156         $this->methodName              = $methodName;
157         $this->cloneArguments          = $cloneArguments;
158         $this->modifier                = $modifier;
159         $this->argumentsForDeclaration = $argumentsForDeclaration;
160         $this->argumentsForCall        = $argumentsForCall;
161         $this->returnType              = $returnType;
162         $this->reference               = $reference;
163         $this->callOriginalMethod      = $callOriginalMethod;
164         $this->static                  = $static;
165         $this->deprecation             = $deprecation;
166         $this->allowsReturnNull        = $allowsReturnNull;
167     }
168
169     public function getName(): string
170     {
171         return $this->methodName;
172     }
173
174     /**
175      * @throws RuntimeException
176      */
177     public function generateCode(): string
178     {
179         if ($this->static) {
180             $templateFile = 'mocked_static_method.tpl';
181         } elseif ($this->returnType instanceof VoidType) {
182             $templateFile = \sprintf(
183                 '%s_method_void.tpl',
184                 $this->callOriginalMethod ? 'proxied' : 'mocked'
185             );
186         } else {
187             $templateFile = \sprintf(
188                 '%s_method.tpl',
189                 $this->callOriginalMethod ? 'proxied' : 'mocked'
190             );
191         }
192
193         $deprecation = $this->deprecation;
194
195         if (null !== $this->deprecation) {
196             $deprecation         = "The $this->className::$this->methodName method is deprecated ($this->deprecation).";
197             $deprecationTemplate = $this->getTemplate('deprecation.tpl');
198
199             $deprecationTemplate->setVar([
200                 'deprecation' => \var_export($deprecation, true),
201             ]);
202
203             $deprecation = $deprecationTemplate->render();
204         }
205
206         $template = $this->getTemplate($templateFile);
207
208         $template->setVar(
209             [
210                 'arguments_decl'     => $this->argumentsForDeclaration,
211                 'arguments_call'     => $this->argumentsForCall,
212                 'return_declaration' => $this->returnType->getReturnTypeDeclaration(),
213                 'arguments_count'    => !empty($this->argumentsForCall) ? \substr_count($this->argumentsForCall, ',') + 1 : 0,
214                 'class_name'         => $this->className,
215                 'method_name'        => $this->methodName,
216                 'modifier'           => $this->modifier,
217                 'reference'          => $this->reference,
218                 'clone_arguments'    => $this->cloneArguments ? 'true' : 'false',
219                 'deprecation'        => $deprecation,
220             ]
221         );
222
223         return $template->render();
224     }
225
226     public function getReturnType(): Type
227     {
228         return $this->returnType;
229     }
230
231     private function getTemplate(string $template): \Text_Template
232     {
233         $filename = __DIR__ . \DIRECTORY_SEPARATOR . 'Generator' . \DIRECTORY_SEPARATOR . $template;
234
235         if (!isset(self::$templates[$filename])) {
236             self::$templates[$filename] = new \Text_Template($filename);
237         }
238
239         return self::$templates[$filename];
240     }
241
242     /**
243      * Returns the parameters of a function or method.
244      *
245      * @throws RuntimeException
246      */
247     private static function getMethodParameters(\ReflectionMethod $method, bool $forCall = false): string
248     {
249         $parameters = [];
250
251         foreach ($method->getParameters() as $i => $parameter) {
252             $name = '$' . $parameter->getName();
253
254             /* Note: PHP extensions may use empty names for reference arguments
255              * or "..." for methods taking a variable number of arguments.
256              */
257             if ($name === '$' || $name === '$...') {
258                 $name = '$arg' . $i;
259             }
260
261             if ($parameter->isVariadic()) {
262                 if ($forCall) {
263                     continue;
264                 }
265
266                 $name = '...' . $name;
267             }
268
269             $nullable        = '';
270             $default         = '';
271             $reference       = '';
272             $typeDeclaration = '';
273
274             if (!$forCall) {
275                 if ($parameter->hasType() && $parameter->allowsNull()) {
276                     $nullable = '?';
277                 }
278
279                 if ($parameter->hasType()) {
280                     $type = $parameter->getType();
281
282                     if ($type instanceof \ReflectionNamedType && $type->getName() !== 'self') {
283                         $typeDeclaration = $type->getName() . ' ';
284                     } else {
285                         try {
286                             $class = $parameter->getClass();
287                             // @codeCoverageIgnoreStart
288                         } catch (\ReflectionException $e) {
289                             throw new RuntimeException(
290                                 \sprintf(
291                                     'Cannot mock %s::%s() because a class or ' .
292                                     'interface used in the signature is not loaded',
293                                     $method->getDeclaringClass()->getName(),
294                                     $method->getName()
295                                 ),
296                                 0,
297                                 $e
298                             );
299                         }
300                         // @codeCoverageIgnoreEnd
301
302                         if ($class !== null) {
303                             $typeDeclaration = $class->getName() . ' ';
304                         }
305                     }
306                 }
307
308                 if (!$parameter->isVariadic()) {
309                     if ($parameter->isDefaultValueAvailable()) {
310                         try {
311                             $value = \var_export($parameter->getDefaultValue(), true);
312                             // @codeCoverageIgnoreStart
313                         } catch (\ReflectionException $e) {
314                             throw new RuntimeException(
315                                 $e->getMessage(),
316                                 (int) $e->getCode(),
317                                 $e
318                             );
319                         }
320                         // @codeCoverageIgnoreEnd
321
322                         $default = ' = ' . $value;
323                     } elseif ($parameter->isOptional()) {
324                         $default = ' = null';
325                     }
326                 }
327             }
328
329             if ($parameter->isPassedByReference()) {
330                 $reference = '&';
331             }
332
333             $parameters[] = $nullable . $typeDeclaration . $reference . $name . $default;
334         }
335
336         return \implode(', ', $parameters);
337     }
338
339     private static function deriveReturnType(\ReflectionMethod $method): Type
340     {
341         $returnType = $method->getReturnType();
342
343         if ($returnType === null) {
344             return new UnknownType();
345         }
346
347         // @see https://bugs.php.net/bug.php?id=70722
348         if ($returnType->getName() === 'self') {
349             return ObjectType::fromName($method->getDeclaringClass()->getName(), $returnType->allowsNull());
350         }
351
352         // @see https://github.com/sebastianbergmann/phpunit-mock-objects/issues/406
353         if ($returnType->getName() === 'parent') {
354             $parentClass = $method->getDeclaringClass()->getParentClass();
355
356             if ($parentClass === false) {
357                 throw new RuntimeException(
358                     \sprintf(
359                         'Cannot mock %s::%s because "parent" return type declaration is used but %s does not have a parent class',
360                         $method->getDeclaringClass()->getName(),
361                         $method->getName(),
362                         $method->getDeclaringClass()->getName()
363                     )
364                 );
365             }
366
367             return ObjectType::fromName($parentClass->getName(), $returnType->allowsNull());
368         }
369
370         return Type::fromName($returnType->getName(), $returnType->allowsNull());
371     }
372 }