Примечание: следующее было протестировано с PHP 5.3.8. Другие версии могут демонстрировать другое поведение.
Поскольку формальной спецификации для PHP не существует, нет способа ответить на это с точки зрения того, что должно произойти . Самое близкое, что мы можем получить - это утверждение о protected из руководства по PHP:
Члены, объявленные защищенными, могут быть доступны только внутри самого класса и унаследованных и родительских классов.
Хотя элемент может быть переопределен в ChildClass
(с сохранением «защищенного» спецификатора), изначально он был объявлен в BaseClass
, поэтому он остается видимым в потомках BaseClass
.
В прямом противоречии с этой интерпретацией сравните поведение для защищенного свойства:
<?php
abstract class BaseClass {
protected $_foo = 'foo';
abstract protected function __construct();
}
class MommasBoy extends BaseClass {
protected $_foo = 'foobar';
protected function __construct(){
echo __METHOD__, "\n";
}
}
class LatchkeyKid extends BaseClass {
public function __construct() {
echo 'In ', __CLASS__, ":\n";
$kid = new MommasBoy();
echo $kid->_foo, "\n";
}
}
$obj = new LatchkeyKid();
Выход:
In LatchkeyKid:
MommasBoy::__construct
Fatal error: Cannot access protected property MommasBoy::$_foo in - on line 18
Изменение абстрактного __construct
на конкретную функцию с пустой реализацией дает желаемое поведение.
abstract class BaseClass {
protected function __construct() {}
}
Однако немагические методы видны у родственников, независимо от того, являются ли они абстрактными (большинство магических методов должны быть открытыми).
<?php
abstract class BaseClass {
abstract protected function abstract_protected();
protected function concrete() {}
}
class MommasBoy extends BaseClass {
/* accessible in relatives */
protected function abstract_protected() {
return __METHOD__;
}
protected function concrete() {
return __METHOD__;
}
}
class LatchkeyKid extends BaseClass {
function abstract_protected() {}
public function __construct() {
echo 'In ', __CLASS__, ":\n";
$kid = new MommasBoy();
echo $kid->abstract_protected(), "\n", $kid->concrete(), "\n";
}
}
$obj = new LatchkeyKid();
Выход:
In LatchkeyKid:
MommasBoy::abstract_protected
MommasBoy::concrete
Если вы игнорируете предупреждения и объявляете магические методы (отличные от __construct
, __destruct
и __clone
) как protected
, они кажутся доступными в родственниках, как и с немагическими методами.
Защищенные __clone
и __destruct
недоступны для родственников, независимо от того, являются ли они абстрактными. Это приводит меня к мысли, что поведение абстрактного __construct
является ошибкой.
<?php
abstract class BaseClass {
abstract protected function __clone();
}
class MommasBoy extends BaseClass {
protected function __clone() {
echo __METHOD__, "\n";
}
}
class LatchkeyKid extends BaseClass {
public function __construct() {
echo 'In ', __CLASS__, ": \n";
$kid = new MommasBoy();
$kid = clone $kid;
}
public function __clone() {}
}
$obj = new LatchkeyKid();
Выход:
In LatchkeyKid:
Fatal error: Call to protected MommasBoy::__clone() from context 'LatchkeyKid' in - on line 16
Доступ к __clone
обеспечивается в zend_vm_def.h (в частности, обработчик кода операции ZEND_CLONE
). Это в дополнение к проверке доступа к методам, что может быть связано с другим поведением. Тем не менее, я не вижу особого подхода для доступа к __destruct
, так что, очевидно, есть еще кое-что.
Стас Малышев (привет, Стас!), Один из разработчиков PHP, изучил __construct
, __clone
и __destruct
и сказал следующее:
В общем, функция, определенная в базовом классе, должна быть доступна всем
[потомки] этого класса. Обоснование этого заключается в том, что если вы определяете
функция (даже абстрактная) в вашем базовом классе, вы говорите, что это будет
доступны для любого экземпляра (в том числе расширенного) этого класса. Так
любой потомок этого класса может использовать его.
[...] Я проверил, почему ctor ведет себя по-другому, и это потому, что родительский ctor
считается прототипом для дочернего ctor (с подписью
и т. д.) только в том случае, если оно объявлено абстрактным или
интерфейс. Итак, объявив ctor абстрактным или сделав его частью
интерфейс, вы делаете его частью договора и, таким образом, доступны для всех
иерархия. Если вы этого не сделаете, ctors совершенно не связаны с каждым
другой (это отличается для всех других нестатических методов) и, таким образом,
наличие родительского ctor ничего не говорит о дочернем ctor, поэтому parent
видимость ctor не переносится. Так что для ctor это не ошибка. [Примечание: это похоже на ответ Дж. Бруни.]
Я все еще думаю, что это, скорее всего, ошибка для __clone и __destruct.
[...]
Я отправил ошибку # 61782 , чтобы отследить проблему с помощью __clone и __destruct.