如何在运行时生成或修改PHP类?

gre*_*emo 2 php runtime php-5.3

schmittjoh/CG-库看来我需要什么,但没有文件可言.

该库提供了生成PHP代码通常需要的一些工具.其中一个优势在于通过行为增强现有类.

鉴于A课程:

class A {}
Run Code Online (Sandbox Code Playgroud)

我想在运行时和一些缓存机制上修改类A,使其实现给定的接口:

interface I
{
    public function mustImplement();
}
Run Code Online (Sandbox Code Playgroud)

......与方法"默认"的实现mustImplement()A类.

hak*_*kre 5

注意: OP需要PHP 5.3(以前没有标记过),这个问题是PHP 5.4的一般概要.

您可以通过定义接口并添加包含这些接口的默认实现的特征来实现.

然后创建一个新的类定义

  • 延伸自你的基础班,
  • 实现该接口和
  • 使用默认特征.

例如,请参阅PHP中的特征 - 任何真实世界的示例/最佳实践?

您可以轻松生成该类定义代码,并将其直接存储includeeval直接存储.

如果您创建包含其所包含的所有信息的新类名(在您的情况下是基类名和接口),则可以防止轻松创建重复的类定义.

这适用于没有像runkit这样的任何PHP扩展.如果你带入serialize游戏,你甚至可以在运行时使用新界面重载现有对象,以防他们可以序列化/反序列化.

您可以在以下位置找到已实现此功能的代码示例:


Gor*_*don 5

您还可以使用角色对象模式和良好的旧聚合.

您不必拥有包含所有业务逻辑的智能实体,而是让它们变得愚蠢,并将所有业务逻辑移动到聚合愚蠢实体的角色中.你的行为就是生活在角色里面的一等公民.

例:

interface BannableUser
{
    public function ban();
}
Run Code Online (Sandbox Code Playgroud)

具有一个特定行为的接口遵循接口隔离原则.它还极大地增加了可能的重用,因为您比具有特定于应用程序的行为集合的实体更可能重用个体行为.

现在要实现它,您可以创建适当的角色类:

class BannableUserRole implements BannableUser
{
     private $user;

     public function __construct(User $user)
     {
         $this->user = $user;
     }

     public function ban()
     {
         $this->user->isBanned = true;
     }
}
Run Code Online (Sandbox Code Playgroud)

你仍然拥有一个用户实体,但它完全被剥夺了所有行为.它基本上只是一袋Getters and Setters或公共财产.它代表你的系统是什么.它是数据部分,而不是交互部分.现在,交互在角色内部.

class User
{
    public $isBanned;

    // … more properties
}
Run Code Online (Sandbox Code Playgroud)

现在假设您有某种Web UI,您可以在控制器中执行以下操作:

class BanUserController implements RequestHandler
{
    // …

    public function handleRequest(Request $request)
    {
        $userId = $request->getVar('user_id');
        $user = $this->userRepository->findById($userId);
        $bannableUser = new BannableUserRole($user);
        $bannableUser->ban();
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以通过移动用户的实际查找和将角色分配到UseCase类来进一步解耦.我们称之为上下文:

class BanUserContext implements Context
{
    public function run($userId)
    {
        $user = $this->userRepository->findById($userId);
        $bannableUser = new BannableUserRole($user);
        $bannableUser->ban();
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,您拥有Model层中的所有业务逻辑,并与用户界面完全隔离.上下文就是您的系统所做的事情.您的Controller将仅委托给适当的上下文:

class BanUserController implements RequestHandler
{
    // …

    public function handleRequest(Request $request)
    {
        $this->banUserContext->run($request->getVar('user_id'));

    }
}
Run Code Online (Sandbox Code Playgroud)

就是这样.不需要Runkit或类似的hackery.以上是数据上下文交互架构模式的简化版本,以防您需要进一步研究.