Как правило, мнения расходятся касательно того, хорошей ли практикой является использование публичных свойств в PHP классах или всё же стоит использовать геттеры и сеттеры (и хранить свойства приватными или защищёнными). Ещё одно, компромиссное мнение, состоит в том, чтобы использовать магические методы
У каждого из подходов существуют свои достоинства и недостатки, давайте взглянем на них…
Пример использования публичных свойств:
В данном примере bar является публичным свойством класса Foo. При таком подходе мы можем манипулировать данным свойством как угодно и хранить в нём любые данные.
Достоинства публичных свойств
Недостатки публичных свойств
Пример использования геттеров и сеттеров:
Свойство
Достоинства геттеров и сеттеров
Недостатки геттеров и сеттеров
Пример использования магических геттеров и сеттеров:
В данном случае свойство
Достоинства магических геттеров и сеттеров
Недостатки магических геттеров и сеттеров
Какой подход использовать
Очевидно, что у геттеров и сеттеров есть ряд существенных преимуществ, и некоторые люди считают, что их стоит использовать всё время (особенно те, у кого прошлое связано с
Другие альтернативы?
До изучения PHP я использовал C#. В C# все свойства имеют методы доступа, но вам не нужно вызывать их как методы, вы можете манипулировать свойсвами напрямую и соответствующие методы будут вызываться магически. Это в некотором роде это похоже на магические методы
Печально, но, RFC необходимый для реализации C# подобного синтаксиса определения методов доступа к свойствам не набрал необходимых две трети голосов: wiki.php.net/rfc/propertygetsetsyntax-v1.2 Вот так!
__get() и__set(). У каждого из подходов существуют свои достоинства и недостатки, давайте взглянем на них…
Пример использования публичных свойств:
class Foo
{
public $bar;
}
$foo = new Foo();
$foo->bar = 1;
$foo->bar++;
В данном примере bar является публичным свойством класса Foo. При таком подходе мы можем манипулировать данным свойством как угодно и хранить в нём любые данные.
Достоинства публичных свойств
- По сравнению с геттерами и сеттерами, нужно печатать на много меньше текста!
- Код выглядит более читабельным и работать с ним проще, чем с вызовами геттеров и сеттеров.
- Вызов публичного свойства (вместо
setилиget) работает быстрее и использует меньше памяти чем вызов метода, но выгода от этого будет почти незаметной до тех пор, пока вы не будете вызывать метод множество раз в длинном цикле. - Объекты с публичными свойствами могут использоваться в качестве параметров для некоторых PHP функций (таких как
http_build_query).
Недостатки публичных свойств
- Нет возможности осуществлять контроль за тем, какие именно данные содержатся в свойствах — их очень легко заполнить данными, некорректными для методов этого класса. Например, если класс будет использоваться сторонним разработчиком, отличным от автора класса, то могут возникать проблемы связанные с тем, что он может быть не в курсе (да он и не обязан) внутреннего устройства класса. В конце концов, ни для кого не секрет, что зачастую по прошествии несколько месяцев даже сам автор забывает логику написанного им кода.
- Если вы хотите использовать публичные свойства в API, то этого нельзя будет сделать используя интерфейс, так как в PHP интерфейсы допускают только определения сигнатур методов.
Пример использования геттеров и сеттеров:
class Foo
{
private $bar;
public function getBar()
{
return $this->bar;
}
public function setBar($bar)
{
$this->bar = $bar;
}
}
$foo = new Foo();
$foo->setBar(1);
$foo->setBar($foo->getBar() + 1);
Свойство
bar тут является приватным, в связи с этим, доступ к нему не может быть получен напрямую. Для того, чтобы получить значение свойства, вам придётся использовать метод getBar, или setBar для присвоения свойству значения. Чтобы вы могли быть уверенны в том, что входные данные полностью корректны, данные методы могут включать в себя соответствующий функционал для их валидации.Достоинства геттеров и сеттеров
- Используя геттеры и сеттеры вы можете осуществлять контроль за тем, какие именно данные содержатся в свойствах объекта, и отклонять любые некорректные значения.
- Так же вы можете осуществлять дополнительные операции перед тем, как установить или получить значение свойства (например, если обновление данного свойства должно вызывать некоторое действие, такое как оповещение пользователя).
- При установке значения, которое является объектом или массивом, вы можете явно указать тип переменной в сигнатуре функции(прим.
public function setBar(Bar $bar)). К большому сожалению, PHP не позволяет проделывать тоже самое с типамиintиstring! - Если значение свойства должно получаться из внешнего источника или среды исполнения, вы можете использовать ленивую загрузку данных — таким образом ресурсы, требуемые для загрузки данных, будут задействованы непосредственно во время получения значения свойства. Разумеется, в данном случае нужно соблюдать осторожность, и не следует получать данные из внешнего источника при каждом обращении к свойству. Будет лучше сделать одно обращение к базе данных и заполнить значения всех свойств сразу, чем делать это для каждого в отдельности.
- Вы можете сделать свойство доступным только на чтение или только на запись, путём создания только геттера или только сеттера.
- Вы можете добавить геттеры и сеттеры в интерфейс для того, чтобы отобразить их в API.
Недостатки геттеров и сеттеров
- Для разработчиков, которые используют прямой доступ к свойствам, геттеры и сеттеры кажутся настоящей головной болью! Для каждого свойства нужно определить само свойство, геттер и сеттер; и для того чтобы использовать данное свойство в коде, нужно осуществлять дополнительные вызовы метода — намного легче написать
$foo->bar++;вместо$foo->setBar($foo->getBar() + 1);(хотя, конечно, можно добавить ещё один метод$foo->incrementBar();) - Как уже отмечалось выше, существуют небольшие дополнительные расходы, затрачиваемые на вызов метода.
- Имена геттеров и сеттеров принято начинать с глаголов
getиset, но данные глаголы так же могут использоваться и в других методах, которые ни коим образом не относятся к свойствам класса.
Пример использования магических геттеров и сеттеров:
class Foo
{
protected $bar;
public function __get($property)
{
switch ($property)
{
case 'bar':
return $this->bar;
//etc.
}
}
public function __set($property, $value)
{
switch ($property)
{
case 'bar':
$this->bar = $value;
break;
//etc.
}
}
}
$foo = new Foo();
$foo->bar = 1;
$foo->bar++;
В данном случае свойство
bar не является публичным, однако в коде оно используется так, как если бы было публичным. Когда PHP не может найти соответствующего публичного свойства он вы��ывает соответствующий магический метод (__get() для получения значения, __set() для установки значения). Данный подход может показаться золотой серединой, но у него есть существенный недостаток (см. недостатки ниже!). Следует также отметить, что __get() и __set() методы НЕ вызываются для публичных свойств, и вызываются в случае, если свойство помечено как protected или private и находится за пределами области видимости, или если свойство не определено.Достоинства магических геттеров и сеттеров
- Вы можете манипулировать свойствами напрямую (как если бы они были публичными) и сохраняете полный контроль над тем, какие данные хранятся в каких свойствах.
- Аналогично использованию обычных геттеров и сеттеров, вы можете осуществлять дополнительные операции, когда свойство используется.
- Вы можете использовать ленивую загрузку данных.
- Вы можете сделать свойства доступными только на чтение или только на запись.
- Вы можете использовать магические методы для перехвата всех вызовов к свойствам, которые не были определены и обрабатывать их некоторым образом
Недостатки магических геттеров и сеттеров
Недостатком магических методов является то, что они не делают свойство доступным(«видимым»). Для того чтобы использовать или расширить класс, вам необходимо «просто знать» какие свойства он содержит. В большинстве случаев это невозможно (если только вы не один из хардкорных программистов, которые думают что notepad является IDE!), хотя бывают случаи, когда упомянутые выше пре��мущества, перевешивают это ограничение.Как заметил один из комментаторов, этот недостаток может быть устранён использованием phpDoc тегов@property,@property-read, и@property-write. Круто.
Какой подход использовать
Очевидно, что у геттеров и сеттеров есть ряд существенных преимуществ, и некоторые люди считают, что их стоит использовать всё время (особенно те, у кого прошлое связано с
Java!). Но на мой взгляд, этим они нарушают естественное развитие языка, и их излишняя сложность и детализация принуждают меня работать с этим, даже если в этом нет надобности (меня раздражает, когда обычные геттеры и сеттеры НЕ делают ничего, кроме получения и установки свойства). В таких случаях я стараюсь использовать публичные свойства, а геттеры и сеттеры я использую для критических свойств, которые необходимо более строго контролировать, или если в них необходимо использовать отложенную загрузку данных.Другие альтернативы?
До изучения PHP я использовал C#. В C# все свойства имеют методы доступа, но вам не нужно вызывать их как методы, вы можете манипулировать свойсвами напрямую и соответствующие методы будут вызываться магически. Это в некотором роде это похоже на магические методы
__get() и __set() в PHP, однако свойства остаются определены и доступны. Это золотая середина и было бы очень хорошо увидеть аналогичную возможность в PHP.Печально, но, RFC необходимый для реализации C# подобного синтаксиса определения методов доступа к свойствам не набрал необходимых две трети голосов: wiki.php.net/rfc/propertygetsetsyntax-v1.2 Вот так!
