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

时间:2012-09-29 08:28:04

标签: php runtime php-5.3

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

  

此库提供了一些通常需要生成的工具   PHP代码。其中一个优势在于现有的增强   有行为的课程。

给定A类:

class A {}

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

interface I
{
    public function mustImplement();
}

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

5 个答案:

答案 0 :(得分:5)

  

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

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

然后创建一个新的类定义

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

有关示例,请参阅Traits in PHP – any real world examples/best practices?

您可以轻松生成该类定义代码,并将其直接存储到include或直接eval

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

这没有像runkit那样的任何PHP扩展。如果将serialize带入游戏中,您甚至可以在运行时使用新界面重载现有对象,以防它们进行序列化/反序列化。

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

答案 1 :(得分:5)

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

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

示例:

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

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

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

答案 2 :(得分:1)

我需要一个编辑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));

答案 3 :(得分:0)

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

答案 4 :(得分:0)

您可以查看:https://github.com/ptrofimov/jslikeobject

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

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