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