PHP __get और __set जादू के तरीके


85

जब तक मैं पूरी तरह से गलत नहीं हूँ, __getऔर __setतरीकों को ओवरलोडिंग की अनुमति दी जानी चाहिए → getऔर set

उदाहरण के लिए, निम्नलिखित कथनों को __getविधि को लागू करना चाहिए :

echo $foo->bar;
$var = $foo->bar;

और निम्नलिखित __setविधि का उपयोग करना चाहिए :

$foo->bar = 'test';

यह मेरे कोड में काम नहीं कर रहा था, और इस सरल उदाहरण के साथ प्रतिलिपि प्रस्तुत करने योग्य है:

class foo {

    public $bar;
    public function __get($name) {

        echo "Get:$name";
        return $this->$name;
    }

    public function __set($name, $value) {

        echo "Set:$name to $value";
        $this->$name = $value;
    }
}


$foo = new foo();

echo $foo->bar;
$foo->bar = 'test';

echo "[$foo->bar]";

यह केवल परिणाम में है:

[test]

die()वहां कुछ कॉल डालने से पता चलता है कि यह बिल्कुल भी नहीं है।

अभी के लिए, मैंने सिर्फ यह कहा है कि स्क्रू करें, और मैं मैन्युअल रूप से इसका उपयोग कर रहा हूं, __getजहां अभी इसकी आवश्यकता है, लेकिन यह बहुत गतिशील नहीं है और ज्ञान की आवश्यकता है कि 'अतिभारित' कोड वास्तव में कॉल नहीं किया जा रहा है जब तक कि विशेष रूप से कॉल न किया जाए। मैं यह जानना चाहता हूं कि क्या यह या तो उस तरीके से काम करने वाला नहीं है जिस तरह से मैंने समझा है कि यह काम करना चाहिए या क्यों नहीं।

यह चल रहा है php 5.3.3

जवाबों:


164

__get, __set, __callऔर __callStaticलागू कर रहे हैं जब विधि या संपत्ति दुर्गम है। आपकी $barजनता सार्वजनिक है और दुर्गम नहीं है।

मैनुअल में प्रॉपर्टी ओवरलोडिंग पर अनुभाग देखें :

  • __set() दुर्गम गुणों के लिए डेटा लिखते समय चलाया जाता है।
  • __get() दुर्गम गुणों से डेटा पढ़ने के लिए उपयोग किया जाता है।

जादू के तरीके गेटर्स और सेटर के लिए विकल्प नहीं हैं। वे आपको केवल विधि कॉल या संपत्ति पहुंच को संभालने की अनुमति देते हैं जो अन्यथा एक त्रुटि के परिणामस्वरूप होगा। जैसे, त्रुटि से निपटने के लिए बहुत अधिक संबंधित हैं। यह भी ध्यान दें कि वे उचित गेट्टर और सेटर या प्रत्यक्ष विधि कॉल का उपयोग करने की तुलना में काफी धीमी हैं।


5
इस पर विस्तार करने के लिए, बस "सार्वजनिक $ बार" को हटा दें ताकि संपत्ति का कोई अस्तित्व न हो और यह एक आकर्षण की तरह काम करेगा।
स्टीफन मुलर

धन्यवाद। यही कारण है कि मुझे मैन्युअल रूप से पढ़ने और इसके बजाय कुछ लोगों के ब्लॉग उदाहरण को देखने के लिए नहीं मिलता है;) अभी भी PHP में सच्चे ऑपरेटर के ओवरलोडिंग की प्रतीक्षा कर रहा है, हालांकि।
एयरबियर


1
@Pooya इसलिए कि node$ फू की संपत्ति नहीं है, लेकिन की संपत्ति है doesNotExist। इसलिए, जब तक 'doNotExist' एक ऑब्जेक्ट नहीं है (जो __set को लागू करता है या जिसके पास सार्वजनिक संपत्ति है जिसे नोड कहा जाता है) यह काम नहीं करेगा।
टिवी

1
@ सभी को हाँ, यह करता है।
गॉर्डन

34

मैं सभी मूल्यों को संग्रहीत करने के लिए एक सरणी का उपयोग करने की सलाह दूंगा __set()

class foo {

    protected $values = array();

    public function __get( $key )
    {
        return $this->values[ $key ];
    }

    public function __set( $key, $value )
    {
        $this->values[ $key ] = $value;
    }

}

इस तरह से आप सुनिश्चित करते हैं, कि आप $valuesटक्करों से बचने के लिए चर को दूसरे तरीके से (नोट जो सुरक्षित है) तक नहीं पहुँचा सकते हैं ।


19

से पीएचपी मैनुअल :

  • __set () दुर्गम गुणों के डेटा लिखते समय चलाया जाता है।
  • __get () का उपयोग दुर्गम गुणों के डेटा को पढ़ने के लिए किया जाता है।

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


7

बेरी के उत्तर पर विस्तार करने के लिए, कि संरक्षित स्तर तक पहुँच स्थापित करने की अनुमति देता है __get और __set को स्पष्ट रूप से घोषित गुणों (जब कक्षा के बाहर एक्सेस किया जाता है, कम से कम) और गति काफी धीमी होने के साथ उपयोग करने की अनुमति देता है, मैं दूसरे प्रश्न से एक टिप्पणी उद्धृत करूंगा। इस विषय पर और वैसे भी इसका उपयोग करने के लिए एक मामला बनाएं:

मैं मानता हूँ कि __get एक कस्टम गेट फ़ंक्शन (समान कामों) को करने में अधिक धीमा है, यह 0.0124455 __get () के लिए समय है और यह 0.0024445 10000 लूप के बाद कस्टम गेट () के लिए है। - Melsi Nov 23 '12 22:32 सर्वश्रेष्ठ अभ्यास: PHP मैजिक मेथड्स __set और __get

मेल्सी के परीक्षणों के अनुसार, काफी धीमा लगभग 5 गुना धीमा है। यह निश्चित रूप से काफी धीमा है, लेकिन यह भी ध्यान दें कि परीक्षणों से पता चलता है कि आप अभी भी 10,000 बार लूप पुनरावृत्ति के लिए गिनती के समय में इस पद्धति के साथ एक संपत्ति तक पहुंच सकते हैं, लगभग 1/100 सेकंड में। यह वास्तविक रूप से निर्धारित और निर्धारित विधियों की तुलना में काफी धीमा है, और यह एक समझ है, लेकिन चीजों की भव्य योजना में, यहां तक ​​कि 5 गुना धीमी गति वास्तव में धीमी नहीं है।

ऑपरेशन का कंप्यूटिंग समय अभी भी नगण्य है और वास्तविक दुनिया के 99% अनुप्रयोगों में विचार करने लायक नहीं है। वास्तव में केवल तभी बचा जाना चाहिए जब आप वास्तव में एकल अनुरोध में 10,000 से अधिक बार संपत्तियों तक पहुंच बनाने जा रहे हों। यदि वे अपने अनुप्रयोगों को चालू रखने के लिए कुछ और सर्वरों को फेंक नहीं सकते, तो उच्च यातायात साइटें वास्तव में कुछ गलत कर रही हैं। एक उच्च ट्रैफ़िक साइट के पाद लेख पर एक एकल पंक्ति पाठ विज्ञापन जहाँ पहुँच दर एक समस्या बन जाती है वह संभवतः पाठ की उस पंक्ति के साथ 1,000 सर्वरों के फ़ार्म के लिए भुगतान कर सकता है। अंतिम उपयोगकर्ता कभी भी यह कहते हुए अपनी उंगलियों को टैप करने वाला नहीं है कि पेज को लोड करने के लिए इतना लंबा समय क्यों लग रहा है क्योंकि आपके आवेदन की संपत्ति का उपयोग सेकंड का दसवां हिस्सा लेता है।

मैं इसे एक .NET में पृष्ठभूमि से आने वाले डेवलपर के रूप में बोल रहा हूं, लेकिन उपभोक्ता को अदृश्य तरीके और सेट किए गए तरीके .NET के आविष्कार नहीं हैं। वे बस उनके बिना गुण नहीं हैं, और ये जादुई तरीके PHP के डेवलपर की कृपा के गुणों के अपने संस्करण को "गुणों" के लिए बुला रहे हैं। इसके अलावा, PHP के लिए विजुअल स्टूडियो एक्सटेंशन संरक्षित गुणों के साथ इंटेलीजेंस का समर्थन करता है, उस चाल को ध्यान में रखते हुए, मुझे लगता है। मैं इस तरह से जादू __get और __set विधियों का उपयोग करने वाले पर्याप्त डेवलपर्स के साथ सोचूंगा, PHP डेवलपर्स डेवलपर समुदाय को पूरा करने के लिए निष्पादन समय को ट्यून करेंगे।

संपादित करें: सिद्धांत रूप में, संरक्षित गुण ऐसा लगता था कि यह अधिकांश स्थिति में काम करेगा। व्यवहार में, यह पता चलता है कि बहुत बार आप अपने गेटर्स का उपयोग करना चाहते हैं और वर्ग की परिभाषा और विस्तारित कक्षाओं के भीतर गुणों तक पहुँचते समय बस जाते हैं। एक बेहतर समाधान अन्य वर्गों का विस्तार करते समय के लिए एक बेस क्लास और इंटरफ़ेस है, इसलिए आप बेस क्लास से कोड की कुछ पंक्तियों को लागू करने वाली क्लास में कॉपी कर सकते हैं। मैं अपनी परियोजना के आधार वर्ग के साथ थोड़ा अधिक कर रहा हूं, इसलिए मेरे पास अभी प्रदान करने के लिए कोई इंटरफ़ेस नहीं है, लेकिन यहां जादू की संपत्ति के साथ वर्गीय परिभाषा को छीन लिया गया है और गुणों को हटाने और स्थानांतरित करने के लिए प्रतिबिंब का उपयोग करके सेट किया गया है। एक संरक्षित सरणी:

/** Base class with magic property __get() and __set() support for defined properties. */
class Component {
    /** Gets the properties of the class stored after removing the original
     * definitions to trigger magic __get() and __set() methods when accessed. */
    protected $properties = array();

    /** Provides property get support. Add a case for the property name to
     * expand (no break;) or replace (break;) the default get method. When
     * overriding, call parent::__get($name) first and return if not null,
     * then be sure to check that the property is in the overriding class
     * before doing anything, and to implement the default get routine. */
    public function __get($name) {
        $caller = array_shift(debug_backtrace());
        $max_access = ReflectionProperty::IS_PUBLIC;
        if (is_subclass_of($caller['class'], get_class($this)))
            $max_access = ReflectionProperty::IS_PROTECTED;
        if ($caller['class'] == get_class($this))
            $max_access = ReflectionProperty::IS_PRIVATE;
        if (!empty($this->properties[$name])
            && $this->properties[$name]->class == get_class()
            && $this->properties[$name]->access <= $max_access)
            switch ($name) {
                default:
                    return $this->properties[$name]->value;
            }
    }

    /** Provides property set support. Add a case for the property name to
     * expand (no break;) or replace (break;) the default set method. When
     * overriding, call parent::__set($name, $value) first, then be sure to
     * check that the property is in the overriding class before doing anything,
     * and to implement the default set routine. */
    public function __set($name, $value) {
        $caller = array_shift(debug_backtrace());
        $max_access = ReflectionProperty::IS_PUBLIC;
        if (is_subclass_of($caller['class'], get_class($this)))
            $max_access = ReflectionProperty::IS_PROTECTED;
        if ($caller['class'] == get_class($this))
            $max_access = ReflectionProperty::IS_PRIVATE;
        if (!empty($this->properties[$name])
            && $this->properties[$name]->class == get_class()
            && $this->properties[$name]->access <= $max_access)
            switch ($name) {
                default:
                    $this->properties[$name]->value = $value;
            }
    }

    /** Constructor for the Component. Call first when overriding. */
    function __construct() {
        // Removing and moving properties to $properties property for magic
        // __get() and __set() support.
        $reflected_class = new ReflectionClass($this);
        $properties = array();
        foreach ($reflected_class->getProperties() as $property) {
            if ($property->isStatic()) { continue; }
            $properties[$property->name] = (object)array(
                'name' => $property->name, 'value' => $property->value
                , 'access' => $property->getModifier(), 'class' => get_class($this));
            unset($this->{$property->name}); }
        $this->properties = $properties;
    }
}

कोड में कोई भी बग होने पर मेरी क्षमा याचना।


मुझे 'पहुंच' => $ संपत्ति-> getModifier ()
macki

5

यह इसलिए है क्योंकि $ बार एक सार्वजनिक संपत्ति है।

$foo->bar = 'test';

ऊपर चलाने पर जादू की पद्धति को कॉल करने की कोई आवश्यकता नहीं है।

public $bar;अपनी कक्षा से हटाकर इसे ठीक करना चाहिए।


1

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

"मैजिक मेथड गेटर्स एंड सेवर्स के लिए विकल्प नहीं हैं। वे आपको केवल मेथड कॉल या प्रॉपर्टी एक्सेस को संभालने की अनुमति देते हैं, अन्यथा परिणाम में त्रुटि होती है।"

यही कारण है कि हमें दोनों का उपयोग करना चाहिए।

कक्षा का विषय

    /*
    * Item class
    */
class Item{
    private $data = array();

    function __construct($options=""){ //set default to none
        $this->setNewDataClass($options); //calling function
    }

    private function setNewDataClass($options){
        foreach ($options as $key => $value) {
            $method = 'set'.ucfirst($key); //capitalize first letter of the key to preserve camel case convention naming
            if(is_callable(array($this, $method))){  //use seters setMethod() to set value for this data[key];      
                $this->$method($value); //execute the setters function
            }else{
                $this->data[$key] = $value; //create new set data[key] = value without seeters;
            }   
        }
    }

    private function setNameOfTheItem($value){ // no filter
        $this->data['name'] = strtoupper($value); //assign the value
        return $this->data['name']; // return the value - optional
    }

    private function setWeight($value){ //use some kind of filter
        if($value >= "100"){ 
            $value = "this item is too heavy - sorry - exceeded weight of maximum 99 kg [setters filter]";
        }
        $this->data['weight'] = strtoupper($value); //asign the value
        return $this->data['weight']; // return the value - optional
    }

    function __set($key, $value){
        $method = 'set'.ucfirst($key); //capitalize first letter of the key to preserv camell case convention naming
        if(is_callable(array($this, $method))){  //use seters setMethod() to set value for this data[key];      
            $this->$method($value); //execute the seeter function
        }else{
            $this->data[$key] = $value; //create new set data[key] = value without seeters;
        }
    }

    function __get($key){
        return $this->data[$key];
    }

    function dump(){
        var_dump($this);
    }
}

index.php

$data = array(
    'nameOfTheItem' => 'tv',
    'weight' => '1000',
    'size' => '10x20x30'
);

$item = new Item($data);
$item->dump();

$item->somethingThatDoNotExists = 0; // this key (key, value) will trigger magic function __set() without any control or check of the input,
$item->weight = 99; // this key will trigger predefined setter function of a class - setWeight($value) - value is valid,
$item->dump();

$item->weight = 111; // this key will trigger predefined setter function of a class - setWeight($value) - value invalid - will generate warning.
$item->dump(); // display object info

आउटपुट

object(Item)[1]
  private 'data' => 
    array (size=3)
      'name' => string 'TV' (length=2)
      'weight' => string 'THIS ITEM IS TOO HEAVY - SORRY - EXIDED WEIGHT OF MAXIMUM 99 KG [SETTERS FILTER]' (length=80)
      'size' => string '10x20x30' (length=8)
object(Item)[1]
  private 'data' => 
    array (size=4)
      'name' => string 'TV' (length=2)
      'weight' => string '99' (length=2)
      'size' => string '10x20x30' (length=8)
      'somethingThatDoNotExists' => int 0
object(Item)[1]
  private 'data' => 
    array (size=4)
      'name' => string 'TV' (length=2)
      'weight' => string 'THIS ITEM IS TOO HEAVY - SORRY - EXIDED WEIGHT OF MAXIMUM 99 KG [SETTERS FILTER]' (length=80)
      'size' => string '10x20x30' (length=8)
      'somethingThatDoNotExists' => int 0


हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.