PHP: Статическое наследование подкласса - дочерние элементы совместно используют статические переменные?
Как вы можете видеть ниже, у меня есть суперкласс (статья) и два подкласса. Я хочу, чтобы каждый из подклассов имел статический массив, который будет содержать все его объекты.
abstract class Article
{
public static $articles = array(); // Variable for storing all the objects of each sub-class.
public function add_Object_To_Array()
{
array_push(self::$articles, $this);
}
}
class Report extends Article{}
class Interview extends Article{}
- Создание двух объектов отчета и добавление их в свой массив:
$tmp = new Report();
$tmp->add_Object_To_Array();
$tmp = new Report();
$tmp->add_Object_To_Array();
- Создание двух объектов интервью и добавление их в свой массив:
$tmp = new Interview();
$tmp->add_Object_To_Array();
$tmp = new Interview();
$tmp->add_Object_To_Array();
print_r(Report::$articles);
print_r(Interview::$articles);
- Приведенный выше скрипт выплевывает два араи:
Array
(
[0] => Report Object()
[1] => Report Object()
[2] => Interview Object()
[3] => Interview Object()
)
Array
(
[0] => Report Object()
[1] => Report Object()
[2] => Interview Object()
[3] => Interview Object()
)
Который выглядит довольно похоже, если вы спросите меня, но первый должен содержать только Отчеты, а второй только интервью.
1. Кажется, что существует только один массив, почему это только один массив?
2. У меня есть статический контейнер объектов одного класса, это плохое кодирование? (Любые предложения?)
Я довольно новичок в php, но у меня есть опыт работы с java.
2 answers
Все собирается только в один массив по двум причинам:
-
Свойство
$articles
определено только в классеArticle
.Свойства статического класса наследуются не так, как можно было бы ожидать, если вы привыкли к нестатическим свойствам. Хотя они доступны для дочерних классов, они все еще ссылаются на одну переменную в родительском классе, что приводит к поведению, которое вы видите здесь, когда оба дочерних класса используют один и тот же массив.
Единственный способ предотвратить это - определить отдельный массив в каждом из ваших дочерних классов, например:
class Report extends Article { public static $articles = array(); } class Interview extends Article { public static $articles = array(); }
Это действительно имеет смысл, если вы думаете об объявлениях статических переменных как о коде, который запускается при определении класса. Создание статической переменной и назначение ей пустого массива происходит, когда определен класс
Article
. Это не повторяется, когда определены классыInterview
иReport
. Есть только один раз, когда пустой массив получает назначено - есть только одна общая переменная. -
Вы используете
self
в своем методеadd_Object_To_Array()
вместоstatic
.self::
относится к классу, в котором он определен, поэтому, поскольку ваш методadd_Object_To_Array()
определен в классеArticle
, он будет ссылаться на массивArticle::$articles
.static::
Доступен, начиная с PHP 5.3, и относится к классу, в котором он называется. Это известно как Поздняя статическая привязка и приведет к тому, чтоadd_Object_To_Array()
будет ссылаться на либоReport::$articles
, либоInterview::$articles
в зависимости от типа объекта, на который вы его вызываете.
Этот код будет ссылаться на два массива, которые мы объявили на первом шаге:
public function add_Object_To_Array() { array_push(static::$articles, $this); }
Я подумал, что представлю альтернативное решение, которое немного изменит ваш дизайн, но не потребует статических определений в каждом подклассе. В зависимости от вашего использования, это может быть или не быть лучшим вариантом, чем первое решение.
Это решение использует get_class()
чтобы найти тип объекта, который мы храним, а затем сохраняем его в родительском классе - в подмассиве с ключом classname:
abstract class Article
{
public static $articles = array();
public function add_Object_To_Array()
{
// get the actual class of the current object
$class = get_class($this);
// define an empty subarray for this class if we haven't seen it before
if (!isset(self::$articles[$class])) {
self::$articles[$class] = array();
}
// add this object to the appropriate subarray
array_push(self::$articles[$class], $this);
}
}
class Report extends Article{}
class Interview extends Article{}
Приведенный выше код не использует позднюю статическую привязку, поэтому он будет работать с любыми Версия PHP, а не просто PHP 5.3+.
Когда вы запустите эту версию с вашим исходным примером, вы получите следующий вывод:
Array
(
[Report] => Array
(
[0] => Report Object
(
)
[1] => Report Object
(
)
)
[Interview] => Array
(
[0] => Interview Object
(
)
[1] => Interview Object
(
)
)
)
Если у вас есть PHP 5.3 или более поздней версии, вы можете расширить его еще больше и использовать get_called_class()
функция для определения статического метода getInstances()
(в классе Article
), который работает следующим образом:
public static function getInstances()
{
$class = get_called_class();
// return isset(self::$articles[$class]) ? self::$articles[$class] : array();
if (isset(self::$articles[$class])) {
return self::$articles[$class];
} else {
return array();
}
}
Затем вы можете вызвать этот метод в своем примере следующим образом:
print_r(Report::getInstances());
print_r(Interview::getInstances());