使用PHP库类,我希望将其所有公共函数包装在子类中......以下内容:
class BaseClass
{
function do_something()
{
some;
stuff;
}
function do_something_else()
{
other;
stuff;
}
/*
* 20-or-so other functions here!
*/
}
class SubClass extends BaseClass
{
function magicalOverrideEveryone()
{
stuff-to-do-before; // i.e. Display header
call_original_function(); // i.e. Display otherwise-undecorated content
stuff-to-do-after; // i.e. Display footer
}
}
将其简化,我不希望不必使用相同的包装代码覆盖每个超类方法,如果有一种[有点优雅/干净]的方式在一个地方完成所有这些。
这可能吗?我怀疑我是在这里进行元编程的,并且甚至不知道PHP是否提供了这样的野兽,但我想问我......
答案 0 :(得分:20)
您可以使用__call
magic method和一个不直接从基类继承的通用“代理”类轻松完成此操作。
这是一个代理类的(近)完整实现,它包装你传递它的任何对象。它将围绕每个方法调用调用一些“之前”和“之后”代码。
class MyProxy {
function __construct($object) {
$this->object = $object;
}
function __call($method, $args) {
// Run before code here
// Invoke original method on our proxied object
call_user_func_array(array($this->object, $method), $args);
// Run after code here
}
}
$base = new BaseClass();
$proxy = new MyProxy($base);
$proxy->doSomething(); // invoke $base->doSomething();
您当然希望添加一些错误处理,例如询问代理对象是否响应__call
中的给定方法,如果不响应则引发错误。您甚至可以将Proxy类设计为其他代理的基类。子代理类可以实现before
和after
方法。
缺点是您的“子类”不再实现BaseClass
,这意味着如果您正在使用type-hinting并且想要求只将BaseClass
类型的对象传递给功能,这种方法会失败。
答案 1 :(得分:2)
如果SubClass的方法名称可能与BaseClass的原始方法名称略有不同,则可以使用__call()
编写通用包装器。如果方法名称必须匹配,我不知道如何在不手动覆盖每个方法的情况下实现目标。也许您可以使用funcall PECL来执行此操作 - 但您必须首先加载该PECL。
如果您可以创建BaseClass protected
的方法,则SubClass中的__call()
方法将起作用。
如果你不需要来扩展课程,@ meager的方法就完全没问题了。请注意,__ call()和call_user_func_array()会产生一定的开销。
答案 2 :(得分:0)
您可能正在寻找装饰模式:
class A
{
function do1(){
}
}
class DecoratorForA extends A
{
private A original;
public DecoratorForA( A original )
{
this.original = original;
}
function do1(){ //<-- override keyword in php?
stuffBefore();
original->do1();
stuffAfter();
}
}
因为这不是你想要的,也许这个链接有帮助吗? http://code.google.com/p/php-aop/
答案 3 :(得分:0)
如果我理解你正确,你想扩展一个类,但不允许调用父类的任何方法。相反,您希望在新类中的一个方法中自己调用所有方法。
那么为什么你甚至想要继承?这听起来很像你应该为你的BaseClass创建一个适配器/装饰器。
class SubClass
{
function magicalOverrideEveryone()
{
BaseClass bc = new BaseClass();
bc.do_something();
bc.do_something_else();
}
}