【问题标题】:How to generate or modify a PHP class at runtime?如何在运行时生成或修改 PHP 类?
【发布时间】:2012-09-20 23:57:01
【问题描述】:

schmittjoh/cg-library 似乎是我需要的,但根本没有文档。

这个库提供了一些您通常需要生成的工具 PHP 代码。它的优势之一在于增强现有的 具有行为的类。

给定A 类:

class A {}

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

interface I
{
    public function mustImplement();
}

...在A 类中使用方法mustImplement() 的“默认”实现。

【问题讨论】:

  • 您的特殊要求是什么,通常如果您解释您的问题,人们可以提出替代解决方案
  • 坏主意,在运行时生成代码。
  • 您在寻找Lazy Loading吗?请添加更多上下文,因为使用常规设计模式有多种方法可以实现这一点,但哪一种取决于您需要它的原因和内容。

标签: php runtime php-5.3


【解决方案1】:

注意: OP 需要 PHP 5.3(之前没有这样标记),这个问题是 PHP 5.4 的概述。

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

然后你创建一个新的类定义

  • 从您的基类扩展
  • 实现该接口和
  • 使用默认特征。

例如,见Traits in PHP – any real world examples/best practices?

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

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

这无需任何 PHP 扩展(如 runkit)即可工作。如果您将serialize 带入游戏,您甚至可以在运行时使用新接口重载现有对象,以防它们可以序列化/反序列化。

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

【讨论】:

  • 是的,由于特性,这需要 PHP 5.4,但它可以工作。您可以通过在运行时注入函数定义并将它们存储在之前的类中来模拟特征。那就是更多的工作。
  • 我赞成您的回答,但无法接受。我还不能升级到 5.4。
  • 好吧,您可以通过模拟特征来为 PHP
  • 那么,CG库有什么不好的地方呢?我没有使用它,但看起来它为这些操作提供了一些支持。再读一遍托比艾伦的评论,里面有很多道理。
  • 好的,现在我必须更好地考虑我正在尝试做的事情。您的解决方案似乎很好,因此您的关于特征的链接。
【解决方案2】:

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

不是拥有包含所有业务逻辑的智能实体,而是让它们变得愚蠢,然后将所有业务逻辑移动到聚合这些愚蠢实体的角色中。那么你的行为就是生活在角色中的一等公民。

例子:

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;
     }
}

您仍然有一个 User 实体,但它已完全剥离了所有行为。它本质上只是一袋 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();
    }
}

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

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 或类似的黑客。以上是数据上下文交互架构模式的简化版本,以防您想进一步研究。

【讨论】:

  • Traits 为接口提供默认实现。
【解决方案3】:

我需要一个工具来编辑 PHP 类(特别是 Doctrine Entities),但我找不到它,所以我创建了一个可以帮助你的工具,我大量记录了它。因此,如果您想尝试一下,我很乐意提供帮助。

在这里,SourceEditor

它为您提供了这样的 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));

【讨论】:

    【解决方案4】:

    Runkit分机可以帮到你

    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
    

    【讨论】:

    • 这更像是一个评论而不是一个答案。
    【解决方案5】:

    你可以看:https://github.com/ptrofimov/jslikeobject

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

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

    【讨论】:

      猜你喜欢
      • 2010-12-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-16
      • 2014-06-08
      • 1970-01-01
      相关资源
      最近更新 更多