ফ্লাইয়ের পিএইচপি অবজেক্টে কীভাবে একটি নতুন পদ্ধতি যুক্ত করবেন?


101

"ফ্লাইয়ের উপরে" কোনও অবজেক্টে কীভাবে আমি একটি নতুন পদ্ধতি যুক্ত করব?

$me= new stdClass;
$me->doSomething=function ()
 {
    echo 'I\'ve done something';
 };
$me->doSomething();

//Fatal error: Call to undefined method stdClass::doSomething()

এটি কি বিশেষত কোনও ক্লাস ব্যবহার না করে?
থোমাস-পিটার

4
আপনি __call ব্যবহার করতে পারেন। তবে দয়া করে __call ব্যবহার করবেন না। আপনার কোডটি অপঠনযোগ্য এবং অবিশ্বাস্য করে তোলার জন্য কোনও বস্তুর আচরণ পরিবর্তনশীল একটি খুব সহজ উপায়।
গর্ডনএম

উত্তর:


96

আপনি এর __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();

6
একটি বাস্তব বিশ্বের প্রকল্প আইএমওতে খুব, খুব চালাক তবে ক্লডজি! (এবং +1 নামবিহীন ডাউনওয়টারকে মোকাবেলা করার জন্য)
পেক্কা

4
@ পেপকা - তবে কীভাবে, ঠিক এটি ক্লডজি? অবশ্যই, আমি বলছি না যে আমি পিএইচপি-তে রানটাইম চলাকালীন কোনও অবধি বাড়িয়ে তোলার বিশেষজ্ঞ, তবে আমি সত্যই বলতে পারি না যে আমি এটিতে খুব বেশি ভুল দেখছি। (সম্ভবত আমার কেবল স্বাদ খারাপ)
karim79

16
আমি যদি এটির পাশাপাশি একটি আইস_এলএল চেক যুক্ত করব (সুতরাং আপনি ঘটনাক্রমে কোনও স্ট্রিং কল করার চেষ্টা করবেন না)। এবং যদি আপনি কোনও পদ্ধতি খুঁজে না পান তবে একটি ব্যাডমেথডক্যালএক্সেপশন () নিক্ষেপ করুন, যাতে আপনার এটি ফিরে না আসে এবং মনে হয় এটি সফলভাবে ফিরে এসেছে। এছাড়াও, ফাংশনটির প্রথম প্যারামটি নিজেই বস্তুটি তৈরি করুন এবং তারপরে array_unshift($args, $this);পদ্ধতি কলের আগে একটি করুন, যাতে ফাংশনটি স্পষ্টভাবে এটির সাথে আবদ্ধ হওয়ার প্রয়োজন ছাড়াই অবজেক্টের একটি রেফারেন্স পায় ...
ইরকম্যাক্সেল

4
আমি মনে করি লোকেরা বিন্দুটি নিখোঁজ করছে, আসল প্রয়োজনীয়তা ছিল একটি শ্রেণিবিহীন বস্তু ব্যবহার করা।
থোমাস-পিটার

4
আমি আসলে আপনাকে পরামর্শ দেব যে আপনি সম্পূর্ণরূপে আইসেট () পরীক্ষাটি সরিয়ে ফেলুন কারণ যদি সেই ফাংশনটি সংজ্ঞায়িত না করা হয় তবে আপনি সম্ভবত নিঃশব্দে ফিরে আসতে চান না।
অ্যালেক্সিস উইলক


22

রানটাইমে নতুন পদ্ধতি যুক্ত করার অনুমতি দেওয়ার জন্য কেবল __call ব্যবহারের ক্ষেত্রে প্রধান অসুবিধা রয়েছে যে এই পদ্ধতিগুলি instance এই উদাহরণটি উল্লেখটি ব্যবহার করতে পারে না। কোডটিতে এগুলি যুক্ত পদ্ধতিগুলি ব্যবহার না করা অবধি সবকিছু দুর্দান্ত কাজ করে।

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"

12

আপডেট: এখানে দেখানো পদ্ধতির একটি বড় ঘাটতি রয়েছে: নতুন ফাংশনটি শ্রেণীর পুরোপুরি যোগ্যতাসম্পন্ন সদস্য নয়; $thisএইভাবে অনুরোধ করার সময় পদ্ধতিতে উপস্থিত নেই। এর অর্থ হ'ল আপনি যদি কোনও অবজেক্টের উদাহরণ থেকে ডেটা বা ফাংশন নিয়ে কাজ করতে চান তবে আপনাকে প্যারামিটার হিসাবে ফাংশনে ফাংশনটি পাস করতে হবে! এছাড়াও, আপনি এই ক্রিয়াকলাপ থেকে ক্লাসের সদস্যদের অ্যাক্সেস করতে পারবেন না privateবা protectedসদস্য হতে পারবেন না ।

নতুন বেনামী ফাংশনগুলি ব্যবহার করে ভাল প্রশ্ন এবং চতুর ধারণা!

মজার বিষয়, এটি কাজ করে: প্রতিস্থাপন করুন

$me->doSomething();    // Doesn't work

ফাংশনে নিজেই কল_উসার_ফুনক দ্বারা :

call_user_func($me->doSomething);    // Works!

যা কাজ করে না তা হ'ল "সঠিক" উপায়:

call_user_func(array($me, "doSomething"));   // Doesn't work

যদি সেভাবে বলা হয়, পিএইচপি ক্লাস সংজ্ঞাতে ঘোষিত পদ্ধতি প্রয়োজন।

এটি কি private/ public/ protectedদৃশ্যমানতার সমস্যা?

আপডেট: না। এমনকি ক্লাসের মধ্যে থেকেই ফাংশনটিকে স্বাভাবিক উপায়ে কল করা অসম্ভব, সুতরাং এটি দৃশ্যমানতার সমস্যা নয়। আসল ফাংশনটির কাছে call_user_func()যাওয়া কেবলমাত্র এই উপায়টিই মনে হয়।


2

আপনি একটি অ্যারেতে ফাংশনগুলিও সংরক্ষণ করতে পারেন:

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

1

ওয়াল দিয়ে এটি কীভাবে করা যায় তা দেখতে, আপনি আমার পিএইচপি মাইক্রো-ফ্রেমওয়ার্ক, হালসিওন, যা গিথুব এ উপলব্ধ তা একবার দেখে নিতে পারেন । এটি যথেষ্ট ছোট যে আপনার কোনও সমস্যা ছাড়াই এটি বের করতে সক্ষম হওয়া উচিত - হালসিওনক্লাসমঙ্গার ক্লাসে মনোনিবেশ করুন।


আমি ধরে নিচ্ছি (এবং আমার কোডটিও তাই) যে আপনি পিএইচপি <5.3.0 এ চলছে এবং সুতরাং এই কাজটি করার জন্য ক্লডেজ এবং হ্যাকের প্রয়োজন।
আইভান


এটি সম্ভবত, এখানে প্রায় কিছু গণ ডাউন ডাউন হয়। তবে আপনি যা করেছেন তার উপর আপনি কিছুটা বিশদভাবে বলতে চান? আপনি দেখতে পাচ্ছেন, এটি এখনও একটি নির্দিষ্ট উত্তর সহ একটি আকর্ষণীয় প্রশ্ন।
পেক্কা

1

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

0

একটি আছে স্ট্যাকওভারফ্লোতে অনুরূপ পোস্ট রয়েছে যা পরিষ্কার করে দেয় যে এটি কেবল নির্দিষ্ট নকশার নিদর্শনগুলির প্রয়োগের মাধ্যমেই অর্জনযোগ্য।

ক্লাসকিট ব্যবহারের মাধ্যমে অন্য একমাত্র উপায় হ'ল একটি পরীক্ষামূলক পিএইচপি এক্সটেনশন। (পোস্টেও)

হ্যাঁ এটি নির্ধারণের পরে কোনও পিএইচপি ক্লাসে কোনও পদ্ধতি যুক্ত করা সম্ভব। আপনি ক্লাসকিটটি ব্যবহার করতে চান, এটি একটি "পরীক্ষামূলক" এক্সটেনশন। এটি প্রদর্শিত হয় যে এই এক্সটেনশনটি যদিও ডিফল্টরূপে সক্ষম করা হয়নি, তাই এটি উইন্ডোতে যদি আপনি কোনও কাস্টম পিএইচপি বাইনারি সংকলন করতে পারেন বা পিএইচপি ডিএলএলগুলি লোড করতে পারেন তা নির্ভর করে (উদাহরণস্বরূপ ড্রিমহোস্ট কাস্টম পিএইচপি বাইনারিগুলিকে অনুমতি দেয় এবং সেগুলি সেটআপ করা বেশ সহজ হয়) )।


0

কারিম answer৯ উত্তরটি কাজ করে তবে এটি পদ্ধতির বৈশিষ্ট্যগুলির মধ্যে বেনাম ফাংশনগুলি সংরক্ষণ করে এবং তার ঘোষণাগুলি একই নামের বিদ্যমান বৈশিষ্ট্যগুলিকে ওভাররাইট করতে পারে বা বিদ্যমান সম্পত্তি যদি privateমারাত্মক ত্রুটিযুক্ত অব্যবস্থার কারণ হয়ে থাকে তবে তা কাজ করে না বলে আমি মনে করি তাদের আলাদা অ্যারেতে সংরক্ষণ করা এবং সেটার ব্যবহার করা পরিষ্কার সমাধান solution পদ্ধতি ইনজেক্টর সেটার বৈশিষ্ট্যগুলি ব্যবহার করে প্রতিটি বস্তুতে স্বয়ংক্রিয়ভাবে যুক্ত হতে পারে ।

পিএস অবশ্যই এটি হ্যাক যা এটি ব্যবহার করা উচিত নয় কারণ এটি সলাইডের খোলা বন্ধ নীতি লঙ্ঘন করে।

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

যদি এটি হ্যাক হয় যা ব্যবহার করা উচিত নয়, তবে এটি উত্তর হিসাবে পোস্ট করা উচিত নয়।
miken32
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.