PHP __get ve __set sihirli yöntemleri


86

Tamamen Yanılmıyorsam, __getve __setyöntemlerinin aşırı izin gerekiyordu → edilir getve set.

Örneğin, aşağıdaki ifadeler __getyöntemi çağırmalıdır :

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

Ve aşağıdakiler __setyöntemi kullanmalıdır :

$foo->bar = 'test';

Bu, kodumda çalışmıyordu ve şu basit örnekle yeniden üretilebilir:

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]";

Bu sadece şunlarla sonuçlanır:

[test]

die()Oraya bazı aramalar koymak , ona hiç ulaşmadığını gösterir.

Şimdilik, sadece boşver dedim ve __getşimdilik ihtiyaç duyulan yerde manuel olarak kullanıyorum , ancak bu çok dinamik değil ve 'aşırı yüklenmiş' kodun aslında özellikle çağrılmadıkça çağrılmayacağı bilgisini gerektiriyor. Bunun ya olması gerektiğini anladığım şekilde işlememesi gerekip gerekmediğini ya da neden çalışmadığını bilmek isterim.

Bu devam ediyor php 5.3.3.

Yanıtlar:


166

__get, __set, __callVe __callStaticyöntem veya özellik erişilemez olduğunda çağrılır. Siz $barhalka açıksınız ve bu nedenle erişilemez değil.

Kılavuzdaki Mülk Aşırı Yüklemesi bölümüne bakın :

  • __set() erişilemez özelliklere veri yazarken çalıştırılır.
  • __get() erişilemeyen mülklerden veri okumak için kullanılır.

Sihirli yöntemler alıcıların ve ayarlayıcıların yerini tutmaz. Aksi takdirde bir hatayla sonuçlanacak yöntem çağrılarını veya özellik erişimini işlemenize izin verirler. Bu nedenle, hata işleme ile ilgili çok daha fazlası vardır. Ayrıca, uygun alıcı ve ayarlayıcı veya doğrudan yöntem çağrılarını kullanmaktan önemli ölçüde daha yavaş olduklarını unutmayın.


6
Bunu detaylandırmak için, "public $ bar" seçeneğini kaldırmanız yeterlidir, böylece mülk artık mevcut olmaz ve bir cazibe gibi çalışır.
Steffen Müller

Teşekkürler. Bu, kılavuzu tam olarak okumadığım ve bunun yerine bazılarının blog örneğine baktığım için elde ettiğim şey bu;) Yine de PHP'de gerçek operatör aşırı yüklemesini bekliyorum.
airbear

@airbear, operatörleri aşırı yüklemenize izin veren Sara Golemon'un eski bir PECL paketi var . PHP.current ile ne kadar uyumlu olduğunu bilmiyorum.
Gordon

1
@Pooya Bunun nedeni node$ foo'nun bir özelliği değil doesNotExist. Bu nedenle, 'doesNotExist' bir nesne olmadığı sürece (__set uygulayan veya düğüm adı verilen bir genel özelliğe sahip olan) çalışmaz.
Tivie

1
@Allen evet, öyle.
Gordon

34

Tüm değerleri depolamak için bir dizi kullanmanızı tavsiye ederim __set().

class foo {

    protected $values = array();

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

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

}

Bu şekilde $values, çarpışmaları önlemek için değişkenlere başka bir yolla ( korumalı olduğunu unutmayın ) erişemeyeceğinizden emin olursunuz .


19

Gönderen PHP Kılavuzu :

  • Erişilemeyen özelliklere veri yazarken __set () çalıştırılır.
  • __get () erişilemeyen özelliklerden veri okumak için kullanılır.

Bu sadece erişilemeyen özelliklerin okunması / yazılması için çağrılır . Ancak mülkünüz halka açıktır, bu da erişilebilir olduğu anlamına gelir. Erişim değiştiriciyi korumalı olarak değiştirmek sorunu çözer.


7

Berry'nin cevabını genişletmek gerekirse, erişim düzeyini korumalı olarak ayarlamak, __get ve __set'in açıkça beyan edilen özelliklerle (en azından sınıfın dışından erişildiğinde) kullanılmasına ve hızın oldukça yavaş olmasına izin verir, başka bir sorudan bir yorum alıntılayacağım bu konuda ve yine de kullanmak için bir durum oluşturun:

__Get'in özel bir get işlevine göre daha yavaş olduğunu (aynı şeyleri yapıyor) kabul ediyorum, bu __get () için 0.0124455 ve bu 0.0024445 10000 döngüden sonra özel get () içindir. - Melsi 23 Kasım 2012 saat 22:32 En iyi uygulama: PHP Sihir Yöntemleri __set ve __get

Melsi'nin testlerine göre, oldukça yavaş, yaklaşık 5 kat daha yavaştır. Bu kesinlikle çok daha yavaştır, ancak testlerin, bu yöntemle bir mülke hala saniyenin yaklaşık 1 / 100'ü içinde döngü yinelemesi için süreyi sayarak 10.000 kez erişebileceğinizi gösterdiğini unutmayın. Tanımlanan gerçek get ve set yöntemlerine kıyasla önemli ölçüde daha yavaştır ve bu bir eksikliktir, ancak genel şemada, 5 kat daha yavaş bile asla yavaş değildir.

İşlemin hesaplama süresi hala ihmal edilebilir düzeydedir ve gerçek dünya uygulamalarının% 99'unda dikkate alınmaya değmez. Gerçekten kaçınılması gereken tek zaman, mülklere tek bir istekte 10.000'den fazla kez erişeceğiniz zamandır. Yüksek trafikli siteler, uygulamalarını çalışır durumda tutmak için birkaç sunucu daha atmayı göze alamazlarsa gerçekten yanlış bir şey yapıyorlar. Erişim oranının sorun haline geldiği yüksek trafikli bir sitenin altbilgisindeki tek satırlık bir metin reklam, muhtemelen bu metin satırına sahip 1.000 sunucudan oluşan bir çiftlik için ödeme yapabilir. Son kullanıcı, uygulamanızın mülk erişimi saniyenin milyonda biri kadar sürdüğü için, sayfanın yüklenmesinin bu kadar uzun sürdüğünü merak ederek asla parmaklarına dokunmayacaktır.

Bunu .NET'te bir arka plandan gelen bir geliştirici olarak söylüyorum, ancak tüketiciye görünmeyen alma ve ayarlama yöntemleri .NET'in icadı değil. Onlar sadece onlar olmadan özellik değildirler ve bu sihirli yöntemler PHP'nin geliştiricisinin kendi özellik sürümlerini "özellikler" olarak adlandırması için bile tasarruf etme yetkisidir. Ayrıca, PHP için Visual Studio uzantısı, bu hile göz önünde bulundurularak, korumalı özelliklerle intellisense'i desteklediğini düşünüyorum. Bu şekilde sihirli __get ve __set yöntemlerini kullanan yeterli geliştiriciyle, PHP geliştiricilerinin, geliştirici topluluğunun ihtiyaçlarını karşılamak için yürütme süresini ayarlayacağını düşünüyorum.

Düzenleme: Teoride, korunan mülkler çoğu durumda işe yarayacak gibi görünüyordu. Pratikte, sınıf tanımı ve genişletilmiş sınıflar içindeki özelliklere erişirken birçok kez alıcılarınızı ve ayarlayıcılarınızı kullanmak isteyeceğiniz ortaya çıkıyor. Daha iyi bir çözüm, diğer sınıfları genişletirken temel sınıf ve arabirimdir; bu nedenle, temel sınıftan birkaç kod satırını uygulama sınıfına kopyalayabilirsiniz. Projemin temel sınıfıyla biraz daha fazlasını yapıyorum, bu yüzden şu anda sağlamak için bir arayüzüm yok, ancak burada, özellikleri kaldırmak ve taşımak için yansımayı kullanarak sihirli özellik alma ve ayarlama ile test edilmemiş soyulmuş sınıf tanımı var. korumalı bir dizi:

/** 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;
    }
}

Kodda herhangi bir hata varsa özür dilerim.


'Access' => $ property-> getModifier ()
macki

5

Bunun nedeni, $ bar'ın bir kamu mülkü olmasıdır.

$foo->bar = 'test';

Yukarıdakileri çalıştırırken sihirli yöntemi çağırmaya gerek yoktur.

public $bar;Sınıfınızdan silmek bunu düzeltmelidir.


1

Aşağıdaki örnekte olduğu gibi önceden tanımlanmış özel set / get Yöntemleri ile en iyi sihirli set / get yöntemlerini kullanın. Bu şekilde iki dünyanın en iyisini birleştirebilirsiniz. Hız açısından biraz daha yavaş olduklarına katılıyorum ama farkı hissedebiliyor musunuz? Aşağıdaki örnek ayrıca veri dizisini önceden tanımlanmış ayarlayıcılara göre doğrulamaktadır.

"Sihirli yöntemler, alıcıların ve ayarlayıcıların yerine geçmez. Yalnızca, aksi takdirde bir hatayla sonuçlanacak yöntem çağrılarını veya özellik erişimini işlemenizi sağlar.

Bu yüzden ikisini de kullanmalıyız.

SINIF ÖRNEĞİ

    /*
    * 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

ÇIKTI

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

0

public $bar;Beyanı bırakın ve beklendiği gibi çalışmalıdır.


-6

Intenta con:

__GET($k){
 return $this->$k;
}

_SET($k,$v){
 return $this->$k = $v;
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.