Практические применения магических методов PHP - __get, __set и __call - PullRequest
13 голосов
/ 12 мая 2011

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

Ответы [ 6 ]

4 голосов
/ 12 мая 2011

__call()

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

Псевдокод выглядит так:

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

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

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

__get()/__set()

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

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

$entity->name = 'foo'; // set column name to 'foo'
4 голосов
/ 12 мая 2011

Основная причина в том, что вам не нужно печатать так много. Вы можете использовать их, скажем, для записи 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() имеют еще одно преимущество перед публичными участниками: вы можете проверять / форматировать свои данные.

3 голосов
/ 12 мая 2011

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

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. Это может иметь или не иметь значения для вас.

0 голосов
/ 24 апреля 2015

Когда бы вы ни захотели, пока магические свойства / методы задокументированы; недокументированной магии следует избегать, если вы не работаете с очень абстрактным слоем кода, например, при разработке 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 голосов
/ 12 мая 2011

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

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 голосов
/ 12 мая 2011

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

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

...