La meilleure façon de permettre à des plugins pour une application PHP

voix
245

Je commence une nouvelle application web en PHP et cette fois je veux créer quelque chose que les gens peuvent étendre à l'aide d'une interface de plug-in.

Comment peut-on aller sur l'écriture de « crochets » dans leur code afin que les plugins peuvent joindre à des événements spécifiques?

Créé 01/08/2008 à 13:50
source utilisateur
Dans d'autres langues...                            


8 réponses

voix
148

Vous pouvez utiliser un modèle d'observateur. Une façon simple et fonctionnelle pour y parvenir:

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

Sortie:

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

Remarques:

Pour cet exemple, le code source, vous devez déclarer tous vos plugins avant le code source que vous voulez être extensible. J'ai inclus un exemple de la façon de gérer une ou plusieurs valeurs étant transmises au plug-in. La partie la plus difficile de c'est en train d'écrire la documentation réelle qui énumère les arguments sont transmis à chaque crochet.

Ceci est juste une méthode de réalisation d'un système de plug-in en PHP. Il existe de meilleures alternatives, je vous suggère de consulter la documentation WordPress pour plus d'informations.

Désolé, il apparaît des caractères de soulignement sont remplacés par des entités HTML par Markdown? Je peux repostez ce code lorsque ce bug sera corrigé.

Edit: Nevermind il apparaît que cette façon lorsque vous modifiez

Créé 01/08/2008 à 14:46
source utilisateur

voix
51

Alors disons que vous ne voulez pas le modèle d'observateur , car il faut que vous changer vos méthodes de classe pour gérer la tâche d'écoute, et que vous voulez quelque chose de générique. Et disons que vous ne voulez pas utiliser l' extendshéritage parce que vous pouvez déjà héritez dans votre classe d'une autre classe. Ne serait - il pas génial d'avoir un moyen générique de faire toute la classe connectable sans trop d' effort ? Voici comment:

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

Dans la partie 1, c'est ce que vous pourriez inclure un require_once()appel en haut de votre script PHP. Il charge les classes pour faire quelque chose connectable.

Dans la partie 2, c'est là on charge une classe. Notez que je ne ai pas eu à faire quelque chose de spécial à la classe, ce qui est nettement différente de celle du modèle d'observateur.

Dans la partie 3, c'est là que nous allumons notre classe autour en étant « connectables » (qui est, prend en charge les plugins qui nous permettent de remplacer les méthodes et propriétés de classe). Ainsi, par exemple, si vous avez une application web, vous pourriez avoir un registre de plug - in, et vous pouvez activer les plugins ici. Notez également la Dog_bark_beforeEvent()fonction. Si je mets $mixed = 'BLOCK_EVENT'avant la déclaration de retour, il va bloquer le chien d'aboyer et bloquerait aussi le Dog_bark_afterEvent parce qu'il n'y aurait pas de cas.

Dans la partie 4, qui est le code de fonctionnement normal, mais il faut noter que ce que vous pourriez penser courrait ne fonctionne pas comme ça du tout. Par exemple, le chien n'annonce pas le nom de comme « Fido », mais « Coco ». Le chien ne dit pas « miaou », mais « Woof ». Et quand vous voulez regarder le nom du chien après, vous trouvez qu'il est « différent » au lieu de « Coco ». Tous les remplacements ont été fournis dans la partie 3.

Alors, comment ça marche? Eh bien, Eliminons eval()(que tout le monde dit est « mal ») et à l' article que ce n'est pas un modèle d'observateur. Ainsi, la façon dont il fonctionne est la classe sournoise vide appelée Pluggable, qui ne contient pas les méthodes et propriétés utilisées par la classe Dog. Ainsi, étant donné que cela se produit, les méthodes magiques engageront pour nous. Voilà pourquoi dans les parties 3 et 4 nous mess avec l'objet dérivé de la classe Pluggable, pas la classe chien lui - même. Au lieu de cela, nous laissons la classe Plugin faire le « toucher » sur l'objet de chien pour nous. (Si c'est une sorte de modèle de conception , je ne sais pas - s'il vous plaît laissez - moi savoir.)

Créé 01/06/2009 à 06:59
source utilisateur

voix
31

Le crochet et l' auditeur méthode est la plus couramment utilisée, mais il y a d' autres choses que vous pouvez faire. En fonction de la taille de votre application, et qui vous allez permettre voir le code (cela va être un script des logiciels libres, ou quelque chose dans la maison) influencera grandement la façon dont vous voulez autoriser les plugins.

kdeloach a un bel exemple, mais sa mise en œuvre et la fonction de crochet est un peu dangereux. Je demanderais pour vous donner plus d'informations sur la nature de l'application php votre écriture, et comment vous voir montage plugins dans.

+1 à kdeloach de moi.

Créé 01/08/2008 à 18:23
source utilisateur

voix
19

Voici une approche que je l'ai utilisé, il est une tentative de copier des signaux Qt / mécanisme de créneaux horaires, une sorte de modèle d'observation. Les objets peuvent émettre des signaux. Chaque signal a une carte d'identité dans le système - il est composé par id de l'expéditeur + nom d'objet Chaque signal peut être binded aux récepteurs, ce qui est tout simplement un « appelable » Vous utilisez une classe de bus pour passer les signaux à toute personne intéressée par les recevoir Quand quelque chose cela arrive, vous « envoyer » un signal. Ci-dessous et la mise en œuvre d'exemple

    <?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();

?>
Créé 25/09/2008 à 22:29
source utilisateur

voix
14

Je crois que la façon la plus facile serait de suivre propres conseils de Jeff et un coup d'oeil autour de code existant. Essayez de regarder Wordpress, Drupal, Joomla et autres CMS de base PHP bien connu pour voir comment leurs crochets API look and feel. De cette façon, vous pouvez même obtenir des idées que vous avez peut-être pas pensé auparavant pour rendre les choses un peu plus rubust.

Une réponse plus directe serait d'écrire des fichiers généraux qu'ils « include_once » dans leur dossier qui fournirait la facilité d'utilisation dont ils auraient besoin. Ce serait divisé en catégories et non prévus dans un MASSIVE fichier « hooks.php ». Attention cependant, car ce qui finit par se produire est que les fichiers qu'ils comprennent finissent par avoir de plus en plus les dépendances et la fonctionnalité améliore. Essayez de garder les dépendances API faible. IE moins de fichiers pour qu'ils comprennent.

Créé 01/08/2008 à 14:44
source utilisateur

voix
13

Il y a un joli projet appelé épinoche par Matt Zandstra à Yahoo qui gère une grande partie du travail pour le traitement des plugins en PHP.

Il applique l'interface d'une classe de plug - in, prend en charge une interface de ligne de commande et n'est pas trop difficile de se lever et courir - surtout si vous lisez l'histoire de couverture à ce sujet dans le magazine architecte PHP .

Créé 17/09/2008 à 20:00
source utilisateur

voix
10

Un bon conseil est de regarder comment d' autres projets ont fait. De nombreux appels pour avoir des plugins installés et leur « nom » enregistré pour les services (comme le fait wordpress) si vous avez des « points » dans votre code où vous appelez une fonction qui identifie les auditeurs enregistrés et les exécute. Un bagout de conception standard OO est le modèle d'observateur , ce qui serait une bonne option pour mettre en œuvre dans un système PHP objet vraiment orienté.

Le Zend Framework utilise de nombreuses méthodes d'accrochage, et est très bien architecturé. Ce serait un bon système pour regarder.

Créé 17/09/2008 à 20:38
source utilisateur

voix
7

Je suis surpris que la plupart des réponses ici semblent être axées sur les plugins qui sont locaux à l'application Web, par exemple, des plugins qui fonctionnent sur le serveur web local.

Qu'en est-il si vous vouliez les plug-ins de fonctionner sur un autre - à distance - serveur? La meilleure façon de le faire serait de fournir un formulaire qui vous permet de définir des URL différentes qui seraient appelées lorsque des événements se produisent dans votre application.

Différents événements enverraient différentes informations en fonction de l'événement qui vient de se produire.

De cette façon, vous serait juste effectuer un appel cURL à l'URL qui a été fourni à l'application (par exemple via https) où des serveurs distants peuvent effectuer des tâches en fonction des informations qui a été envoyé par votre application.

Cela offre deux avantages:

  1. Vous ne devez pas héberger un code sur votre serveur local (sécurité)
  2. Le code peut être sur des serveurs distants () dans les langues extensibilité différentes autres que PHP (portabilité)
Créé 22/04/2013 à 08:41
source utilisateur

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