Как создать или изменить класс PHP во время выполнения?

Библиотека schmittjoh / cg кажется мне необходимой, но документации нет вообще.

Эта библиотека предоставляет некоторые инструменты, которые вам обычно нужны для генерации кода PHP. Одна из его сил заключается в улучшении существующих классов с поведением.

Учитывая класс A :

 class A {} 

Я бы хотел, во время выполнения и с некоторым механизмом кэширования, модифицировать, класс A , чтобы он реализовал данный интерфейс:

 interface I { public function mustImplement(); } 

… с реализацией по умолчанию для метода mustImplement() в классе A

    Примечание: OP нуждается в PHP 5.3 (ранее это не было отмечено ранее), этот вопрос является общей схемой для PHP 5.4.

    Это можно сделать с определением интерфейса и добавлением черт, которые содержат реализацию по умолчанию для этих интерфейсов.

    Затем вы создаете новое определение класса, которое

    • простирается от вашего базового класса,
    • реализует этот интерфейс и
    • использует значения по умолчанию.

    Например, см. « Черты в PHP» – примеры реальных примеров / лучших практик?

    Вы можете легко сгенерировать этот код определения класса и либо сохранить его, либо include его, либо eval его прямо.

    Если вы создадите новое имя класса, содержащее всю содержащуюся в нем информацию (в вашем случае имя базового класса и интерфейс), вы можете легко создать описания дубликатов классов.

    Это работает без расширения PHP, такого как runkit. Если вы включите serialize в игру, вы можете даже перегрузить существующие объекты во время выполнения с новым интерфейсом, если они могут сериализоваться / десериализоваться.

    Вы можете найти пример кода, который реализует это в:

    • DCI-Account-Example-in-PHP / src / DCI / Casting.php ( использование )

    Вы также можете использовать шаблон ролевого объекта и старую старую агрегацию.

    Вместо того, чтобы иметь умные сущности, которые содержат всю вашу бизнес-логику, вы делаете их немыми и переместите всю бизнес-логику в роли, которые затем объединяют немых объектов. Ваше поведение – это первоклассные граждане, живущие внутри Ролей.

    Пример:

     interface BannableUser { public function ban(); } 

    Наличие интерфейса с одним конкретным поведением следует Принципу разделения сегментов. Это также значительно увеличивает возможное повторное использование, так как вы более склонны повторно использовать индивидуальное поведение, чем Entity, с набором приложений для конкретных приложений.

    Теперь, чтобы реализовать это, вы создаете соответствующий класс ролей:

     class BannableUserRole implements BannableUser { private $user; public function __construct(User $user) { $this->user = $user; } public function ban() { $this->user->isBanned = true; } } 

    У вас все еще есть пользовательский объект, но он полностью лишен всех поведений. Это, по сути, всего лишь мешок Getters и Setters или публичные свойства. Он представляет собой вашу систему. Это часть данных, а не часть взаимодействия. Теперь взаимодействие находится внутри ролей.

     class User { public $isBanned; // … more properties } 

    Теперь, предполагая, что у вас есть какой-то веб-интерфейс, вы можете сделать следующее в своем контроллере:

     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 или подобном хакере. Вышеупомянутая версия представляет собой упрощенную версию архитектурного шаблона взаимодействия с контекстом данных , если вы хотите продолжить исследование этого.

    Мне нужен инструмент для редактирования 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)); ,  /* @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)); 

    Расширение 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 

    Вы можете посмотреть: https://github.com/ptrofimov/jslikeobject

    Автор реализовал динамические JS-подобные объекты с поддержкой наследования.

    Но это скорее шутка, чем реальное предложение.