Генератор форм

Для веб-мастера

Реклама

Объектно-ориентированное программирование в PHP.  Методы-перехватчики

Рейтинг:
Дата: 7 сентября 2011 Просмотры 10906 Комментарии 1

В PHP предусмотрены встроенные методы-перехватчики, их еще называют «магические методы», которые позволяют перехватывать сообщения посланные неопределенным методам или свойствам. Как и в случае метода __construct(), вызов этих методов происходит неявно, когда выполняются соответствующие условия, а названия этих методов начинаются с двух символов подчеркивания.

Что же это за методы такие?

  • __get($property) - вызывается при обращении к неопределенному свойству
  • __set($property,$value) - вызывается, когда неопределенному свойству присваивается значение
  • __unset($property) - вызывается, когда функция unset() вызывается для неопределенного свойства
  • __isset($property) - вызывается, когда функция isset() вызывается для неопределенного свойства
  • __call($method,$arg array) - вызывается при обращении к неопределенному методу

Методы __get() и __set() предназначены для работы со свойствами, которые не были объявлены в классе или его родителе.

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

Все, что вернет метод __get(), будет отослано обратно клиенту, как будто искомое свойство существует с этим значением. Рассмотрим короткий пример.

Class Person{
    function __get($property){
        $method = 'get'.$ property;

        if(method_exists($this,$method)){
            return $this->$method();
        }
    }

    function getname(){
        return 'Иван';
    }

    function getage(){
        return '26';
    }
}

Когда клиентский код пытается получить доступ к неопределенному свойству, вызывается метод  __get(), в котором перед именем переданного ему свойства добавляется строка «get». Затем полученная строка, содержащая новое имя метода, передается функции method_exists(). Этой функции передается также ссылка на текущий объект, для которого проверяется существование метода. Если метод существует, мы вызываем его и передаем возвращенное им значение клиентскому коду. Поэтому если клиентский код запрашивает свойство $name:

$p = new Person();
echo $p->name;

то метод getname() вызывается неявно и выводится строка «Иван». Если же метод не существует, то ничего не происходит. Свойству, к которому пользователь пытается обратиться, присваивается значение NULL.

Метод __isset() работает аналогично методу __get(). Он вызывается после того, как клиентский код вызывает функция isset () для неопределенного свой­ства. Рассмотрим пример расширения класса Person.

function  __isset($property){
    $method = 'get'.$property;
    return method_exists($this,$method);
}

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

if ( isset ( $p->name ) } {
    print $p->name;
}

Метод __set() вызывается, когда клиентский код пытается присвоить значение неопределенному свойству. При этом передается два аргумента: имя свойства и зна­чение, которое клиентский код пытается присвоить. Затем вы можете решить, как работать с этими аргументами. Давайте продолжим расширение класса Person.

Class Person{
    private $_name;
    private $_age;

    function __set($property, $value){
        $method = 'set'.$property;
        if(method_exists($this, $method)){
            return $this->$method($value);
        }
    }

    function setname($value){
        $this->_name = $value;
        if(!is_null($value)){
            $this->_name = strtoupper($this->_name);
        }
    }

    function setage($value){
        $this->_age = $value;
    }
}

В этом примере мы работаем с методами-установщиками (setter), а не с метода­ми-получателями (getter). Если пользователь попытается присвоить значение не­определенному свойству, то методу  __set() будет передано имя этого свойства и присваиваемое ему значение. В методе __set() проверяется, существует ли ука­занный метод, и если да, то он вызывается. В результате мы можем отфильтровать присваиваемое свойству значение.

Итак, если мы создаем объект типа Person, а затем пытаемся установить свой­ство Person:: $name, то вызывается метод __set(), потому что в этом классе не оп­ределено свойство $name. Методу передается две строки, содержащие имя свойства и значение, которое мы хотим установить. И уже от нас зависит, что мы сделаем с этой информацией. В данном примере мы создаем новое имя метода, добавив пе­ред именем свойства строку «set». Программа находит метод setname() и запус­кает его должным образом. Он преобразует входящую строку в символы верхнего регистра и сохраняет ее в реальном свойстве.

setLocale(LC_ALL, «ru_RU.CP1251″); 
$р = new Person(); 
$p->name  = "Иван";
//Реальному свойству $_name присваивается строка "Иван"

Как и можно было ожидать, метод __unset () является зеркальным отражени­ем метода __set (). Он вызывается в случае, когда функции unset () передается имя неопределенного свойства. Имя этого свойства и передается методу __unset ().

С полученной информацией можно делать все что угодно. В приведенном ниже примере значение null передается найденному результирующему методу тем же самым способом, который мы использовали при рассмотрении метода __set().

function __unset($property, $value){

    $method = 'set'.$property;
    if(method_exists($this, $method)){
        return $this->$method(NULL);
    }
}

Метод __call(), вероятно, — самый полезный из всех методов-перехватчиков.

Он вызывается, когда клиентский код обращается к неопределенно методу. При этом методу __call () передается имя несуществующего метода и массив, в кото­ром содержатся все аргументы, переданные клиентом. Значение, возвращаемое методом __call (), передается клиенту так, как будто оно было возвращено вы­званным несуществующим методом.

Метод __call () может использоваться для делегирования. Делегирование — это механизм, посредством которого один объект может вызвать метод другого объек­та. Это чем-то напоминает наследование, когда дочерний класс вызывает метод, реализованный в родительском классе. В случае наследования взаимосвязь между родительским и дочерним классами фиксирована. Поэтому возможность изменить объект-получатель во время выполнения программы означает, что делегирование является более гибким, чем наследование. Чтобы лучше это понять, давайте про­иллюстрируем все на примере. Рассмотрим простой класс, предназначенный для форматирования информации, полученной от класса Person,

Class PersonWriter{
    function writeName(Person $p){
        echo $p->getName()."\n";
    }

    function writeAge(Person $p){
        echo $p->getAge()."\n";
    }
}

Конечно, мы можем создать подкласс от этого класса, чтобы выводить данные о классе Person различными способами. Вот реализация класса Person, в которой используются и объект PersonWriter, и метод __call ().

class Person {
    private $writer;
    function __ construct( PersonWriter $writer ) {
        $this->writer = $writer;
    }
    function __ call( $methodname, $args ) {
        if ( method_exists( $this->writer, $methodname ) ) {
            return $this->writer->$methodname( $this );
        }
    }
    function getName { return "Иван"; }
    function getAge { return 44; }
}

Здесь конструктору класса Person в качестве аргумента передается объект типа PersonWriter, который сохраняется в переменной свойства. В методе __call() используется значение аргумента $methodname и проверяется наличие метода с таким же именем в объекте PersonWriter, ссылка на который была сохранена в конструкторе. Если такой метод найден, его вызов делегируется объекту PersonWriter. При этом методу передается ссылка на текущий экземпляр объекта типа Person, которая хранится в псевдопеременной $this. Поэтому если клиент вызо­вет несуществующий в классе Person метод

$person = new Person( new PersonWriter() );
$person->writeName();

то будет вызван метод __call (). В нем определяется, что в объекте типа PersonWriter существует метод с именем writeName, который и вызывается. Это по­зволяет избежать вызова делегированного метода вручную, как показано ниже.

function writeName() {
    $this->writer->writeName( $this );
}

Таким образом класс Person, как по волшебству, получил два новых метода класса PersonWriter. Хотя автоматическое делегирование избавляет вас от рутинной рабо­ты по однотипному кодированию вызовов методов, сам код становится труден для понимания. И если в вашей программе активно используется делегирование, то для внешнего мира создается динамический интерфейс, который не поддается рефлексии (исследованию аспектов класса во время выполнения программы) и не всегда с первого взгляда понятен программисту клиентского кода. Причина в том, что логика, лежащая в основе взаимодействия между делегирующим классом и це­левым объектом, может быть непонятной. Ведь она скрыта в таких методах, как __call (), а не явно задана отношениями наследования или уточнениями типа ар­гументов методов. Методы-перехватчики имеют свою область применения, но ис­пользовать их следует с осторожностью. И в классах, которые применяют эти мето­ды, факт такого применения необходимо очень четко и ясно документировать.

Таким образом в этой статье Вы изучили основные методы-перехватчики в ООП на PHP и узнали, как их можно использовать на практике

Оцените эту статью:



Похожие статьи
Интересно почитать

    Получать новые материалы этого сайта на свой email адрес?

    Отправлять мне комментарии по эл. почте?

Простая CRM

Нашли ошибку в тексте

Система Orphus
https://biznesguide.ru/coding/144.html
3,94 из 5 на основе 9 оценок.