Практическое применение магических методов PHP - получение, установка и вызов


Я обычно старался держаться подальше от волшебных методов PHP, потому что они, похоже, запутывают открытый интерфейс объекта. Тем не менее, они, похоже, используются все чаще и чаще, по крайней мере, в коде, который я прочитал, поэтому я должен спросить: есть ли какой-либо консенсус в отношении того, когда их использовать? Существуют ли какие-либо общие закономерности для использования этих трех магических методов?

Author: Major Productions, 2011-05-12

6 answers

__call()

Я видел, как он использовался для реализации поведения, например, для добавления дополнительных функций в класс через подключаемый интерфейс.

Псевдокод вот так:

$method = function($self) {};
$events->register('object.method', $method);
$entity->method(); // $method($this);

Это также облегчает написание в основном похожих функций, например, в ORMS. например:

$entity->setName('foo'); // set column name to 'foo'

__get()/__set()

Я в основном видел, как он использовался для переноса доступа к частным переменным.

ORM - лучший пример, который приходит на ум:

$entity->name = 'foo'; // set column name to 'foo'
 4
Author: Denis de Bernardy, 2011-05-12 15:00:27

Основная причина заключается в том, что вам не нужно вводить так много. Вы могли бы использовать их, скажем, для записи ORM и действовать как неявные установщики/получатели:

Использование __call():

$user = new User();
$user->setName("Foo Bar");
$user->setAge(42);
$user->save();

Использование __set():

$user->name = "Foo Bar";
$user->age = 42;

Который сопоставляется с простым массивом:

array(
    "name" => "Foo Bar",
    "age"  => 42
)

Гораздо проще записать такой массив в базу данных, чем выполнять множество ручных вызовов для сбора всей необходимой информации. __set() и __get() имеют еще одно преимущество перед общедоступными участниками: вы можете проверять/форматировать свои данные.

 4
Author: jwueller, 2011-05-12 14:56:59

Это позволяет вам делать такие вещи:

class myclass {
    private $propertybag;

    public function __get($name) {
        if(isset($this->propertybag[$name]) {return $this->propertybag[$name];}
        throw new Exception("Unknown property " . (string) $name);
    }

 }

Затем вы можете заполнить $propertybag из SQL-запроса в одной строке, а не задавать целую кучу свойств по одному.

Кроме того, он позволяет вам иметь определенные свойства, которые доступны только для чтения (т.Е. Не позволяют изменять их с помощью __set()). Может быть полезно, например, для поля идентификатора.

Кроме того, вы можете поместить код в __get() и __set(), чтобы вы могли сделать что-то более сложное, чем просто получить или установить одна переменная. Например, если у вас есть поле storeID, вы также можете указать свойство storeName. Вы можете реализовать это в __get() с помощью поиска по перекрестным ссылкам, поэтому вам может не понадобиться, чтобы имя на самом деле сохранялось в классе. И, конечно, storeName не хотел бы быть реализован в __get().

Там много возможностей.

Конечно, есть некоторые недостатки использования магических методов. Самым большим для меня является тот факт, что вы теряете функцию автозаполнения в ваша IDE. Это может иметь или не иметь для вас значения.

 3
Author: Spudley, 2011-05-12 15:31:58

Поскольку магические методы могут сэкономить вам МНОГО времени на кодировании, когда дело доходит до повторяющихся задач, таких как определение элементов, их заполнение и последующее извлечение - вместо выполнения этой скучной, длинной работы вы можете использовать упомянутые 3 метода, чтобы сократить время на кодирование всего этого. При необходимости я могу привести несколько примеров, которые можно найти в различных учебных пособиях по сети.

Я не знаю, является ли это общим консенсусом, но следует применять обычное - использовать там, где это уместно. Если вы найдете самостоятельно выполнять повторяющиеся задачи (определять участника, заполнять участника, получать участника, вызывать функции X, которые немного отличаются) - вам могут помочь магические методы.

 0
Author: Michael J.V., 2011-05-12 15:01:49

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

class db
{
    static private $instance = null;

    static public function getInstance()
    {
        if( self::$instance == NULL )
            self::$instance = new db;

        return self::$instance;
    }

    function fetch()
    {
        echo "I'm fetching\n";
    }
}

class dataHandler
{
    function __call($name, $argv)
    {
        if( substr($name, 0, 4) == 'data' )
        {
            $fn = substr($name, 4);
            db::getInstance()->$fn($argv);
        }
    }
}

$dh = new dataHandler;
$dh->datafetch('foo', 'bar');

Одни и те же принципы могут быть использованы для управления различными бэкэндами одной и той же функциональности без необходимости менять драйвер.

 0
Author: Mel, 2011-05-12 15:19:14

Когда захотите, при условии, что магические свойства/методы задокументированы; недокументированной магии следует избегать, если только вы не работаете с очень абстрактным слоем кода, например, при разработке ORM.

Приемлемо на абстрактном уровне

/**
 * DB backed model base class.
 */
class Model {
    protected $attributes = [];

    function __get($name) {
        return @$this->attributes[$name];
    }
}

Приемлемо, если задокументировано

/**
 * User model backed by DB tables.
 * @property-read string $first_name
 * @property-read string $last_name
 */
class UserModel extends Model {

}

Ленивый и неприемлемый (и распространенный при использовании ORM)

/**
 * This class is magical and awesome and I am a lazy shithead!
 */
class UserModel extends WhoCaresWhenEverythingIsMagical {

}
 0
Author: rich remer, 2015-04-24 20:52:29