6

一部のクラスが一部のメソッド呼び出しをインターセプトして操作するための汎用ラッパー クラスが必要です。メソッド呼び出し転送、インターセプト、今のところ問題ありません。しかし、しばらく考えた後、解決策のない問題を見つけました。アプリケーションのあらゆる場所で組み込みの instanceof-operator を使用しています。もちろん、ラッパーはその中のクラスのインスタンスではないため、これはもう機能しません。他の関数に置き換えるのではなく、引き続き演算子を使用したいと思います。

この問題の回避策を実装する方法はありますか? このオペレーターはどのように機能しますか? おそらくラッパーで上書きできるクラスのコア関数を呼び出しますか?

これがこのオペレーターを操作するための本当に「クリーンな」ソリューションではないことはわかっていますが、これが私にとって最も簡単なソリューションになると思います。そして、私たちが知っているように、PHPにはそれほどきれいではないものがたくさんあります... :-)

答えてくれてありがとう、ベン

4

5 に答える 5

3

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

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

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

ご覧のとおり、すべてがラッピング プロセス中に行われます。

より多くのラッパーがある場合は、新しいラップされたクラス名を別の方法で準備できます (たとえば、ラッパー名と関連付けるなど)。

于 2016-07-06T22:21:37.053 に答える
2

おそらく、私はあなたのニーズに対する解決策を説明することができます. (免責事項:私はGo! AOP Frameworkの作成者です)あなたの説明から、クラスに触れずにメソッドに追加のロジックを動的に追加したいようです。私が正しければ、ソースコードにインターセプターの概念を導入するAspect-Oriented Paradigmを見ることができます。さらに重要なことは、元のクラスは変更されません。

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

AOP の優れた機能は、元のクラス名を保持することです。これは、コード内のタイプヒントを変更したり、instanceof演算子を乗っ取ったりしてはならないことを意味します。追加のロジックを使用してクラスを取得します。

于 2016-07-11T07:13:56.823 に答える
1

まったく不可能です。実際、おそらく将来: https://bugs.php.net/bug.php?id=71352

于 2016-07-11T16:36:20.900 に答える
0

具象クラスの代わりにインターフェースを使用してください。インターフェイスを Wrapper と Concrete Class に適用します。

http://de3.php.net/manual/en/language.oop5.interfaces.phpを参照してください。

于 2011-02-06T19:15:32.973 に答える
0

デコレータ パターンを見てください。ラッパー/ラップされたクラスが同じインターフェイスを実装している場合、すべてをエレガントに実行できます (コード全体で instanceofインターフェイスを使用できます)。

この問題の回避策を実装する方法はありますか? このオペレーターはどのように機能しますか? おそらくラッパーで上書きできるクラスのコア関数を呼び出しますか?

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

インターフェイス/クラスを実装/拡張するクラスを宣言するときはいつでも、エントリが $interfaces または $parent フィールドに置かれ、スクリプトが終了するまで不変のままになると想像してください。

于 2016-07-09T07:41:13.220 に答える