259


111

私はPHPで新しいWebアプリケーションを始めようとしていますが、今回はプラグインインターフェースを使って拡張できるものを作りたいと思います。

プラグインが特定のイベントにアタッチできるように、どのようにしてコードに 'フック’を書いていくのでしょうか。

8 Answer


156


Observerパターンを使うことができます。 これを達成するための簡単な機能的方法:


出力:

これは私のCRAZYアプリケーションです4 5 = 9 4 * 5 = 20

ノート:

この例のソースコードでは、拡張可能にしたい実際のソースコードの前にすべてのプラグインを宣言する必要があります。 プラグインに渡される単一または複数の値を処理する方法の例を含めました。 これの最も難しい部分は、どの引数が各フックに渡されるかをリストした実際のドキュメントを書くことです。

これは、PHPでプラグインシステムを実現するための1つの方法です。 より良い代替手段があります、私はあなたが詳細についてはWordPressドキュメンテーションをチェックすることを勧めます。

_申し訳ありませんが、アンダースコア文字はMarkdownによってHTMLエンティティに置き換えられているようです。 このバグが修正されたら、私はこのコードを再投稿することができます。

_編集:決して気にしないでください、編集しているときにだけ表示されます。


55


それでは、Observerパターンは望ましくないとしましょう。リスニングのタスクを処理するようにクラスメソッドを変更する必要があり、汎用的なものが必要だからです。 そして、他のクラスからすでにあなたのクラスを継承しているかもしれないので、 `extends`継承を使いたくないとしましょう。 多くの労力を費やすことなく_すべてのクラスをプラガブルにするための一般的な方法があるのは素晴らしいことではないでしょうか。 方法は次のとおりです。

_Class = get_class(&$RefObject);
        $this->_RefObject = $RefObject;
    }

    public function __set($sProperty,$mixed) {
        $sPlugin = $this->_Class . '_' . $sProperty . '_setEvent';
        if (is_callable($sPlugin)) {
            $mixed = call_user_func_array($sPlugin, $mixed);
        }
        $this->_RefObject->$sProperty = $mixed;
    }

    public function __get($sProperty) {
        $asItems = (array) $this->_RefObject;
        $mixed = $asItems[$sProperty];
        $sPlugin = $this->_Class . '_' . $sProperty . '_getEvent';
        if (is_callable($sPlugin)) {
            $mixed = call_user_func_array($sPlugin, $mixed);
        }
        return $mixed;
    }

    public function __call($sMethod,$mixed) {
        $sPlugin = $this->_Class . '_' .  $sMethod . '_beforeEvent';
        if (is_callable($sPlugin)) {
            $mixed = call_user_func_array($sPlugin, $mixed);
        }
        if ($mixed != 'BLOCK_EVENT') {
            call_user_func_array(array(&$this->_RefObject, $sMethod), $mixed);
            $sPlugin = $this->_Class . '_' . $sMethod . '_afterEvent';
            if (is_callable($sPlugin)) {
                call_user_func_array($sPlugin, $mixed);
            }
        }
    }

} //end class Plugin

class Pluggable extends Plugin {
} //end class Pluggable

////////////////////
// PART 2
////////////////////

class Dog {

    public $Name = '';

    public function bark(&$sHow) {
        echo "$sHow
\n";
    }

    public function sayName() {
        echo "
\nMy Name is: " . $this->Name . "
\n";
    }


} //end class Dog

$Dog = new Dog();

////////////////////
// PART 3
////////////////////

$PDog = new Pluggable($Dog);

function Dog_bark_beforeEvent(&$mixed) {
    $mixed = 'Woof'; // Override saying 'meow' with 'Woof'
    //$mixed = 'BLOCK_EVENT'; // if you want to block the event
    return $mixed;
}

function Dog_bark_afterEvent(&$mixed) {
    echo $mixed; // show the override
}

function Dog_Name_setEvent(&$mixed) {
    $mixed = 'Coco'; // override 'Fido' with 'Coco'
    return $mixed;
}

function Dog_Name_getEvent(&$mixed) {
    $mixed = 'Different'; // override 'Coco' with 'Different'
    return $mixed;
}

////////////////////
// PART 4
////////////////////

$PDog->Name = 'Fido';
$PDog->Bark('meow');
$PDog->SayName();
echo 'My New Name is: ' . $PDog->Name;

第1回では、それがPHPスクリプトの先頭にある `require_once()`呼び出しに含まれるかもしれないものです。 それは何かをプラガブルにするためにクラスをロードします。

パート2では、ここでクラスをロードします。 注クラスに特別なことをする必要はありませんでした。これは、Observerパターンとは大きく異なる点です。

パート3では、ここからクラスを「プラグイン可能」に切り替えます(つまり、クラスのメソッドとプロパティをオーバーライドできるプラグインをサポートします)。 したがって、たとえば、Webアプリケーションを使用している場合は、プラグインレジストリがある可能性があり、ここでプラグインをアクティブにすることができます。 `Dog_bark_beforeEvent()`関数にも注目してください。 return文の前に `$ mixed = 'BLOCK_EVENT’を設定すると、犬が吠えるのを阻止し、またDog_bark_afterEventも阻止するでしょう。

第4回では、これが通常の操作コードですが、実行すると思われるものがまったく実行されないことに注意してください。 例えば、犬はその名前が 'Fido’ではなく 'Coco’であることを発表しません。 犬は「ニャー」とは言わず、「ウーフ」と言います。 あとで犬の名前を見たいときは、「ココ」ではなく「違う」とわかります。 これらすべてのオーバーライドは、第3部で提供されています。

それで、これはどのように機能しますか? さて、 eval()(みんなが「悪」だと言っている)を除外し、それがObserverパターンではないことを除外しましょう。 つまり、その動作はPluggableと呼ばれる卑劣な空のクラスです。これには、Dogクラスで使用されるメソッドやプロパティは含まれていません。 したがって、それが起こるので、魔法の方法は私たちのために働くでしょう。 そのため、パート3と4では、Dogクラス自体ではなく、Pluggableクラスから派生したオブジェクトを使用しています。 その代わりに、PluginクラスにDogオブジェクトを「触れさせる」ようにしています。 (もしそれが私の知らないデザインパターンのようなものであれば - 教えてください。)


34


_hook_および_listener_メソッドが最も一般的に使用されていますが、他にもできることがあります。 あなたのアプリのサイズ、そして誰があなたがコードを見ることを許可しようとしているか(これはFOSSスクリプトになるか、または社内の何かになるでしょう)に依存します。

kdeloachには良い例がありますが、彼の実装とフック関数は少し危険です。 私はあなたがあなたの文章のphpアプリの性質についてのより多くの情報を与えるようにあなたに頼むでしょうそしてそしてあなたはどのようにあなたはプラグインが合うのを見るか。

1私からkdeloachする。


22


これが私が使ったアプローチです、それはQtシグナル/スロットメカニズムから一種のオブザーバーパターンをコピーする試みです。 オブジェクトは信号を出すことができます。 すべてのシグナルはシステム内にIDを持ちます - 送信者のIDオブジェクト名で構成されています。すべてのシグナルは受信側にバインドできます。これは単に「呼び出し可能」ですシグナルを受信したい人にシグナルを渡すにはバスクラスを使用します。あなたはシグナルを「送る」。 以下は実装例です

ログイン();

?


17


私は、最も簡単な方法は、Jeff自身の助言に従い、既存のコードを見直すことだと思います。 Wordpress、Drupal、Joomla、その他の有名なPHPベースのCMSを見て、それらのAPIフックがどのように見えるのかを確認してください。 このようにして、あなたは物事をもう少し頑固にするためにあなたが以前は考えていなかったかもしれないアイデアさえ得ることができます。

もっと直接的な答えは、それらが必要とする使いやすさを提供するであろう彼らのファイルに "include_once"する一般的なファイルを書くことでしょう。 これはカテゴリに分割され、1つのMASSIVEの "hooks.php"ファイルでは提供されません。 なぜなら、それらが含まれるファイルは、ますます多くの依存関係と機能性を持つことになるからです。 APIの依存関係を低く抑えるようにしてください。 I.E含まれるファイルが少なくなりました。


14


PHPでプラグインを処理するための作業の多くを処理する、YahooのMatt Zandstraによる Sticklebackという素晴らしいプロジェクトがあります。

それはプラグインクラスのインターフェースを強化し、コマンドラインインターフェースをサポートし、起動して実行するのはそれほど難しくありません - 特にあなたがhttp://www.phparch.comでそれについてカバーストーリーを読んでいるならば。 ]。


10


他のプロジェクトがそれをどのようにして行ったかを調べることは良いアドバイスです。 多くの人はプラグインをインストールし、それらの「名前」をサービスに登録することを要求するので(ワードプレスのように)、登録されたリスナを識別してそれらを実行する関数を呼び出すコードに「ポイント」があります。 標準的なオブジェクト指向デザインパターンは、http://www.phppatterns.com/docs/design/observer_pattern [Observer Pattern]です。これは、真のオブジェクト指向PHPシステムに実装するのに適したオプションです。

http://framework.zend.com[Zend Framework]は多くのフックメソッドを利用しており、非常にうまく設計されています。 それは見て良いシステムでしょう。


7


ここでの答えのほとんどは、Webアプリケーションにローカルなプラグイン、つまりローカルWebサーバー上で動作するプラグインを対象としているように思われることに驚きました。

プラグインを別のリモートサーバーで実行したい場合はどうなりますか? これを行う最善の方法は、アプリケーションで特定のイベントが発生したときに呼び出されるさまざまなURLを定義できるフォームを提供することです。

発生したばかりのイベントに基づいて、さまざまなイベントがさまざまな情報を送信します。

この方法では、リモートサーバーがアプリケーションから送信された情報に基づいてタスクを実行できるように、アプリケーションに提供されているURL(たとえばhttps経由)へのcURL呼び出しを実行するだけです。

これには2つの利点があります。

  1. ローカルサーバーでコードをホストする必要はありません(セキュリティ)

  2. コードは、PHP以外のさまざまな言語のリモートサーバー(拡張性)に置くことができます(移植性)。