Чтобы расширить ответ Берри, установка уровня доступа на защищенный позволяет использовать __get и __set с явно объявленными свойствами (по крайней мере, при доступе вне класса), а скорость значительно ниже, я процитирую комментарий из другого вопроса на эту тему и аргументируйте это тем, что используете:
Я согласен, что __get более медленен для пользовательской функции get (делает то же самое), это 0,0124455 времени для __get (), и этот 0,0024445 для пользовательского get () после 10000 циклов. - Melsi 23 ноября '12 в 22:32 Лучшая практика: магические методы PHP __set и __get
Согласно тестам Мелси, значительно медленнее, примерно в 5 раз медленнее. Это определенно значительно медленнее, но также обратите внимание, что тесты показывают, что вы все еще можете обращаться к свойству с помощью этого метода 10000 раз, считая время для итерации цикла примерно за 1/100 секунды. Он значительно медленнее по сравнению с фактическими определенными методами get и set, и это преуменьшение, но в общей схеме, даже в 5 раз медленнее никогда не бывает на самом деле медленно.
Время вычисления операции все еще незначительно и не стоит учитывать в 99% реальных приложений. Единственный раз, когда этого действительно следует избегать, это когда вы на самом деле собираетесь обращаться к свойствам более 10 000 раз за один запрос. Сайты с высоким трафиком делают что-то действительно неправильное, если они не могут позволить себе задействовать еще несколько серверов для поддержки своих приложений. Однострочная текстовая реклама в нижнем колонтитуле сайта с высоким трафиком, где скорость доступа становится проблемой, вероятно, может заплатить за ферму из 1000 серверов с этой строкой текста. Конечный пользователь никогда не станет стучать пальцами, задаваясь вопросом, что так долго загружает страницу, потому что доступ к свойству приложения занимает миллионную долю секунды.
Я говорю это как разработчик, пришедший из среды .NET, но невидимые методы get и set для потребителя не являются изобретением .NET. Они просто не являются свойствами без них, и эти магические методы являются спасительной милостью разработчика PHP для того, чтобы даже назвать свою версию свойств «свойствами» вообще. Кроме того, расширение Visual Studio для PHP поддерживает intellisense с защищенными свойствами, с учетом этого трюка, я думаю. Я думаю, что при достаточном количестве разработчиков, использующих магические методы __get и __set, разработчики PHP настроят время выполнения для удовлетворения потребностей сообщества разработчиков.
Редактировать: Теоретически защищенные свойства выглядели так, как будто они работают в большинстве ситуаций. На практике оказывается, что вы часто захотите использовать свои методы получения и установки при доступе к свойствам в пределах определения класса и расширенных классов. Лучшим решением является базовый класс и интерфейс для расширения других классов, так что вы можете просто скопировать несколько строк кода из базового класса в реализующий класс. Я делаю немного больше с базовым классом моего проекта, поэтому у меня нет интерфейса для предоставления прямо сейчас, но вот непроверенное урезанное определение класса с получением и установкой магического свойства с использованием отражения для удаления и перемещения свойств в защищенный массив:
/** Base class with magic property __get() and __set() support for defined properties. */
class Component {
/** Gets the properties of the class stored after removing the original
* definitions to trigger magic __get() and __set() methods when accessed. */
protected $properties = array();
/** Provides property get support. Add a case for the property name to
* expand (no break;) or replace (break;) the default get method. When
* overriding, call parent::__get($name) first and return if not null,
* then be sure to check that the property is in the overriding class
* before doing anything, and to implement the default get routine. */
public function __get($name) {
$caller = array_shift(debug_backtrace());
$max_access = ReflectionProperty::IS_PUBLIC;
if (is_subclass_of($caller['class'], get_class($this)))
$max_access = ReflectionProperty::IS_PROTECTED;
if ($caller['class'] == get_class($this))
$max_access = ReflectionProperty::IS_PRIVATE;
if (!empty($this->properties[$name])
&& $this->properties[$name]->class == get_class()
&& $this->properties[$name]->access <= $max_access)
switch ($name) {
default:
return $this->properties[$name]->value;
}
}
/** Provides property set support. Add a case for the property name to
* expand (no break;) or replace (break;) the default set method. When
* overriding, call parent::__set($name, $value) first, then be sure to
* check that the property is in the overriding class before doing anything,
* and to implement the default set routine. */
public function __set($name, $value) {
$caller = array_shift(debug_backtrace());
$max_access = ReflectionProperty::IS_PUBLIC;
if (is_subclass_of($caller['class'], get_class($this)))
$max_access = ReflectionProperty::IS_PROTECTED;
if ($caller['class'] == get_class($this))
$max_access = ReflectionProperty::IS_PRIVATE;
if (!empty($this->properties[$name])
&& $this->properties[$name]->class == get_class()
&& $this->properties[$name]->access <= $max_access)
switch ($name) {
default:
$this->properties[$name]->value = $value;
}
}
/** Constructor for the Component. Call first when overriding. */
function __construct() {
// Removing and moving properties to $properties property for magic
// __get() and __set() support.
$reflected_class = new ReflectionClass($this);
$properties = array();
foreach ($reflected_class->getProperties() as $property) {
if ($property->isStatic()) { continue; }
$properties[$property->name] = (object)array(
'name' => $property->name, 'value' => $property->value
, 'access' => $property->getModifier(), 'class' => get_class($this));
unset($this->{$property->name}); }
$this->properties = $properties;
}
}
Приношу свои извинения, если в коде есть ошибки.