]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - tests/PHPUnit/PHPUnit/Framework/MockObject/Generator.php
Release 6.2.1
[Github/sugarcrm.git] / tests / PHPUnit / PHPUnit / Framework / MockObject / Generator.php
1 <?php
2 /**
3  * PHPUnit
4  *
5  * Copyright (c) 2010-2011, Sebastian Bergmann <sb@sebastian-bergmann.de>.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  *   * Redistributions of source code must retain the above copyright
13  *     notice, this list of conditions and the following disclaimer.
14  *
15  *   * Redistributions in binary form must reproduce the above copyright
16  *     notice, this list of conditions and the following disclaimer in
17  *     the documentation and/or other materials provided with the
18  *     distribution.
19  *
20  *   * Neither the name of Sebastian Bergmann nor the names of his
21  *     contributors may be used to endorse or promote products derived
22  *     from this software without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
27  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
28  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
29  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
30  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
34  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35  * POSSIBILITY OF SUCH DAMAGE.
36  *
37  * @package    PHPUnit_MockObject
38  * @author     Sebastian Bergmann <sb@sebastian-bergmann.de>
39  * @copyright  2010-2011 Sebastian Bergmann <sb@sebastian-bergmann.de>
40  * @license    http://www.opensource.org/licenses/bsd-license.php  BSD License
41  * @link       http://github.com/sebastianbergmann/phpunit-mock-objects
42  * @since      File available since Release 1.0.0
43  */
44
45 require_once 'Text/Template.php';
46
47 /**
48  * Mock Object Code Generator
49  *
50  * @package    PHPUnit_MockObject
51  * @author     Sebastian Bergmann <sb@sebastian-bergmann.de>
52  * @copyright  2010-2011 Sebastian Bergmann <sb@sebastian-bergmann.de>
53  * @license    http://www.opensource.org/licenses/bsd-license.php  BSD License
54  * @version    Release: 1.0.9
55  * @link       http://github.com/sebastianbergmann/phpunit-mock-objects
56  * @since      Class available since Release 1.0.0
57  */
58 class PHPUnit_Framework_MockObject_Generator
59 {
60     /**
61      * @var array
62      */
63     protected static $cache = array();
64
65     /**
66      * @var array
67      */
68     protected static $blacklistedMethodNames = array(
69       '__clone' => TRUE,
70       'abstract' => TRUE,
71       'and' => TRUE,
72       'array' => TRUE,
73       'as' => TRUE,
74       'break' => TRUE,
75       'case' => TRUE,
76       'catch' => TRUE,
77       'class' => TRUE,
78       'clone' => TRUE,
79       'const' => TRUE,
80       'continue' => TRUE,
81       'declare' => TRUE,
82       'default' => TRUE,
83       'die' => TRUE,
84       'do' => TRUE,
85       'echo' => TRUE,
86       'else' => TRUE,
87       'elseif' => TRUE,
88       'empty' => TRUE,
89       'enddeclare' => TRUE,
90       'endfor' => TRUE,
91       'endforeach' => TRUE,
92       'endif' => TRUE,
93       'endswitch' => TRUE,
94       'endwhile' => TRUE,
95       'eval' => TRUE,
96       'exit' => TRUE,
97       'extends' => TRUE,
98       'final' => TRUE,
99       'for' => TRUE,
100       'foreach' => TRUE,
101       'function' => TRUE,
102       'global' => TRUE,
103       'goto' => TRUE,
104       'if' => TRUE,
105       'implements' => TRUE,
106       'include' => TRUE,
107       'include_once' => TRUE,
108       'instanceof' => TRUE,
109       'interface' => TRUE,
110       'isset' => TRUE,
111       'list' => TRUE,
112       'namespace' => TRUE,
113       'new' => TRUE,
114       'or' => TRUE,
115       'print' => TRUE,
116       'private' => TRUE,
117       'protected' => TRUE,
118       'public' => TRUE,
119       'require' => TRUE,
120       'require_once' => TRUE,
121       'return' => TRUE,
122       'static' => TRUE,
123       'switch' => TRUE,
124       'throw' => TRUE,
125       'try' => TRUE,
126       'unset' => TRUE,
127       'use' => TRUE,
128       'var' => TRUE,
129       'while' => TRUE,
130       'xor' => TRUE
131     );
132
133     /**
134      * @var boolean
135      */
136     protected static $soapLoaded = NULL;
137
138     /**
139      * Returns a mock object for the specified class.
140      *
141      * @param  string  $originalClassName
142      * @param  array   $methods
143      * @param  array   $arguments
144      * @param  string  $mockClassName
145      * @param  boolean $callOriginalConstructor
146      * @param  boolean $callOriginalClone
147      * @param  boolean $callAutoload
148      * @return object
149      * @throws InvalidArgumentException
150      * @since  Method available since Release 1.0.0
151      */
152     public static function getMock($originalClassName, $methods = array(), array $arguments = array(), $mockClassName = '', $callOriginalConstructor = TRUE, $callOriginalClone = TRUE, $callAutoload = TRUE)
153     {
154         if (!is_string($originalClassName)) {
155             throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
156         }
157
158         if (!is_string($mockClassName)) {
159             throw PHPUnit_Util_InvalidArgumentHelper::factory(4, 'string');
160         }
161
162         if (!is_array($methods) && !is_null($methods)) {
163             throw new InvalidArgumentException;
164         }
165
166         if ($mockClassName != '' && class_exists($mockClassName, FALSE)) {
167             throw new PHPUnit_Framework_Exception(
168               sprintf(
169                 'Class "%s" already exists.',
170                 $mockClassName
171               )
172             );
173         }
174
175         $mock = self::generate(
176           $originalClassName,
177           $methods,
178           $mockClassName,
179           $callOriginalClone,
180           $callAutoload
181         );
182
183         if (!class_exists($mock['mockClassName'], FALSE)) {
184             eval($mock['code']);
185         }
186
187         if ($callOriginalConstructor &&
188             !interface_exists($originalClassName, $callAutoload)) {
189             if (count($arguments) == 0) {
190                 $mockObject = new $mock['mockClassName'];
191             } else {
192                 $mockClass  = new ReflectionClass($mock['mockClassName']);
193                 $mockObject = $mockClass->newInstanceArgs($arguments);
194             }
195         } else {
196             // Use a trick to create a new object of a class
197             // without invoking its constructor.
198             $mockObject = unserialize(
199               sprintf(
200                 'O:%d:"%s":0:{}',
201                 strlen($mock['mockClassName']), $mock['mockClassName']
202               )
203             );
204         }
205
206         return $mockObject;
207     }
208
209     /**
210      * Returns a mock object for the specified abstract class with all abstract
211      * methods of the class mocked. Concrete methods are not mocked.
212      *
213      * @param  string  $originalClassName
214      * @param  array   $arguments
215      * @param  string  $mockClassName
216      * @param  boolean $callOriginalConstructor
217      * @param  boolean $callOriginalClone
218      * @param  boolean $callAutoload
219      * @return object
220      * @since  Method available since Release 1.0.0
221      * @throws InvalidArgumentException
222      */
223     public static function getMockForAbstractClass($originalClassName, array $arguments = array(), $mockClassName = '', $callOriginalConstructor = TRUE, $callOriginalClone = TRUE, $callAutoload = TRUE)
224     {
225         if (!is_string($originalClassName)) {
226             throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'string');
227         }
228
229         if (!is_string($mockClassName)) {
230             throw PHPUnit_Util_InvalidArgumentHelper::factory(3, 'string');
231         }
232
233         if (class_exists($originalClassName, $callAutoload)) {
234             $methods   = array();
235             $reflector = new ReflectionClass($originalClassName);
236
237             foreach ($reflector->getMethods() as $method) {
238                 if ($method->isAbstract()) {
239                     $methods[] = $method->getName();
240                 }
241             }
242
243             if (empty($methods)) {
244                 $methods = NULL;
245             }
246
247             return self::getMock(
248               $originalClassName,
249               $methods,
250               $arguments,
251               $mockClassName,
252               $callOriginalConstructor,
253               $callOriginalClone,
254               $callAutoload
255             );
256         } else {
257             throw new PHPUnit_Framework_Exception(
258               sprintf(
259                 'Class "%s" does not exist.',
260                 $originalClassName
261               )
262             );
263         }
264     }
265
266     /**
267      * @param  string  $originalClassName
268      * @param  array   $methods
269      * @param  string  $mockClassName
270      * @param  boolean $callOriginalClone
271      * @param  boolean $callAutoload
272      * @return array
273      */
274     public static function generate($originalClassName, array $methods = NULL, $mockClassName = '', $callOriginalClone = TRUE, $callAutoload = TRUE)
275     {
276         if ($mockClassName == '') {
277             $key = md5(
278               $originalClassName .
279               serialize($methods) .
280               serialize($callOriginalClone)
281             );
282
283             if (isset(self::$cache[$key])) {
284                 return self::$cache[$key];
285             }
286         }
287
288         $mock = self::generateMock(
289           $originalClassName,
290           $methods,
291           $mockClassName,
292           $callOriginalClone,
293           $callAutoload
294         );
295
296         if (isset($key)) {
297             self::$cache[$key] = $mock;
298         }
299
300         return $mock;
301     }
302
303     /**
304      * @param  string $wsdlFile
305      * @param  string $originalClassName
306      * @param  array  $methods
307      * @return array
308      */
309     public static function generateClassFromWsdl($wsdlFile, $originalClassName, array $methods = array())
310     {
311         if (self::$soapLoaded === NULL) {
312             self::$soapLoaded = extension_loaded('soap');
313         }
314
315         if (self::$soapLoaded) {
316             $client   = new SOAPClient($wsdlFile);
317             $_methods = array_unique($client->__getFunctions());
318             unset($client);
319
320             $templateDir    = dirname(__FILE__) . DIRECTORY_SEPARATOR .
321                               'Generator' . DIRECTORY_SEPARATOR;
322             $methodTemplate = new Text_Template(
323                                 $templateDir . 'wsdl_method.tpl'
324                               );
325             $methodsBuffer  = '';
326
327             foreach ($_methods as $method) {
328                 $nameStart = strpos($method, ' ') + 1;
329                 $nameEnd   = strpos($method, '(');
330                 $name      = substr($method, $nameStart, $nameEnd - $nameStart);
331
332                 if (empty($methods) || in_array($name, $methods)) {
333                     $args    = explode(
334                                  ',',
335                                  substr(
336                                    $method,
337                                    $nameEnd + 1,
338                                    strpos($method, ')') - $nameEnd - 1
339                                  )
340                                );
341                     $numArgs = count($args);
342
343                     for ($i = 0; $i < $numArgs; $i++) {
344                         $args[$i] = substr($args[$i], strpos($args[$i], '$'));
345                     }
346
347                     $methodTemplate->setVar(
348                       array(
349                         'method_name' => $name,
350                         'arguments'   => join(', ', $args)
351                       )
352                     );
353
354                     $methodsBuffer .= $methodTemplate->render();
355                 }
356             }
357
358             $classTemplate = new Text_Template(
359               $templateDir . 'wsdl_class.tpl'
360             );
361
362             $classTemplate->setVar(
363               array(
364                 'class_name' => $originalClassName,
365                 'wsdl'       => $wsdlFile,
366                 'methods'    => $methodsBuffer
367               )
368             );
369
370             return $classTemplate->render();
371         } else {
372             throw new PHPUnit_Framework_Exception(
373               'The SOAP extension is required to generate a mock object ' .
374               'from WSDL.'
375             );
376         }
377     }
378
379     /**
380      * @param  string     $originalClassName
381      * @param  array|null $methods
382      * @param  string     $mockClassName
383      * @param  boolean    $callOriginalClone
384      * @param  boolean    $callAutoload
385      * @return array
386      */
387     protected static function generateMock($originalClassName, $methods, $mockClassName, $callOriginalClone, $callAutoload)
388     {
389         $templateDir   = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'Generator' .
390                          DIRECTORY_SEPARATOR;
391         $classTemplate = new Text_Template(
392                            $templateDir . 'mocked_class.tpl'
393                          );
394         $cloneTemplate = '';
395         $isClass       = FALSE;
396         $isInterface   = FALSE;
397
398         $mockClassName = self::generateMockClassName(
399           $originalClassName, $mockClassName
400         );
401
402         if (class_exists($mockClassName['fullClassName'], $callAutoload)) {
403             $isClass = TRUE;
404         } else {
405             if (interface_exists($mockClassName['fullClassName'], $callAutoload)) {
406                 $isInterface = TRUE;
407             }
408         }
409
410         if (!class_exists($mockClassName['fullClassName'], $callAutoload) &&
411             !interface_exists($mockClassName['fullClassName'], $callAutoload)) {
412             $prologue = 'class ' . $mockClassName['className'] . "\n{\n}\n\n";
413
414             if (!empty($mockClassName['namespaceName'])) {
415                 $prologue = 'namespace ' . $mockClassName['namespaceName'] .
416                             " {\n\n" . $prologue . "}\n\n" .
417                             "namespace {\n\n";
418
419                 $epilogue = "\n\n}";
420             }
421
422             $cloneTemplate = new Text_Template(
423               $templateDir . 'mocked_clone.tpl'
424             );
425         } else {
426             $class = new ReflectionClass($mockClassName['fullClassName']);
427
428             if ($class->isFinal()) {
429                 throw new PHPUnit_Framework_Exception(
430                   sprintf(
431                     'Class "%s" is declared "final" and cannot be mocked.',
432                     $mockClassName['fullClassName']
433                   )
434                 );
435             }
436
437             if ($class->hasMethod('__clone')) {
438                 $cloneMethod = $class->getMethod('__clone');
439
440                 if (!$cloneMethod->isFinal()) {
441                     if ($callOriginalClone && !$isInterface) {
442                         $cloneTemplate = new Text_Template(
443                           $templateDir . 'unmocked_clone.tpl'
444                         );
445                     } else {
446                         $cloneTemplate = new Text_Template(
447                           $templateDir . 'mocked_clone.tpl'
448                         );
449                     }
450                 }
451             } else {
452                 $cloneTemplate = new Text_Template(
453                   $templateDir . 'mocked_clone.tpl'
454                 );
455             }
456         }
457
458         if (is_object($cloneTemplate)) {
459             $cloneTemplate = $cloneTemplate->render();
460         }
461
462         if (is_array($methods) && empty($methods) &&
463             ($isClass || $isInterface)) {
464             $methods = get_class_methods($mockClassName['fullClassName']);
465         }
466
467         if (!is_array($methods)) {
468             $methods = array();
469         }
470
471         $constructor   = NULL;
472         $mockedMethods = '';
473
474         if (isset($class)) {
475             if ($class->hasMethod('__construct')) {
476                 $constructor = $class->getMethod('__construct');
477             }
478
479             else if ($class->hasMethod($originalClassName)) {
480                 $constructor = $class->getMethod($originalClassName);
481             }
482
483             foreach ($methods as $methodName) {
484                 try {
485                     $method = $class->getMethod($methodName);
486
487                     if (self::canMockMethod($method)) {
488                         $mockedMethods .= self::generateMockedMethodDefinitionFromExisting(
489                           $templateDir, $method
490                         );
491                     }
492                 }
493
494                 catch (ReflectionException $e) {
495                     $mockedMethods .= self::generateMockedMethodDefinition(
496                       $templateDir, $mockClassName['fullClassName'], $methodName
497                     );
498                 }
499             }
500         } else {
501             foreach ($methods as $methodName) {
502                 $mockedMethods .= self::generateMockedMethodDefinition(
503                   $templateDir, $mockClassName['fullClassName'], $methodName
504                 );
505             }
506         }
507
508         $classTemplate->setVar(
509           array(
510             'prologue'          => isset($prologue) ? $prologue : '',
511             'epilogue'          => isset($epilogue) ? $epilogue : '',
512             'class_declaration' => self::generateMockClassDeclaration(
513                                      $mockClassName, $isInterface
514                                    ),
515             'clone'             => $cloneTemplate,
516             'mock_class_name'   => $mockClassName['mockClassName'],
517             'mocked_methods'    => $mockedMethods
518           )
519         );
520
521         return array(
522           'code'          => $classTemplate->render(),
523           'mockClassName' => $mockClassName['mockClassName']
524         );
525     }
526
527     /**
528      * @param  string $originalClassName
529      * @param  string $mockClassName
530      * @return array
531      */
532     protected static function generateMockClassName($originalClassName, $mockClassName)
533     {
534         if ($originalClassName[0] == '\\') {
535             $originalClassName = substr($originalClassName, 1);
536         }
537
538         $classNameParts = explode('\\', $originalClassName);
539
540         if (count($classNameParts) > 1) {
541             $originalClassName = array_pop($classNameParts);
542             $namespaceName     = join('\\', $classNameParts);
543             $fullClassName     = $namespaceName . '\\' . $originalClassName;
544         } else {
545             $namespaceName = '';
546             $fullClassName = $originalClassName;
547         }
548
549         if ($mockClassName == '') {
550             do {
551                 $mockClassName = 'Mock_' . $originalClassName . '_' .
552                                  substr(md5(microtime()), 0, 8);
553             }
554             while (class_exists($mockClassName, FALSE));
555         }
556
557         return array(
558           'mockClassName' => $mockClassName,
559           'className'     => $originalClassName,
560           'fullClassName' => $fullClassName,
561           'namespaceName' => $namespaceName
562         );
563     }
564
565     /**
566      * @param  array   $mockClassName
567      * @param  boolean $isInterface
568      * @return array
569      */
570     protected static function generateMockClassDeclaration(array $mockClassName, $isInterface)
571     {
572         $buffer = 'class ';
573
574         if ($isInterface) {
575             $buffer .= sprintf(
576               "%s implements PHPUnit_Framework_MockObject_MockObject, %s%s",
577               $mockClassName['mockClassName'],
578               !empty($mockClassName['namespaceName']) ? $mockClassName['namespaceName'] . '\\' : '',
579               $mockClassName['className']
580             );
581         } else {
582             $buffer .= sprintf(
583               "%s extends %s%s implements PHPUnit_Framework_MockObject_MockObject",
584               $mockClassName['mockClassName'],
585               !empty($mockClassName['namespaceName']) ? $mockClassName['namespaceName'] . '\\' : '',
586               $mockClassName['className']
587             );
588         }
589
590         return $buffer;
591     }
592
593     /**
594      * @param  string           $templateDir
595      * @param  ReflectionMethod $method
596      * @return string
597      */
598     protected static function generateMockedMethodDefinitionFromExisting($templateDir, ReflectionMethod $method)
599     {
600         if ($method->isPrivate()) {
601             $modifier = 'private';
602         }
603
604         else if ($method->isProtected()) {
605             $modifier = 'protected';
606         }
607
608         else {
609             $modifier = 'public';
610         }
611
612         if ($method->isStatic()) {
613             $static = TRUE;
614         } else {
615             $static = FALSE;
616         }
617
618         if ($method->returnsReference()) {
619             $reference = '&';
620         } else {
621             $reference = '';
622         }
623
624         return self::generateMockedMethodDefinition(
625           $templateDir,
626           $method->getDeclaringClass()->getName(),
627           $method->getName(),
628           $modifier,
629           PHPUnit_Util_Class::getMethodParameters($method),
630           PHPUnit_Util_Class::getMethodParameters($method, TRUE),
631           $reference,
632           $static
633         );
634     }
635
636     /**
637      * @param  string  $templateDir
638      * @param  string  $className
639      * @param  string  $methodName
640      * @param  string  $modifier
641      * @param  string  $arguments_decl
642      * @param  string  $arguments_call
643      * @param  string  $reference
644      * @param  boolean $static
645      * @return string
646      */
647     protected static function generateMockedMethodDefinition($templateDir, $className, $methodName, $modifier = 'public', $arguments_decl = '', $arguments_call = '', $reference = '', $static = FALSE)
648     {
649         if ($static) {
650             $template = new Text_Template(
651               $templateDir . 'mocked_static_method.tpl'
652             );
653         } else {
654             $template = new Text_Template(
655               $templateDir . 'mocked_object_method.tpl'
656             );
657         }
658
659         $template->setVar(
660           array(
661             'arguments_decl'  => $arguments_decl,
662             'arguments_call'  => $arguments_call,
663             'arguments_count' => !empty($arguments_call) ? count(explode(',', $arguments_call)) : 0,
664             'class_name'      => $className,
665             'method_name'     => $methodName,
666             'modifier'        => $modifier,
667             'reference'       => $reference
668           )
669         );
670
671         return $template->render();
672     }
673
674     /**
675      * @param  ReflectionMethod $method
676      * @return boolean
677      */
678     protected static function canMockMethod(ReflectionMethod $method)
679     {
680         if ($method->isConstructor() || $method->isFinal() ||
681             isset(self::$blacklistedMethodNames[$method->getName()])) {
682             return FALSE;
683         }
684
685         return TRUE;
686     }
687 }