Der beste Weg, Plugins für eine PHP-Anwendung zu ermöglichen,

stimmen
245

Ich beginne eine neue Web-Anwendung in PHP und dieses Mal ich etwas schaffen will, dass die Menschen durch die Verwendung einer Plugin-Schnittstelle erweitern können.

Wie geht ein über ‚Haken‘ in ihren Code zu schreiben, so dass Plugins auf bestimmte Ereignisse anhängen kann?

Veröffentlicht am 01/08/2008 um 13:50
quelle vom benutzer
In anderen Sprachen...                            


8 antworten

stimmen
148

Sie könnten ein Beobachter-Muster verwenden. Eine einfache funktionale Art und Weise, dies zu erreichen:

<?php

/** Plugin system **/

$listeners = array();

/* Create an entry point for plugins */
function hook() {
    global $listeners;

    $num_args = func_num_args();
    $args = func_get_args();

    if($num_args < 2)
        trigger_error("Insufficient arguments", E_USER_ERROR);

    // Hook name should always be first argument
    $hook_name = array_shift($args);

    if(!isset($listeners[$hook_name]))
        return; // No plugins have registered this hook

    foreach($listeners[$hook_name] as $func) {
        $args = $func($args); 
    }
    return $args;
}

/* Attach a function to a hook */
function add_listener($hook, $function_name) {
    global $listeners;
    $listeners[$hook][] = $function_name;
}

/////////////////////////

/** Sample Plugin **/
add_listener('a_b', 'my_plugin_func1');
add_listener('str', 'my_plugin_func2');

function my_plugin_func1($args) {
    return array(4, 5);
}

function my_plugin_func2($args) {
    return str_replace('sample', 'CRAZY', $args[0]);
}

/////////////////////////

/** Sample Application **/

$a = 1;
$b = 2;

list($a, $b) = hook('a_b', $a, $b);

$str  = "This is my sample application\n";
$str .= "$a + $b = ".($a+$b)."\n";
$str .= "$a * $b = ".($a*$b)."\n";

$str = hook('str', $str);
echo $str;
?>

Ausgabe:

This is my CRAZY application
4 + 5 = 9
4 * 5 = 20

Anmerkungen:

Für dieses Beispiel Quellcode, müssen Sie alle Ihre Plugins vor dem eigentlichen Quellcode deklarieren, die Sie sein ausfahrbaren wollen. Ich habe ein Beispiel enthalten, wie einzelne oder mehrere Werte an das Plugin übergeben wird behandeln. Der schwierigste Teil davon schreibt die aktuelle Dokumentation, die auflistet, welche Argumente an jeden Haken übergeben bekommen.

Dies ist nur ein Verfahren zur Herstellung eines Plugin-System in PHP erreichen. Es gibt bessere Alternativen, empfehle ich Ihnen die Wordpress-Dokumentation für weitere Informationen zu überprüfen.

Sorry, es erscheint Unterstrichen ersetzt werden durch HTML-Entitäten, die von Markdown? Ich kann diesen Code neu schreiben, wenn dieser Fehler behoben wird.

Edit: Nevermind, es scheint nur so, wenn Sie bearbeiten,

Beantwortet am 01/08/2008 um 14:46
quelle vom benutzer

stimmen
51

Also lassen Sie uns sagen , dass Sie nicht das Beobachter - Muster wollen , weil es erfordert , dass Sie Ihre Klassenmethoden ändern , um die Aufgabe des Zuhörens zu handhaben , und wollen etwas generisch. Und lassen Sie uns sagen , Sie wollen nicht verwenden extendsVererbung , weil Sie bereits in Ihrer Klasse von einer anderen Klasse erben werden. Wäre es nicht toll, eine generische Art und Weise zu haben , um jede Klasse steckbar ohne viel Aufwand ? Hier ist wie:

<?php

////////////////////
// PART 1
////////////////////

class Plugin {

    private $_RefObject;
    private $_Class = '';

    public function __construct(&$RefObject) {
        $this->_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<br />\n";
    }

    public function sayName() {
        echo "<br />\nMy Name is: " . $this->Name . "<br />\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;

In Teil 1, das ist, was man mit einem umfassen könnte require_once()Anruf an der Spitze Ihres PHP - Skript. Es lädt die Klassen etwas steckbar zu machen.

In Teil 2, das ist, wo wir eine Klasse zu laden. Hinweis Ich habe nichts zu tun haben, speziell zu der Klasse, die deutlich anders als die Beobachter-Muster ist.

In Teil 3, das ist , wo wir unsere Klasse wechseln um in den „pluggable“ zu sein (das heißt, unterstützt Plugins , die uns Klasse Methoden und Eigenschaften außer Kraft setzen lassen). So zum Beispiel, wenn Sie einen Web - App haben, können Sie eine Plugin - Registrierung haben, und Sie können Plugins hier aktivieren. Beachten Sie auch die Dog_bark_beforeEvent()Funktion. Wenn ich festgelegt , $mixed = 'BLOCK_EVENT'bevor die return - Anweisung, wird es den Hund vom Bellen blockieren und würde auch die Dog_bark_afterEvent blockieren , weil es kein Ereignis sein würde.

In Teil 4, das ist der normale Operationscode, bemerken aber, dass Sie laufen nicht wie das überhaupt was könnte denken, laufen würde. Zum Beispiel hat der Hund nicht seinen Namen als ‚Fido‘, sondern ‚Coco‘ bekannt geben. Der Hund sagt nicht ‚Miau‘, sondern ‚Wuff‘. Und wenn man bei dem Namen des Hundes danach suchen möchten, finden Sie es ist ‚Different‘ anstelle von ‚Coco‘. All diese Überschreibungen wurden in Teil 3 zur Verfügung gestellt.

Also , wie funktioniert das? Nun, lassen Sie uns ausschließen eval()(was jeder sagt , ist „böse“) und ausschließen, dass es nicht ein Beobachter - Muster. So, so , wie es funktioniert , ist die hinterhältige leere Klasse genannt steckbar, die nicht die Methoden und Eigenschaften der Klasse Dog verwendet enthält. Da somit das geschieht, werden die magischen Methoden für uns engagieren. Aus diesem Grunde in den Teilen 3 und 4 wir Schlamassel mit dem Objekt aus der Pluggable - Klasse abgeleitet, nicht die Hundeklasse selbst. Stattdessen lassen wir die Plugin - Klasse für uns die „berührend“ auf dem Dog - Objekt zu tun. (Wenn die irgendeine Art von Design - Muster , weiß ich nicht über - lassen Sie es mich wissen.)

Beantwortet am 01/06/2009 um 06:59
quelle vom benutzer

stimmen
31

Der Haken und Hörer Methode ist die am häufigsten verwendeten, aber es gibt andere Dinge , die Sie tun können. Je nach Größe der App, und wer Sie gehen zu ermöglichen , den Code zu sehen (ist das Gehen ein FOSS Skript oder etwas im Haus sein) wird stark beeinflussen , wie Sie Plugins zulassen möchten.

kdeloach hat ein schönes Beispiel, aber seine Implementierung und Hook-Funktion ist ein wenig unsicher. Ich bitte Sie weitere Informationen über die Art von PHP-App Ihr ​​Schreiben zu geben, Und wie Sie sehen Plugins Einbau in.

+1 von mir kdeloach.

Beantwortet am 01/08/2008 um 18:23
quelle vom benutzer

stimmen
19

Hier ist ein Ansatz, den ich verwendet habe, es ist ein Versuch, von Qt Signalen / Slots Mechanismus zu kopieren, eine Art Beobachter-Muster. Objekte können Signale aussenden. Jedes Signal hat eine ID im System - durch Absenders id + Objektnamen zusammengesetzt ist jedes Signal an die Empfänger binded werden können, die einfach ein „aufrufbar“ Sie können eine Bus-Klasse verwenden, um die Signale zu jemand daran interessiert, sie zu empfangen passieren Wenn etwas Fall Sie ein Signal „senden“. Im Folgenden finden und Implementierungsbeispiel

    <?php

class SignalsHandler {


    /**
     * hash of senders/signals to slots
     *
     * @var array
     */
    private static $connections = array();


    /**
     * current sender
     *
     * @var class|object
     */
    private static $sender;


    /**
     * connects an object/signal with a slot
     *
     * @param class|object $sender
     * @param string $signal
     * @param callable $slot
     */
    public static function connect($sender, $signal, $slot) {
        if (is_object($sender)) {
            self::$connections[spl_object_hash($sender)][$signal][] = $slot;
        }
        else {
            self::$connections[md5($sender)][$signal][] = $slot;
        }
    }


    /**
     * sends a signal, so all connected slots are called
     *
     * @param class|object $sender
     * @param string $signal
     * @param array $params
     */
    public static function signal($sender, $signal, $params = array()) {
        self::$sender = $sender;
        if (is_object($sender)) {
            if ( ! isset(self::$connections[spl_object_hash($sender)][$signal])) {
                return;
            }
            foreach (self::$connections[spl_object_hash($sender)][$signal] as $slot) {
                call_user_func_array($slot, (array)$params);
            }

        }
        else {
            if ( ! isset(self::$connections[md5($sender)][$signal])) {
                return;
            }
            foreach (self::$connections[md5($sender)][$signal] as $slot) {
                call_user_func_array($slot, (array)$params);
            }
        }

        self::$sender = null;
    }


    /**
     * returns a current signal sender
     *
     * @return class|object
     */
    public static function sender() {
        return self::$sender;
    }

}   

class User {

    public function login() {
        /**
         * try to login
         */
        if ( ! $logged ) {
            SignalsHandler::signal(this, 'loginFailed', 'login failed - username not valid' );
        }
    }

}

class App {
    public static function onFailedLogin($message) {
        print $message;
    }
}


$user = new User();
SignalsHandler::connect($user, 'loginFailed', array($Log, 'writeLog'));
SignalsHandler::connect($user, 'loginFailed', array('App', 'onFailedLogin'));

$user->login();

?>
Beantwortet am 25/09/2008 um 22:29
quelle vom benutzer

stimmen
14

Ich glaube, der einfachste Weg, Jeffs eigenen Rat folgen würde und einen Blick in vorhandenen Code hat. Versuchen Sie, bei Wordpress suchen, Drupal, Joomla und andere gut bekannte PHP-basierten CMS, um zu sehen, wie sie ihre API Haken aussehen und sich anfühlen. Auf diese Weise können auch Ideen bekommen können Sie vorher nicht gedacht haben kann die Dinge ein wenig mehr rubust zu machen.

Eine direkte Antwort wäre allgemeine Dateien zu schreiben, sie würden „include_once“ in ihre Datei, die die Nutzbarkeit bieten würden sie brauchen würden. Dies würde bis in Kategorien unterteilt werden, und nicht in einer MASSIVEN „hooks.php“ Datei zur Verfügung gestellt. Seien Sie aber vorsichtig, weil das, was am Ende passiert ist, dass Dateien, die sie enthalten am Ende mehr und mehr Abhängigkeiten und Funktionalität verbessert. Versuchen API Abhängigkeiten gering zu halten. IE weniger Dateien für sie aufzunehmen.

Beantwortet am 01/08/2008 um 14:44
quelle vom benutzer

stimmen
13

Es ist ein nettes Projekt namens Stickleback von Matt Zandstra bei Yahoo , die für den Umgang mit Plugins in PHP viel von der Arbeit behandelt.

Es erzwingt die Schnittstelle einer Plugin - Klasse, eine Kommandozeilen - Schnittstelle unterstützt und ist nicht allzu schwer zu bekommen und läuft - vor allem wenn man die Titelgeschichte über sie in dem Lese PHP Architekten Magazin .

Beantwortet am 17/09/2008 um 20:00
quelle vom benutzer

stimmen
10

Guter Rat ist zu sehen , wie andere Projekte haben es geschafft. Viele Call für Plug - ins installiert und deren „name“ registriert für Dienste, die (wie Wordpress tut) , so dass Sie haben „Punkte“ in Ihrem Code , wo Sie eine Funktion aufrufen , die registrierte Hörer identifiziert und führt sie aus . Ein Standard - OO - Design Rüttler ist die Observer - Muster , die eine gute Option zu implementieren in einem wirklich orientierten PHP Systemobjekt wären.

Das Zend Framework nutzt viele Haken Methoden und ist sehr schön architected. Das wäre ein gutes System sein , zu betrachten.

Beantwortet am 17/09/2008 um 20:38
quelle vom benutzer

stimmen
7

Ich bin überrascht, dass die meisten der hier Antworten scheinen über Plugins ausgerichtet zu sein, die auf der Web-Anwendung lokal sind, dh Plugins, die auf dem lokalen Web-Server laufen.

Was ist, wenn Sie die Plugins laufen auf einem anderen wollen - Remote - Server? Der beste Weg, dies zu tun wäre, um eine Form zu schaffen, die Sie verschiedene URLs definieren kann, die aufgerufen werden würden, wenn bestimmte Ereignisse in der Anwendung auftreten.

Verschiedene Ereignisse würden unterschiedliche Informationen auf der Grundlage der Veranstaltung senden, die gerade stattgefunden.

Auf diese Weise würden Sie nur einen cURL Aufruf die URL ausführen, die (zB über https) für Ihre Anwendung zur Verfügung gestellt wurden, in den Remote-Server Aufgaben basierend auf Informationen durchführen kann, die von der Anwendung gesendet wurden.

Dies bietet zwei Vorteile:

  1. Sie müssen keinen Code auf dem lokalen Server hosten (Security)
  2. Der Code kann in verschiedenen anderen Sprachen dann PHP (Portabilität) auf Remote-Servern (Erweiterbarkeit) seine
Beantwortet am 22/04/2013 um 08:41
quelle vom benutzer

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more