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

如何在运行时生成或修改PHP类?,php,runtime,php-5.3,Php,Runtime,Php 5.3,这似乎是我需要的,但根本没有文档 该库提供了一些生成 PHP代码。它的优势之一在于增强现有的 具有行为的类 给定A类: class A{} 当然,我想在运行时通过一些缓存机制修改classA,使其实现给定的接口: 接口I { 公共功能必须实现(); } 。。。在a类中为方法mustImplement()使用“默认”实现。扩展可以帮助您 Runkit_Sandbox — Runkit Sandbox Class -- PHP Virtual Machine Runkit_Sandbox_Par

这似乎是我需要的,但根本没有文档

该库提供了一些生成 PHP代码。它的优势之一在于增强现有的 具有行为的类

给定
A
类:

class A{}
当然,我想在运行时通过一些缓存机制修改class
A
,使其实现给定的接口:

接口I
{
公共功能必须实现();
}
。。。在
a
类中为方法
mustImplement()
使用“默认”实现。

扩展可以帮助您

Runkit_Sandbox — Runkit Sandbox Class -- PHP Virtual Machine
Runkit_Sandbox_Parent — Runkit Anti-Sandbox Class
runkit_class_adopt — Convert a base class to an inherited class, add ancestral methods when appropriate
runkit_class_emancipate — Convert an inherited class to a base class, removes any method whose scope is ancestral
runkit_constant_add — Similar to define(), but allows defining in class definitions as well
runkit_constant_redefine — Redefine an already defined constant
runkit_constant_remove — Remove/Delete an already defined constant
runkit_function_add — Add a new function, similar to create_function
runkit_function_copy — Copy a function to a new function name
runkit_function_redefine — Replace a function definition with a new implementation
runkit_function_remove — Remove a function definition
runkit_function_rename — Change the name of a function
runkit_import — Process a PHP file importing function and class definitions, overwriting where appropriate
runkit_lint_file — Check the PHP syntax of the specified file
runkit_lint — Check the PHP syntax of the specified php code
runkit_method_add — Dynamically adds a new method to a given class
runkit_method_copy — Copies a method from class to another
runkit_method_redefine — Dynamically changes the code of the given method
runkit_method_remove — Dynamically removes the given method
runkit_method_rename — Dynamically changes the name of the given method
runkit_return_value_used — Determines if the current functions return value will be used
runkit_sandbox_output_handler — Specify a function to capture and/or process output from a runkit sandbox
runkit_superglobals — Return numerically indexed array of registered superglobals
注意:OP需要PHP5.3(以前没有这样标记),这个问题是PHP5.4的概述

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

然后创建一个新的类定义

  • 从基类扩展而来
  • 实现该接口并
  • 使用默认特性
有关示例,请参见

您可以很容易地生成该类定义代码,并将其存储和
包含它,或者直接将其存储在
eval

如果使新类名包含它所包含的所有信息(在本例中是基类名和接口),则可以防止轻松创建重复的类定义

这可以在没有任何PHP扩展(如runkit)的情况下工作。如果将
serialize
带到游戏中,甚至可以在运行时使用新接口重载现有对象,以防它们可以序列化/反序列化

您可以在中找到实现此功能的代码示例:

  • ()

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

与使用包含所有业务逻辑的智能实体不同,您可以将它们设置为哑巴,然后将所有业务逻辑移动到聚合哑巴实体的角色中。你的行为就是生活在角色中的一流公民

例如:

interface BannableUser
{
    public function ban();
}
具有一个特定行为的接口遵循接口分离原则。它还极大地增加了可能的重用,因为与具有特定于应用程序的行为集合的实体相比,您更可能重用单个行为

现在要实现这一点,您需要创建一个适当的角色类:

class BannableUserRole implements BannableUser
{
     private $user;

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

     public function ban()
     {
         $this->user->isBanned = true;
     }
}
您仍然有一个用户实体,但它完全没有任何行为。它本质上只是一包getter和setter或公共属性。它代表您的系统是什么。这是数据部分,而不是交互部分。交互现在在角色内部

class User
{
    public $isBanned;

    // … more properties
}
现在假设您有某种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();
    }
}
您可以通过将用户的实际查找和角色分配移动到一个用例类中来进一步解耦。让我们称之为上下文:

class BanUserContext implements Context
{
    public function run($userId)
    {
        $user = $this->userRepository->findById($userId);
        $bannableUser = new BannableUserRole($user);
        $bannableUser->ban();
    }
}
现在,所有业务逻辑都在模型层中,并且与用户界面完全隔离。上下文就是您的系统所做的。您的控制器将仅委托给适当的上下文:

class BanUserController implements RequestHandler
{
    // …

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

    }
}
就这样。不需要Runkit或类似的黑客软件。以上是数据上下文交互体系结构模式的简化版本,以备进一步研究。

您可以查看:

作者在继承的支持下实现了类似JS的动态对象


但这更像是一个笑话,而不是一个真正的命题。

我需要一个工具来编辑PHP类(特别是条令实体),我找不到它,所以我创建了一个可能对您有所帮助的工具,我大量地记录了它。所以如果你想试试,我很乐意帮你

在这里

它为您提供了如下API:

    /* @var $classEditor DocDigital\Lib\SourceEditor\PhpClassEditor */
    $classEditor->parseFile($classPath);
    $classEditor->getClass('a')->getMethod('b')->addAnnotation('@auth Juan Manuel Fernandez <juanmf@gmail.com>');
    $classEditor->getClass('a')->getAttribute('attr')->addAnnotation('@Assert\Choice(...)');
    $classEditor->getClass('a')->addAttribute($attr2);
    $classEditor->getClass('a')->addUse('use DocDigital\Bundle\DocumentBundle\DocumentGenerator\Annotation as DdMapping;');
    $classEditor->getClass('a')->addConst('    const CONSTANT = 1;');

    file_put_contents($classPath, $classEditor->getClass('a')->render(false));
/*@var$classEditor DocDigital\Lib\SourceEditor\PhpClassEditor*/
$classEditor->parseFile($classPath);
$classEditor->getClass('a')->getMethod('b')->addAnnotation('auth Juan Manuel Fernandez');
$classEditor->getClass('a')->getAttribute('attr')->addAnnotation('@Assert\Choice(…)');
$classEditor->getClass('a')->addAttribute($attr2);
$classEditor->getClass('a')->addUse('use DocDigital\Bundle\DocumentBundle\DocumentGenerator\Annotation as DdMapping;');
$classEditor->getClass('a')->addConst('const CONSTANT=1;');
文件内容($classPath,$classEditor->getClass('a')->render(false));

您的特殊需求是什么?通常,如果您解释您的问题,人们会建议替代解决方案。坏主意,在运行时生成代码。您在寻找什么?请添加更多的上下文,因为有几种方法可以通过常规设计模式实现这一点,但哪种方法取决于您需要它的原因和原因。是的,这需要PHP5.4,因为它的特点,但它可以工作。您还可以通过在运行时注入函数定义来模拟特性,并在之前将它们存储在类中。那就需要更多的工作了。我对你的答案投了赞成票,但不能接受。我还不能迁移到5.4。好吧,您可以通过模拟traits为PHP<5.4创建一个采用这个的平台。PHP中的特性是在编译器级别进行复制和粘贴,因此您可以将默认实现存储在它自己的类中,并复制到类主体上,将其注入新类中。对于覆盖,您需要创建自己的机制(复制函数等),然后使用反射来控制它。在这种情况下,CG库有什么不好的地方?我没有使用它,但它似乎为这些操作提供了一些支持。再读一遍托比·艾伦的评论,里面有很多道理。好吧,现在我必须更好地思考我要做的事情。你的解决方案看起来不错,因此你的关于traits的链接也不错。这更多的是一个评论,而不是一个答案。traits提供了接口的默认实现。