Лучшая практика: PHP Magic Methods set и get [дубликат]

Возможные Дубликаты:
магические методы лучшей практики в PHP?

Это простые примеры, но представьте, что у вас больше свойств, чем два в вашем классе.

что было бы лучшей практикой?

a) использование _ _ get и _ _ 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) использование традиционных сеттеров и геттеров

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

В этой статье: http://blog.webspecies.co.uk/2011-05-23/the-new-era-of-php-frameworks.html

автор утверждает, что использование магических методов не является хорошей идеей:

прежде всего, тогда было очень популярно использовать магические функции PHP (__get, __call и т. д.). С первого взгляда в них нет ничего плохого, но на самом деле они действительно опасны. Они делают API неясными, автоматическое завершение невозможным и, самое главное, они медленные. Случай использования для них было взломать PHP, чтобы делать то, что он не хотел. И это сработало. Но делал плохие вещи.

но я хотел бы услышать больше мнений об этом.

9 ответов


Я бы точно в вашем случае в прошлом. И я пошел на магические методы.

Это была ошибка, последняя часть вашего вопроса говорит все это :

  • это медленнее (чем геттеры/сеттеры)
  • здесь нет автозавершения (и это действительно серьезная проблема), и тип управления IDE для рефакторинга и просмотра кода (в Zend Studio / PhpStorm это можно обработать с помощью @property PHPDoc аннотации, но это требует, чтобы поддерживать их: довольно боль)
  • на документация (phpdoc) не соответствует тому, как ваш код должен использоваться, и просмотр вашего класса также не приносит много ответов. Это сбивает с толку.
  • добавлено после редактирования: наличие геттеров для свойства более соответствует "реальным" методам здесь getXXX() не только возвращает частное свойство, но и делает реальную логику. У вас одинаковые имена. Например, у вас есть $user->getName() (возвращает частную собственность) и $user->getToken($key) (вычисляется). В тот день, когда ваш геттер получает больше, чем геттер, и должен сделать некоторую логику, все по-прежнему последовательно.

наконец, и это самая большая проблема IMO:это магия. А магия очень, очень плоха, потому что вы должны знать, как она работает, чтобы правильно ее использовать. Это проблема, с которой я столкнулся в команде: каждый должен понимать магию, а не только вы.

геттеры и сеттеры-это боль, чтобы писать (я ненавижу их), но они того стоят.


вам нужно использовать магию, только если объект действительно "волшебный". Если у вас есть классический объект с фиксированными свойствами, используйте сеттеры и геттеры, они работают нормально.

Если ваш объект имеет динамические свойства например, он является частью слоя абстракции базы данных, и его параметры определяются во время выполнения, то вы действительно нужны магические методы для удобства.


я использую __get (и public properties) как можно больше, потому что они делают код гораздо более читаемым. Сравнить:

этот код недвусмысленно говорит о том, что я делаю:

echo $user->name;

этот код заставляет меня чувствовать себя глупо, что мне не нравится:

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

echo $user->getName();

разница между ними особенно очевидна при одновременном доступе к нескольким свойствам.

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

и

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

ли $a->b действительно надо do что-то или просто возвращать значение является обязанностью абонента. Для звонящего, $user->name и $user->accountBalance должно выглядеть одинаково, хотя последнее может включать сложные вычисления. В моих классах данных я использую следующий небольшой метод:

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

когда кто-то звонит $obj->xxx и класс get_xxx определено, этот метод вызывается неявно. Таким образом, вы можете определить геттер, если вам это нужно, сохраняя при этом свой интерфейс однородным и прозрачным. Как дополнительный бонус это обеспечивает элегантный способ запоминания расчетов:

  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

итог: php-это динамический язык сценариев, используйте его таким образом, не притворяйтесь, что вы делаете Java или C#.


Я делаю смесь ответа Эдема и вашего второго кода. Таким образом, у меня есть преимущества общих геттеров / сеттеров (завершение кода в вашей IDE), простота кодирования, если я хочу, исключения из-за несуществующих свойств (отлично подходит для обнаружения опечаток: $foo->naem вместо $foo->name), читать только свойства и свойства соединения.

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

Я голосую за третий вариант. Я использую это в своих проектах, и Symfony использует что-то вроде этого:

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

таким образом, у вас есть автоматические геттеры (вы также можете писать сеттеры), и вам нужно писать только новые методы, если есть особый случай для переменной-члена.


вы должны использовать stdClass Если вам нужны магические члены, если вы пишете класс-определите, что он содержит.


лучшей практикой было бы использование традиционных геттеров и сеттеров из-за самоанализа или размышления. В PHP (точно так же, как в Java) есть способ получить имя метода или всех методов. Такая вещь вернет " _ _ get "в первом случае и" getFirstField"," getSecondField " во втором (плюс сеттеры).

подробнее об этом: http://php.net/manual/en/book.reflection.php


теперь я возвращаюсь к сеттерам и геттерам, но я также помещаю геттеров и сеттеров в magic methos __get и __set. Таким образом, у меня есть поведение по умолчанию, когда я это делаю

$class - >var;

Это просто вызовет геттер, который я установил в _ _ get. Обычно я просто использую геттер напрямую, но есть еще несколько случаев, когда это просто проще.


второй пример кода гораздо более правильный способ сделать это, потому что вы берете полный контроль над данными, которые даны class. Есть случаи, когда __set и __get полезны, но не в этом случае.