Лучшая практика: PHP Magic Methods set and get [duplicate]


Возможные Дубликаты:
являются ли магические методы лучшей практикой в 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 110

9 ответов:

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

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

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

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

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

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

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

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

этот код однозначно говорит, что я делаю:

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#.

Я делаю смесь ответа Эдема и вашего второго кода. Таким образом, у меня есть преимущества common getter / setters (завершение кода в вашей 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 полезны, но не в этом случае.