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 }
|