Najlepsza praktyka: PHP magiczne metody set and get [duplicate]

Możliwy duplikat:
czy magiczne metody są najlepszą praktyką w PHP?

Są to proste przykłady, ale wyobraź sobie, że masz więcej właściwości niż dwie w swojej klasie.

Jaka byłaby najlepsza praktyka?

A) używanie _ _ get I _ _ set

class MyClass {
    private $firstField;
    private $secondField;

    public function __get($property) {
            if (property_exists($this, $property)) {
                return $this->$property;
            }
    }

    public function __set($property, $value) {
        if (property_exists($this, $property)) {
            $this->$property = $value;
        }
    }
}

$myClass = new MyClass();

$myClass->firstField = "This is a foo line";
$myClass->secondField = "This is a bar line";

echo $myClass->firstField;
echo $myClass->secondField;

/* Output:
    This is a foo line
    This is a bar line
 */

B) za pomocą tradycyjnych seterów i getterów

class MyClass {

    private $firstField;
    private $secondField;

    public function getFirstField() {
        return $this->firstField;
    }

    public function setFirstField($firstField) {
        $this->firstField = $firstField;
    }

    public function getSecondField() {
        return $this->secondField;
    }

    public function setSecondField($secondField) {
        $this->secondField = $secondField;
    }

}

$myClass = new MyClass();

$myClass->setFirstField("This is a foo line");
$myClass->setSecondField("This is a bar line");

echo $myClass->getFirstField();
echo $myClass->getSecondField();

/* Output:
    This is a foo line
    This is a bar line
 */

W tym artykule: http://blog.webspecies.co.uk/2011-05-23/the-new-era-of-php-frameworks.html

Autor twierdzi że używanie magicznych metod nie jest dobrym pomysłem:

Po pierwsze, wtedy bardzo popularne było używanie magicznych funkcji PHP (__get, _ _ call itp.). Nie ma z nimi nic złego Od pierwszego spojrzenia, ale są naprawdę bardzo niebezpieczne. Sprawiają, że API są niejasne, autouzupełnianie niemożliwe i co najważniejsze są powolne. Przykładem użycia dla nich było zhakowanie PHP, aby zrobić rzeczy, których nie chciał. I zadziałało. Ale sprawił, że stało się coś złego.

Ale chciałbym poznaj więcej opinii na ten temat.
Author: Community, 2011-05-31

9 answers

Byłem dokładnie w Twoim przypadku w przeszłości. A ja wybrałam magiczne metody.

To był błąd, ostatnia część twojego pytania mówi wszystko:

  • to jest wolniejsze (niż gettery/settery)
  • nie ma żadnego automatycznego uzupełniania (a to jest poważny problem), oraz zarządzanie typami przez IDE do refaktoryzacji i przeglądania kodu (pod Zend Studio/PhpStorm może to być obsługiwane za pomocą adnotacji @property phpdoc, ale wymaga to utrzymuj je: dość boli)
  • Dokumentacja (phpdoc) nie pasuje do tego, jak powinien być używany Twój kod, A przeglądanie twojej klasy nie przynosi zbyt wielu odpowiedzi. To jest mylące.
  • dodane po edycji: posiadanie getterów dla właściwości jest bardziej zgodne z" rzeczywistymi " metodami gdzie getXXX() nie tylko zwraca własność prywatną, ale robi prawdziwą logikę. Masz to samo imię. Na przykład masz $user->getName() (zwraca własność prywatną) i $user->getToken($key) (obliczony). Dzień, w którym twój getter dostaje więcej niż getter i musi zrobić trochę logiki, wszystko jest nadal spójne.

Wreszcie, i to jest największy problem IMO: to jest magia. A magia jest bardzo, bardzo zła, ponieważ trzeba wiedzieć, jak działa magia, aby ją właściwie wykorzystać. To jest problem, który spotkałem w zespole: każdy musi zrozumieć magię, nie tylko Ty.

Gettery i setery to ból do pisania (nienawidzę ich), ale są tego warte.

 137
Author: Matthieu Napoli,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-30 06:41:19

Musisz używać magii tylko wtedy, gdy obiekt jest rzeczywiście "magiczny". Jeśli masz klasyczny obiekt o stałych właściwościach, użyj setterów i getterów, działają dobrze.

Jeśli twój obiekt ma właściwości dynamiczne na przykład jest częścią warstwy abstrakcji bazy danych, a jego parametry są ustawiane w czasie wykonywania, to rzeczywiście potrzebujesz magicznych metod dla wygody.

 104
Author: vbence,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-06 12:59:48

Używam __get (i public properties) w miarę możliwości, ponieważ sprawiają, że kod jest o wiele bardziej czytelny. Porównaj:

Ten kod jednoznacznie mówi, co robię:

echo $user->name;

Ten kod sprawia, że czuję się głupio, czego nie lubię:

function getName() { return $this->_name; }
....

echo $user->getName();

Różnica między nimi jest szczególnie oczywista, gdy uzyskujesz dostęp do wielu właściwości jednocześnie.

echo "
    Dear $user->firstName $user->lastName!
    Your purchase:
        $product->name  $product->count x $product->price
"

I

echo "
    Dear " . $user->getFirstName() . " " . $user->getLastName() . "
    Your purchase: 
        " . $product->getName() . " " . $product->getCount() . "  x " . $product->getPrice() . " ";

Czy $a->b naprawdę powinno zrobić coś lub po prostu zwrócić wartość jest odpowiedzialność kallee. Dla wywołującego, $user->name i $user->accountBalance powinny wyglądać tak samo, chociaż te ostatnie mogą wiązać się ze skomplikowanymi obliczeniami. W moich klasach danych używam następującej małej metody:

 function __get($p) { 
      $m = "get_$p";
      if(method_exists($this, $m)) return $this->$m();
      user_error("undefined property $p");
 }

Gdy ktoś wywoła $obj->xxx i klasa ma zdefiniowaną get_xxx, ta metoda zostanie wywołana domyślnie. Możesz więc zdefiniować getter, jeśli tego potrzebujesz, zachowując jednolity i przejrzysty interfejs. Jako dodatkowy bonus zapewnia elegancki sposób zapamiętywania obliczenia:

  function get_accountBalance() {
      $result = <...complex stuff...>
      // since we cache the result in a public property, the getter will be called only once
      $this->accountBalance = $result;
  }

  ....


   echo $user->accountBalance; // calculate the value
   ....
   echo $user->accountBalance; // use the cached value

Podsumowując: php jest dynamicznym językiem skryptowym, używaj go w ten sposób, nie udawaj, że robisz Javę lub C#.

 78
Author: user187291,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-04 11:23:00

Robię mieszankę odpowiedzi edema i Twojego drugiego kodu. W ten sposób, mam korzyści z common getter / setters(uzupełnianie kodu w IDE), łatwość kodowania, jeśli chcę, wyjątki ze względu na nieistniejące właściwości (świetne do odkrywania literówek: $foo->naem zamiast $foo->name), właściwości tylko do odczytu i Właściwości złożone.

class Foo
{
    private $_bar;
    private $_baz;

    public function getBar()
    {
        return $this->_bar;
    }

    public function setBar($value)
    {
        $this->_bar = $value;
    }

    public function getBaz()
    {
        return $this->_baz;
    }

    public function getBarBaz()
    {
        return $this->_bar . ' ' . $this->_baz;
    }

    public function __get($var)
    {
        $func = 'get'.$var;
        if (method_exists($this, $func))
        {
            return $this->$func();
        } else {
            throw new InexistentPropertyException("Inexistent property: $var");
        }
    }

    public function __set($var, $value)
    {
        $func = 'set'.$var;
        if (method_exists($this, $func))
        {
            $this->$func($value);
        } else {
            if (method_exists($this, 'get'.$var))
            {
                throw new ReadOnlyException("property $var is read-only");
            } else {
                throw new InexistentPropertyException("Inexistent property: $var");
            }
        }
    }
}
 4
Author: Carlos Campderrós,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-05-31 08:09:10

Głosuję za trzecim rozwiązaniem. Używam tego w moich projektach i Symfony też używa czegoś takiego:

public function __call($val, $x) {
    if(substr($val, 0, 3) == 'get') {
        $varname = strtolower(substr($val, 3));
    }
    else {
        throw new Exception('Bad method.', 500);
    }
    if(property_exists('Yourclass', $varname)) {
        return $this->$varname;
    } else {
        throw new Exception('Property does not exist: '.$varname, 500);
    }
}

W ten sposób Masz automatyczne gettery (możesz też pisać settery) i musisz pisać nowe metody tylko wtedy, gdy istnieje specjalny przypadek dla zmiennej członkowskiej.

 0
Author: Adam Arold,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-05-31 07:44:19

Powinieneś użyć stdClass jeśli chcesz mieć członków magic, jeśli napiszesz klasę-określ, co ona zawiera.

 -2
Author: Wesley van Opdorp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-05-31 07:38:50

Najlepszą praktyką byłoby stosowanie tradycyjnych getterów i seterów, ze względu na introspekcję lub refleksję. W PHP (podobnie jak w Javie) istnieje sposób na uzyskanie nazwy metody lub wszystkich metod. Takie coś zwróci "_ _ get "w pierwszym przypadku i "getFirstField", "getSecondField" w drugim (Plus setery).

Więcej na ten temat: http://php.net/manual/en/book.reflection.php

 -3
Author: SteeveDroz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-05-31 07:43:24

Teraz wracam do setterów i getterów, ale umieszczam również getterów i seterów w magicznym methos _ _ get I _ _ set. W ten sposób mam domyślne zachowanie, gdy to robię

$class - > var;

To po prostu wywoła getter, który ustawiłem w _ _ get. Normalnie po prostu użyję gettera bezpośrednio, ale nadal istnieją przypadki, w których jest to po prostu prostsze.

 -3
Author: AntonioCS,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-05-31 09:18:18

Drugi przykład kodu jest o wiele bardziej odpowiedni, ponieważ przejmujesz pełną kontrolę nad danymi, które są przekazywane class. Są przypadki, w których __set i __get są przydatne, ale nie w tym przypadku.

 -3
Author: skowron-line,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-10-17 04:43:38