PageRenderTime 47ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/phpunit/phpunit/src/Framework/MockObject/MockMethod.php

https://gitlab.com/madwanz64/laravel
PHP | 434 lines | 294 code | 62 blank | 78 comment | 32 complexity | 15e81aba6d3cb6b620fd91595b642526 MD5 | raw file
  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. use const DIRECTORY_SEPARATOR;
  12. use function explode;
  13. use function implode;
  14. use function is_object;
  15. use function is_string;
  16. use function preg_match;
  17. use function preg_replace;
  18. use function sprintf;
  19. use function strlen;
  20. use function strpos;
  21. use function substr;
  22. use function substr_count;
  23. use function trim;
  24. use function var_export;
  25. use ReflectionIntersectionType;
  26. use ReflectionMethod;
  27. use ReflectionNamedType;
  28. use ReflectionParameter;
  29. use ReflectionUnionType;
  30. use SebastianBergmann\Template\Exception as TemplateException;
  31. use SebastianBergmann\Template\Template;
  32. use SebastianBergmann\Type\ReflectionMapper;
  33. use SebastianBergmann\Type\Type;
  34. use SebastianBergmann\Type\UnknownType;
  35. /**
  36. * @internal This class is not covered by the backward compatibility promise for PHPUnit
  37. */
  38. final class MockMethod
  39. {
  40. /**
  41. * @var Template[]
  42. */
  43. private static $templates = [];
  44. /**
  45. * @var string
  46. */
  47. private $className;
  48. /**
  49. * @var string
  50. */
  51. private $methodName;
  52. /**
  53. * @var bool
  54. */
  55. private $cloneArguments;
  56. /**
  57. * @var string string
  58. */
  59. private $modifier;
  60. /**
  61. * @var string
  62. */
  63. private $argumentsForDeclaration;
  64. /**
  65. * @var string
  66. */
  67. private $argumentsForCall;
  68. /**
  69. * @var Type
  70. */
  71. private $returnType;
  72. /**
  73. * @var string
  74. */
  75. private $reference;
  76. /**
  77. * @var bool
  78. */
  79. private $callOriginalMethod;
  80. /**
  81. * @var bool
  82. */
  83. private $static;
  84. /**
  85. * @var ?string
  86. */
  87. private $deprecation;
  88. /**
  89. * @throws ReflectionException
  90. * @throws RuntimeException
  91. */
  92. public static function fromReflection(ReflectionMethod $method, bool $callOriginalMethod, bool $cloneArguments): self
  93. {
  94. if ($method->isPrivate()) {
  95. $modifier = 'private';
  96. } elseif ($method->isProtected()) {
  97. $modifier = 'protected';
  98. } else {
  99. $modifier = 'public';
  100. }
  101. if ($method->isStatic()) {
  102. $modifier .= ' static';
  103. }
  104. if ($method->returnsReference()) {
  105. $reference = '&';
  106. } else {
  107. $reference = '';
  108. }
  109. $docComment = $method->getDocComment();
  110. if (is_string($docComment) &&
  111. preg_match('#\*[ \t]*+@deprecated[ \t]*+(.*?)\r?+\n[ \t]*+\*(?:[ \t]*+@|/$)#s', $docComment, $deprecation)) {
  112. $deprecation = trim(preg_replace('#[ \t]*\r?\n[ \t]*+\*[ \t]*+#', ' ', $deprecation[1]));
  113. } else {
  114. $deprecation = null;
  115. }
  116. return new self(
  117. $method->getDeclaringClass()->getName(),
  118. $method->getName(),
  119. $cloneArguments,
  120. $modifier,
  121. self::getMethodParametersForDeclaration($method),
  122. self::getMethodParametersForCall($method),
  123. (new ReflectionMapper)->fromReturnType($method),
  124. $reference,
  125. $callOriginalMethod,
  126. $method->isStatic(),
  127. $deprecation
  128. );
  129. }
  130. public static function fromName(string $fullClassName, string $methodName, bool $cloneArguments): self
  131. {
  132. return new self(
  133. $fullClassName,
  134. $methodName,
  135. $cloneArguments,
  136. 'public',
  137. '',
  138. '',
  139. new UnknownType,
  140. '',
  141. false,
  142. false,
  143. null
  144. );
  145. }
  146. 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)
  147. {
  148. $this->className = $className;
  149. $this->methodName = $methodName;
  150. $this->cloneArguments = $cloneArguments;
  151. $this->modifier = $modifier;
  152. $this->argumentsForDeclaration = $argumentsForDeclaration;
  153. $this->argumentsForCall = $argumentsForCall;
  154. $this->returnType = $returnType;
  155. $this->reference = $reference;
  156. $this->callOriginalMethod = $callOriginalMethod;
  157. $this->static = $static;
  158. $this->deprecation = $deprecation;
  159. }
  160. public function getName(): string
  161. {
  162. return $this->methodName;
  163. }
  164. /**
  165. * @throws RuntimeException
  166. */
  167. public function generateCode(): string
  168. {
  169. if ($this->static) {
  170. $templateFile = 'mocked_static_method.tpl';
  171. } elseif ($this->returnType->isNever() || $this->returnType->isVoid()) {
  172. $templateFile = sprintf(
  173. '%s_method_never_or_void.tpl',
  174. $this->callOriginalMethod ? 'proxied' : 'mocked'
  175. );
  176. } else {
  177. $templateFile = sprintf(
  178. '%s_method.tpl',
  179. $this->callOriginalMethod ? 'proxied' : 'mocked'
  180. );
  181. }
  182. $deprecation = $this->deprecation;
  183. if (null !== $this->deprecation) {
  184. $deprecation = "The {$this->className}::{$this->methodName} method is deprecated ({$this->deprecation}).";
  185. $deprecationTemplate = $this->getTemplate('deprecation.tpl');
  186. $deprecationTemplate->setVar(
  187. [
  188. 'deprecation' => var_export($deprecation, true),
  189. ]
  190. );
  191. $deprecation = $deprecationTemplate->render();
  192. }
  193. $template = $this->getTemplate($templateFile);
  194. $template->setVar(
  195. [
  196. 'arguments_decl' => $this->argumentsForDeclaration,
  197. 'arguments_call' => $this->argumentsForCall,
  198. 'return_declaration' => !empty($this->returnType->asString()) ? (': ' . $this->returnType->asString()) : '',
  199. 'return_type' => $this->returnType->asString(),
  200. 'arguments_count' => !empty($this->argumentsForCall) ? substr_count($this->argumentsForCall, ',') + 1 : 0,
  201. 'class_name' => $this->className,
  202. 'method_name' => $this->methodName,
  203. 'modifier' => $this->modifier,
  204. 'reference' => $this->reference,
  205. 'clone_arguments' => $this->cloneArguments ? 'true' : 'false',
  206. 'deprecation' => $deprecation,
  207. ]
  208. );
  209. return $template->render();
  210. }
  211. public function getReturnType(): Type
  212. {
  213. return $this->returnType;
  214. }
  215. /**
  216. * @throws RuntimeException
  217. */
  218. private function getTemplate(string $template): Template
  219. {
  220. $filename = __DIR__ . DIRECTORY_SEPARATOR . 'Generator' . DIRECTORY_SEPARATOR . $template;
  221. if (!isset(self::$templates[$filename])) {
  222. try {
  223. self::$templates[$filename] = new Template($filename);
  224. } catch (TemplateException $e) {
  225. throw new RuntimeException(
  226. $e->getMessage(),
  227. (int) $e->getCode(),
  228. $e
  229. );
  230. }
  231. }
  232. return self::$templates[$filename];
  233. }
  234. /**
  235. * Returns the parameters of a function or method.
  236. *
  237. * @throws RuntimeException
  238. */
  239. private static function getMethodParametersForDeclaration(ReflectionMethod $method): string
  240. {
  241. $parameters = [];
  242. foreach ($method->getParameters() as $i => $parameter) {
  243. $name = '$' . $parameter->getName();
  244. /* Note: PHP extensions may use empty names for reference arguments
  245. * or "..." for methods taking a variable number of arguments.
  246. */
  247. if ($name === '$' || $name === '$...') {
  248. $name = '$arg' . $i;
  249. }
  250. $nullable = '';
  251. $default = '';
  252. $reference = '';
  253. $typeDeclaration = '';
  254. $type = null;
  255. $typeName = null;
  256. if ($parameter->hasType()) {
  257. $type = $parameter->getType();
  258. if ($type instanceof ReflectionNamedType) {
  259. $typeName = $type->getName();
  260. }
  261. }
  262. if ($parameter->isVariadic()) {
  263. $name = '...' . $name;
  264. } elseif ($parameter->isDefaultValueAvailable()) {
  265. $default = ' = ' . self::exportDefaultValue($parameter);
  266. } elseif ($parameter->isOptional()) {
  267. $default = ' = null';
  268. }
  269. if ($type !== null) {
  270. if ($typeName !== 'mixed' && $parameter->allowsNull() && !$type instanceof ReflectionIntersectionType && !$type instanceof ReflectionUnionType) {
  271. $nullable = '?';
  272. }
  273. if ($typeName === 'self') {
  274. $typeDeclaration = $method->getDeclaringClass()->getName() . ' ';
  275. } elseif ($typeName !== null) {
  276. $typeDeclaration = $typeName . ' ';
  277. } elseif ($type instanceof ReflectionUnionType) {
  278. $typeDeclaration = self::unionTypeAsString(
  279. $type,
  280. $method->getDeclaringClass()->getName()
  281. );
  282. } elseif ($type instanceof ReflectionIntersectionType) {
  283. $typeDeclaration = self::intersectionTypeAsString($type);
  284. }
  285. }
  286. if ($parameter->isPassedByReference()) {
  287. $reference = '&';
  288. }
  289. $parameters[] = $nullable . $typeDeclaration . $reference . $name . $default;
  290. }
  291. return implode(', ', $parameters);
  292. }
  293. /**
  294. * Returns the parameters of a function or method.
  295. *
  296. * @throws ReflectionException
  297. */
  298. private static function getMethodParametersForCall(ReflectionMethod $method): string
  299. {
  300. $parameters = [];
  301. foreach ($method->getParameters() as $i => $parameter) {
  302. $name = '$' . $parameter->getName();
  303. /* Note: PHP extensions may use empty names for reference arguments
  304. * or "..." for methods taking a variable number of arguments.
  305. */
  306. if ($name === '$' || $name === '$...') {
  307. $name = '$arg' . $i;
  308. }
  309. if ($parameter->isVariadic()) {
  310. continue;
  311. }
  312. if ($parameter->isPassedByReference()) {
  313. $parameters[] = '&' . $name;
  314. } else {
  315. $parameters[] = $name;
  316. }
  317. }
  318. return implode(', ', $parameters);
  319. }
  320. /**
  321. * @throws ReflectionException
  322. */
  323. private static function exportDefaultValue(ReflectionParameter $parameter): string
  324. {
  325. try {
  326. $defaultValue = $parameter->getDefaultValue();
  327. if (!is_object($defaultValue)) {
  328. return (string) var_export($defaultValue, true);
  329. }
  330. $parameterAsString = $parameter->__toString();
  331. return (string) explode(
  332. ' = ',
  333. substr(
  334. substr(
  335. $parameterAsString,
  336. strpos($parameterAsString, '<optional> ') + strlen('<optional> ')
  337. ),
  338. 0,
  339. -2
  340. )
  341. )[1];
  342. // @codeCoverageIgnoreStart
  343. } catch (\ReflectionException $e) {
  344. throw new ReflectionException(
  345. $e->getMessage(),
  346. (int) $e->getCode(),
  347. $e
  348. );
  349. }
  350. // @codeCoverageIgnoreEnd
  351. }
  352. private static function unionTypeAsString(ReflectionUnionType $union, string $self): string
  353. {
  354. $types = [];
  355. foreach ($union->getTypes() as $type) {
  356. if ((string) $type === 'self') {
  357. $types[] = $self;
  358. } else {
  359. $types[] = $type;
  360. }
  361. }
  362. return implode('|', $types) . ' ';
  363. }
  364. private static function intersectionTypeAsString(ReflectionIntersectionType $intersection): string
  365. {
  366. $types = [];
  367. foreach ($intersection->getTypes() as $type) {
  368. $types[] = $type;
  369. }
  370. return implode('&', $types) . ' ';
  371. }
  372. }