mageekguy\atoum\mock\generator: lines coverage

93% of 2960

OPs

96% of 533

Lines

88% of 560

Branches

2% of 5739

Paths
Method OPs OPs % Lines Line % Branches Branches % Paths Path %
mageekguy\atoum\mock\generator::__construct() 12 100% 4 100% 1 0% 1 100%
mageekguy\atoum\mock\generator::callsToParentClassAreShunted() 6 100% 1 100% 1 100% 1 100%
mageekguy\atoum\mock\generator::setAdapter() 16 100% 2 100% 1 100% 1 100%
mageekguy\atoum\mock\generator::getAdapter() 6 100% 1 100% 1 100% 1 100%
mageekguy\atoum\mock\generator::setReflectionClassFactory() 12 100% 3 100% 1 100% 1 100%
mageekguy\atoum\mock\generator::getReflectionClassFactory() 6 100% 1 100% 1 100% 1 100%
mageekguy\atoum\mock\generator::setDefaultNamespace() 15 100% 2 100% 1 100% 1 100%
mageekguy\atoum\mock\generator::getDefaultNamespace() 15 100% 2 100% 5 100% 2 100%
mageekguy\atoum\mock\generator::overload() 19 100% 3 100% 1 100% 1 100%
mageekguy\atoum\mock\generator::isOverloaded() 13 100% 1 100% 1 100% 1 100%
mageekguy\atoum\mock\generator::getOverload() 23 100% 2 100% 5 100% 2 100%
mageekguy\atoum\mock\generator::shunt() 25 100% 5 100% 4 100% 2 100%
mageekguy\atoum\mock\generator::isShunted() 20 100% 2 100% 1 100% 1 100%
mageekguy\atoum\mock\generator::shuntParentClassCalls() 9 100% 2 100% 1 100% 1 100%
mageekguy\atoum\mock\generator::unshuntParentClassCalls() 9 100% 2 100% 1 100% 1 100%
mageekguy\atoum\mock\generator::orphanize() 30 100% 5 100% 4 100% 2 100%
mageekguy\atoum\mock\generator::isOrphanized() 15 100% 2 100% 1 100% 1 100%
mageekguy\atoum\mock\generator::allIsInterface() 9 100% 2 100% 1 100% 1 100%
mageekguy\atoum\mock\generator::testedClassIs() 14 100% 3 100% 1 100% 1 100%
mageekguy\atoum\mock\generator::getMockedClassCode() 214 99% 31 100% 33 85% 146 5%
mageekguy\atoum\mock\generator::generate() 19 100% 2 100% 1 100% 1 100%
mageekguy\atoum\mock\generator::methodIsMockable() 81 96% 11 100% 21 86% 8 63%
mageekguy\atoum\mock\generator::disallowUndefinedMethodInInterface() 9 0% 2 50% 1 0% 1 0%
mageekguy\atoum\mock\generator::disallowUndefinedMethodUsage() 9 100% 2 100% 1 100% 1 100%
mageekguy\atoum\mock\generator::allowUndefinedMethodInInterface() 9 0% 1 100% 1 0% 1 0%
mageekguy\atoum\mock\generator::allowUndefinedMethodUsage() 9 0% 3 33% 1 0% 1 0%
mageekguy\atoum\mock\generator::undefinedMethodInInterfaceAreAllowed() 9 0% 1 0% 1 0% 1 0%
mageekguy\atoum\mock\generator::undefinedMethodUsageIsAllowed() 8 0% 1 0% 1 0% 1 0%
mageekguy\atoum\mock\generator::generateClassMethodCode() 743 92% 138 93% 101 82% 4096 0%
mageekguy\atoum\mock\generator::generateClassCode() 50 100% 8 100% 1 100% 1 100%
mageekguy\atoum\mock\generator::generateInterfaceMethodCode() 424 87% 79 92% 57 72% 1216 0%
mageekguy\atoum\mock\generator::generateInterfaceCode() 93 100% 14 100% 14 93% 10 20%
mageekguy\atoum\mock\generator::getNamespace() 42 100% 3 100% 5 100% 2 100%
mageekguy\atoum\mock\generator::getParameters() 75 73% 12 100% 19 68% 7 14%
mageekguy\atoum\mock\generator::getParametersSignature() 154 89% 19 100% 35 91% 138 1%
mageekguy\atoum\mock\generator::getClassName() 35 100% 4 100% 5 100% 2 100%
mageekguy\atoum\mock\generator::getParameterType() 62 94% 10 100% 15 67% 7 43%
mageekguy\atoum\mock\generator::isVariadic() 22 73% 4 75% 4 25% 2 50%
mageekguy\atoum\mock\generator::hasVariadic() 35 97% 5 100% 4 75% 2 100%
mageekguy\atoum\mock\generator::generateMockControllerMethods() 80 100% 19 100% 1 100% 1 100%
mageekguy\atoum\mock\generator::generateDefaultConstructor() 80 100% 22 100% 4 100% 2 100%
mageekguy\atoum\mock\generator::generate__call() 52 100% 13 100% 1 100% 1 100%
mageekguy\atoum\mock\generator::generateGetMockedMethod() 30 100% 5 100% 1 100% 1 100%
mageekguy\atoum\mock\generator::generateUnknownClassCode() 60 100% 9 100% 1 100% 1 100%
mageekguy\atoum\mock\generator::methodNameIsReservedWord() 282 99% 70 100% 198 99% 66 98%
#
1
<?php
2

                    
3
namespace mageekguy\atoum\mock;
4

                    
5
use
6
	mageekguy\atoum,
7
	mageekguy\atoum\mock,
8
	mageekguy\atoum\exceptions
9
;
10

                    
11
class generator
12
{
13
	const defaultNamespace = 'mock';
14

                    
15
	protected $adapter = null;
16
	protected $reflectionClassFactory = null;
17
	protected $shuntedMethods = array();
18
	protected $overloadedMethods = array();
19
	protected $orphanizedMethods = array();
20
	protected $shuntParentClassCalls = false;
21
	protected $allowUndefinedMethodsUsage = true;
22
	protected $allIsInterface = false;
23
	protected $testedClass = '';
24

                    
25
	private $defaultNamespace = null;
26

                    
27
	public function __construct()100%
28
	{
29
		$this
30
			->setAdapter()
31
			->setReflectionClassFactory()
32
		;
33
	}
34

                    
35
	public function callsToParentClassAreShunted()100%
36
	{
37
		return $this->shuntParentClassCalls;
38
	}
39

                    
40
	public function setAdapter(atoum\adapter $adapter = null)100%
41
	{
42
		$this->adapter = $adapter ?: new atoum\adapter();
43

                    
44
		return $this;
45
	}
46

                    
47
	public function getAdapter()100%
48
	{
49
		return $this->adapter;
50
	}
51

                    
52
	public function setReflectionClassFactory(\closure $factory = null)100%
53
	{
54
		$this->reflectionClassFactory = $factory ?: function($class) { return new \reflectionClass($class); };
55

                    
56
		return $this;
57
	}
58

                    
59
	public function getReflectionClassFactory()100%
60
	{
61
		return $this->reflectionClassFactory;
62
	}
63

                    
64
	public function setDefaultNamespace($namespace)100%
65
	{
66
		$this->defaultNamespace = trim($namespace, '\\');
67

                    
68
		return $this;
69
	}
70

                    
71
	public function getDefaultNamespace()100%
72
	{
73
		return ($this->defaultNamespace === null ? self::defaultNamespace : $this->defaultNamespace);
74
	}
75

                    
76
	public function overload(php\method $method)
77
	{
78
		$this->overloadedMethods[strtolower($method->getName())] = $method;
79

                    
80
		return $this;
81
	}
82

                    
83
	public function isOverloaded($method)100%
84
	{
85
		return ($this->getOverload($method) !== null);
86
	}
87

                    
88
	public function getOverload($method)100%
89
	{
90
		return (isset($this->overloadedMethods[$method = strtolower($method)]) === false ? null : $this->overloadedMethods[$method]);
91
	}
92

                    
93
	public function shunt($method)100%
94
	{
95
		if ($this->isShunted($method) === false)
96
		{
97
			$this->shuntedMethods[] = strtolower($method);
98
		}
99

                    
100
		return $this;
101
	}
102

                    
103
	public function isShunted($method)100%
104
	{
105
		return (in_array(strtolower($method), $this->shuntedMethods) === true);
106
	}
107

                    
108
	public function shuntParentClassCalls()100%
109
	{
110
		$this->shuntParentClassCalls = true;
111

                    
112
		return $this;
113
	}
114

                    
115
	public function unshuntParentClassCalls()100%
116
	{
117
		$this->shuntParentClassCalls = false;
118

                    
119
		return $this;
120
	}
121

                    
122
	public function orphanize($method)100%
123
	{
124
		if ($this->isOrphanized($method) === false)
125
		{
126
			$this->orphanizedMethods[] = strtolower($method);
127
		}
128

                    
129
		return $this->shunt($method);
130
	}
131

                    
132
	public function isOrphanized($method)
133
	{
134
		return (in_array($method, $this->orphanizedMethods) === true);
135
	}
136

                    
137
	public function allIsInterface()100%
138
	{
139
		$this->allIsInterface = true;
140

                    
141
		return $this;
142
	}
143

                    
144
	public function testedClassIs($testedClass)100%
145
	{
146
		$this->testedClass = strtolower($testedClass);
147

                    
148
		return $this;
149
	}
150

                    
151
	public function getMockedClassCode($class, $mockNamespace = null, $mockClass = null)
152
	{
153
		if (trim($class, '\\') == '' || rtrim($class, '\\') != $class)
154
		{
155
			throw new exceptions\runtime('Class name \'' . $class . '\' is invalid');
156
		}
157

                    
158
		if ($mockNamespace === null)
159
		{
160
			$mockNamespace = $this->getNamespace($class);
161
		}
162

                    
163
		$class = '\\' . ltrim($class, '\\');
164

                    
165
		if ($mockClass === null)
166
		{
167
			$mockClass = self::getClassName($class);
168
		}
169

                    
170
		if ($this->adapter->class_exists($mockNamespace . '\\' . $mockClass, false) === true || $this->adapter->interface_exists($mockNamespace . '\\' . $mockClass, false) === true)
171
		{
172
			throw new exceptions\logic('Class \'' . $mockNamespace . '\\' . $mockClass . '\' already exists');
173
		}
174

                    
175
		if ($this->adapter->class_exists($class, true) === false && $this->adapter->interface_exists($class, true) === false)
176
		{
177
			$code = self::generateUnknownClassCode($class, $mockNamespace, $mockClass);
178
		}
179
		else
180
		{
181
			$reflectionClass = call_user_func($this->reflectionClassFactory, $class);
182

                    
183
			if ($reflectionClass->isFinal() === true)
184
			{
185
				throw new exceptions\logic('Class \'' . $class . '\' is final, unable to mock it');
186
			}
187

                    
188
			$code = $reflectionClass->isInterface() === false ? $this->generateClassCode($reflectionClass, $mockNamespace, $mockClass) : $this->generateInterfaceCode($reflectionClass, $mockNamespace, $mockClass);
189
		}
190

                    
191
		$this->shuntedMethods = $this->overloadedMethods = $this->orphanizedMethods = array();
192

                    
193
		$this->unshuntParentClassCalls();
194

                    
195
		return $code;
196
	}
197

                    
198
	public function generate($class, $mockNamespace = null, $mockClass = null)100%
199
	{
200
		eval($this->getMockedClassCode($class, $mockNamespace, $mockClass));
201

                    
202
		return $this;
203
	}
204

                    
205
	public function methodIsMockable(\reflectionMethod $method)100%
206
	{
207
		switch (true)
208
		{
209
			case $method->isFinal():
210
			case $method->isStatic():
211
			case static::methodNameIsReservedWord($method):
212
				return false;
213

                    
214
			case $method->isPrivate():
215
			case $method->isProtected() && $method->isAbstract() === false:
216
				return $this->isOverloaded($method->getName());
217

                    
218
			default:
219
				return true;
220
		}
221
	}
222

                    
223
	public function disallowUndefinedMethodInInterface()50%
224
	{
225
		return $this->disallowUndefinedMethodUsage();
226
	}
227

                    
228
	public function disallowUndefinedMethodUsage()100%
229
	{
230
		$this->allowUndefinedMethodsUsage = false;
231

                    
232
		return $this;
233
	}
234

                    
235
	public function allowUndefinedMethodInInterface()100%
236
	{
237
		return $this->allowUndefinedMethodUsage();
238
	}
239

                    
240
	public function allowUndefinedMethodUsage()33%
241
	{
242
		$this->allowUndefinedMethodsUsage = true;
243

                    
244
		return $this;
245
	}
246

                    
247
	public function undefinedMethodInInterfaceAreAllowed()0%
248
	{
249
		return $this->undefinedMethodUsageIsAllowed();
250
	}
251

                    
252
	public function undefinedMethodUsageIsAllowed()0%
253
	{
254
		return $this->allowUndefinedMethodsUsage === true;
255
	}
256

                    
257
	protected function generateClassMethodCode(\reflectionClass $class)93%
258
	{
259
		$mockedMethods = '';
260
		$mockedMethodNames = array();
261
		$className = $class->getName();
262

                    
263
		if ($this->allIsInterface && strtolower($className) != $this->testedClass)
264
		{
265
			foreach ($class->getMethods() as $method)
266
			{
267
				if ($this->methodIsMockable($method) === true)
268
				{
269
					$this->orphanize($method->getName());
270
				}
271
			}
272
		}
273

                    
274
		$constructor = $class->getConstructor();
275

                    
276
		if ($constructor === null)
277
		{
278
			$mockedMethods .= self::generateDefaultConstructor();
279
			$mockedMethodNames[] = '__construct';
280
		}
281
		else if ($constructor->isFinal() === false)
282
		{
283
			$constructorName = $constructor->getName();
284

                    
285
			$overload = $this->getOverload($constructorName);
286

                    
287
			if ($constructor->isPublic() === false)
288
			{
289
				$this->shuntParentClassCalls();
290

                    
291
				if ($overload === null)
292
				{
293
					$this->overload(new php\method('__construct'));
294

                    
295
					$overload = $this->getOverload('__construct');
296
				}
297
			}
298

                    
299
			$parameters = $this->getParameters($constructor);
300

                    
301
			if ($overload === null)
302
			{
303
				$mockedMethods .= "\t" . 'public function __construct(' . $this->getParametersSignature($constructor) . ')';
304
			}
305
			else
306
			{
307
				$overload
308
					->addArgument(
309
						php\method\argument::get('mockController')
310
							->isObject('\\' . __NAMESPACE__ . '\\controller')
311
							->setDefaultValue(null)
312
					)
313
				;
314

                    
315
				$mockedMethods .= "\t" . $overload;
316
			}
317

                    
318
			$mockedMethods .= PHP_EOL;
319
			$mockedMethods .= "\t" . '{' . PHP_EOL;
320

                    
321
			if (self::hasVariadic($constructor) === true)
322
			{
323
				$mockedMethods .= "\t\t" . '$arguments = func_get_args();' . PHP_EOL;
324
				$mockedMethods .= "\t\t" . '$mockController = \mageekguy\atoum\mock\controller::get();' . PHP_EOL;
325
			}
326
			else
327
			{
328
				$mockedMethods .= "\t\t" . '$arguments = array_merge(array(' . join(', ', $parameters) . '), array_slice(func_get_args(), ' . sizeof($parameters) . ', -1));' . PHP_EOL;
329
				$mockedMethods .= "\t\t" . 'if ($mockController === null)' . PHP_EOL;
330
				$mockedMethods .= "\t\t" . '{' . PHP_EOL;
331
				$mockedMethods .= "\t\t\t" . '$mockController = \mageekguy\atoum\mock\controller::get();' . PHP_EOL;
332
				$mockedMethods .= "\t\t" . '}' . PHP_EOL;
333
			}
334

                    
335
			$mockedMethods .= "\t\t" . 'if ($mockController !== null)' . PHP_EOL;
336
			$mockedMethods .= "\t\t" . '{' . PHP_EOL;
337
			$mockedMethods .= "\t\t\t" . '$this->setMockController($mockController);' . PHP_EOL;
338
			$mockedMethods .= "\t\t" . '}' . PHP_EOL;
339

                    
340
			if ($constructor->isAbstract() === true || $this->isShunted('__construct') === true || $this->isShunted($className) === true)
341
			{
342
				$methodName = ($this->isShunted($className) === true ? $className : '__construct');
343

                    
344
				$mockedMethods .= "\t\t" . 'if (isset($this->getMockController()->' . $methodName . ') === false)' . PHP_EOL;
345
				$mockedMethods .= "\t\t" . '{' . PHP_EOL;
346
				$mockedMethods .= "\t\t\t" . '$this->getMockController()->' . $methodName . ' = function() {};' . PHP_EOL;
347
				$mockedMethods .= "\t\t" . '}' . PHP_EOL;
348
				$mockedMethods .= "\t\t" . '$this->getMockController()->invoke(\'' . $methodName . '\', $arguments);' . PHP_EOL;
349
			}
350
			else
351
			{
352
				$mockedMethods .= "\t\t" . 'if (isset($this->getMockController()->' . $constructorName . ') === true)' . PHP_EOL;
353
				$mockedMethods .= "\t\t" . '{' . PHP_EOL;
354
				$mockedMethods .= "\t\t\t" . '$this->getMockController()->invoke(\'' . $constructorName . '\', $arguments);' . PHP_EOL;
355
				$mockedMethods .= "\t\t" . '}' . PHP_EOL;
356
				$mockedMethods .= "\t\t" . 'else' . PHP_EOL;
357
				$mockedMethods .= "\t\t" . '{' . PHP_EOL;
358
				$mockedMethods .= "\t\t\t" . '$this->getMockController()->addCall(\'' . $constructorName . '\', $arguments);' . PHP_EOL;
359

                    
360
				if ($this->shuntParentClassCalls === false)
361
				{
362
					$mockedMethods .= "\t\t\t" . 'call_user_func_array(\'parent::' . $constructorName . '\', $arguments);' . PHP_EOL;
363
				}
364

                    
365
				$mockedMethods .= "\t\t" . '}' . PHP_EOL;
366
			}
367

                    
368
			$mockedMethods .= "\t" . '}' . PHP_EOL;
369

                    
370
			$mockedMethodNames[] = strtolower($constructorName);
371
		}
372

                    
373
		foreach ($class->getMethods() as $method)
374
		{
375
			if ($method->isConstructor() === false && $this->methodIsMockable($method) === true)
376
			{
377
				$methodName = $method->getName();
378
				$mockedMethodNames[] = strtolower($methodName);
379
				$overload = $this->getOverload($methodName);
380
				$parameters = $this->getParameters($method);
381

                    
382
				if ($overload !== null)
383
				{
384
					$mockedMethods .= "\t" . $overload;
385
				}
386
				else
387
				{
388
					$mockedMethods .= "\t" . ($method->isPublic() === true ? 'public' : 'protected') . ' function' . ($method->returnsReference() === false ? '' : ' &') . ' ' . $methodName . '(' . $this->getParametersSignature($method) . ')';
389
				}
390

                    
391
				$mockedMethods .= PHP_EOL . "\t" . '{' . PHP_EOL;
392

                    
393
				if (self::hasVariadic($method) === true)
394
				{
395
					$mockedMethods .= "\t\t" . '$arguments = func_get_args();' . PHP_EOL;
396
				}
397
				else
398
				{
399
					$mockedMethods .= "\t\t" . '$arguments = array_merge(array(' . join(', ', $parameters) . '), array_slice(func_get_args(), ' . sizeof($parameters) . '));' . PHP_EOL;
400
				}
401

                    
402
				if ($this->isShunted($methodName) === true || $method->isAbstract() === true)
403
				{
404
					$mockedMethods .= "\t\t" . 'if (isset($this->getMockController()->' . $methodName . ') === false)' . PHP_EOL;
405
					$mockedMethods .= "\t\t" . '{' . PHP_EOL;
406
					$mockedMethods .= "\t\t\t" . '$this->getMockController()->' . $methodName . ' = function() {};' . PHP_EOL;
407
					$mockedMethods .= "\t\t" . '}' . PHP_EOL;
408
					$mockedMethods .= "\t\t" . '$return = $this->getMockController()->invoke(\'' . $methodName . '\', $arguments);' . PHP_EOL;
409
					$mockedMethods .= "\t\t" . 'return $return;' . PHP_EOL;
410
				}
411
				else
412
				{
413
					$mockedMethods .= "\t\t" . 'if (isset($this->getMockController()->' . $methodName . ') === true)' . PHP_EOL;
414
					$mockedMethods .= "\t\t" . '{' . PHP_EOL;
415
					$mockedMethods .= "\t\t\t" . '$return = $this->getMockController()->invoke(\'' . $methodName . '\', $arguments);' . PHP_EOL;
416
					$mockedMethods .= "\t\t\t" . 'return $return;' . PHP_EOL;
417
					$mockedMethods .= "\t\t" . '}' . PHP_EOL;
418
					$mockedMethods .= "\t\t" . 'else' . PHP_EOL;
419
					$mockedMethods .= "\t\t" . '{' . PHP_EOL;
420

                    
421
					if ($methodName === '__call')
422
					{
423
						$mockedMethods .= "\t\t\t" . '$this->getMockController()->addCall(current(array_slice($arguments, 0, 1)), current(array_slice($arguments, 1)));' . PHP_EOL;
424
					}
425

                    
426
					$mockedMethods .= "\t\t\t" . '$this->getMockController()->addCall(\'' . $methodName . '\', $arguments);' . PHP_EOL;
427

                    
428
					if ($this->shuntParentClassCalls === false)
429
					{
430
						$mockedMethods .= "\t\t\t" . '$return = call_user_func_array(\'parent::' . $methodName . '\', $arguments);' . PHP_EOL;
431
						$mockedMethods .= "\t\t\t" . 'return $return;' . PHP_EOL;
432
					}
433

                    
434
					$mockedMethods .= "\t\t" . '}' . PHP_EOL;
435
				}
436

                    
437
				$mockedMethods .= "\t" . '}' . PHP_EOL;
438
			}
439
		}
440

                    
441
		if ($class->isAbstract() && $this->allowUndefinedMethodsUsage === true && in_array('__call', $mockedMethodNames) === false)
442
		{
443
			$mockedMethods .= self::generate__call();
444
			$mockedMethodNames[] = '__call';
445
		}
446

                    
447
		return $mockedMethods . self::generateGetMockedMethod($mockedMethodNames);
448
	}
449

                    
450
	protected function generateClassCode(\reflectionClass $class, $mockNamespace, $mockClass)100%
451
	{
452
		return 'namespace ' . ltrim($mockNamespace, '\\') . ' {' . PHP_EOL .
453
			'final class ' . $mockClass . ' extends \\' . $class->getName() . ' implements \\' . __NAMESPACE__ . '\\aggregator' . PHP_EOL .
454
			'{' . PHP_EOL .
455
			self::generateMockControllerMethods() .
456
			$this->generateClassMethodCode($class) .
457
			'}' . PHP_EOL .
458
			'}'
459
		;
460
	}
461

                    
462
	protected function generateInterfaceMethodCode(\reflectionClass $class, $addIteratorAggregate)
463
	{
464
		$mockedMethods = '';
465
		$mockedMethodNames = array();
466
		$hasConstructor = false;
467

                    
468
		$methods = $class->getMethods(\reflectionMethod::IS_PUBLIC);
469

                    
470
		if ($addIteratorAggregate === true)
471
		{
472
			$iteratorInterface = call_user_func($this->reflectionClassFactory, 'iteratorAggregate');
473

                    
474
			$methods = array_merge($methods, $iteratorInterface->getMethods(\reflectionMethod::IS_PUBLIC));
475
		}
476

                    
477
		foreach ($methods as $method)
478
		{
479
			$methodName = $method->getName();
480

                    
481
			$mockedMethodNames[] = strtolower($methodName);
482

                    
483
			$parameters = $this->getParameters($method);
484

                    
485
			switch (true)
486
			{
487
				case $method->isFinal() === false && $method->isStatic() === false:
488
					$isConstructor = $methodName === '__construct';
489

                    
490
					if ($isConstructor === true)
491
					{
492
						$hasConstructor = true;
493
					}
494

                    
495
					$methodCode = "\t" . 'public function' . ($method->returnsReference() === false ? '' : ' &') . ' ' . $methodName . '(' . $this->getParametersSignature($method, $isConstructor) . ')' . PHP_EOL;
496
					$methodCode .= "\t" . '{' . PHP_EOL;
497

                    
498
					if (self::hasVariadic($method) === true)
499
					{
500
						$methodCode .= "\t\t" . '$arguments = func_get_args();' . PHP_EOL;
501
					}
502
					else
503
					{
504
						$methodCode .= "\t\t" . '$arguments = array_merge(array(' . join(', ', $parameters) . '), array_slice(func_get_args(), ' . sizeof($parameters) . ($isConstructor === false ? '' : ', -1') . '));' . PHP_EOL;
505
					}
506

                    
507
					if ($isConstructor === true)
508
					{
509
						if (self::hasVariadic($method) === true)
510
						{
511
							$methodCode .= "\t\t" . '$mockController = \mageekguy\atoum\mock\controller::get();' . PHP_EOL;
512
						}
513
						else
514
						{
515
							$methodCode .= "\t\t" . 'if ($mockController === null)' . PHP_EOL;
516
							$methodCode .= "\t\t" . '{' . PHP_EOL;
517
							$methodCode .= "\t\t\t" . '$mockController = \mageekguy\atoum\mock\controller::get();' . PHP_EOL;
518
							$methodCode .= "\t\t" . '}' . PHP_EOL;
519
						}
520

                    
521
						$methodCode .= "\t\t" . 'if ($mockController !== null)' . PHP_EOL;
522
						$methodCode .= "\t\t" . '{' . PHP_EOL;
523
						$methodCode .= "\t\t\t" . '$this->setMockController($mockController);' . PHP_EOL;
524
						$methodCode .= "\t\t" . '}' . PHP_EOL;
525
					}
526

                    
527
					$methodCode .= "\t\t" . 'if (isset($this->getMockController()->' . $methodName . ') === false)' . PHP_EOL;
528
					$methodCode .= "\t\t" . '{' . PHP_EOL;
529
					$methodCode .= "\t\t\t" . '$this->getMockController()->' . $methodName . ' = function() {};' . PHP_EOL;
530
					$methodCode .= "\t\t" . '}' . PHP_EOL;
531

                    
532
					if ($isConstructor === true)
533
					{
534
						$methodCode .= "\t\t" . '$this->getMockController()->invoke(\'' . $methodName . '\', $arguments);' . PHP_EOL;
535
					}
536
					else
537
					{
538
						$methodCode .= "\t\t" . '$return = $this->getMockController()->invoke(\'' . $methodName . '\', $arguments);' . PHP_EOL;
539
						$methodCode .= "\t\t" . 'return $return;' . PHP_EOL;
540
					}
541
					$methodCode .= "\t" . '}' . PHP_EOL;
542
					break;
543

                    
544
				case $method->isStatic() === true:
545
					$methodCode = "\t" . 'public static function' . ($method->returnsReference() === false ? '' : ' &') . ' ' . $methodName . '(' . $this->getParametersSignature($method) . ')' . PHP_EOL;
546
					$methodCode .= "\t" . '{' . PHP_EOL;
547
					$methodCode .= "\t\t" . '$arguments = array_merge(array(' . join(', ', $parameters) . '), array_slice(func_get_args(), ' . sizeof($parameters) . ', -1));' . PHP_EOL;
548
					$methodCode .= "\t\t" . 'return call_user_func_array(array(\'parent\', \'' . $methodName . '\'), $arguments);' . PHP_EOL;
549
					$methodCode .= "\t" . '}' . PHP_EOL;
550
					break;
551

                    
552
				default:
553
					$methodCode = '';
554
			}
555

                    
556
			$mockedMethods .= $methodCode;
557
		}
558

                    
559
		if ($hasConstructor === false)
560
		{
561
			$mockedMethods .= self::generateDefaultConstructor();
562
			$mockedMethodNames[] = '__construct';
563
		}
564

                    
565
		if ($this->allowUndefinedMethodsUsage === true)
566
		{
567
			$mockedMethods .= self::generate__call();
568
			$mockedMethodNames[] = '__call';
569
		}
570

                    
571
		$mockedMethods .= self::generateGetMockedMethod($mockedMethodNames);
572

                    
573
		return $mockedMethods;
574
	}
575

                    
576
	protected function generateInterfaceCode(\reflectionClass $class, $mockNamespace, $mockClass)100%
577
	{
578
		$addIteratorAggregate = (
579
				$class->isInstantiable() === false
580
			&& (
581
					$class->implementsInterface('traversable') === true
582
				&& $class->implementsInterface('iterator') === false
583
				&& $class->implementsInterface('iteratorAggregate') === false
584
			)
585
		);
586

                    
587
		return 'namespace ' . ltrim($mockNamespace, '\\') . ' {' . PHP_EOL .
588
			'final class ' . $mockClass . ' implements \\' . ($addIteratorAggregate === false ? '' : 'iteratorAggregate, \\') . $class->getName() . ', \\' . __NAMESPACE__ . '\\aggregator' . PHP_EOL .
589
			'{' . PHP_EOL .
590
			self::generateMockControllerMethods() .
591
			$this->generateInterfaceMethodCode($class, $addIteratorAggregate) .
592
			'}' . PHP_EOL .
593
			'}'
594
		;
595
	}
596

                    
597
	protected function getNamespace($class)100%
598
	{
599
		$class = ltrim($class, '\\');
600
		$lastAntiSlash = strrpos($class, '\\');
601

                    
602
		return '\\' . $this->getDefaultNamespace() . ($lastAntiSlash === false ? '' : '\\' . substr($class, 0, $lastAntiSlash));
603
	}
604

                    
605
	protected function getParameters(\reflectionMethod $method)100%
606
	{
607
		$parameters = array();
608

                    
609
		$overload = $this->getOverload($method->getName());
610

                    
611
		if ($overload === null)
612
		{
613
			foreach ($method->getParameters() as $parameter)
614
			{
615
				$parameters[] = ($parameter->isPassedByReference() === false ? '' : '& ') . '$' . $parameter->getName();
616
			}
617
		}
618
		else
619
		{
620
			foreach ($overload->getArguments() as $argument)
621
			{
622
				$parameters[] = $argument->getVariable();
623
			}
624
		}
625

                    
626
		return $parameters;
627
	}
628

                    
629
	protected function getParametersSignature(\reflectionMethod $method, $forceMockController = false)100%
630
	{
631
		$parameters = array();
632

                    
633
		$mustBeNull = $this->isOrphanized($method->getName());
634

                    
635
		foreach ($method->getParameters() as $parameter)
636
		{
637
			$parameterCode = self::getParameterType($parameter) . ($parameter->isPassedByReference() == false ? (self::isVariadic($parameter) == false ? '' : '... ') : '& ') . '$' . $parameter->getName();
638

                    
639
			switch (true)
640
			{
641
				case $parameter->isDefaultValueAvailable():
642
					$parameterCode .= ' = ' . var_export($parameter->getDefaultValue(), true);
643
					break;
644

                    
645
				case $parameter->isOptional() && self::isVariadic($parameter) == false:
646
				case $mustBeNull:
647
					$parameterCode .= ' = null';
648
			}
649

                    
650
			$parameters[] = $parameterCode;
651
		}
652

                    
653
		if (self::hasVariadic($method) === false && ($method->isConstructor() || $forceMockController))
654
		{
655
			$parameters[] = '\\' . __NAMESPACE__ . '\\controller $mockController = null';
656
		}
657

                    
658
		return join(', ', $parameters);
659
	}
660

                    
661
	protected static function getClassName($class)
662
	{
663
		$class = ltrim($class, '\\');
664
		$lastAntiSlash = strrpos($class, '\\');
665

                    
666
		return ($lastAntiSlash === false ? $class : substr($class, $lastAntiSlash + 1));
667
	}
668

                    
669
	protected static function getParameterType(\reflectionParameter $parameter)100%
670
	{
671
		switch (true)
672
		{
673
			case $parameter->isArray():
674
				return 'array ';
675

                    
676
			case method_exists($parameter, 'isCallable') && $parameter->isCallable():
677
				return 'callable ';
678

                    
679
			case ($class = $parameter->getClass()):
680
				return '\\' . $class->getName() . ' ';
681

                    
682
			default:
683
				return '';
684
		}
685
	}
686

                    
687
	protected static function isVariadic(\reflectionParameter $parameter)75%
688
	{
689
		if (method_exists($parameter, 'isVariadic'))
690
		{
691
			return $parameter->isVariadic();
692
		}
693

                    
694
		return false;
695
	}
696

                    
697
	protected static function hasVariadic(\reflectionMethod $method)100%
698
	{
699
		$parameters = $method->getParameters();
700

                    
701
		if (sizeof($parameters) === 0)
702
		{
703
			return false;
704
		}
705

                    
706
		return self::isVariadic(end($parameters));
707
	}
708

                    
709
	protected static function generateMockControllerMethods()100%
710
	{
711
		return
712
			"\t" . 'public function getMockController()' . PHP_EOL .
713
			"\t" . '{' . PHP_EOL .
714
			"\t\t" . '$mockController = \mageekguy\atoum\mock\controller::getForMock($this);' . PHP_EOL .
715
			"\t\t" . 'if ($mockController === null)' . PHP_EOL .
716
			"\t\t" . '{' . PHP_EOL .
717
			"\t\t\t" . '$this->setMockController($mockController = new \\' . __NAMESPACE__ . '\\controller());' . PHP_EOL .
718
			"\t\t" . '}' . PHP_EOL .
719
			"\t\t" . 'return $mockController;' . PHP_EOL .
720
			"\t" . '}' . PHP_EOL .
721
			"\t" . 'public function setMockController(\\' . __NAMESPACE__ . '\\controller $controller)' . PHP_EOL .
722
			"\t" . '{' . PHP_EOL .
723
			"\t\t" . 'return $controller->control($this);' . PHP_EOL .
724
			"\t" . '}' . PHP_EOL .
725
			"\t" . 'public function resetMockController()' . PHP_EOL .
726
			"\t" . '{' . PHP_EOL .
727
			"\t\t" . '\mageekguy\atoum\mock\controller::getForMock($this)->reset();' . PHP_EOL .
728
			"\t\t" . 'return $this;' . PHP_EOL .
729
			"\t" . '}' . PHP_EOL
730
		;
731
	}
732

                    
733
	protected static function generateDefaultConstructor($disableMethodChecking = false)100%
734
	{
735
		$defaultConstructor =
736
			"\t" . 'public function __construct(\\' . __NAMESPACE__ . '\\controller $mockController = null)' . PHP_EOL .
737
			"\t" . '{' . PHP_EOL .
738
			"\t\t" . 'if ($mockController === null)' . PHP_EOL .
739
			"\t\t" . '{' . PHP_EOL .
740
			"\t\t\t" . '$mockController = \mageekguy\atoum\mock\controller::get();' . PHP_EOL .
741
			"\t\t" . '}' . PHP_EOL .
742
			"\t\t" . 'if ($mockController !== null)' . PHP_EOL .
743
			"\t\t" . '{' . PHP_EOL .
744
			"\t\t\t" . '$this->setMockController($mockController);' . PHP_EOL .
745
			"\t\t" . '}' . PHP_EOL
746
		;
747

                    
748
		if ($disableMethodChecking === true)
749
		{
750
			$defaultConstructor .= "\t\t" . '$this->getMockController()->disableMethodChecking();' . PHP_EOL;
751
		}
752

                    
753
		$defaultConstructor .=
754
			"\t\t" . 'if (isset($this->getMockController()->__construct) === true)' . PHP_EOL .
755
			"\t\t" . '{' . PHP_EOL .
756
			"\t\t\t" . '$this->getMockController()->invoke(\'__construct\', func_get_args());' . PHP_EOL .
757
			"\t\t" . '}' . PHP_EOL .
758
			"\t" . '}' . PHP_EOL
759
		;
760

                    
761
		return $defaultConstructor;
762
	}
763

                    
764
	protected static function generate__call()100%
765
	{
766
		return
767
			"\t" . 'public function __call($methodName, $arguments)' . PHP_EOL .
768
			"\t" . '{' . PHP_EOL .
769
			"\t\t" . 'if (isset($this->getMockController()->{$methodName}) === true)' . PHP_EOL .
770
			"\t\t" . '{' . PHP_EOL .
771
			"\t\t\t" . '$return = $this->getMockController()->invoke($methodName, $arguments);' . PHP_EOL .
772
			"\t\t\t" . 'return $return;' . PHP_EOL .
773
			"\t\t" . '}' . PHP_EOL .
774
			"\t\t" . 'else' . PHP_EOL .
775
			"\t\t" . '{' . PHP_EOL .
776
			"\t\t\t" . '$this->getMockController()->addCall($methodName, $arguments);' . PHP_EOL .
777
			"\t\t" . '}' . PHP_EOL .
778
			"\t" . '}' . PHP_EOL
779
		;
780
	}
781

                    
782
	protected static function generateGetMockedMethod(array $mockedMethodNames)100%
783
	{
784
		return
785
			"\t" . 'public static function getMockedMethods()' . PHP_EOL .
786
			"\t" . '{' . PHP_EOL .
787
			"\t\t" . 'return ' . var_export($mockedMethodNames, true) . ';' . PHP_EOL .
788
			"\t" . '}' . PHP_EOL
789
		;
790
	}
791

                    
792
	protected static function generateUnknownClassCode($class, $mockNamespace, $mockClass)100%
793
	{
794
		return 'namespace ' . ltrim($mockNamespace, '\\') . ' {' . PHP_EOL .
795
			'final class ' . $mockClass . ' implements \\' . __NAMESPACE__ . '\\aggregator' . PHP_EOL .
796
			'{' . PHP_EOL .
797
			self::generateMockControllerMethods() .
798
			self::generateDefaultConstructor(true) .
799
			self::generate__call() .
800
			self::generateGetMockedMethod(array('__call')) .
801
			'}' . PHP_EOL .
802
			'}'
803
		;
804
	}
805

                    
806
	protected static function methodNameIsReservedWord(\reflectionMethod $method)100%
807
	{
808
		switch ($method->getName())
809
		{
810
			case '__halt_compiler':
811
			case 'abstract':
812
			case 'and':
813
			case 'array':
814
			case 'as':
815
			case 'break':
816
			case 'callable':
817
			case 'case':
818
			case 'catch':
819
			case 'class':
820
			case 'clone':
821
			case 'const':
822
			case 'continue':
823
			case 'declare':
824
			case 'default':
825
			case 'die':
826
			case 'do':
827
			case 'echo':
828
			case 'else':
829
			case 'elseif':
830
			case 'empty':
831
			case 'enddeclare':
832
			case 'endfor':
833
			case 'endforeach':
834
			case 'endif':
835
			case 'endswitch':
836
			case 'endwhile':
837
			case 'eval':
838
			case 'exit':
839
			case 'extends':
840
			case 'final':
841
			case 'for':
842
			case 'foreach':
843
			case 'function':
844
			case 'global':
845
			case 'goto':
846
			case 'if':
847
			case 'implements':
848
			case 'include':
849
			case 'include_once':
850
			case 'instanceof':
851
			case 'insteadof':
852
			case 'interface':
853
			case 'isset':
854
			case 'list':
855
			case 'namespace':
856
			case 'new':
857
			case 'or':
858
			case 'print':
859
			case 'private':
860
			case 'protected':
861
			case 'public':
862
			case 'require':
863
			case 'require_once':
864
			case 'return':
865
			case 'static':
866
			case 'switch':
867
			case 'throw':
868
			case 'trait':
869
			case 'try':
870
			case 'unset':
871
			case 'use':
872
			case 'var':
873
			case 'while':
874
			case 'xor':
875
				return true;
876

                    
877
			default:
878
				return false;
879
		}
880
	}
881
}