/** * Build a fake class dynamically that extends the desired class, * but will act more like a Friend class on a dynamically created mock. * This lets you use the mock methods in order to validate inputs and * return good, simulated values. * * By using the class renaming functionality of Renamer, the mock is * generated whenever you use the "new" keyword on the class we're * changing. Please consider restructuring your code to use some sort * of dependency injection since overriding "new" is an icky hack. * * Arguments for the callback, when used, should be like this: * function ($testObject, $newMockObject, $constructorArguments) * * @param string $type Name of class to mock * @param callback $callback Constructor callback for mock set up * @param array $methodsToKeep array to pass to getMockExceptMethods * @param boolean $callParent should the mock call the parent constructor */ public function stubWithMock($mocker, $className, $methodsToKeep = array(), $callParent = false) { $methodsToKeep = (array) $methodsToKeep; // Build unique information about this mock for building a key $mockInfo = array(); $mockInfo['class'] = strtolower($className); $methodsToKeep = array_map('strtolower', $methodsToKeep); sort($methodsToKeep); $mockInfo['methodsToKeep'] = $methodsToKeep; $mockInfo['callParent'] = $callParent; // Generate a key so we don't make the same sort of mock twice, then // check that key to see if we need to build the object $mockKey = serialize($mockInfo); if (!isset(self::$stubWithMockCache[$mockKey])) { // First, we will need to generate or reuse a mock object as our base class $mockObject = $mocker(); $skeleton = new Skeleton($mockObject); $php = 'PHPToolsTestUtil::stubWithMockConstructor("' . addslashes($mockKey) . '", $this, func_get_args());'; if ($callParent) { $reflection = $skeleton->reflect()->getConstructor(); $php .= $skeleton->chainMethod($reflection); } $skeleton->setConstructor($php); $skeletonClass = $skeleton->create(); self::$stubWithMockCache[$mockKey] = (object) array('className' => $skeletonClass, 'key' => $mockKey); } $mockDef = self::$stubWithMockCache[$mockKey]; $mockDef->callback = null; self::getRenamer()->renameClass($className, $mockDef->className); return $mockDef; }