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