Геттеры и сеттеры при помощи трейта или сильные стороны PHP

в 3:21, , рубрики: php, ооп, Программирование, прототипирование, метки: ,

Геттеры и сеттеры при помощи трейта или сильные стороны PHP

Небольшая идея о реализации геттеров и сеттеров в PHP версии > 5.4 и размышления о подходе к свойствам класса в контексте PHP.

Теория

Есть несколько способов обращения к свойствам класса:

1. Сделать их public.
2. Определить методы доступа типа getA, setA.
3. Использовать магические методы __get, __set.

Тут стоит остановиться на специфике PHP, как языка в целом. Динамическая типизация, легкий синтаксис и множество готовых решений открывают безграничные просторы для быстрого прототипирования. PHP действительно позволяет написать много работающего кода без продумывания всей архитектуры в самом начале. И это действительно важно, в современном мире бешеной конкуренции и, практически, беззащитности большинства идей. Поэтому, не стоит рассматривать конечный код PHP и сравнивать его с другими языками — нужно рассматривать весь процесс разработки.

Итак, беспечное, противоречащее канонам ООП и хорошего стиля, назначение аттрибута public нетипизированным свойствам на самом деле имеет огромное значение в первоначальной разработке архитектуры. На этом этапе мы можем с легкостью менять типы свойств объекта, или их имена, нам не нужно беспокоиться о их защите или корректности.
Также логично, что обратиться к свойству класса $a->b = 1 быстрее и короче и на этом этапе не имеет смысла создавать методы доступа (ведь пока нас не волнуют вопросы инкапсуляции — мы занимаемся творчеством).

И вот, у нас появился работающий прототип — настало время подумать о дальнейшей поддержке кода, о защите информации и других программистах. После первого этапа у нас остались классы, определились названия их свойств. И во всем проекте мы обращаемся к свойствам напрямую $a->b. Тут возникает проблема — как нам ограничить доступ к свойствам, не меняя обращения к ним везде. К сожалению, в PHP пока отсутствуют нативные решения этой проблемы. Но все же мы можем это реализовать чуть менее красиво.

Вопрос решается магическими методами __get и __set и созданием методов доступа. А для удобства разработки мы добавляем doc-комментарии к классу. В целом, практически любой объект удобно использовать по такой схеме и логично было бы вынести одинаковые части в отдельный код. До PHP 5.4 это было возможно реализовать только при помощи наследования, но практически — было невозможно из-за отсутствия множественного наследования. Да и в целом — наследовать другой класс с целью лишь вынесения общего кода нарушает логику связей объектов.

И вот, в PHP 5.4 нам подарили не геттеры и сеттеры, а трейты. Честно говоря, я не вижу много причин их использования — в основном желание воспользоваться ими говорит о плохой объектной системе проекта. Но вот некоторую не хватающую функциональность PHP они закрывают. Лично меня больше бы устроили геттеры и сеттеры.

Практика

Нижеприведенный код является небольшим примером реализации геттеров и сеттеров. При желании можно добавлять различные возможности. Главное, что с трейтами это нужно будет делать лишь в одном месте.

	trait GetterSetter
	{
		public function __get($name)
		{
			$getter = 'get' . ucfirst($name);
			if ( ! method_exists($this, $getter) )
			{
				throw new Exception('Not found getter for property - ' . $name);
			}
			return $this->$getter();
		}
		public function __set($name, $value)
		{
			$setter = 'set' . ucfirst($name);
			if ( ! method_exists($this, $setter) )
			{
				throw new Exception('Not found setter for property - ' . $name);
			}
			$this->$setter($value);
			return $this;
		}
	}

В классе A, свойство b контролируется нами с помощью трейта, а c пока «в свободном плаванье».

	/**
	 * 
	 * @property int $b
	 *
	 */
	class A
	{
        use GetterSetter;
        
        /**
         * @var unknown Временно любой тип значения
        */
        public $c;
        private $_b;
		public function getB()
        {
            return $this->_b + 1;
        }
		public function setB($value)
        {
            if ( ! ($value === 2 || $value === 3 ) )
            {
                throw new Exception('Invalid value ' . $value . ' for b' );
            }
            $this->_b = $value - 1;
            return $this;
        }        
	}

Пример обращения к свойству.

    $a = new A;
    $a->b = 7;

Что еще можно сделать на основе такого трейта:

1. Свойства read-only, write-only и т.д.
2. Реализовать присвоение значение свойству в трейте, а в самом классе методы проверки типа checkB().

P.S. Ждем ваши варианты в опрос и примеры использования трейтов.

Автор: arvitaly

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js