PHP एप्लिकेशन के लिए प्लगइन्स की अनुमति देने का सबसे अच्छा तरीका


276

मैं PHP में एक नया वेब एप्लिकेशन शुरू कर रहा हूं और इस समय के आसपास मैं कुछ ऐसा बनाना चाहता हूं जिसे लोग प्लगइन इंटरफ़ेस का उपयोग करके बढ़ा सकते हैं।

अपने कोड में 'हुक' लिखने के बारे में कैसे जाना जाता है ताकि प्लगइन्स विशिष्ट घटनाओं से जुड़ सकें?

जवाबों:


162

आप एक ऑब्जर्वर पैटर्न का उपयोग कर सकते हैं। इसे पूरा करने का एक सरल कार्यात्मक तरीका:

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

टिप्पणियाँ:

इस उदाहरण स्रोत कोड के लिए, आपको अपने सभी प्लगइन्स को वास्तविक स्रोत कोड से पहले घोषित करना होगा जिसे आप बढ़ाना चाहते हैं। मैंने एक उदाहरण शामिल किया है कि कैसे एकल या एकाधिक मानों को प्लगइन में दिया जाए। इसका सबसे कठिन हिस्सा वास्तविक दस्तावेज़ीकरण लिख रहा है जो यह बताता है कि प्रत्येक हुक के लिए क्या तर्क मिलते हैं।

यह PHP में एक प्लगइन सिस्टम को पूरा करने का सिर्फ एक तरीका है। बेहतर विकल्प हैं, मेरा सुझाव है कि आप अधिक जानकारी के लिए वर्डप्रेस प्रलेखन की जांच करें।


3
ध्यान दें कि PHP> = 5.0 के लिए आप SPL में परिभाषित ऑब्जर्वर / सब्जेक्ट इंटरफेस का उपयोग करके इसे लागू कर सकते हैं: php.net/manual/en/class.splobserver.php
जॉन कार्टर

20
पेडिटिक नोट: यह ऑब्जर्वर पैटर्न का उदाहरण नहीं है। इसका एक उदाहरण है Mediator Pattern। सच्चे पर्यवेक्षक विशुद्ध रूप से अधिसूचना हैं, कोई संदेश पास या सशर्त अधिसूचना नहीं है (और न ही सूचनाओं को नियंत्रित करने के लिए एक केंद्रीय प्रबंधक है)। यह उत्तर को गलत नहीं बनाता है , लेकिन यह गलत नाम से लोगों को फोन करने से रोकने के लिए ध्यान दिया जाना चाहिए ...
ircmaxell

ध्यान दें कि कई हुक / श्रोताओं का उपयोग करते समय, आपको केवल तार या सरणियाँ वापस करनी चाहिए, दोनों नहीं। मैंने हाउंड सीएमएस के लिए कुछ इसी तरह से लागू किया है - getbutterfly.com/hound
सिप्रियान

59

तो मान लें कि आप ऑब्जर्वर पैटर्न नहीं चाहते हैं क्योंकि यह आवश्यक है कि आप सुनने के कार्य को संभालने के लिए अपने क्लास के तरीकों को बदलें, और कुछ सामान्य चाहते हैं। और मान लें कि आप 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 स्क्रिप्ट के शीर्ष पर एक कॉल के साथ शामिल कर सकते हैं । यह कुछ प्लग करने योग्य बनाने के लिए कक्षाओं को लोड करता है।

भाग 2 में, यही वह जगह है जहाँ हम एक कक्षा को लोड करते हैं। नोट मुझे क्लास के लिए कुछ खास नहीं करना था, जो ऑब्जर्वर पैटर्न से काफी अलग है।

भाग 3 में, यही वह जगह है जहां हम अपनी कक्षा को "प्लगगेबल" होने के लिए स्विच करते हैं (अर्थात, उन प्लगइन्स का समर्थन करता है जो हमें क्लास के तरीकों और गुणों को ओवरराइड करते हैं)। उदाहरण के लिए, यदि आपके पास एक वेब ऐप है, तो आपके पास एक प्लगइन रजिस्ट्री हो सकती है, और आप यहां प्लग इन सक्रिय कर सकते हैं। Dog_bark_beforeEvent()फंक्शन को भी नोटिस करें । यदि मैं $mixed = 'BLOCK_EVENT'रिटर्न स्टेटमेंट से पहले सेट करता हूं , तो यह कुत्ते को भौंकने से रोक देगा और डॉग_बर्क_ आफ्टर-ईवेंट को भी ब्लॉक कर देगा क्योंकि कोई घटना नहीं होगी।

भाग 4 में, यह सामान्य ऑपरेशन कोड है, लेकिन ध्यान दें कि जो आप सोच सकते हैं कि वह चलेगा वह बिल्कुल भी नहीं चलता है। उदाहरण के लिए, कुत्ता 'फिदो' के नाम से नहीं बल्कि 'कोको' के नाम की घोषणा करता है। कुत्ते को 'म्याऊ' नहीं, बल्कि 'वूफ' कहा जाता है। और जब आप कुत्ते के नाम को बाद में देखना चाहते हैं, तो आप पाते हैं कि यह 'कोको' के बजाय 'अलग' है। उन सभी ओवरराइड को भाग 3 में प्रदान किया गया था।

यह कैसे काम करता है? ठीक है, चलो शासन करते हैं eval()(जो सभी कहते हैं कि "बुराई" है) और शासन करें कि यह एक पर्यवेक्षक पैटर्न नहीं है। तो, जिस तरह से यह काम करता है वह स्केगी खाली वर्ग है जिसे प्लगगेबल कहा जाता है, जिसमें डॉग क्लास द्वारा उपयोग किए जाने वाले तरीके और गुण शामिल नहीं हैं। इस प्रकार, चूंकि ऐसा होता है, जादू की विधियां हमारे लिए संलग्न होंगी। इसीलिए भागों 3 और 4 में हम प्लगगबल वर्ग से प्राप्त वस्तु के साथ खिलवाड़ करते हैं, न कि डॉग क्लास से। इसके बजाय, हम प्लगइन क्लास को हमारे लिए डॉग ऑब्जेक्ट पर "टचिंग" करने देते हैं। (यदि यह किसी प्रकार का डिज़ाइन पैटर्न है जिसके बारे में मुझे नहीं पता है - कृपया मुझे बताएं।)


3
क्या यह डेकोरेटर नहीं है?
एम.वी.

1
मैं विकिपीडिया पर इस बारे में पढ़ा और, वाह, तुम सही हो! :)
19

35

हुक और श्रोता विधि सबसे अधिक इस्तेमाल किया है, लेकिन वहाँ अन्य बातों के आप कर सकते हैं। आपके ऐप के आकार पर निर्भर करता है, और जो आपके कोड को देखने की अनुमति देने के लिए जा रहा है (क्या यह FOSS स्क्रिप्ट है, या घर में कुछ होने जा रहा है) बहुत प्रभावित करेगा कि आप प्लग इन की अनुमति कैसे देना चाहते हैं।

kdeloach का एक अच्छा उदाहरण है, लेकिन उसका कार्यान्वयन और हुक कार्य थोड़ा असुरक्षित है। मैं आपसे अपने लिखने की php ऐप की प्रकृति के बारे में अधिक जानकारी देने के लिए कहूँगा, और आप प्लग इन को कैसे देख सकते हैं।

मेरी ओर से kdeloach को +1।


25

यहां एक दृष्टिकोण है जिसका मैंने उपयोग किया है, यह क्यूटी सिग्नल / स्लॉट तंत्र, एक प्रकार का ऑब्जर्वर पैटर्न से कॉपी करने का प्रयास है। वस्तुएं संकेतों का उत्सर्जन कर सकती हैं। सिस्टम में प्रत्येक सिग्नल की एक आईडी होती है - यह प्रेषक के आईडी + ऑब्जेक्ट नाम से बना होता है। प्रत्येक सिग्नल को रिसीवर्स में बांधा जा सकता है, जो कि बस एक "कॉल करने योग्य" होता है। आप सिग्नल प्राप्त करने के लिए किसी बस क्लास का उपयोग करते हैं, ताकि उन्हें प्राप्त करने में रुचि रखने वाले किसी भी व्यक्ति को संकेत मिल सके। ऐसा होता है, आप सिग्नल भेजते हैं। नीचे और उदाहरण कार्यान्वयन है

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

?>

18

मेरा मानना ​​है कि जेफ की सलाह का पालन करना सबसे आसान तरीका होगा और मौजूदा कोड के इर्द-गिर्द एक नजर होगी। Wordpress, Drupal, Joomla और अन्य जाने-माने PHP-आधारित CMS को देखने की कोशिश करें कि उनके API हुक कैसे दिखते और महसूस होते हैं। इस तरह से आप उन विचारों को भी प्राप्त कर सकते हैं जिनके बारे में आपने पहले नहीं सोचा होगा कि चीजों को थोड़ा अधिक रगड़ें।

एक अधिक प्रत्यक्ष उत्तर सामान्य फाइलों को लिखने के लिए होगा कि वे अपनी फ़ाइल में "__ce" को शामिल करेंगे जो कि उस उपयोगिता को प्रदान करेगा जिसकी उन्हें आवश्यकता होगी। इसे श्रेणियों में तोड़ दिया जाएगा और एक मेसिव "हुकसेफ" फाइल में प्रदान नहीं किया जाएगा। हालांकि सावधान रहें, क्योंकि जो कुछ हो रहा है, वह यह है कि जिन फाइलों में वे शामिल हैं, उनमें अधिक से अधिक निर्भरता और कार्यक्षमता में सुधार होता है। API निर्भरता कम रखने का प्रयास करें। IE उन्हें शामिल करने के लिए कम फ़ाइलें।


मैं DokuWiki को उन सिस्टमों की सूची में शामिल करूँगा जिन्हें आप देख सकते हैं। इसमें एक अच्छा ईवेंट सिस्टम है जो एक समृद्ध प्लगइन पारिस्थितिकी तंत्र के लिए अनुमति देता है।
चिबॉर्ग

15

याहू पर मैट ज़ैंड्सट्रा द्वारा स्टिकबैक नाम से एक साफ-सुथरी परियोजना है जो PHP में प्लगइन्स को संभालने के लिए बहुत काम करती है।

यह एक प्लगइन क्लास के इंटरफ़ेस को लागू करता है, एक कमांड लाइन इंटरफ़ेस का समर्थन करता है और उठना और चलाना बहुत मुश्किल नहीं है - खासकर यदि आप इसके बारे में कवर कहानी को PHP वास्तुकार पत्रिका में पढ़ते हैं ।


11

अच्छी सलाह यह देखने के लिए है कि अन्य परियोजनाओं ने कैसे किया है। प्लग इन स्थापित करने के लिए कई कॉल और सेवाओं के लिए पंजीकृत उनका "नाम" (जैसे वर्डप्रेस करता है) तो आपके पास अपने कोड में "अंक" हैं जहां आप एक फ़ंक्शन को कॉल करते हैं जो पंजीकृत श्रोताओं की पहचान करता है और उन्हें निष्पादित करता है। एक मानक OO डिजाइन संरक्षक ऑब्जर्वर पैटर्न है , जो एक सही मायने में ऑब्जेक्ट ओरिएंटेड PHP सिस्टम में लागू करने के लिए एक अच्छा विकल्प होगा।

Zend फ्रेमवर्क कई hooking तरीकों का उपयोग करता है, और बहुत अच्छी तरह से architected है। यह देखने के लिए एक अच्छी प्रणाली होगी।


8

मुझे आश्चर्य है कि यहाँ अधिकांश उत्तर उन प्लगइन्स के बारे में लगते हैं जो वेब अनुप्रयोग के लिए स्थानीय हैं, अर्थात, स्थानीय वेब सर्वर पर चलने वाले प्लगइन्स।

यदि आप प्लगइन्स को एक अलग - दूरस्थ - सर्वर पर चलाना चाहते हैं तो क्या होगा? ऐसा करने का सबसे अच्छा तरीका एक फॉर्म प्रदान करना होगा जो आपको अलग-अलग URL को परिभाषित करने की अनुमति देता है जो कि आपके आवेदन में होने वाली विशेष घटनाओं को कहते हैं।

अलग-अलग घटनाओं के आधार पर होने वाली घटना के आधार पर अलग-अलग जानकारी भेजी जाएगी।

इस तरह, आप बस उस URL पर एक CURL कॉल करेंगे जो आपके एप्लिकेशन को प्रदान किया गया है (उदाहरण के लिए https) जहां दूरस्थ सर्वर आपके एप्लिकेशन द्वारा भेजी गई जानकारी के आधार पर कार्य कर सकते हैं।

इससे दो लाभ मिलते हैं:

  1. आपको अपने स्थानीय सर्वर (सुरक्षा) पर किसी भी कोड को होस्ट करने की आवश्यकता नहीं है
  2. कोड दूरस्थ सर्वर (एक्स्टेंसिबिलिटी) पर विभिन्न भाषाओं में फिर PHP (पोर्टेबिलिटी) पर हो सकता है

8
यह "प्लगइन" सिस्टम की तुलना में "पुश एपीआई" का अधिक है - आप चयनित घटनाओं की अधिसूचना प्राप्त करने के लिए अन्य सेवाओं के लिए एक तरीका प्रदान कर रहे हैं। आम तौर पर "प्लगइन्स" का मतलब यह है कि आप एप्लिकेशन इंस्टॉल कर सकते हैं और फिर अपने उद्देश्यों के लिए उसके व्यवहार को अनुकूलित करने के लिए कार्यक्षमता जोड़ सकते हैं, जिसके लिए प्लगइन को स्थानीय रूप से चलाना आवश्यक है - या कम से कम सुरक्षित और कुशल 2-रास्ता संचार प्रदान करना है जानकारी के लिए आवेदन सिर्फ यह नहीं ले से यह। दो विशेषताएं कुछ हद तक अलग हैं, और कई मामलों के लिए एक "फ़ीड" (जैसे आरएसएस, iCal) एक पुश एपीआई का एक सरल विकल्प है।
IMSoP
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.