Ο καλύτερος τρόπος για να επιτρέψει plugins για μια εφαρμογή PHP

ψήφοι
245

Αρχίζω μια νέα web εφαρμογή σε PHP και αυτή τη φορά θέλω να δημιουργήσω κάτι που οι άνθρωποι μπορούν να επεκτείνουν χρησιμοποιώντας μια διεπαφή plugin.

Πως μπορεί κάποιος να πάει για τη σύνταξη «αγκίστρια» στον κώδικα τους, έτσι ώστε plugins μπορεί να συνδεθεί με συγκεκριμένα γεγονότα;

Δημοσιεύθηκε 01/08/2008 στις 13:50
πηγή χρήστη
Σε άλλες γλώσσες...                            


8 απαντήσεις

ψήφοι
148

Θα μπορούσατε να χρησιμοποιήσετε ένα πρότυπο Observer. Ένας απλός λειτουργικός τρόπος για να επιτευχθεί αυτό:

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

Παραγωγή:

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

Σημειώσεις:

Για αυτό το παράδειγμα κώδικα πηγής, θα πρέπει να δηλώνουν όλα τα plugins σας πριν από την πραγματική του πηγαίου κώδικα που θέλετε να είναι επεκτάσιμο. Έχω περιλάβει ένα παράδειγμα για το πώς να χειριστούν απλούς ή πολλαπλούς τιμές που διαβιβάστηκε στη plugin. Το πιο δύσκολο μέρος αυτής γράφει την πραγματική τεκμηρίωση που αναφέρει ποια επιχειρήματα να περάσει σε κάθε γάντζο.

Αυτή είναι μόνο μία μέθοδος για την επίτευξη ενός συστήματος plugin σε PHP. Υπάρχουν καλύτερες εναλλακτικές λύσεις, σας προτείνω να δείτε την τεκμηρίωση WordPress για περισσότερες πληροφορίες.

Δυστυχώς, φαίνεται υπογραμμίζουν οι χαρακτήρες αντικαθίστανται από HTML οντότητες από Markdown; Μπορώ να ξανά-καταχωρήσετε αυτό τον κωδικό, όταν αυτό το σφάλμα παίρνει σταθερό.

Επεξεργασία: Nevermind, φαίνεται μόνο με αυτόν τον τρόπο κατά την επεξεργασία

Απαντήθηκε 01/08/2008 στις 14:46
πηγή χρήστη

ψήφοι
51

Ας πούμε ότι δεν θέλετε το μοτίβο Παρατηρητής, διότι απαιτεί να αλλάξει τις μεθόδους τάξη σας να χειριστεί το έργο της ακρόασης, και θέλουν κάτι γενικό. Και ας πούμε ότι δεν θέλετε να χρησιμοποιήσετε extendsτην κληρονομιά γιατί μπορεί ήδη να κληρονομήσουν στην τάξη σας από κάποια άλλη κατηγορία. Δεν θα ήταν υπέροχο να έχουμε ένα γενικό τρόπο για να κάνει οποιαδήποτε κατηγορία συνδεόμενο χωρίς πολλή προσπάθεια ; Εδώ είναι πώς:

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

Στο μέρος 1, αυτό είναι αυτό που θα μπορούσε να περιλαμβάνει μια require_once()κλήση στην κορυφή της PHP script σας. Φορτώνει τα μαθήματα για να κάνει κάτι pluggable.

Στο μέρος 2, αυτό είναι όπου θα φορτώσει μια τάξη. Σημείωση Δεν χρειάζεται να κάνετε κάτι το ιδιαίτερο στην τάξη, η οποία διαφέρει σημαντικά από το πρότυπο Observer.

Στο μέρος 3, αυτό είναι όπου θα στραφούν τάξη μας γύρω στην ύπαρξη «pluggable» (δηλαδή, υποστηρίζει plugins που ας παρακάμψετε τις μεθόδους τάξης και ιδιότητες). Έτσι, για παράδειγμα, αν έχετε ένα web app, μπορείτε να έχετε ένα μητρώο plugin, και μπορείτε να ενεργοποιήσετε plugins εδώ. Παρατηρήστε επίσης τη Dog_bark_beforeEvent()λειτουργία. Αν εγώ που $mixed = 'BLOCK_EVENT'πριν από την ανακοίνωση επιστροφής, θα μπλοκάρει το σκυλί από την αποφλοίωση και θα εμποδίσει επίσης την Dog_bark_afterEvent επειδή δεν θα υπήρχε καμία περίπτωση.

Στο μέρος 4, που είναι η κανονική κώδικα λειτουργίας, αλλά παρατηρήσετε ότι αυτό που μπορείτε να σκεφτείτε θα τρέξει, δεν τρέχει καθόλου έτσι. Για παράδειγμα, ο σκύλος δεν ανακοινώνει το όνομα αυτό ως «Fido», αλλά «Coco». Ο σκύλος δεν λέει «νιαούρισμα», αλλά «Woof». Και όταν θέλετε να δείτε το όνομα του σκύλου στη συνέχεια, μπορείτε να βρείτε ότι είναι «διαφορετικοί» αντί του «Coco». Όλες αυτές οι αντικαταστάσεις αυτές που προβλέπονται στο Μέρος 3.

Και πώς αυτό το έργο; Λοιπόν, ας αποκλείσουμε eval()(το οποίο ο καθένας λέει ότι είναι «κακό») και να αποκλείσει ότι δεν είναι ένα πρότυπο Observer. Έτσι, ο τρόπος που λειτουργεί είναι ο ύπουλος άδειο κατηγορία που ονομάζεται Pluggable, η οποία δεν περιέχει τις μεθόδους και τις ιδιότητες που χρησιμοποιούνται από την κατηγορία σκυλιών. Έτσι, δεδομένου ότι συμβαίνει, οι μέθοδοι μαγεία θα συμμετάσχουν για εμάς. Γι 'αυτό και στα μέρη 3 και 4 θα το χάος με το αντικείμενο που προέρχεται από το Pluggable τάξη, όχι η ίδια η τάξη σκυλί. Αντ 'αυτού, ας το Plugin τάξη να κάνει το «άγγιγμα» στο αντικείμενο σκυλί για εμάς. (Εάν αυτό είναι ένα είδος πρότυπο σχεδιασμού Δεν ξέρω για - παρακαλώ επιτρέψτε μου να ξέρω.)

Απαντήθηκε 01/06/2009 στις 06:59
πηγή χρήστη

ψήφοι
31

Το άγκιστρο και ακροατή μέθοδος είναι η πιο συχνά χρησιμοποιούμενη, αλλά υπάρχουν και άλλα πράγματα που μπορείτε να κάνετε. Ανάλογα με το μέγεθος της εφαρμογής σας και που θα σας για να επιτρέψει δείτε τον κωδικό (είναι αυτή η μετάβαση να είναι ένα σενάριο FOSS, ή κάτι στο σπίτι) θα επηρεάσει σε μεγάλο βαθμό τον τρόπο που θέλετε να επιτρέψει plugins.

kdeloach έχει ένα ωραίο παράδειγμα, αλλά η εφαρμογή του και η λειτουργία άγκιστρο είναι λίγο ανασφαλείς. Θα ήθελα να ζητήσω να δώσετε περισσότερες πληροφορίες για τη φύση της php εφαρμογή γραφής σας, και πώς βλέπετε plugins τοποθέτηση σε.

+1 για να kdeloach από μένα.

Απαντήθηκε 01/08/2008 στις 18:23
πηγή χρήστη

ψήφοι
19

Εδώ είναι μια προσέγγιση που έχω χρησιμοποιήσει, είναι μια προσπάθεια να αντιγράψετε από Qt μηχανισμό σήματα / slots, ένα είδος Παρατηρητής μοτίβο. Αντικείμενα μπορούν να εκπέμπουν σήματα. Κάθε σήμα έχει ένα αναγνωριστικό στο σύστημα - αυτό είναι που αποτελείται από το όνομα id + αντικειμένου αποστολέα Κάθε σήμα μπορεί να δένει με τους δέκτες, το οποίο απλά είναι μια «απαιτητών» Μπορείτε να χρησιμοποιήσετε μια τάξη λεωφορείο για να περάσει τα σήματα σε οποιονδήποτε ενδιαφέρεται για την παραλαβή τους όταν κάτι συμβαίνει, θα «στείλει» ένα σήμα. Παρακάτω είναι και η εφαρμογή π.χ.

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

?>
Απαντήθηκε 25/09/2008 στις 22:29
πηγή χρήστη

ψήφοι
14

Πιστεύω ότι ο πιο εύκολος τρόπος θα ήταν να ακολουθήσουν τις δικές τις συμβουλές του Jeff και ρίξτε μια ματιά γύρω υπάρχοντα κώδικα. Δοκιμάστε κοιτάζοντας το Wordpress, Drupal, Joomla και άλλα γνωστά PHP με βάση CMS για να δείτε πώς API αγκίστρια τους εμφάνιση και αίσθηση. Με αυτό τον τρόπο μπορείτε να πάρετε ακόμα και ιδέες που μπορεί να μην έχετε σκεφτεί στο παρελθόν να κάνει τα πράγματα λίγο πιο rubust.

Μια πιο άμεση απάντηση θα ήταν να γράψει γενικά αρχεία που θα «include_once» στο αρχείο τους, που θα παρέχει τη δυνατότητα χρήσης που θα χρειαστεί. Αυτό θα χωρίζεται σε κατηγορίες και ΔΕΝ παρέχονται σε μία μαζική αρχείο «hooks.php». Προσοχή όμως, γιατί αυτό που καταλήγει να συμβαίνει είναι ότι τα αρχεία που περιλαμβάνουν καταλήγουν να έχουν όλο και περισσότερο εξαρτήσεις και τη λειτουργικότητα βελτιώνεται. Προσπαθήστε να κρατήσετε τις εξαρτήσεις API χαμηλά. IE λιγότερα αρχεία για να συμπεριλάβει.

Απαντήθηκε 01/08/2008 στις 14:44
πηγή χρήστη

ψήφοι
13

Υπάρχει ένα τακτοποιημένο πρόγραμμα που ονομάζεται stickleback από τον Matt Zandstra στο Yahoo που χειρίζεται μεγάλο μέρος του έργου για το χειρισμό plugins σε PHP.

Επιβάλλει το περιβάλλον της τάξης plugin, υποστηρίζει μια διασύνδεση γραμμής εντολών και δεν είναι πάρα πολύ δύσκολο να σηκωθεί και να λειτουργήσει - ειδικά αν έχετε διαβάσει την ιστορία κάλυψης σχετικά με αυτό το περιοδικό PHP αρχιτέκτονα .

Απαντήθηκε 17/09/2008 στις 20:00
πηγή χρήστη

ψήφοι
10

Η καλή συμβουλή είναι να δούμε πώς άλλα έργα έχουν κάνει. Πολλοί πρόσκληση για το γεγονός ότι plugins εγκατασταθεί και το «όνομα» τους που ταξινομήθηκαν για τις υπηρεσίες (όπως το wordpress κάνει), έτσι ώστε να έχουν «σημεία» στον κώδικά σας, όπου μπορείτε να καλέσετε μια συνάρτηση που προσδιορίζει ονομαστικών ακροατές και τις εκτελεί. Ένα πρότυπο σχεδιασμού OO κορακίστικα είναι το πρότυπο Observer , η οποία θα ήταν μια καλή επιλογή για να εφαρμόσουν μια πραγματικά αντικειμενοστρεφής συστήματος PHP.

Η Zend-πλαίσιο κάνει χρήση πολλών μεθόδων αγκίστρωσης, και είναι πολύ όμορφα architected. Αυτό θα ήταν ένα καλό σύστημα για να δούμε.

Απαντήθηκε 17/09/2008 στις 20:38
πηγή χρήστη

ψήφοι
7

Με εκπλήσσει το γεγονός ότι οι περισσότερες από τις απαντήσεις εδώ φαίνεται να προσανατολίζεται για plugins που είναι τοπικά στο web εφαρμογή, δηλαδή, plugins που τρέχουν στο τοπικό web server.

Τι γίνεται αν θέλετε τα plugins για να τρέξει σε ένα διαφορετικό - απομακρυσμένο - server; Ο καλύτερος τρόπος για να γίνει αυτό θα ήταν να παρέχει μια φόρμα που σας επιτρέπει να ορίσετε διαφορετικές διευθύνσεις URL που θα καλείται όταν συμβαίνουν συγκεκριμένα γεγονότα στην αίτησή σας.

Διαφορετικές εκδηλώσεις θα στείλει διαφορετικές πληροφορίες με βάση το γεγονός ότι ακριβώς συνέβη.

Με αυτό τον τρόπο, θα εκτελέσει μόνο μια κλήση cURL στη διεύθυνση URL που έχει δοθεί στην αίτησή σας (π.χ. μέσω https) όπου απομακρυσμένους διακομιστές μπορεί να εκτελέσει τα καθήκοντα με βάση τις πληροφορίες που έχει σταλεί από την εφαρμογή σας.

Αυτό παρέχει δύο οφέλη:

  1. Δεν χρειάζεται να φιλοξενήσει οποιαδήποτε κώδικα σε τοπικό server σας (ασφαλείας)
  2. Ο κωδικός μπορεί να είναι σε απομακρυσμένους διακομιστές (επεκτασιμότητα) σε διάφορες γλώσσες άλλες από την PHP (φορητότητα)
Απαντήθηκε 22/04/2013 στις 08:41
πηγή χρήστη

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