annotate vendor/phpunit/phpunit-mock-objects/src/Generator.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents 129ea1e6d783
children
rev   line source
Chris@14 1 <?php
Chris@14 2 /*
Chris@14 3 * This file is part of the phpunit-mock-objects package.
Chris@14 4 *
Chris@14 5 * (c) Sebastian Bergmann <sebastian@phpunit.de>
Chris@14 6 *
Chris@14 7 * For the full copyright and license information, please view the LICENSE
Chris@14 8 * file that was distributed with this source code.
Chris@14 9 */
Chris@14 10 namespace PHPUnit\Framework\MockObject;
Chris@14 11
Chris@14 12 use Doctrine\Instantiator\Exception\ExceptionInterface as InstantiatorException;
Chris@14 13 use Doctrine\Instantiator\Instantiator;
Chris@14 14 use Iterator;
Chris@14 15 use IteratorAggregate;
Chris@14 16 use PHPUnit\Framework\Exception;
Chris@14 17 use PHPUnit\Util\InvalidArgumentHelper;
Chris@14 18 use ReflectionClass;
Chris@14 19 use ReflectionException;
Chris@14 20 use ReflectionMethod;
Chris@14 21 use SoapClient;
Chris@14 22 use Text_Template;
Chris@14 23 use Traversable;
Chris@14 24
Chris@14 25 /**
Chris@14 26 * Mock Object Code Generator
Chris@14 27 */
Chris@14 28 class Generator
Chris@14 29 {
Chris@14 30 /**
Chris@14 31 * @var array
Chris@14 32 */
Chris@14 33 private static $cache = [];
Chris@14 34
Chris@14 35 /**
Chris@14 36 * @var Text_Template[]
Chris@14 37 */
Chris@14 38 private static $templates = [];
Chris@14 39
Chris@14 40 /**
Chris@14 41 * @var array
Chris@14 42 */
Chris@14 43 private $blacklistedMethodNames = [
Chris@14 44 '__CLASS__' => true,
Chris@14 45 '__DIR__' => true,
Chris@14 46 '__FILE__' => true,
Chris@14 47 '__FUNCTION__' => true,
Chris@14 48 '__LINE__' => true,
Chris@14 49 '__METHOD__' => true,
Chris@14 50 '__NAMESPACE__' => true,
Chris@14 51 '__TRAIT__' => true,
Chris@14 52 '__clone' => true,
Chris@14 53 '__halt_compiler' => true,
Chris@14 54 ];
Chris@14 55
Chris@14 56 /**
Chris@14 57 * Returns a mock object for the specified class.
Chris@14 58 *
Chris@14 59 * @param string|string[] $type
Chris@14 60 * @param array $methods
Chris@14 61 * @param array $arguments
Chris@14 62 * @param string $mockClassName
Chris@14 63 * @param bool $callOriginalConstructor
Chris@14 64 * @param bool $callOriginalClone
Chris@14 65 * @param bool $callAutoload
Chris@14 66 * @param bool $cloneArguments
Chris@14 67 * @param bool $callOriginalMethods
Chris@14 68 * @param object $proxyTarget
Chris@14 69 * @param bool $allowMockingUnknownTypes
Chris@14 70 *
Chris@14 71 * @return MockObject
Chris@14 72 *
Chris@14 73 * @throws Exception
Chris@14 74 * @throws RuntimeException
Chris@14 75 * @throws \PHPUnit\Framework\Exception
Chris@14 76 * @throws \ReflectionException
Chris@14 77 */
Chris@14 78 public function getMock($type, $methods = [], array $arguments = [], $mockClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true, $cloneArguments = true, $callOriginalMethods = false, $proxyTarget = null, $allowMockingUnknownTypes = true)
Chris@14 79 {
Chris@14 80 if (!\is_array($type) && !\is_string($type)) {
Chris@14 81 throw InvalidArgumentHelper::factory(1, 'array or string');
Chris@14 82 }
Chris@14 83
Chris@14 84 if (!\is_string($mockClassName)) {
Chris@14 85 throw InvalidArgumentHelper::factory(4, 'string');
Chris@14 86 }
Chris@14 87
Chris@14 88 if (!\is_array($methods) && null !== $methods) {
Chris@14 89 throw InvalidArgumentHelper::factory(2, 'array', $methods);
Chris@14 90 }
Chris@14 91
Chris@14 92 if ($type === 'Traversable' || $type === '\\Traversable') {
Chris@14 93 $type = 'Iterator';
Chris@14 94 }
Chris@14 95
Chris@14 96 if (\is_array($type)) {
Chris@14 97 $type = \array_unique(
Chris@14 98 \array_map(
Chris@14 99 function ($type) {
Chris@14 100 if ($type === 'Traversable' ||
Chris@14 101 $type === '\\Traversable' ||
Chris@14 102 $type === '\\Iterator') {
Chris@14 103 return 'Iterator';
Chris@14 104 }
Chris@14 105
Chris@14 106 return $type;
Chris@14 107 },
Chris@14 108 $type
Chris@14 109 )
Chris@14 110 );
Chris@14 111 }
Chris@14 112
Chris@14 113 if (!$allowMockingUnknownTypes) {
Chris@14 114 if (\is_array($type)) {
Chris@14 115 foreach ($type as $_type) {
Chris@14 116 if (!\class_exists($_type, $callAutoload) &&
Chris@14 117 !\interface_exists($_type, $callAutoload)) {
Chris@14 118 throw new RuntimeException(
Chris@14 119 \sprintf(
Chris@14 120 'Cannot stub or mock class or interface "%s" which does not exist',
Chris@14 121 $_type
Chris@14 122 )
Chris@14 123 );
Chris@14 124 }
Chris@14 125 }
Chris@14 126 } else {
Chris@14 127 if (!\class_exists($type, $callAutoload) &&
Chris@14 128 !\interface_exists($type, $callAutoload)
Chris@14 129 ) {
Chris@14 130 throw new RuntimeException(
Chris@14 131 \sprintf(
Chris@14 132 'Cannot stub or mock class or interface "%s" which does not exist',
Chris@14 133 $type
Chris@14 134 )
Chris@14 135 );
Chris@14 136 }
Chris@14 137 }
Chris@14 138 }
Chris@14 139
Chris@14 140 if (null !== $methods) {
Chris@14 141 foreach ($methods as $method) {
Chris@14 142 if (!\preg_match('~[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*~', $method)) {
Chris@14 143 throw new RuntimeException(
Chris@14 144 \sprintf(
Chris@14 145 'Cannot stub or mock method with invalid name "%s"',
Chris@14 146 $method
Chris@14 147 )
Chris@14 148 );
Chris@14 149 }
Chris@14 150 }
Chris@14 151
Chris@14 152 if ($methods !== \array_unique($methods)) {
Chris@14 153 throw new RuntimeException(
Chris@14 154 \sprintf(
Chris@14 155 'Cannot stub or mock using a method list that contains duplicates: "%s" (duplicate: "%s")',
Chris@14 156 \implode(', ', $methods),
Chris@14 157 \implode(', ', \array_unique(\array_diff_assoc($methods, \array_unique($methods))))
Chris@14 158 )
Chris@14 159 );
Chris@14 160 }
Chris@14 161 }
Chris@14 162
Chris@14 163 if ($mockClassName !== '' && \class_exists($mockClassName, false)) {
Chris@14 164 $reflect = new ReflectionClass($mockClassName);
Chris@14 165
Chris@14 166 if (!$reflect->implementsInterface(MockObject::class)) {
Chris@14 167 throw new RuntimeException(
Chris@14 168 \sprintf(
Chris@14 169 'Class "%s" already exists.',
Chris@14 170 $mockClassName
Chris@14 171 )
Chris@14 172 );
Chris@14 173 }
Chris@14 174 }
Chris@14 175
Chris@14 176 if ($callOriginalConstructor === false && $callOriginalMethods === true) {
Chris@14 177 throw new RuntimeException(
Chris@14 178 'Proxying to original methods requires invoking the original constructor'
Chris@14 179 );
Chris@14 180 }
Chris@14 181
Chris@14 182 $mock = $this->generate(
Chris@14 183 $type,
Chris@14 184 $methods,
Chris@14 185 $mockClassName,
Chris@14 186 $callOriginalClone,
Chris@14 187 $callAutoload,
Chris@14 188 $cloneArguments,
Chris@14 189 $callOriginalMethods
Chris@14 190 );
Chris@14 191
Chris@14 192 return $this->getObject(
Chris@14 193 $mock['code'],
Chris@14 194 $mock['mockClassName'],
Chris@14 195 $type,
Chris@14 196 $callOriginalConstructor,
Chris@14 197 $callAutoload,
Chris@14 198 $arguments,
Chris@14 199 $callOriginalMethods,
Chris@14 200 $proxyTarget
Chris@14 201 );
Chris@14 202 }
Chris@14 203
Chris@14 204 /**
Chris@14 205 * @param string $code
Chris@14 206 * @param string $className
Chris@14 207 * @param array|string $type
Chris@14 208 * @param bool $callOriginalConstructor
Chris@14 209 * @param bool $callAutoload
Chris@14 210 * @param array $arguments
Chris@14 211 * @param bool $callOriginalMethods
Chris@14 212 * @param object $proxyTarget
Chris@14 213 *
Chris@14 214 * @return MockObject
Chris@14 215 *
Chris@14 216 * @throws \ReflectionException
Chris@14 217 * @throws RuntimeException
Chris@14 218 */
Chris@14 219 private function getObject($code, $className, $type = '', $callOriginalConstructor = false, $callAutoload = false, array $arguments = [], $callOriginalMethods = false, $proxyTarget = null)
Chris@14 220 {
Chris@14 221 $this->evalClass($code, $className);
Chris@14 222
Chris@14 223 if ($callOriginalConstructor &&
Chris@14 224 \is_string($type) &&
Chris@14 225 !\interface_exists($type, $callAutoload)) {
Chris@14 226 if (\count($arguments) === 0) {
Chris@14 227 $object = new $className;
Chris@14 228 } else {
Chris@14 229 $class = new ReflectionClass($className);
Chris@14 230 $object = $class->newInstanceArgs($arguments);
Chris@14 231 }
Chris@14 232 } else {
Chris@14 233 try {
Chris@14 234 $instantiator = new Instantiator;
Chris@14 235 $object = $instantiator->instantiate($className);
Chris@14 236 } catch (InstantiatorException $exception) {
Chris@14 237 throw new RuntimeException($exception->getMessage());
Chris@14 238 }
Chris@14 239 }
Chris@14 240
Chris@14 241 if ($callOriginalMethods) {
Chris@14 242 if (!\is_object($proxyTarget)) {
Chris@14 243 if (\count($arguments) === 0) {
Chris@14 244 $proxyTarget = new $type;
Chris@14 245 } else {
Chris@14 246 $class = new ReflectionClass($type);
Chris@14 247 $proxyTarget = $class->newInstanceArgs($arguments);
Chris@14 248 }
Chris@14 249 }
Chris@14 250
Chris@14 251 $object->__phpunit_setOriginalObject($proxyTarget);
Chris@14 252 }
Chris@14 253
Chris@14 254 return $object;
Chris@14 255 }
Chris@14 256
Chris@14 257 /**
Chris@14 258 * @param string $code
Chris@14 259 * @param string $className
Chris@14 260 */
Chris@14 261 private function evalClass($code, $className)
Chris@14 262 {
Chris@14 263 if (!\class_exists($className, false)) {
Chris@14 264 eval($code);
Chris@14 265 }
Chris@14 266 }
Chris@14 267
Chris@14 268 /**
Chris@14 269 * Returns a mock object for the specified abstract class with all abstract
Chris@14 270 * methods of the class mocked. Concrete methods to mock can be specified with
Chris@14 271 * the last parameter
Chris@14 272 *
Chris@14 273 * @param string $originalClassName
Chris@14 274 * @param array $arguments
Chris@14 275 * @param string $mockClassName
Chris@14 276 * @param bool $callOriginalConstructor
Chris@14 277 * @param bool $callOriginalClone
Chris@14 278 * @param bool $callAutoload
Chris@14 279 * @param array $mockedMethods
Chris@14 280 * @param bool $cloneArguments
Chris@14 281 *
Chris@14 282 * @return MockObject
Chris@14 283 *
Chris@14 284 * @throws \ReflectionException
Chris@14 285 * @throws RuntimeException
Chris@14 286 * @throws Exception
Chris@14 287 */
Chris@14 288 public function getMockForAbstractClass($originalClassName, array $arguments = [], $mockClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true, $mockedMethods = [], $cloneArguments = true)
Chris@14 289 {
Chris@14 290 if (!\is_string($originalClassName)) {
Chris@14 291 throw InvalidArgumentHelper::factory(1, 'string');
Chris@14 292 }
Chris@14 293
Chris@14 294 if (!\is_string($mockClassName)) {
Chris@14 295 throw InvalidArgumentHelper::factory(3, 'string');
Chris@14 296 }
Chris@14 297
Chris@14 298 if (\class_exists($originalClassName, $callAutoload) ||
Chris@14 299 \interface_exists($originalClassName, $callAutoload)) {
Chris@14 300 $reflector = new ReflectionClass($originalClassName);
Chris@14 301 $methods = $mockedMethods;
Chris@14 302
Chris@14 303 foreach ($reflector->getMethods() as $method) {
Chris@14 304 if ($method->isAbstract() && !\in_array($method->getName(), $methods)) {
Chris@14 305 $methods[] = $method->getName();
Chris@14 306 }
Chris@14 307 }
Chris@14 308
Chris@14 309 if (empty($methods)) {
Chris@14 310 $methods = null;
Chris@14 311 }
Chris@14 312
Chris@14 313 return $this->getMock(
Chris@14 314 $originalClassName,
Chris@14 315 $methods,
Chris@14 316 $arguments,
Chris@14 317 $mockClassName,
Chris@14 318 $callOriginalConstructor,
Chris@14 319 $callOriginalClone,
Chris@14 320 $callAutoload,
Chris@14 321 $cloneArguments
Chris@14 322 );
Chris@14 323 }
Chris@14 324
Chris@14 325 throw new RuntimeException(
Chris@14 326 \sprintf('Class "%s" does not exist.', $originalClassName)
Chris@14 327 );
Chris@14 328 }
Chris@14 329
Chris@14 330 /**
Chris@14 331 * Returns a mock object for the specified trait with all abstract methods
Chris@14 332 * of the trait mocked. Concrete methods to mock can be specified with the
Chris@14 333 * `$mockedMethods` parameter.
Chris@14 334 *
Chris@14 335 * @param string $traitName
Chris@14 336 * @param array $arguments
Chris@14 337 * @param string $mockClassName
Chris@14 338 * @param bool $callOriginalConstructor
Chris@14 339 * @param bool $callOriginalClone
Chris@14 340 * @param bool $callAutoload
Chris@14 341 * @param array $mockedMethods
Chris@14 342 * @param bool $cloneArguments
Chris@14 343 *
Chris@14 344 * @return MockObject
Chris@14 345 *
Chris@14 346 * @throws \ReflectionException
Chris@14 347 * @throws RuntimeException
Chris@14 348 * @throws Exception
Chris@14 349 */
Chris@14 350 public function getMockForTrait($traitName, array $arguments = [], $mockClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true, $mockedMethods = [], $cloneArguments = true)
Chris@14 351 {
Chris@14 352 if (!\is_string($traitName)) {
Chris@14 353 throw InvalidArgumentHelper::factory(1, 'string');
Chris@14 354 }
Chris@14 355
Chris@14 356 if (!\is_string($mockClassName)) {
Chris@14 357 throw InvalidArgumentHelper::factory(3, 'string');
Chris@14 358 }
Chris@14 359
Chris@14 360 if (!\trait_exists($traitName, $callAutoload)) {
Chris@14 361 throw new RuntimeException(
Chris@14 362 \sprintf(
Chris@14 363 'Trait "%s" does not exist.',
Chris@14 364 $traitName
Chris@14 365 )
Chris@14 366 );
Chris@14 367 }
Chris@14 368
Chris@14 369 $className = $this->generateClassName(
Chris@14 370 $traitName,
Chris@14 371 '',
Chris@14 372 'Trait_'
Chris@14 373 );
Chris@14 374
Chris@14 375 $classTemplate = $this->getTemplate('trait_class.tpl');
Chris@14 376
Chris@14 377 $classTemplate->setVar(
Chris@14 378 [
Chris@14 379 'prologue' => 'abstract ',
Chris@14 380 'class_name' => $className['className'],
Chris@14 381 'trait_name' => $traitName
Chris@14 382 ]
Chris@14 383 );
Chris@14 384
Chris@14 385 $this->evalClass(
Chris@14 386 $classTemplate->render(),
Chris@14 387 $className['className']
Chris@14 388 );
Chris@14 389
Chris@14 390 return $this->getMockForAbstractClass($className['className'], $arguments, $mockClassName, $callOriginalConstructor, $callOriginalClone, $callAutoload, $mockedMethods, $cloneArguments);
Chris@14 391 }
Chris@14 392
Chris@14 393 /**
Chris@14 394 * Returns an object for the specified trait.
Chris@14 395 *
Chris@14 396 * @param string $traitName
Chris@14 397 * @param array $arguments
Chris@14 398 * @param string $traitClassName
Chris@14 399 * @param bool $callOriginalConstructor
Chris@14 400 * @param bool $callOriginalClone
Chris@14 401 * @param bool $callAutoload
Chris@14 402 *
Chris@14 403 * @return object
Chris@14 404 *
Chris@14 405 * @throws \ReflectionException
Chris@14 406 * @throws RuntimeException
Chris@14 407 * @throws Exception
Chris@14 408 */
Chris@14 409 public function getObjectForTrait($traitName, array $arguments = [], $traitClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true)
Chris@14 410 {
Chris@14 411 if (!\is_string($traitName)) {
Chris@14 412 throw InvalidArgumentHelper::factory(1, 'string');
Chris@14 413 }
Chris@14 414
Chris@14 415 if (!\is_string($traitClassName)) {
Chris@14 416 throw InvalidArgumentHelper::factory(3, 'string');
Chris@14 417 }
Chris@14 418
Chris@14 419 if (!\trait_exists($traitName, $callAutoload)) {
Chris@14 420 throw new RuntimeException(
Chris@14 421 \sprintf(
Chris@14 422 'Trait "%s" does not exist.',
Chris@14 423 $traitName
Chris@14 424 )
Chris@14 425 );
Chris@14 426 }
Chris@14 427
Chris@14 428 $className = $this->generateClassName(
Chris@14 429 $traitName,
Chris@14 430 $traitClassName,
Chris@14 431 'Trait_'
Chris@14 432 );
Chris@14 433
Chris@14 434 $classTemplate = $this->getTemplate('trait_class.tpl');
Chris@14 435
Chris@14 436 $classTemplate->setVar(
Chris@14 437 [
Chris@14 438 'prologue' => '',
Chris@14 439 'class_name' => $className['className'],
Chris@14 440 'trait_name' => $traitName
Chris@14 441 ]
Chris@14 442 );
Chris@14 443
Chris@14 444 return $this->getObject($classTemplate->render(), $className['className']);
Chris@14 445 }
Chris@14 446
Chris@14 447 /**
Chris@14 448 * @param array|string $type
Chris@14 449 * @param array $methods
Chris@14 450 * @param string $mockClassName
Chris@14 451 * @param bool $callOriginalClone
Chris@14 452 * @param bool $callAutoload
Chris@14 453 * @param bool $cloneArguments
Chris@14 454 * @param bool $callOriginalMethods
Chris@14 455 *
Chris@14 456 * @return array
Chris@14 457 *
Chris@14 458 * @throws \ReflectionException
Chris@14 459 * @throws \PHPUnit\Framework\MockObject\RuntimeException
Chris@14 460 */
Chris@14 461 public function generate($type, array $methods = null, $mockClassName = '', $callOriginalClone = true, $callAutoload = true, $cloneArguments = true, $callOriginalMethods = false)
Chris@14 462 {
Chris@14 463 if (\is_array($type)) {
Chris@14 464 \sort($type);
Chris@14 465 }
Chris@14 466
Chris@14 467 if ($mockClassName === '') {
Chris@14 468 $key = \md5(
Chris@14 469 \is_array($type) ? \implode('_', $type) : $type .
Chris@14 470 \serialize($methods) .
Chris@14 471 \serialize($callOriginalClone) .
Chris@14 472 \serialize($cloneArguments) .
Chris@14 473 \serialize($callOriginalMethods)
Chris@14 474 );
Chris@14 475
Chris@14 476 if (isset(self::$cache[$key])) {
Chris@14 477 return self::$cache[$key];
Chris@14 478 }
Chris@14 479 }
Chris@14 480
Chris@14 481 $mock = $this->generateMock(
Chris@14 482 $type,
Chris@14 483 $methods,
Chris@14 484 $mockClassName,
Chris@14 485 $callOriginalClone,
Chris@14 486 $callAutoload,
Chris@14 487 $cloneArguments,
Chris@14 488 $callOriginalMethods
Chris@14 489 );
Chris@14 490
Chris@14 491 if (isset($key)) {
Chris@14 492 self::$cache[$key] = $mock;
Chris@14 493 }
Chris@14 494
Chris@14 495 return $mock;
Chris@14 496 }
Chris@14 497
Chris@14 498 /**
Chris@14 499 * @param string $wsdlFile
Chris@14 500 * @param string $className
Chris@14 501 * @param array $methods
Chris@14 502 * @param array $options
Chris@14 503 *
Chris@14 504 * @return string
Chris@14 505 *
Chris@14 506 * @throws RuntimeException
Chris@14 507 */
Chris@14 508 public function generateClassFromWsdl($wsdlFile, $className, array $methods = [], array $options = [])
Chris@14 509 {
Chris@14 510 if (!\extension_loaded('soap')) {
Chris@14 511 throw new RuntimeException(
Chris@14 512 'The SOAP extension is required to generate a mock object from WSDL.'
Chris@14 513 );
Chris@14 514 }
Chris@14 515
Chris@14 516 $options = \array_merge($options, ['cache_wsdl' => WSDL_CACHE_NONE]);
Chris@14 517 $client = new SoapClient($wsdlFile, $options);
Chris@14 518 $_methods = \array_unique($client->__getFunctions());
Chris@14 519 unset($client);
Chris@14 520
Chris@14 521 \sort($_methods);
Chris@14 522
Chris@14 523 $methodTemplate = $this->getTemplate('wsdl_method.tpl');
Chris@14 524 $methodsBuffer = '';
Chris@14 525
Chris@14 526 foreach ($_methods as $method) {
Chris@14 527 $nameStart = \strpos($method, ' ') + 1;
Chris@14 528 $nameEnd = \strpos($method, '(');
Chris@14 529 $name = \substr($method, $nameStart, $nameEnd - $nameStart);
Chris@14 530
Chris@14 531 if (empty($methods) || \in_array($name, $methods)) {
Chris@14 532 $args = \explode(
Chris@14 533 ',',
Chris@14 534 \substr(
Chris@14 535 $method,
Chris@14 536 $nameEnd + 1,
Chris@14 537 \strpos($method, ')') - $nameEnd - 1
Chris@14 538 )
Chris@14 539 );
Chris@14 540
Chris@14 541 foreach (\range(0, \count($args) - 1) as $i) {
Chris@14 542 $args[$i] = \substr($args[$i], \strpos($args[$i], '$'));
Chris@14 543 }
Chris@14 544
Chris@14 545 $methodTemplate->setVar(
Chris@14 546 [
Chris@14 547 'method_name' => $name,
Chris@14 548 'arguments' => \implode(', ', $args)
Chris@14 549 ]
Chris@14 550 );
Chris@14 551
Chris@14 552 $methodsBuffer .= $methodTemplate->render();
Chris@14 553 }
Chris@14 554 }
Chris@14 555
Chris@14 556 $optionsBuffer = 'array(';
Chris@14 557
Chris@14 558 foreach ($options as $key => $value) {
Chris@14 559 $optionsBuffer .= $key . ' => ' . $value;
Chris@14 560 }
Chris@14 561
Chris@14 562 $optionsBuffer .= ')';
Chris@14 563
Chris@14 564 $classTemplate = $this->getTemplate('wsdl_class.tpl');
Chris@14 565 $namespace = '';
Chris@14 566
Chris@14 567 if (\strpos($className, '\\') !== false) {
Chris@14 568 $parts = \explode('\\', $className);
Chris@14 569 $className = \array_pop($parts);
Chris@14 570 $namespace = 'namespace ' . \implode('\\', $parts) . ';' . "\n\n";
Chris@14 571 }
Chris@14 572
Chris@14 573 $classTemplate->setVar(
Chris@14 574 [
Chris@14 575 'namespace' => $namespace,
Chris@14 576 'class_name' => $className,
Chris@14 577 'wsdl' => $wsdlFile,
Chris@14 578 'options' => $optionsBuffer,
Chris@14 579 'methods' => $methodsBuffer
Chris@14 580 ]
Chris@14 581 );
Chris@14 582
Chris@14 583 return $classTemplate->render();
Chris@14 584 }
Chris@14 585
Chris@14 586 /**
Chris@14 587 * @param array|string $type
Chris@14 588 * @param array|null $methods
Chris@14 589 * @param string $mockClassName
Chris@14 590 * @param bool $callOriginalClone
Chris@14 591 * @param bool $callAutoload
Chris@14 592 * @param bool $cloneArguments
Chris@14 593 * @param bool $callOriginalMethods
Chris@14 594 *
Chris@14 595 * @return array
Chris@14 596 *
Chris@14 597 * @throws \InvalidArgumentException
Chris@14 598 * @throws \ReflectionException
Chris@14 599 * @throws RuntimeException
Chris@14 600 */
Chris@14 601 private function generateMock($type, $methods, $mockClassName, $callOriginalClone, $callAutoload, $cloneArguments, $callOriginalMethods)
Chris@14 602 {
Chris@14 603 $methodReflections = [];
Chris@14 604 $classTemplate = $this->getTemplate('mocked_class.tpl');
Chris@14 605
Chris@14 606 $additionalInterfaces = [];
Chris@14 607 $cloneTemplate = '';
Chris@14 608 $isClass = false;
Chris@14 609 $isInterface = false;
Chris@14 610 $isMultipleInterfaces = false;
Chris@14 611
Chris@14 612 if (\is_array($type)) {
Chris@14 613 foreach ($type as $_type) {
Chris@14 614 if (!\interface_exists($_type, $callAutoload)) {
Chris@14 615 throw new RuntimeException(
Chris@14 616 \sprintf(
Chris@14 617 'Interface "%s" does not exist.',
Chris@14 618 $_type
Chris@14 619 )
Chris@14 620 );
Chris@14 621 }
Chris@14 622
Chris@14 623 $isMultipleInterfaces = true;
Chris@14 624
Chris@14 625 $additionalInterfaces[] = $_type;
Chris@14 626 $typeClass = new ReflectionClass($this->generateClassName(
Chris@14 627 $_type,
Chris@14 628 $mockClassName,
Chris@14 629 'Mock_'
Chris@14 630 )['fullClassName']
Chris@14 631 );
Chris@14 632
Chris@14 633 foreach ($this->getClassMethods($_type) as $method) {
Chris@14 634 if (\in_array($method, $methods)) {
Chris@14 635 throw new RuntimeException(
Chris@14 636 \sprintf(
Chris@14 637 'Duplicate method "%s" not allowed.',
Chris@14 638 $method
Chris@14 639 )
Chris@14 640 );
Chris@14 641 }
Chris@14 642
Chris@14 643 $methodReflections[$method] = $typeClass->getMethod($method);
Chris@14 644 $methods[] = $method;
Chris@14 645 }
Chris@14 646 }
Chris@14 647 }
Chris@14 648
Chris@14 649 $mockClassName = $this->generateClassName(
Chris@14 650 $type,
Chris@14 651 $mockClassName,
Chris@14 652 'Mock_'
Chris@14 653 );
Chris@14 654
Chris@14 655 if (\class_exists($mockClassName['fullClassName'], $callAutoload)) {
Chris@14 656 $isClass = true;
Chris@14 657 } elseif (\interface_exists($mockClassName['fullClassName'], $callAutoload)) {
Chris@14 658 $isInterface = true;
Chris@14 659 }
Chris@14 660
Chris@14 661 if (!$isClass && !$isInterface) {
Chris@14 662 $prologue = 'class ' . $mockClassName['originalClassName'] . "\n{\n}\n\n";
Chris@14 663
Chris@14 664 if (!empty($mockClassName['namespaceName'])) {
Chris@14 665 $prologue = 'namespace ' . $mockClassName['namespaceName'] .
Chris@14 666 " {\n\n" . $prologue . "}\n\n" .
Chris@14 667 "namespace {\n\n";
Chris@14 668
Chris@14 669 $epilogue = "\n\n}";
Chris@14 670 }
Chris@14 671
Chris@14 672 $cloneTemplate = $this->getTemplate('mocked_clone.tpl');
Chris@14 673 } else {
Chris@14 674 $class = new ReflectionClass($mockClassName['fullClassName']);
Chris@14 675
Chris@14 676 if ($class->isFinal()) {
Chris@14 677 throw new RuntimeException(
Chris@14 678 \sprintf(
Chris@14 679 'Class "%s" is declared "final" and cannot be mocked.',
Chris@14 680 $mockClassName['fullClassName']
Chris@14 681 )
Chris@14 682 );
Chris@14 683 }
Chris@14 684
Chris@14 685 if ($class->hasMethod('__clone')) {
Chris@14 686 $cloneMethod = $class->getMethod('__clone');
Chris@14 687
Chris@14 688 if (!$cloneMethod->isFinal()) {
Chris@14 689 if ($callOriginalClone && !$isInterface) {
Chris@14 690 $cloneTemplate = $this->getTemplate('unmocked_clone.tpl');
Chris@14 691 } else {
Chris@14 692 $cloneTemplate = $this->getTemplate('mocked_clone.tpl');
Chris@14 693 }
Chris@14 694 }
Chris@14 695 } else {
Chris@14 696 $cloneTemplate = $this->getTemplate('mocked_clone.tpl');
Chris@14 697 }
Chris@14 698 }
Chris@14 699
Chris@14 700 if (\is_object($cloneTemplate)) {
Chris@14 701 $cloneTemplate = $cloneTemplate->render();
Chris@14 702 }
Chris@14 703
Chris@14 704 if (\is_array($methods) && empty($methods) &&
Chris@14 705 ($isClass || $isInterface)) {
Chris@14 706 $methods = $this->getClassMethods($mockClassName['fullClassName']);
Chris@14 707 }
Chris@14 708
Chris@14 709 if (!\is_array($methods)) {
Chris@14 710 $methods = [];
Chris@14 711 }
Chris@14 712
Chris@14 713 $mockedMethods = '';
Chris@14 714 $configurable = [];
Chris@14 715
Chris@14 716 foreach ($methods as $methodName) {
Chris@14 717 if ($methodName !== '__construct' && $methodName !== '__clone') {
Chris@14 718 $configurable[] = \strtolower($methodName);
Chris@14 719 }
Chris@14 720 }
Chris@14 721
Chris@14 722 if (isset($class)) {
Chris@14 723 // https://github.com/sebastianbergmann/phpunit-mock-objects/issues/103
Chris@14 724 if ($isInterface && $class->implementsInterface(Traversable::class) &&
Chris@14 725 !$class->implementsInterface(Iterator::class) &&
Chris@14 726 !$class->implementsInterface(IteratorAggregate::class)) {
Chris@14 727 $additionalInterfaces[] = Iterator::class;
Chris@14 728 $methods = \array_merge($methods, $this->getClassMethods(Iterator::class));
Chris@14 729 }
Chris@14 730
Chris@14 731 foreach ($methods as $methodName) {
Chris@14 732 try {
Chris@14 733 $method = $class->getMethod($methodName);
Chris@14 734
Chris@14 735 if ($this->canMockMethod($method)) {
Chris@14 736 $mockedMethods .= $this->generateMockedMethodDefinitionFromExisting(
Chris@14 737 $method,
Chris@14 738 $cloneArguments,
Chris@14 739 $callOriginalMethods
Chris@14 740 );
Chris@14 741 }
Chris@14 742 } catch (ReflectionException $e) {
Chris@14 743 $mockedMethods .= $this->generateMockedMethodDefinition(
Chris@14 744 $mockClassName['fullClassName'],
Chris@14 745 $methodName,
Chris@14 746 $cloneArguments
Chris@14 747 );
Chris@14 748 }
Chris@14 749 }
Chris@14 750 } elseif ($isMultipleInterfaces) {
Chris@14 751 foreach ($methods as $methodName) {
Chris@14 752 if ($this->canMockMethod($methodReflections[$methodName])) {
Chris@14 753 $mockedMethods .= $this->generateMockedMethodDefinitionFromExisting(
Chris@14 754 $methodReflections[$methodName],
Chris@14 755 $cloneArguments,
Chris@14 756 $callOriginalMethods
Chris@14 757 );
Chris@14 758 }
Chris@14 759 }
Chris@14 760 } else {
Chris@14 761 foreach ($methods as $methodName) {
Chris@14 762 $mockedMethods .= $this->generateMockedMethodDefinition(
Chris@14 763 $mockClassName['fullClassName'],
Chris@14 764 $methodName,
Chris@14 765 $cloneArguments
Chris@14 766 );
Chris@14 767 }
Chris@14 768 }
Chris@14 769
Chris@14 770 $method = '';
Chris@14 771
Chris@14 772 if (!\in_array('method', $methods) && (!isset($class) || !$class->hasMethod('method'))) {
Chris@14 773 $methodTemplate = $this->getTemplate('mocked_class_method.tpl');
Chris@14 774
Chris@14 775 $method = $methodTemplate->render();
Chris@14 776 }
Chris@14 777
Chris@14 778 $classTemplate->setVar(
Chris@14 779 [
Chris@14 780 'prologue' => $prologue ?? '',
Chris@14 781 'epilogue' => $epilogue ?? '',
Chris@14 782 'class_declaration' => $this->generateMockClassDeclaration(
Chris@14 783 $mockClassName,
Chris@14 784 $isInterface,
Chris@14 785 $additionalInterfaces
Chris@14 786 ),
Chris@14 787 'clone' => $cloneTemplate,
Chris@14 788 'mock_class_name' => $mockClassName['className'],
Chris@14 789 'mocked_methods' => $mockedMethods,
Chris@14 790 'method' => $method,
Chris@14 791 'configurable' => '[' . \implode(', ', \array_map(function ($m) {
Chris@14 792 return '\'' . $m . '\'';
Chris@14 793 }, $configurable)) . ']'
Chris@14 794 ]
Chris@14 795 );
Chris@14 796
Chris@14 797 return [
Chris@14 798 'code' => $classTemplate->render(),
Chris@14 799 'mockClassName' => $mockClassName['className']
Chris@14 800 ];
Chris@14 801 }
Chris@14 802
Chris@14 803 /**
Chris@14 804 * @param array|string $type
Chris@14 805 * @param string $className
Chris@14 806 * @param string $prefix
Chris@14 807 *
Chris@14 808 * @return array
Chris@14 809 */
Chris@14 810 private function generateClassName($type, $className, $prefix)
Chris@14 811 {
Chris@14 812 if (\is_array($type)) {
Chris@14 813 $type = \implode('_', $type);
Chris@14 814 }
Chris@14 815
Chris@14 816 if ($type[0] === '\\') {
Chris@14 817 $type = \substr($type, 1);
Chris@14 818 }
Chris@14 819
Chris@14 820 $classNameParts = \explode('\\', $type);
Chris@14 821
Chris@14 822 if (\count($classNameParts) > 1) {
Chris@14 823 $type = \array_pop($classNameParts);
Chris@14 824 $namespaceName = \implode('\\', $classNameParts);
Chris@14 825 $fullClassName = $namespaceName . '\\' . $type;
Chris@14 826 } else {
Chris@14 827 $namespaceName = '';
Chris@14 828 $fullClassName = $type;
Chris@14 829 }
Chris@14 830
Chris@14 831 if ($className === '') {
Chris@14 832 do {
Chris@14 833 $className = $prefix . $type . '_' .
Chris@14 834 \substr(\md5(\mt_rand()), 0, 8);
Chris@14 835 } while (\class_exists($className, false));
Chris@14 836 }
Chris@14 837
Chris@14 838 return [
Chris@14 839 'className' => $className,
Chris@14 840 'originalClassName' => $type,
Chris@14 841 'fullClassName' => $fullClassName,
Chris@14 842 'namespaceName' => $namespaceName
Chris@14 843 ];
Chris@14 844 }
Chris@14 845
Chris@14 846 /**
Chris@14 847 * @param array $mockClassName
Chris@14 848 * @param bool $isInterface
Chris@14 849 * @param array $additionalInterfaces
Chris@14 850 *
Chris@14 851 * @return string
Chris@14 852 */
Chris@14 853 private function generateMockClassDeclaration(array $mockClassName, $isInterface, array $additionalInterfaces = [])
Chris@14 854 {
Chris@14 855 $buffer = 'class ';
Chris@14 856
Chris@14 857 $additionalInterfaces[] = MockObject::class;
Chris@14 858 $interfaces = \implode(', ', $additionalInterfaces);
Chris@14 859
Chris@14 860 if ($isInterface) {
Chris@14 861 $buffer .= \sprintf(
Chris@14 862 '%s implements %s',
Chris@14 863 $mockClassName['className'],
Chris@14 864 $interfaces
Chris@14 865 );
Chris@14 866
Chris@14 867 if (!\in_array($mockClassName['originalClassName'], $additionalInterfaces)) {
Chris@14 868 $buffer .= ', ';
Chris@14 869
Chris@14 870 if (!empty($mockClassName['namespaceName'])) {
Chris@14 871 $buffer .= $mockClassName['namespaceName'] . '\\';
Chris@14 872 }
Chris@14 873
Chris@14 874 $buffer .= $mockClassName['originalClassName'];
Chris@14 875 }
Chris@14 876 } else {
Chris@14 877 $buffer .= \sprintf(
Chris@14 878 '%s extends %s%s implements %s',
Chris@14 879 $mockClassName['className'],
Chris@14 880 !empty($mockClassName['namespaceName']) ? $mockClassName['namespaceName'] . '\\' : '',
Chris@14 881 $mockClassName['originalClassName'],
Chris@14 882 $interfaces
Chris@14 883 );
Chris@14 884 }
Chris@14 885
Chris@14 886 return $buffer;
Chris@14 887 }
Chris@14 888
Chris@14 889 /**
Chris@14 890 * @param ReflectionMethod $method
Chris@14 891 * @param bool $cloneArguments
Chris@14 892 * @param bool $callOriginalMethods
Chris@14 893 *
Chris@14 894 * @return string
Chris@14 895 *
Chris@14 896 * @throws \PHPUnit\Framework\MockObject\RuntimeException
Chris@14 897 */
Chris@14 898 private function generateMockedMethodDefinitionFromExisting(ReflectionMethod $method, $cloneArguments, $callOriginalMethods)
Chris@14 899 {
Chris@14 900 if ($method->isPrivate()) {
Chris@14 901 $modifier = 'private';
Chris@14 902 } elseif ($method->isProtected()) {
Chris@14 903 $modifier = 'protected';
Chris@14 904 } else {
Chris@14 905 $modifier = 'public';
Chris@14 906 }
Chris@14 907
Chris@14 908 if ($method->isStatic()) {
Chris@14 909 $modifier .= ' static';
Chris@14 910 }
Chris@14 911
Chris@14 912 if ($method->returnsReference()) {
Chris@14 913 $reference = '&';
Chris@14 914 } else {
Chris@14 915 $reference = '';
Chris@14 916 }
Chris@14 917
Chris@14 918 if ($method->hasReturnType()) {
Chris@14 919 $returnType = (string) $method->getReturnType();
Chris@14 920 } else {
Chris@14 921 $returnType = '';
Chris@14 922 }
Chris@14 923
Chris@14 924 if (\preg_match('#\*[ \t]*+@deprecated[ \t]*+(.*?)\r?+\n[ \t]*+\*(?:[ \t]*+@|/$)#s', $method->getDocComment(), $deprecation)) {
Chris@14 925 $deprecation = \trim(\preg_replace('#[ \t]*\r?\n[ \t]*+\*[ \t]*+#', ' ', $deprecation[1]));
Chris@14 926 } else {
Chris@14 927 $deprecation = false;
Chris@14 928 }
Chris@14 929
Chris@14 930 return $this->generateMockedMethodDefinition(
Chris@14 931 $method->getDeclaringClass()->getName(),
Chris@14 932 $method->getName(),
Chris@14 933 $cloneArguments,
Chris@14 934 $modifier,
Chris@14 935 $this->getMethodParameters($method),
Chris@14 936 $this->getMethodParameters($method, true),
Chris@14 937 $returnType,
Chris@14 938 $reference,
Chris@14 939 $callOriginalMethods,
Chris@14 940 $method->isStatic(),
Chris@14 941 $deprecation,
Chris@14 942 $method->hasReturnType() && PHP_VERSION_ID >= 70100 && $method->getReturnType()->allowsNull()
Chris@14 943 );
Chris@14 944 }
Chris@14 945
Chris@14 946 /**
Chris@14 947 * @param string $className
Chris@14 948 * @param string $methodName
Chris@14 949 * @param bool $cloneArguments
Chris@14 950 * @param string $modifier
Chris@14 951 * @param string $argumentsForDeclaration
Chris@14 952 * @param string $argumentsForCall
Chris@14 953 * @param string $returnType
Chris@14 954 * @param string $reference
Chris@14 955 * @param bool $callOriginalMethods
Chris@14 956 * @param bool $static
Chris@14 957 * @param bool|string $deprecation
Chris@14 958 * @param bool $allowsReturnNull
Chris@14 959 *
Chris@14 960 * @return string
Chris@14 961 *
Chris@14 962 * @throws \InvalidArgumentException
Chris@14 963 */
Chris@14 964 private function generateMockedMethodDefinition($className, $methodName, $cloneArguments = true, $modifier = 'public', $argumentsForDeclaration = '', $argumentsForCall = '', $returnType = '', $reference = '', $callOriginalMethods = false, $static = false, $deprecation = false, $allowsReturnNull = false)
Chris@14 965 {
Chris@14 966 if ($static) {
Chris@14 967 $templateFile = 'mocked_static_method.tpl';
Chris@14 968 } else {
Chris@14 969 if ($returnType === 'void') {
Chris@14 970 $templateFile = \sprintf(
Chris@14 971 '%s_method_void.tpl',
Chris@14 972 $callOriginalMethods ? 'proxied' : 'mocked'
Chris@14 973 );
Chris@14 974 } else {
Chris@14 975 $templateFile = \sprintf(
Chris@14 976 '%s_method.tpl',
Chris@14 977 $callOriginalMethods ? 'proxied' : 'mocked'
Chris@14 978 );
Chris@14 979 }
Chris@14 980 }
Chris@14 981
Chris@14 982 // Mocked interfaces returning 'self' must explicitly declare the
Chris@14 983 // interface name as the return type. See
Chris@14 984 // https://bugs.php.net/bug.php?id=70722
Chris@14 985 if ($returnType === 'self') {
Chris@14 986 $returnType = $className;
Chris@14 987 }
Chris@14 988
Chris@14 989 if (false !== $deprecation) {
Chris@14 990 $deprecation = "The $className::$methodName method is deprecated ($deprecation).";
Chris@14 991 $deprecationTemplate = $this->getTemplate('deprecation.tpl');
Chris@14 992
Chris@14 993 $deprecationTemplate->setVar(
Chris@14 994 [
Chris@14 995 'deprecation' => \var_export($deprecation, true),
Chris@14 996 ]
Chris@14 997 );
Chris@14 998
Chris@14 999 $deprecation = $deprecationTemplate->render();
Chris@14 1000 }
Chris@14 1001
Chris@14 1002 $template = $this->getTemplate($templateFile);
Chris@14 1003
Chris@14 1004 $template->setVar(
Chris@14 1005 [
Chris@14 1006 'arguments_decl' => $argumentsForDeclaration,
Chris@14 1007 'arguments_call' => $argumentsForCall,
Chris@14 1008 'return_delim' => $returnType ? ': ' : '',
Chris@14 1009 'return_type' => $allowsReturnNull ? '?' . $returnType : $returnType,
Chris@14 1010 'arguments_count' => !empty($argumentsForCall) ? \substr_count($argumentsForCall, ',') + 1 : 0,
Chris@14 1011 'class_name' => $className,
Chris@14 1012 'method_name' => $methodName,
Chris@14 1013 'modifier' => $modifier,
Chris@14 1014 'reference' => $reference,
Chris@14 1015 'clone_arguments' => $cloneArguments ? 'true' : 'false',
Chris@14 1016 'deprecation' => $deprecation
Chris@14 1017 ]
Chris@14 1018 );
Chris@14 1019
Chris@14 1020 return $template->render();
Chris@14 1021 }
Chris@14 1022
Chris@14 1023 /**
Chris@14 1024 * @param ReflectionMethod $method
Chris@14 1025 *
Chris@14 1026 * @return bool
Chris@14 1027 *
Chris@14 1028 * @throws \ReflectionException
Chris@14 1029 */
Chris@14 1030 private function canMockMethod(ReflectionMethod $method)
Chris@14 1031 {
Chris@14 1032 return !($method->isConstructor() || $method->isFinal() || $method->isPrivate() || $this->isMethodNameBlacklisted($method->getName()));
Chris@14 1033 }
Chris@14 1034
Chris@14 1035 /**
Chris@14 1036 * Returns whether a method name is blacklisted
Chris@14 1037 *
Chris@14 1038 * @param string $name
Chris@14 1039 *
Chris@14 1040 * @return bool
Chris@14 1041 */
Chris@14 1042 private function isMethodNameBlacklisted($name)
Chris@14 1043 {
Chris@14 1044 return isset($this->blacklistedMethodNames[$name]);
Chris@14 1045 }
Chris@14 1046
Chris@14 1047 /**
Chris@14 1048 * Returns the parameters of a function or method.
Chris@14 1049 *
Chris@14 1050 * @param ReflectionMethod $method
Chris@14 1051 * @param bool $forCall
Chris@14 1052 *
Chris@14 1053 * @return string
Chris@14 1054 *
Chris@14 1055 * @throws RuntimeException
Chris@14 1056 */
Chris@14 1057 private function getMethodParameters(ReflectionMethod $method, $forCall = false)
Chris@14 1058 {
Chris@14 1059 $parameters = [];
Chris@14 1060
Chris@14 1061 foreach ($method->getParameters() as $i => $parameter) {
Chris@14 1062 $name = '$' . $parameter->getName();
Chris@14 1063
Chris@14 1064 /* Note: PHP extensions may use empty names for reference arguments
Chris@14 1065 * or "..." for methods taking a variable number of arguments.
Chris@14 1066 */
Chris@14 1067 if ($name === '$' || $name === '$...') {
Chris@14 1068 $name = '$arg' . $i;
Chris@14 1069 }
Chris@14 1070
Chris@14 1071 if ($parameter->isVariadic()) {
Chris@14 1072 if ($forCall) {
Chris@14 1073 continue;
Chris@14 1074 }
Chris@14 1075
Chris@14 1076 $name = '...' . $name;
Chris@14 1077 }
Chris@14 1078
Chris@14 1079 $nullable = '';
Chris@14 1080 $default = '';
Chris@14 1081 $reference = '';
Chris@14 1082 $typeDeclaration = '';
Chris@14 1083
Chris@14 1084 if (!$forCall) {
Chris@14 1085 if (PHP_VERSION_ID >= 70100 && $parameter->hasType() && $parameter->allowsNull()) {
Chris@14 1086 $nullable = '?';
Chris@14 1087 }
Chris@14 1088
Chris@14 1089 if ($parameter->hasType() && (string) $parameter->getType() !== 'self') {
Chris@14 1090 $typeDeclaration = (string) $parameter->getType() . ' ';
Chris@14 1091 } elseif ($parameter->isArray()) {
Chris@14 1092 $typeDeclaration = 'array ';
Chris@14 1093 } elseif ($parameter->isCallable()) {
Chris@14 1094 $typeDeclaration = 'callable ';
Chris@14 1095 } else {
Chris@14 1096 try {
Chris@14 1097 $class = $parameter->getClass();
Chris@14 1098 } catch (ReflectionException $e) {
Chris@14 1099 throw new RuntimeException(
Chris@14 1100 \sprintf(
Chris@14 1101 'Cannot mock %s::%s() because a class or ' .
Chris@14 1102 'interface used in the signature is not loaded',
Chris@14 1103 $method->getDeclaringClass()->getName(),
Chris@14 1104 $method->getName()
Chris@14 1105 ),
Chris@14 1106 0,
Chris@14 1107 $e
Chris@14 1108 );
Chris@14 1109 }
Chris@14 1110
Chris@14 1111 if ($class !== null) {
Chris@14 1112 $typeDeclaration = $class->getName() . ' ';
Chris@14 1113 }
Chris@14 1114 }
Chris@14 1115
Chris@14 1116 if (!$parameter->isVariadic()) {
Chris@14 1117 if ($parameter->isDefaultValueAvailable()) {
Chris@16 1118 $value = $parameter->getDefaultValueConstantName();
Chris@16 1119
Chris@16 1120 if ($value === null) {
Chris@16 1121 $value = \var_export($parameter->getDefaultValue(), true);
Chris@17 1122 } elseif (!\defined($value)) {
Chris@17 1123 $rootValue = \preg_replace('/^.*\\\\/', '', $value);
Chris@17 1124 $value = \defined($rootValue) ? $rootValue : $value;
Chris@16 1125 }
Chris@16 1126
Chris@16 1127 $default = ' = ' . $value;
Chris@14 1128 } elseif ($parameter->isOptional()) {
Chris@14 1129 $default = ' = null';
Chris@14 1130 }
Chris@14 1131 }
Chris@14 1132 }
Chris@14 1133
Chris@14 1134 if ($parameter->isPassedByReference()) {
Chris@14 1135 $reference = '&';
Chris@14 1136 }
Chris@14 1137
Chris@14 1138 $parameters[] = $nullable . $typeDeclaration . $reference . $name . $default;
Chris@14 1139 }
Chris@14 1140
Chris@14 1141 return \implode(', ', $parameters);
Chris@14 1142 }
Chris@14 1143
Chris@14 1144 /**
Chris@14 1145 * @param string $className
Chris@14 1146 *
Chris@14 1147 * @return array
Chris@14 1148 *
Chris@14 1149 * @throws \ReflectionException
Chris@14 1150 */
Chris@14 1151 public function getClassMethods($className)
Chris@14 1152 {
Chris@14 1153 $class = new ReflectionClass($className);
Chris@14 1154 $methods = [];
Chris@14 1155
Chris@14 1156 foreach ($class->getMethods() as $method) {
Chris@14 1157 if ($method->isPublic() || $method->isAbstract()) {
Chris@14 1158 $methods[] = $method->getName();
Chris@14 1159 }
Chris@14 1160 }
Chris@14 1161
Chris@14 1162 return $methods;
Chris@14 1163 }
Chris@14 1164
Chris@14 1165 /**
Chris@14 1166 * @param string $template
Chris@14 1167 *
Chris@14 1168 * @return Text_Template
Chris@14 1169 *
Chris@14 1170 * @throws \InvalidArgumentException
Chris@14 1171 */
Chris@14 1172 private function getTemplate($template)
Chris@14 1173 {
Chris@14 1174 $filename = __DIR__ . DIRECTORY_SEPARATOR . 'Generator' . DIRECTORY_SEPARATOR . $template;
Chris@14 1175
Chris@14 1176 if (!isset(self::$templates[$filename])) {
Chris@14 1177 self::$templates[$filename] = new Text_Template($filename);
Chris@14 1178 }
Chris@14 1179
Chris@14 1180 return self::$templates[$filename];
Chris@14 1181 }
Chris@14 1182 }