2011-02-06 8 views
6

いくつかのクラスでは、メソッドコールのいくつかを傍受して操作するための汎用ラッパークラスが必要です。メソッド呼び出しフォワーディング、インターセプト、これまでのところ問題ありません。しかし、しばらく考えた後、私は解決策がない問題を発見しました。アプリケーションのどこにでも組み込みのinstanceof-operatorを使用しています。もちろん、これはもう機能しません。なぜなら、ラッパーはその内部のクラスのインスタンスではないからです。私は演算子を使い続け、他の関数と置き換えないようにしたいと思います。ラッパークラスのPHP-instanceof-operatorを操作する

この問題の回避策を実装する方法はありますか?この演算子はどのように機能しますか?それはおそらく私のラッパーで上書きすることができるクラスのコア機能を呼び出すのでしょうか?

私はこれが本当にこのオペレータを操作する "クリーンな"ソリューションではないことを知っていますが、これは私にとっては最も簡単な解決策だと思います。そして、私たちが知っているように、そのクリーンでないPHPで多くのものは、あなたの答えを

おかげで... :-)ある、ベン

+0

これは私が取り組もうとしている問題とまったく同じです。あなたは何とかこれを働かせることができましたか?もしそうなら、私はあなたのやり方に非常に興味があります。 – mrjames

答えて

0

ではなく、具体的なクラスのインタフェースを使用してください。ラッパーとコンクリートクラスにインターフェイスを適用します。

私にはわからないhttp://de3.php.net/manual/en/language.oop5.interfaces.php

+0

同じインターフェースを実装しておらず、同じクラスを持たない複数のクラスの汎用ラッパーを使いたいと思います。だからあなたのソリューションで私はすべての異なるクラスのラッパークラスが必要です。または私は間違っていますか? – Ben

+0

@Benあなたは正しいです。それは欠点です。 – Gordon

2

が、それはあなたが望む方法でinstanceofオペレータをだますことが可能である(そうでない場合は、サブクラスとしてクラスを認識)が、私は、私はあなたのニーズに合わせて解決策を見つけたと思います参照してください。 。あなたが問題を正しく理解しているならば、コード全体の変更を最小限に抑えて、どのクラスにもいくつかのメソッドを注入したいだけです。

このケースでは、解決策を準備する最良の方法は、形質を使用することです(hereを参照)。特性を使用すると、直接継承なしで任意のクラスにメソッドを追加することができ、基本クラスのメソッドを上書きすることができます。もちろん、特性を使ってメソッドを上書きするには、サブクラスが必要ですが、動的に作成することができます。私はラッピングプロセスについて何も知らないが、私のソリューションでは特別なクラスを使用した。私の解決策を見てみましょう:

namespace someNameSpace; 

//this is one of your class that you want to wrap - it can be declare under some other namespace if you need 
class yourBaseClass { } 

//your wrapper class as a trait 
trait yourWrapper { } 

//class for wrapping any object 
class ObjectWrapperClass 
{ 
    //method for change object class (described on http://stackoverflow.com/a/3243949/4662836) 
    protected static function objectToObject($instance, $className) 
    { 
     return unserialize(sprintf('O:%d:"%s"%s', strlen($className), $className, strstr(strstr(serialize($instance), '"'), ':'))); 
    } 

    //wrapping method 
    //$object is a object to be wrapped 
    //$wrapper is a full name of the wrapper trait 
    public static function wrap($object, $wrapper) 
    { 
     //take some information about the object to be wrapped 
     $reflection = new \ReflectionClass($object); 
     $baseClass = $reflection->getShortName(); 
     $namespace = $reflection->getNamespaceName(); 

     //perpare the name of the new wrapped class 
     $newClassName = "{$baseClass}Wrapped"; 

     //if new wrapped class has not been declared before we need to do it now 
     if (!class_exists($newClassName)) { 
      //prepare a code of the wrapping class that inject trait 
      $newClassCode = "namespace {$namespace} { class {$newClassName} extends {$baseClass} { use {$wrapper}; } }"; 

      //run the prepared code 
      eval($newClassCode); 
     } 

     //change the object class and return it 
     return self::objectToObject($object, $namespace . '\\' . $newClassName); 
    } 

} 

//lets test this solution 

$originalObject = new yourBaseClass(); 

$wrappedObject = ObjectWrapperClass::wrap($originalObject, 'yourWrapper'); 

if ($wrappedObject instanceof yourBaseClass) { 
    echo 'It is working'; 
} 

あなたが見ることができるようにすべてのラッピングプロセス中に発生します。

さらにラッパーがある場合は、新しいラップされたクラス名を別の方法で準備することができます(たとえば、ラッパー名をコアにするなど)。

+0

これは巧妙な解決策ですが、反射や評価が遅すぎるために使いこなすことはできません。また、私のユースケースでは多く使用されます。 コード生成の問題を解決し、ラップする必要があるすべてのクラスのラッパーを作成します。ラッパーがラップされたクラスから拡張されていれば、問題は解決しますが、本当に好きではありません。 この問題を解決するために、__instanceOfという魔法のメソッドがあったらいいですね。とにかくありがとうございます。 – hchinchilla

0

decorator patternをご覧ください。ラッパー/ラップされたクラスが同じインターフェースを実装している場合は、すべてを上手くやり遂げることができます(コード全体でインターフェース)。

この問題の回避策を実装する方法はありますか?この演算子はどのように機能しますか?それはおそらく私のラッパーで上書きすることができるクラスのコア機能を呼び出すのでしょうか?

instanceof演算子を操作することはできません。あなたが興味を持っていたのでinstanceofはオペレータがどのように実装されるか、ここではオリジナルのCコードのPHP表現である:あなたがインタフェース/クラスを拡張/実装するクラスを宣言するたびに

class php_class { 
    public $interfaces = array(); // array of php_class objects (php classes can implement more than one interface) 
    public $parent = null; // php_class object (php classes can only extend one class) 
} 

function instanceof_operator($implementation, $abstraction) { 
    // forward recursion (iterates recursively through interfaces until a match is found) 
    for($i=0; $i<count($implementation->interfaces); $i++) { 
     if(instanceof_operator($implementation->interfaces[$i], $abstraction)) { 
      return true; 
     } 
    } 
    // backward recursion (iterates recursively through parents until a match is found) 
    while($implementation!=null) { 
     if($implementation == $abstraction) { 
      return true; 
     } 
     $implementation = $implementation->parent; 
    } 
    // no match was found 
    return false; 
} 

、想像エントリは$インターフェイス上に堆積されますか、スクリプトが終了するまで、は変更不可能なのままである$親フィールド。

2

おそらく私はあなたのニーズに合った解決策を記述することができます。 (免責事項:私はGo! AOP Frameworkの著者です)あなたの説明から、クラスに触れることなくメソッドに追加のロジックを動的に追加するように見えます。私が正しいとすれば、Aspect-Oriented Paradigmを見て、ソースコードのインターセプタの概念を紹介することができます。もっと重要なのは、元のクラスに手を触れないことです。

これをコードにどのように適用できるかを知るために、私の記事http://go.aopphp.com/blog/2014/10/19/caching-like-a-pro/を見ると、デコレータ、プロキシのような古典的なオブジェクト指向のパターンの長所と短所がすべて強調されています。私は、クロスカッティングの問題を解決するためにPHPが不可欠で複雑で制限されているため、オブジェクト指向の方法ですべてのインターセプタを個別のモジュールに抽出できないという結論を下すことができます。 AOPは伝統的なOOPモデルを拡張しているため、インターセプタ(アドバイス)を別のクラス(アスペクトと呼ばれます)に抽出することが可能です。

AOPの素晴らしい機能は、元のクラス名を保持するということです。これは、コード内の型ヒントを変更したり、演算子instanceofをハイジャックしないことを意味します。あなたは追加の論理であなたのクラスを得るでしょう。

関連する問題