PHP运行时类修改

问题描述 投票:0回答:4

所以我希望能够在运行时添加/删除类方法。在你告诉我这在 oop 中是可怕的实践之前,它可能是,但我真的不在乎。我希望能够做到这一点的原因是因为我希望应用程序非常模块化,因此某些插件可以扩展一些基类并向其添加方法,而无需杀死主应用程序。

例如,假设我有以下课程:

class User {
    protected $Id;
    protected $Name;
    protected $Password;
    protected $PostsPerPage;
 }

并且,某些插件为用户添加了更改其可见性设置的可能性,向类添加 $Visible 属性。该类将变成:

class User {
    protected $Id;
    protected $Name;
    protected $Password;
    protected $PostsPerPage;
    protected $Visible;
 }

这可以通过 __get 和 __set 来实现,但最常见的实现是获取和设置运行时生成的属性,为每个添加的属性编写伪 getter 和 setter 会很麻烦,而且使用 __set 也是不行的-我认为不。

我的另一个想法是将用户设置单独存储在另一个数组中,例如 $UserVisibilitySettings[userid],但这使得事情不像我希望的那样面向对象。

下一个想法是创建一个辅助类,如下所示:

class UserHelper {
    public function SetVisiblity($user_object,value);
}

然后,我可以使用 __get 和 __set 来实现“朋友”方法/类,但这听起来太老套了。如果我要这样做,不妨重载 __call、__get 和 __set。另外,我不太确定这是好的 OOP 实践,而且它看起来也很难看。

我的最后一个想法是使用 eval() 提供一些在运行时动态创建类的函数(这也是我能想到的 eval 的唯一有效用法)。基本上,从文件中获取类定义,进行一些简单的括号查找以找到类的左括号和右括号并将其发送到 eval。

使用 runkit 是不可能的,因为它已经过时了,我不想强迫用户安装一些扩展。

当我查找我的想法时,最简单且占用CPU资源较少的似乎是重载_call并添加一种在类中注册方法的方法。

请理解任何非“不要这样做”的想法。

php oop reflection class dynamic
4个回答
7
投票

RunKit扩展可以做到这一点(

runkit_method_add()
等)

然而,这是一个实验性的扩展,你已经瞄准了你的脚......

您还有其他选择:

  • 使用
    __get()
    __call()
  • 模拟新字段和方法
  • 使用子类化和工厂模式(
    class Plugin extends BaseImplementation
    并让工厂实例化
    Plugin
    而不是
    BaseImplementation
    )。 Zend Plugin Loader 做了类似的事情。
    这是开销最小的解决方案,但也仅限于一个插件扩展一个类。
  • 添加钩子并使用委托(策略模式)(
    $this->plugin->onFoo()
    )。 有这个库

3
投票

PHP 不允许这样做。在其他方面它可能是一种动态语言,但类系统是故意限制的。您可以安装 runkit 扩展,它会更改语言以允许在运行时模拟类(但是您不再使用纯 PHP),或者您可以使用 magic-methods 来模拟它。


1
投票

您可以覆盖该类,但我不知道您是否可以在同一请求中重新加载它。 一些行为改变可以通过中介设计模式(symfony事件调度程序)来实现,但是您需要提前知道扩展点,并在将来由扩展类捕获触发事件/消息..

是否可以等待下一个请求,如果有则清除缓存。我制作了一个可能对你有帮助的工具。 源编辑器

这里也有类似问题的更多答案,我在其中放置了代码示例。 如何在运行时生成或修改PHP类?


0
投票

动态更改对象类 - 用例

这是动态更改对象类的另一个用例:将类升级到提供调试和日志记录的版本,以便开发人员可以在实际使用用户自己看到的核心逻辑功能时看到额外的日志。

您想要转变为另一个班级

而且程序相当简单。您从原来的课程开始...

class User {
    protected $Id;
    protected $Name;
    protected $Password;
    protected $PostsPerPage;
}

使用旧类的新类

然后你想要一个新类用于同一个变量

$user
,因为你有一个新插件。记得用
extends
来继承...

class UserWithPrivilege extends User {
    [...]
    protected $Visible;
}

使用该对象的旧类版本实例化该对象的变形版本

并且由于

$user
已经有数据,因此您需要为
UserWithPrivilege
创建一个构造函数,该构造函数采用旧的
$user
并在本地重新实现它。即....

class UserWithPrivilege extends User {
    public function __construct($user) {
        $this->Id = $user->Id; # and for all other old attributes)
    }
}

有了这个,您将能够将旧类转换为该类的新版本,所有这些都使用相同的变量,并且不需要在繁重的 CPU 处理方面做太多事情。

© www.soinside.com 2019 - 2024. All rights reserved.