मैं "फ्लाई पर" किसी ऑब्जेक्ट में एक नई विधि कैसे जोड़ूं?
$me= new stdClass;
$me->doSomething=function ()
{
echo 'I\'ve done something';
};
$me->doSomething();
//Fatal error: Call to undefined method stdClass::doSomething()
मैं "फ्लाई पर" किसी ऑब्जेक्ट में एक नई विधि कैसे जोड़ूं?
$me= new stdClass;
$me->doSomething=function ()
{
echo 'I\'ve done something';
};
$me->doSomething();
//Fatal error: Call to undefined method stdClass::doSomething()
जवाबों:
आप इसके लिए दोहन कर सकते हैं __call
:
class Foo
{
public function __call($method, $args)
{
if (isset($this->$method)) {
$func = $this->$method;
return call_user_func_array($func, $args);
}
}
}
$foo = new Foo();
$foo->bar = function () { echo "Hello, this function is added at runtime"; };
$foo->bar();
array_unshift($args, $this);
मेथड कॉल से पहले करें, ताकि फ़ंक्शन को ऑब्जेक्ट को बिना स्पष्ट रूप से बाँधने के लिए संदर्भ मिल जाए ...
PHP 7 के साथ आप अनाम कक्षाओं का उपयोग कर सकते हैं
$myObject = new class {
public function myFunction(){}
};
$myObject->myFunction();
क्रम में नए तरीकों को जोड़ने की अनुमति देने के लिए केवल __call का उपयोग करने से बड़ी खामी यह है कि उन तरीकों से $ इस उदाहरण संदर्भ का उपयोग नहीं किया जा सकता है। सब कुछ बहुत अच्छा काम करता है, जब तक कि जोड़े गए तरीके कोड में इस का उपयोग न करें।
class AnObj extends stdClass
{
public function __call($closure, $args)
{
return call_user_func_array($this->{$closure}, $args);
}
}
$a=new AnObj();
$a->color = "red";
$a->sayhello = function(){ echo "hello!";};
$a->printmycolor = function(){ echo $this->color;};
$a->sayhello();//output: "hello!"
$a->printmycolor();//ERROR: Undefined variable $this
इस समस्या को हल करने के लिए आप इस तरह से पैटर्न को फिर से लिख सकते हैं
class AnObj extends stdClass
{
public function __call($closure, $args)
{
return call_user_func_array($this->{$closure}->bindTo($this),$args);
}
public function __toString()
{
return call_user_func($this->{"__toString"}->bindTo($this));
}
}
इस तरह आप नए तरीकों को जोड़ सकते हैं जो उदाहरण संदर्भ का उपयोग कर सकते हैं
$a=new AnObj();
$a->color="red";
$a->sayhello = function(){ echo "hello!";};
$a->printmycolor = function(){ echo $this->color;};
$a->sayhello();//output: "hello!"
$a->printmycolor();//output: "red"
अपडेट: यहां दिखाए गए दृष्टिकोण में एक बड़ी कमी है: नया फ़ंक्शन वर्ग का पूरी तरह से योग्य सदस्य नहीं है;
$this
इस तरह से लागू होने पर विधि में मौजूद नहीं है। इसका मतलब है कि यदि आप ऑब्जेक्ट उदाहरण से डेटा या फ़ंक्शन के साथ काम करना चाहते हैं तो आपको ऑब्जेक्ट को फ़ंक्शन के पैरामीटर के रूप में पास करना होगा! इसके अलावा, आप का उपयोग करने में सक्षम नहीं होगाprivate
याprotected
इन कार्यों से वर्ग के सदस्य।
नए अनाम कार्यों का उपयोग करके अच्छा प्रश्न और चतुर विचार!
दिलचस्प है, यह काम करता है: बदलें
$me->doSomething(); // Doesn't work
फ़ंक्शन पर call_user_func द्वारा :
call_user_func($me->doSomething); // Works!
क्या काम नहीं करता है "सही" तरीका है:
call_user_func(array($me, "doSomething")); // Doesn't work
अगर इस तरह से बुलाया जाए, तो PHP को वर्ग परिभाषा में घोषित की जाने वाली विधि की आवश्यकता होती है।
क्या यह private
/ public
/ हैprotected
दृश्यता मुद्दा है?
अपडेट: नहीं। फ़ंक्शन को कक्षा के भीतर से भी सामान्य तरीके से कॉल करना असंभव है, इसलिए यह दृश्यता मुद्दा नहीं है। वास्तविक कार्य को पास करनाcall_user_func()
करना एकमात्र तरीका है जिससे मैं यह काम कर सकता हूं।
आप किसी सरणी में फ़ंक्शन भी सहेज सकते हैं:
<?php
class Foo
{
private $arrayFuncs=array();// array of functions
//
public function addFunc($name,$function){
$this->arrayFuncs[$name] = $function;
}
//
public function callFunc($namefunc,$params=false){
if(!isset($this->arrayFuncs[$namefunc])){
return 'no function exist';
}
if(is_callable($this->arrayFuncs[$namefunc])){
return call_user_func($this->arrayFuncs[$namefunc],$params);
}
}
}
$foo = new Foo();
//Save function on array variable with params
$foo->addFunc('my_function_call',function($params){
return array('data1'=>$params['num1'],'data2'=>'qwerty','action'=>'add');
});
//Save function on array variable
$foo->addFunc('my_function_call2',function(){
return 'a simple function';
});
//call func 1
$data = $foo->callFunc('my_function_call',array('num1'=>1224343545));
var_dump($data);
//call func 2
$data = $foo->callFunc('my_function_call2');
var_dump($data);
?>
यह स्पष्ट करने के लिए कि यह कैसे किया जाता है, यह देखने के लिए, आप मेरे PHP माइक्रो-फ्रेमवर्क, हेलसीओन को देख सकते हैं, जो लिथुब पर उपलब्ध है । यह काफी छोटा है कि आपको बिना किसी समस्या के इसका पता लगाने में सक्षम होना चाहिए - हैल्सी क्लोक्लास मुंगेर वर्ग पर ध्यान केंद्रित करें।
__call
समाधान के बिना , आप इस तरह से बाध्य bindTo
विधि के साथ कॉल करने के लिए (PHP> = 5.4) का उपयोग कर सकते हैं :$this
$me
call_user_func($me->doSomething->bindTo($me, null));
पूरी स्क्रिप्ट इस तरह दिख सकती है:
$me = new stdClass;
// Property for proving that the method has access to the above object:
$me->message = "I\'ve done something";
$me->doSomething = function () {
echo $this->message;
};
call_user_func($me->doSomething->bindTo($me)); // "I've done something"
वैकल्पिक रूप से, आप बाउंड फ़ंक्शन को एक वैरिएबल पर असाइन कर सकते हैं, और फिर इसे बिना कॉल कर सकते हैं call_user_func
:
$f = $me->doSomething->bindTo($me);
$f();
स्टैकओवरफ़्लो पर एक समान पोस्ट है है जो स्पष्ट करता है कि यह केवल कुछ डिज़ाइन पैटर्न के कार्यान्वयन के माध्यम से प्राप्त करने योग्य है।
एक अन्य तरीका क्लासीकिट , एक प्रयोगात्मक php एक्सटेंशन के उपयोग के माध्यम से है । (पोस्ट में भी)
हाँ यह परिभाषित करने के बाद एक PHP वर्ग के लिए एक विधि जोड़ना संभव है। आप क्लासकिट का उपयोग करना चाहते हैं, जो "प्रयोगात्मक" विस्तार है। ऐसा प्रतीत होता है कि यह एक्सटेंशन डिफ़ॉल्ट रूप से सक्षम नहीं है, इसलिए यह इस बात पर निर्भर करता है कि क्या आप एक कस्टम PHP बाइनरी संकलित कर सकते हैं या विंडोज़ पर PHP DLL लोड कर सकते हैं (उदाहरण के लिए Dreamhost कस्टम PHP बायनेरिज़ की अनुमति देता है, और वे सेटअप करना बहुत आसान है )।
karim79 उत्तर काम करता है, हालांकि यह विधि गुणों के अंदर अनाम कार्यों को संग्रहीत करता है और उसकी घोषणाएं उसी नाम के मौजूदा गुणों को अधिलेखित कर सकती हैं या काम नहीं करती हैं यदि मौजूदा संपत्ति private
घातक त्रुटि का कारण बन रही है तो मुझे लगता है कि उन्हें अलग सरणी में संग्रहीत करना और सेटर का उपयोग करना क्लीनर समाधान है। विधि इंजेक्टर सेटर को हर वस्तु में स्वचालित रूप से लक्षणों का उपयोग करके जोड़ा जा सकता है ।
पीएस बेशक यह हैक है जिसका उपयोग नहीं किया जाना चाहिए क्योंकि यह एसओएलआईडी के खुले बंद सिद्धांत का उल्लंघन करता है।
class MyClass {
//create array to store all injected methods
private $anon_methods = array();
//create setter to fill array with injected methods on runtime
public function inject_method($name, $method) {
$this->anon_methods[$name] = $method;
}
//runs when calling non existent or private methods from object instance
public function __call($name, $args) {
if ( key_exists($name, $this->anon_methods) ) {
call_user_func_array($this->anon_methods[$name], $args);
}
}
}
$MyClass = new MyClass;
//method one
$print_txt = function ($text) {
echo $text . PHP_EOL;
};
$MyClass->inject_method("print_txt", $print_txt);
//method
$add_numbers = function ($n, $e) {
echo "$n + $e = " . ($n + $e);
};
$MyClass->inject_method("add_numbers", $add_numbers);
//Use injected methods
$MyClass->print_txt("Hello World");
$MyClass->add_numbers(5, 10);