2012-09-29 44 views
2

schmittjoh/cg-library似乎是我需要的,但根本沒有任何文檔。如何在運行時生成或修改PHP類?

這個庫提供了一些你通常需要用來生成 PHP代碼的工具。其中一個優勢在於增強具有行爲的現有類別 。

鑑於A類:

class A {} 

我想修改,在課程的運行,並與一些緩存機制,類A,使其實現給定的接口:

interface I 
{ 
    public function mustImplement(); 
} 

...對於方法mustImplement()A類中的「默認」實現。

+3

你是什麼特別的要求,往往如果你解釋你的問題的人可以提出替代解決方案 –

+2

壞主意,代碼生成在運行期間。 – JvdBerg

+0

您是否在尋找[Lazy Loading](http://martinfowler.com/eaaCatalog/lazyLoad.html)?請添加更多的上下文,因爲有幾種方法可以通過常規的設計模式來實現,但哪一種方法取決於爲什麼以及爲什麼需要這樣做。 – Gordon

回答

5

注: OP需要PHP 5.3(這是以前沒有標記的那樣),這個問題是PHP 5.4的概要。

您可以通過定義接口並添加包含這些接口的默認實現的特徵來完成此操作。

然後創建一個新的類定義

  • 延伸從基類
  • 工具該接口和
  • 使用默認的特質。

舉一個例子,看Traits in PHP – any real world examples/best practices?

您可以輕鬆地生成的類定義代碼,並可以保存它,它includeeval它直行。

如果你使新的類名包含它所包含的所有信息(在你的情況下是基類名和接口),你可以防止輕鬆創建重複的類定義。

這個工程沒有像runkit那樣的任何PHP擴展。如果您將serialize帶入遊戲中,您甚至可以使用新界面在運行時重載現有對象,以防它們可以序列化/反序列化。

您可以找到已在開始實施這樣的代碼,例如:

+0

聽起來不錯,但至少需要PHP 5.4 ... – gremo

+0

是的,這需要PHP 5.4的特性,但它的工作原理。通過在運行時注入函數定義並在之前將它們存儲在類中,您也可以模擬特徵。那是更多的工作。 – hakre

+0

我upvoted你的答案,但不能接受它。我不能移動到5.4。 – gremo

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 
+1

這是比答案更多的評論。 – hakre

5

您也可以使用Role Object模式和良好的老聚集。

不是讓智能實體包含所有業務邏輯,而是讓他們變傻,然後將所有業務邏輯轉移到聚集啞實體的角色中。你的行爲是那些生活在角色中的一流公民。

實施例:

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

你仍然有一個用戶實體,但它完全剝離所有的行爲。它本質上只是一袋吸氣劑和固體劑或公共財產。它代表你的系統。這是數據部分,而不是交互部分。互動現在在角色裏面。

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或類似的駭客。以上是數據上下文交互體系結構模式的簡化版本,以備您進一步研究。

+0

特徵提供接口的默認實現。 – juanmf

1

我需要一個編輯PHP類(特別是教義實體)的工具,我找不到它,所以我創建了一個可以幫助你的工具,我將它記錄下來。所以如果你想嘗試一下,我會很樂意幫忙的。

這,SourceEditor

它給你一個這樣的API:

/* @var $classEditor DocDigital\Lib\SourceEditor\PhpClassEditor */ 
    $classEditor->parseFile($classPath); 
    $classEditor->getClass('a')->getMethod('b')->addAnnotation('@auth Juan Manuel Fernandez <[email protected]>'); 
    $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));