Геттеры и сеттеры при помощи трейта или сильные стороны 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. Ждем ваши варианты в опрос и примеры использования трейтов.

А что вам больше всего не хватает в PHP?

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

Никто ещё не голосовал. Воздержавшихся нет.

ссылка на оригинал статьи http://habrahabr.ru/post/186420/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *