我试图建立一个动态模拟(松散地使用术语)有几个原因:
换句话说:请不要告诉我去使用模拟库。我已经使用了模拟库(我已经广泛使用了至少三个PHP库),我决定尝试创建自己的解决方案是有意识的。
所以:我试图做一些概念上看起来相当简单的事情。
如何动态覆盖模拟类中存在于mock扩展类中的所有方法?
换句话说,如果我有一个包含方法A
的类a
,并且我有一个扩展类B
的模拟A
,我该如何捕获所有调用方法A
没有在模拟类a
中显式实现方法B
?
我已尝试使用__call()
魔术方法执行此操作,但这不会起作用,因为__call()
仅捕获对不存在的方法的调用。< / p>
我想避免需要进行大规模架构更改的方法。我的主要要求是,在构造函数中需要类A
实例的任何类都不能告诉mock B
不是类A
的实例。因此,我对模拟课程B
的初步选择扩展了课程A
。我也不必对类A
进行大的更改,例如将其方法设置为私有并让它使用__call()
。
答案 0 :(得分:1)
类似的东西:
class A
{
public function foo()
{
return __CLASS__;
}
}
class B extends A
{
public function foo()
{
return __CLASS__;
}
}
$b = new B();
$reflection = new ReflectionObject($b);
$parentReflection = $reflection->getParentClass();
$parentFooReflection = $parentReflection->getMethod('foo');
$data = $parentFooReflection->invoke($b);
echo $data;
请注意,您可以在invoke(object $object, methods argument1, methods argument2...)
函数
你可以使用Reflections做很多类似的事情,有关详细信息,请查看链接http://php.net/manual/en/book.reflection.php
答案 1 :(得分:0)
我接受了@ dm03514的建议并跳进Phokito's source。至少,Phokito使用模拟构建器动态生成模拟类定义,扩展基类并覆盖其所有方法,然后使用class MockFactory
{
public function buildMock( $class )
{
$reflection = new \ReflectionClass( $class );
$mockedShortName = $reflection->getShortName();
$mockShortName = "Mock$mockedShortName";
$mockClass = "\\$mockShortName";
if ( ! class_exists($mockClass) ) {
$this->declareMockClass( $reflection, $mockShortName, $mockedShortName );
}
return new $mockClass;
}
private function declareMockClass( $reflection, $mockShortName, $mockedShortName )
{
$php = [];
$mockedNamespace = $reflection->getNamespaceName();
$extends = $reflection->isInterface() ? 'implements' : 'extends';
$php[] = <<<EOT
class $mockShortName $extends $mockedNamespace\\$mockedShortName {
public function setReturnValue( \$method, \$returnValue ) {
\$this->\$method = \$returnValue;
}
public function getCalls ( \$method ) {
\$callsProperty = \$method . "Calls";
return \$this->\$callsProperty;
}
EOT;
foreach ( $reflection->getMethods() as $method ) {
$methodName = $method->name;
$params = [];
foreach ( $method->getParameters() as $i => $parameter ) {
if ( $parameter->isArray() ) $type = 'array ';
else if ( $parameterClass = $parameter->getClass() ) $type = '\\' . $parameterClass->getName() . ' ';
else $type = '';
$params[] = "$type \${$parameter->getName()}";
}
$paramString = implode( ',', $params );
$php[] = <<<EOT
private \${$methodName}Calls = [];
public function $methodName($paramString) {
\$this->{$methodName}Calls[] = func_get_args();
return \$this->$methodName;
}
EOT;
}
$php[] = '}';
$toEval = implode( "\n\n", $php );
eval( $toEval );
}
}
声明定义的类。
这是基于这种方法的基本模拟构建器:
"\xef\xbf\xbdPNG\r\x1a\r\x00\x00\x00\rIHDR\x00\x00\x01\xef\xbf\xbd…"