मैं PHP में एक नया वेब एप्लिकेशन शुरू कर रहा हूं और इस समय के आसपास मैं कुछ ऐसा बनाना चाहता हूं जिसे लोग प्लगइन इंटरफ़ेस का उपयोग करके बढ़ा सकते हैं।
अपने कोड में 'हुक' लिखने के बारे में कैसे जाना जाता है ताकि प्लगइन्स विशिष्ट घटनाओं से जुड़ सकें?
मैं PHP में एक नया वेब एप्लिकेशन शुरू कर रहा हूं और इस समय के आसपास मैं कुछ ऐसा बनाना चाहता हूं जिसे लोग प्लगइन इंटरफ़ेस का उपयोग करके बढ़ा सकते हैं।
अपने कोड में 'हुक' लिखने के बारे में कैसे जाना जाता है ताकि प्लगइन्स विशिष्ट घटनाओं से जुड़ सकें?
जवाबों:
आप एक ऑब्जर्वर पैटर्न का उपयोग कर सकते हैं। इसे पूरा करने का एक सरल कार्यात्मक तरीका:
<?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 में एक प्लगइन सिस्टम को पूरा करने का सिर्फ एक तरीका है। बेहतर विकल्प हैं, मेरा सुझाव है कि आप अधिक जानकारी के लिए वर्डप्रेस प्रलेखन की जांच करें।
Mediator Pattern
। सच्चे पर्यवेक्षक विशुद्ध रूप से अधिसूचना हैं, कोई संदेश पास या सशर्त अधिसूचना नहीं है (और न ही सूचनाओं को नियंत्रित करने के लिए एक केंद्रीय प्रबंधक है)। यह उत्तर को गलत नहीं बनाता है , लेकिन यह गलत नाम से लोगों को फोन करने से रोकने के लिए ध्यान दिया जाना चाहिए ...
तो मान लें कि आप ऑब्जर्वर पैटर्न नहीं चाहते हैं क्योंकि यह आवश्यक है कि आप सुनने के कार्य को संभालने के लिए अपने क्लास के तरीकों को बदलें, और कुछ सामान्य चाहते हैं। और मान लें कि आप 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 में हम प्लगगबल वर्ग से प्राप्त वस्तु के साथ खिलवाड़ करते हैं, न कि डॉग क्लास से। इसके बजाय, हम प्लगइन क्लास को हमारे लिए डॉग ऑब्जेक्ट पर "टचिंग" करने देते हैं। (यदि यह किसी प्रकार का डिज़ाइन पैटर्न है जिसके बारे में मुझे नहीं पता है - कृपया मुझे बताएं।)
हुक और श्रोता विधि सबसे अधिक इस्तेमाल किया है, लेकिन वहाँ अन्य बातों के आप कर सकते हैं। आपके ऐप के आकार पर निर्भर करता है, और जो आपके कोड को देखने की अनुमति देने के लिए जा रहा है (क्या यह FOSS स्क्रिप्ट है, या घर में कुछ होने जा रहा है) बहुत प्रभावित करेगा कि आप प्लग इन की अनुमति कैसे देना चाहते हैं।
kdeloach का एक अच्छा उदाहरण है, लेकिन उसका कार्यान्वयन और हुक कार्य थोड़ा असुरक्षित है। मैं आपसे अपने लिखने की php ऐप की प्रकृति के बारे में अधिक जानकारी देने के लिए कहूँगा, और आप प्लग इन को कैसे देख सकते हैं।
मेरी ओर से kdeloach को +1।
यहां एक दृष्टिकोण है जिसका मैंने उपयोग किया है, यह क्यूटी सिग्नल / स्लॉट तंत्र, एक प्रकार का ऑब्जर्वर पैटर्न से कॉपी करने का प्रयास है। वस्तुएं संकेतों का उत्सर्जन कर सकती हैं। सिस्टम में प्रत्येक सिग्नल की एक आईडी होती है - यह प्रेषक के आईडी + ऑब्जेक्ट नाम से बना होता है। प्रत्येक सिग्नल को रिसीवर्स में बांधा जा सकता है, जो कि बस एक "कॉल करने योग्य" होता है। आप सिग्नल प्राप्त करने के लिए किसी बस क्लास का उपयोग करते हैं, ताकि उन्हें प्राप्त करने में रुचि रखने वाले किसी भी व्यक्ति को संकेत मिल सके। ऐसा होता है, आप सिग्नल भेजते हैं। नीचे और उदाहरण कार्यान्वयन है
<?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();
?>
मेरा मानना है कि जेफ की सलाह का पालन करना सबसे आसान तरीका होगा और मौजूदा कोड के इर्द-गिर्द एक नजर होगी। Wordpress, Drupal, Joomla और अन्य जाने-माने PHP-आधारित CMS को देखने की कोशिश करें कि उनके API हुक कैसे दिखते और महसूस होते हैं। इस तरह से आप उन विचारों को भी प्राप्त कर सकते हैं जिनके बारे में आपने पहले नहीं सोचा होगा कि चीजों को थोड़ा अधिक रगड़ें।
एक अधिक प्रत्यक्ष उत्तर सामान्य फाइलों को लिखने के लिए होगा कि वे अपनी फ़ाइल में "__ce" को शामिल करेंगे जो कि उस उपयोगिता को प्रदान करेगा जिसकी उन्हें आवश्यकता होगी। इसे श्रेणियों में तोड़ दिया जाएगा और एक मेसिव "हुकसेफ" फाइल में प्रदान नहीं किया जाएगा। हालांकि सावधान रहें, क्योंकि जो कुछ हो रहा है, वह यह है कि जिन फाइलों में वे शामिल हैं, उनमें अधिक से अधिक निर्भरता और कार्यक्षमता में सुधार होता है। API निर्भरता कम रखने का प्रयास करें। IE उन्हें शामिल करने के लिए कम फ़ाइलें।
याहू पर मैट ज़ैंड्सट्रा द्वारा स्टिकबैक नाम से एक साफ-सुथरी परियोजना है जो PHP में प्लगइन्स को संभालने के लिए बहुत काम करती है।
यह एक प्लगइन क्लास के इंटरफ़ेस को लागू करता है, एक कमांड लाइन इंटरफ़ेस का समर्थन करता है और उठना और चलाना बहुत मुश्किल नहीं है - खासकर यदि आप इसके बारे में कवर कहानी को PHP वास्तुकार पत्रिका में पढ़ते हैं ।
अच्छी सलाह यह देखने के लिए है कि अन्य परियोजनाओं ने कैसे किया है। प्लग इन स्थापित करने के लिए कई कॉल और सेवाओं के लिए पंजीकृत उनका "नाम" (जैसे वर्डप्रेस करता है) तो आपके पास अपने कोड में "अंक" हैं जहां आप एक फ़ंक्शन को कॉल करते हैं जो पंजीकृत श्रोताओं की पहचान करता है और उन्हें निष्पादित करता है। एक मानक OO डिजाइन संरक्षक ऑब्जर्वर पैटर्न है , जो एक सही मायने में ऑब्जेक्ट ओरिएंटेड PHP सिस्टम में लागू करने के लिए एक अच्छा विकल्प होगा।
Zend फ्रेमवर्क कई hooking तरीकों का उपयोग करता है, और बहुत अच्छी तरह से architected है। यह देखने के लिए एक अच्छी प्रणाली होगी।
मुझे आश्चर्य है कि यहाँ अधिकांश उत्तर उन प्लगइन्स के बारे में लगते हैं जो वेब अनुप्रयोग के लिए स्थानीय हैं, अर्थात, स्थानीय वेब सर्वर पर चलने वाले प्लगइन्स।
यदि आप प्लगइन्स को एक अलग - दूरस्थ - सर्वर पर चलाना चाहते हैं तो क्या होगा? ऐसा करने का सबसे अच्छा तरीका एक फॉर्म प्रदान करना होगा जो आपको अलग-अलग URL को परिभाषित करने की अनुमति देता है जो कि आपके आवेदन में होने वाली विशेष घटनाओं को कहते हैं।
अलग-अलग घटनाओं के आधार पर होने वाली घटना के आधार पर अलग-अलग जानकारी भेजी जाएगी।
इस तरह, आप बस उस URL पर एक CURL कॉल करेंगे जो आपके एप्लिकेशन को प्रदान किया गया है (उदाहरण के लिए https) जहां दूरस्थ सर्वर आपके एप्लिकेशन द्वारा भेजी गई जानकारी के आधार पर कार्य कर सकते हैं।
इससे दो लाभ मिलते हैं: