Как правило, мнения расходятся касательно того, хорошей ли практикой является использование публичных свойств в PHP классах или всё же стоит использовать геттеры и сеттеры (и хранить свойства приватными или защищёнными). Ещё одно, компромиссное мнение, состоит в том, чтобы использовать магические методы __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 Вот так!