Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
/**
3
 * PHPUnit
4
 *
5
 * Copyright (c) 2002-2010, 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
 * @category   Testing
38
 * @package    PHPUnit
39
 * @author     Sebastian Bergmann <sb@sebastian-bergmann.de>
40
 * @copyright  2002-2010 Sebastian Bergmann <sb@sebastian-bergmann.de>
41
 * @license    http://www.opensource.org/licenses/bsd-license.php  BSD License
42
 * @link       http://www.phpunit.de/
43
 * @since      File available since Release 3.4.0
44
 */
45
 
46
require_once 'PHPUnit/Framework/MockObject/Matcher.php';
47
require_once 'PHPUnit/Framework/MockObject/Invocation.php';
48
require_once 'PHPUnit/Framework/MockObject/MockObject.php';
49
require_once 'PHPUnit/Util/Class.php';
50
require_once 'PHPUnit/Util/Filter.php';
51
require_once 'PHPUnit/Util/Template.php';
52
 
53
PHPUnit_Util_Filter::addFileToFilter(__FILE__, 'PHPUNIT');
54
 
55
/**
56
 * Mock Object Code Generator
57
 *
58
 * @category   Testing
59
 * @package    PHPUnit
60
 * @author     Sebastian Bergmann <sb@sebastian-bergmann.de>
61
 * @copyright  2002-2010 Sebastian Bergmann <sb@sebastian-bergmann.de>
62
 * @license    http://www.opensource.org/licenses/bsd-license.php  BSD License
63
 * @version    Release: 3.4.15
64
 * @link       http://www.phpunit.de/
65
 * @since      Class available since Release 3.4.0
66
 */
67
class PHPUnit_Framework_MockObject_Generator
68
{
69
    /**
70
     * @var array
71
     */
72
    protected static $cache = array();
73
 
74
    /**
75
     * @var array
76
     */
77
    protected static $blacklistedMethodNames = array(
78
      '__clone' => TRUE,
79
      'abstract' => TRUE,
80
      'and' => TRUE,
81
      'array' => TRUE,
82
      'as' => TRUE,
83
      'break' => TRUE,
84
      'case' => TRUE,
85
      'catch' => TRUE,
86
      'class' => TRUE,
87
      'clone' => TRUE,
88
      'const' => TRUE,
89
      'continue' => TRUE,
90
      'declare' => TRUE,
91
      'default' => TRUE,
92
      'do' => TRUE,
93
      'else' => TRUE,
94
      'elseif' => TRUE,
95
      'enddeclare' => TRUE,
96
      'endfor' => TRUE,
97
      'endforeach' => TRUE,
98
      'endif' => TRUE,
99
      'endswitch' => TRUE,
100
      'endwhile' => TRUE,
101
      'extends' => TRUE,
102
      'final' => TRUE,
103
      'for' => TRUE,
104
      'foreach' => TRUE,
105
      'function' => TRUE,
106
      'global' => TRUE,
107
      'goto' => TRUE,
108
      'if' => TRUE,
109
      'implements' => TRUE,
110
      'interface' => TRUE,
111
      'instanceof' => TRUE,
112
      'namespace' => TRUE,
113
      'new' => TRUE,
114
      'or' => TRUE,
115
      'private' => TRUE,
116
      'protected' => TRUE,
117
      'public' => TRUE,
118
      'static' => TRUE,
119
      'switch' => TRUE,
120
      'throw' => TRUE,
121
      'try' => TRUE,
122
      'use' => TRUE,
123
      'var' => TRUE,
124
      'while' => TRUE,
125
      'xor' => TRUE
126
    );
127
 
128
    /**
129
     * @var boolean
130
     */
131
    protected static $soapLoaded = NULL;
132
 
133
    /**
134
     * @param  string  $originalClassName
135
     * @param  array   $methods
136
     * @param  string  $mockClassName
137
     * @param  boolean $callOriginalClone
138
     * @param  boolean $callAutoload
139
     * @return array
140
     */
141
    public static function generate($originalClassName, array $methods = NULL, $mockClassName = '', $callOriginalClone = TRUE, $callAutoload = TRUE)
142
    {
143
        if ($mockClassName == '') {
144
            $key = md5(
145
              $originalClassName .
146
              serialize($methods) .
147
              serialize($callOriginalClone)
148
            );
149
 
150
            if (isset(self::$cache[$key])) {
151
                return self::$cache[$key];
152
            }
153
        }
154
 
155
        $mock = self::generateMock(
156
          $originalClassName,
157
          $methods,
158
          $mockClassName,
159
          $callOriginalClone,
160
          $callAutoload
161
        );
162
 
163
        if (isset($key)) {
164
            self::$cache[$key] = $mock;
165
        }
166
 
167
        return $mock;
168
    }
169
 
170
    /**
171
     * @param  string $wsdlFile
172
     * @param  string $originalClassName
173
     * @param  array  $methods
174
     * @return array
175
     */
176
    public static function generateClassFromWsdl($wsdlFile, $originalClassName, array $methods = array())
177
    {
178
        if (self::$soapLoaded === NULL) {
179
            self::$soapLoaded = extension_loaded('soap');
180
        }
181
 
182
        if (self::$soapLoaded) {
183
            $client   = new SOAPClient($wsdlFile);
184
            $_methods = array_unique($client->__getFunctions());
185
            unset($client);
186
 
187
            $templateDir    = dirname(__FILE__) . DIRECTORY_SEPARATOR .
188
                             'Generator' . DIRECTORY_SEPARATOR;
189
            $methodTemplate = new PHPUnit_Util_Template(
190
                                $templateDir . 'wsdl_method.tpl'
191
                              );
192
            $methodsBuffer  = '';
193
 
194
            foreach ($_methods as $method) {
195
                $nameStart = strpos($method, ' ') + 1;
196
                $nameEnd   = strpos($method, '(');
197
                $name      = substr($method, $nameStart, $nameEnd - $nameStart);
198
 
199
                if (empty($methods) || in_array($name, $methods)) {
200
                    $args    = explode(
201
                                 ',',
202
                                 substr(
203
                                   $method,
204
                                   $nameEnd + 1,
205
                                   strpos($method, ')') - $nameEnd - 1
206
                                 )
207
                               );
208
                    $numArgs = count($args);
209
 
210
                    for ($i = 0; $i < $numArgs; $i++) {
211
                        $args[$i] = substr($args[$i], strpos($args[$i], '$'));
212
                    }
213
 
214
                    $methodTemplate->setVar(
215
                      array(
216
                        'method_name' => $name,
217
                        'arguments'   => join(', ', $args)
218
                      )
219
                    );
220
 
221
                    $methodsBuffer .= $methodTemplate->render();
222
                }
223
            }
224
 
225
            $classTemplate = new PHPUnit_Util_Template(
226
              $templateDir . 'wsdl_class.tpl'
227
            );
228
 
229
            $classTemplate->setVar(
230
              array(
231
                'class_name' => $originalClassName,
232
                'wsdl'       => $wsdlFile,
233
                'methods'    => $methodsBuffer
234
              )
235
            );
236
 
237
            return $classTemplate->render();
238
        } else {
239
            throw new PHPUnit_Framework_Exception(
240
              'The SOAP extension is required to generate a mock object ' .
241
              'from WSDL.'
242
            );
243
        }
244
    }
245
 
246
    /**
247
     * @param  string  $originalClassName
248
     * @param  array   $methods
249
     * @param  string  $mockClassName
250
     * @param  boolean $callOriginalClone
251
     * @param  boolean $callAutoload
252
     * @return array
253
     */
254
    protected static function generateMock($originalClassName, array $methods = NULL, $mockClassName, $callOriginalClone, $callAutoload)
255
    {
256
        $templateDir   = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'Generator' .
257
                         DIRECTORY_SEPARATOR;
258
        $classTemplate = new PHPUnit_Util_Template(
259
                           $templateDir . 'mocked_class.tpl'
260
                         );
261
        $cloneTemplate = '';
262
        $isClass       = FALSE;
263
        $isInterface   = FALSE;
264
 
265
        $mockClassName = self::generateMockClassName(
266
          $originalClassName, $mockClassName
267
        );
268
 
269
        if (class_exists($mockClassName['fullClassName'], $callAutoload)) {
270
            $isClass = TRUE;
271
        } else {
272
            if (interface_exists($mockClassName['fullClassName'], $callAutoload)) {
273
                $isInterface = TRUE;
274
            }
275
        }
276
 
277
        if (!class_exists($mockClassName['fullClassName'], $callAutoload) &&
278
            !interface_exists($mockClassName['fullClassName'], $callAutoload)) {
279
            $prologue = 'class ' . $mockClassName['className'] . "\n{\n}\n\n";
280
 
281
            if (!empty($mockClassName['namespaceName'])) {
282
                $prologue = 'namespace ' . $mockClassName['namespaceName'] .
283
                            ";\n\n" . $prologue;
284
            }
285
 
286
            $cloneTemplate = new PHPUnit_Util_Template(
287
              $templateDir . 'mocked_clone.tpl'
288
            );
289
        } else {
290
            $class = new ReflectionClass($mockClassName['fullClassName']);
291
 
292
            if ($class->isFinal()) {
293
                throw new PHPUnit_Framework_Exception(
294
                  sprintf(
295
                    'Class "%s" is declared "final" and cannot be mocked.',
296
                    $mockClassName['fullClassName']
297
                  )
298
                );
299
            }
300
 
301
            if ($class->hasMethod('__clone')) {
302
                $cloneMethod = $class->getMethod('__clone');
303
 
304
                if (!$cloneMethod->isFinal()) {
305
                    if ($callOriginalClone) {
306
                        $cloneTemplate = new PHPUnit_Util_Template(
307
                          $templateDir . 'unmocked_clone.tpl'
308
                        );
309
                    } else {
310
                        $cloneTemplate = new PHPUnit_Util_Template(
311
                          $templateDir . 'mocked_clone.tpl'
312
                        );
313
                    }
314
                }
315
            } else {
316
                $cloneTemplate = new PHPUnit_Util_Template(
317
                  $templateDir . 'mocked_clone.tpl'
318
                );
319
            }
320
        }
321
 
322
        if (is_object($cloneTemplate)) {
323
            $cloneTemplate = $cloneTemplate->render();
324
        }
325
 
326
        if (is_array($methods) && empty($methods) &&
327
            ($isClass || $isInterface)) {
328
            $methods = get_class_methods($mockClassName['fullClassName']);
329
        }
330
 
331
        if (!is_array($methods)) {
332
            $methods = array();
333
        }
334
 
335
        $constructor   = NULL;
336
        $mockedMethods = '';
337
 
338
        if (isset($class)) {
339
            if ($class->hasMethod('__construct')) {
340
                $constructor = $class->getMethod('__construct');
341
            }
342
 
343
            else if ($class->hasMethod($originalClassName)) {
344
                $constructor = $class->getMethod($originalClassName);
345
            }
346
 
347
            foreach ($methods as $methodName) {
348
                try {
349
                    $method = $class->getMethod($methodName);
350
 
351
                    if (self::canMockMethod($method)) {
352
                        $mockedMethods .= self::generateMockedMethodDefinitionFromExisting(
353
                          $templateDir, $method
354
                        );
355
                    }
356
                }
357
 
358
                catch (ReflectionException $e) {
359
                    $mockedMethods .= self::generateMockedMethodDefinition(
360
                      $templateDir, $mockClassName['fullClassName'], $methodName
361
                    );
362
                }
363
            }
364
        } else {
365
            foreach ($methods as $methodName) {
366
                $mockedMethods .= self::generateMockedMethodDefinition(
367
                  $templateDir, $mockClassName['fullClassName'], $methodName
368
                );
369
            }
370
        }
371
 
372
        $classTemplate->setVar(
373
          array(
374
            'prologue'          => isset($prologue) ? $prologue : '',
375
            'class_declaration' => self::generateMockClassDeclaration(
376
                                     $mockClassName, $isInterface
377
                                   ),
378
            'clone'             => $cloneTemplate,
379
            'mocked_methods'    => $mockedMethods
380
          )
381
        );
382
 
383
        return array(
384
          'code'          => $classTemplate->render(),
385
          'mockClassName' => $mockClassName['mockClassName']
386
        );
387
    }
388
 
389
    /**
390
     * @param  string $originalClassName
391
     * @param  string $mockClassName
392
     * @return array
393
     */
394
    protected static function generateMockClassName($originalClassName, $mockClassName)
395
    {
396
        $classNameParts = explode('\\', $originalClassName);
397
 
398
        if (count($classNameParts) > 1) {
399
            $originalClassName = array_pop($classNameParts);
400
            $namespaceName     = join('\\', $classNameParts);
401
            $fullClassName     = $namespaceName . '\\' . $originalClassName;
402
        } else {
403
            $namespaceName = '';
404
            $fullClassName = $originalClassName;
405
        }
406
 
407
        if ($mockClassName == '') {
408
            do {
409
                $mockClassName = 'Mock_' . $originalClassName . '_' .
410
                                 substr(md5(microtime()), 0, 8);
411
            }
412
            while (class_exists($mockClassName, FALSE));
413
        }
414
 
415
        return array(
416
          'mockClassName' => $mockClassName,
417
          'className'     => $originalClassName,
418
          'fullClassName' => $fullClassName,
419
          'namespaceName' => $namespaceName
420
        );
421
    }
422
 
423
    /**
424
     * @param  array   $mockClassName
425
     * @param  boolean $isInterface
426
     * @return array
427
     */
428
    protected static function generateMockClassDeclaration(array $mockClassName, $isInterface)
429
    {
430
        $buffer = 'class ';
431
 
432
        if ($isInterface) {
433
            $buffer .= sprintf(
434
              "%s implements PHPUnit_Framework_MockObject_MockObject, %s%s",
435
              $mockClassName['mockClassName'],
436
              !empty($mockClassName['namespaceName']) ? $mockClassName['namespaceName'] . '\\' : '',
437
              $mockClassName['className']
438
            );
439
        } else {
440
            $buffer .= sprintf(
441
              "%s extends %s%s implements PHPUnit_Framework_MockObject_MockObject",
442
              $mockClassName['mockClassName'],
443
              !empty($mockClassName['namespaceName']) ? $mockClassName['namespaceName'] . '\\' : '',
444
              $mockClassName['className']
445
            );
446
        }
447
 
448
        return $buffer;
449
    }
450
 
451
    /**
452
     * @param  string           $templateDir
453
     * @param  ReflectionMethod $method
454
     * @return string
455
     */
456
    protected static function generateMockedMethodDefinitionFromExisting($templateDir, ReflectionMethod $method)
457
    {
458
        if ($method->isPrivate()) {
459
            $modifier = 'private';
460
        }
461
 
462
        else if ($method->isProtected()) {
463
            $modifier = 'protected';
464
        }
465
 
466
        else {
467
            $modifier = 'public';
468
        }
469
 
470
        if ($method->returnsReference()) {
471
            $reference = '&';
472
        } else {
473
            $reference = '';
474
        }
475
 
476
        return self::generateMockedMethodDefinition(
477
          $templateDir,
478
          $method->getDeclaringClass()->getName(),
479
          $method->getName(),
480
          $modifier,
481
          PHPUnit_Util_Class::getMethodParameters($method),
482
          $reference
483
        );
484
    }
485
 
486
    /**
487
     * @param  string  $templateDir
488
     * @param  string  $className
489
     * @param  string  $methodName
490
     * @param  string  $modifier
491
     * @param  string  $arguments
492
     * @param  string  $reference
493
     * @return string
494
     */
495
    protected static function generateMockedMethodDefinition($templateDir, $className, $methodName, $modifier = 'public', $arguments = '', $reference = '')
496
    {
497
        $template = new PHPUnit_Util_Template(
498
          $templateDir . 'mocked_method.tpl'
499
        );
500
 
501
        $template->setVar(
502
          array(
503
            'arguments'   => $arguments,
504
            'class_name'  => $className,
505
            'method_name' => $methodName,
506
            'modifier'    => $modifier,
507
            'reference'   => $reference
508
          )
509
        );
510
 
511
        return $template->render();
512
    }
513
 
514
    /**
515
     * @param  ReflectionMethod $method
516
     * @return boolean
517
     */
518
    protected static function canMockMethod(ReflectionMethod $method)
519
    {
520
        if ($method->isConstructor() ||
521
            $method->isFinal() ||
522
            $method->isStatic() ||
523
            isset(self::$blacklistedMethodNames[$method->getName()])) {
524
            return FALSE;
525
        }
526
 
527
        return TRUE;
528
    }
529
}
530
?>