Skip to content
This repository has been archived by the owner on Nov 23, 2022. It is now read-only.

Commit

Permalink
Fixing up the construction of internal classes such as Phar
Browse files Browse the repository at this point in the history
  • Loading branch information
mlively committed Jan 26, 2015
1 parent 66ade7c commit 7801a34
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 3 deletions.
96 changes: 93 additions & 3 deletions src/Phake/ClassGenerator/MockClass.php
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ class {$newClassName} {$extends}
public \$__PHAKE_name;
public \$__PHAKE_handlerChain;
public \$__PHAKE_constructorArgs;
public function __destruct() {}
Expand All @@ -132,6 +134,8 @@ public function __PHAKE_getMockedClassName()
return '{$mockedClassName}';
}
{$this->generateSafeConstructorOverride($mockedClass)}
{$this->generateMockedMethods($mockedClass)}
}
";
Expand All @@ -151,13 +155,39 @@ public function __PHAKE_getMockedClassName()
*/
public function instantiate($newClassName, Phake_CallRecorder_Recorder $recorder, Phake_Stubber_StubMapper $mapper, Phake_Stubber_IAnswer $defaultAnswer, array $constructorArgs = null)
{
$mockObject = unserialize(sprintf('O:%d:"%s":0:{}', strlen($newClassName), $newClassName));
$reflClass = new ReflectionClass($newClassName);
$constructor = $reflClass->getConstructor();

if ($constructor == null || ($constructor->class == $newClassName && $constructor->getNumberOfParameters() == 0))
{
$mockObject = new $newClassName;
}
elseif (version_compare(PHP_VERSION, '5.4.0', '>='))
{
try
{
$mockObject = $reflClass->newInstanceWithoutConstructor();
}
catch (ReflectionException $ignore)
{
}

if (empty($mockObject))
{
$mockObject = @unserialize(sprintf('O:%d:"%s":0:{}', strlen($newClassName), $newClassName));
if ($mockObject == null)
{
$mockObject = unserialize(sprintf('C:%d:"%s":0:{}', strlen($newClassName), $newClassName));
}
}
}

$mockObject->__PHAKE_callRecorder = $recorder;
$mockObject->__PHAKE_stubMapper = $mapper;
$mockObject->__PHAKE_defaultAnswer = $defaultAnswer;
$mockObject->__PHAKE_isFrozen = false;
$mockObject->__PHAKE_name = $mockObject->__PHAKE_getMockedClassName();
$mockObject->__PHAKE_constructorArgs = $constructorArgs;

$mockObject->__PHAKE_handlerChain = new Phake_ClassGenerator_InvocationHandler_Composite(array(
new Phake_ClassGenerator_InvocationHandler_FrozenObjectCheck(new Phake_MockReader()),
Expand Down Expand Up @@ -203,16 +233,76 @@ protected function generateMockedMethods(ReflectionClass $mockedClass)
return $methodDefs;
}

private function generateSafeConstructorOverride(ReflectionClass $mockedClass)
{
if (!$this->isConstructorDefinedInInterface($mockedClass))
{
$constructorDef = "
public function __construct()
{
{$this->getConstructorChaining($mockedClass)}
}
";
return $constructorDef;
}
else
{
return '';
}
}

private function isConstructorDefinedInInterface(ReflectionClass $mockedClass)
{
$constructor = $mockedClass->getConstructor();

if (empty($constructor) && $mockedClass->hasMethod('__construct'))
{
$constructor = $mockedClass->getMethod('__construct');
}

if (empty($constructor))
{
return false;
}

$reflectionClass = $constructor->getDeclaringClass();

if ($reflectionClass->isInterface())
{
return true;
}

/* @var ReflectionClass $interface */
foreach ($reflectionClass->getInterfaces() as $interface)
{
if ($interface->getConstructor() !== null)
{
return true;
}
}

$parent = $reflectionClass->getParentClass();
if (!empty($parent))
{
return $this->isConstructorDefinedInInterface($parent);
}
else
{
return false;
}
}

/**
* Creates the constructor implementation
*/
protected function getConstructorChaining(ReflectionClass $originalClass)
{
return $originalClass->hasMethod('__construct') ? "
if (is_array(\$constructorArgs))
if (is_array(\$this->__PHAKE_constructorArgs))
{
call_user_func_array(array(\$this, 'parent::__construct'), \$constructorArgs);
call_user_func_array(array(\$this, 'parent::__construct'), \$this->__PHAKE_constructorArgs);
\$this->__PHAKE_constructorArgs = null;
}
" : "";
}
Expand Down
7 changes: 7 additions & 0 deletions tests/PhakeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1167,6 +1167,13 @@ public function testConstructorInterfaceCanBeMocked()
// Generated a fatal error before fixed
$this->assertInstanceOf('Phake_IMock', Phake::mock('PhakeTest_ConstructorInterface'));
}

public function testMockingPhar()
{
$phar = Phake::mock('Phar');

$this->assertInstanceOf('Phar', $phar);
}
}

?>

0 comments on commit 7801a34

Please sign in to comment.