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