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.

Author: MathiasCiarlo, 2013-07-13

2 answers

Все собирается только в один массив по двум причинам:

  1. Свойство $articles определено только в классе Article.

    Свойства статического класса наследуются не так, как можно было бы ожидать, если вы привыкли к нестатическим свойствам. Хотя они доступны для дочерних классов, они все еще ссылаются на одну переменную в родительском классе, что приводит к поведению, которое вы видите здесь, когда оба дочерних класса используют один и тот же массив.

    Единственный способ предотвратить это - определить отдельный массив в каждом из ваших дочерних классов, например:

    class Report extends Article {
        public static $articles = array();
    }
    class Interview extends Article {
        public static $articles = array();
    }
    

    Это действительно имеет смысл, если вы думаете об объявлениях статических переменных как о коде, который запускается при определении класса. Создание статической переменной и назначение ей пустого массива происходит, когда определен класс Article. Это не повторяется, когда определены классы Interview и Report. Есть только один раз, когда пустой массив получает назначено - есть только одна общая переменная.

  2. Вы используете 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);
    }
    
 9
Author: jcsanyi, 2013-07-13 18:38:09

Я подумал, что представлю альтернативное решение, которое немного изменит ваш дизайн, но не потребует статических определений в каждом подклассе. В зависимости от вашего использования, это может быть или не быть лучшим вариантом, чем первое решение.

Это решение использует 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());
 3
Author: jcsanyi, 2013-07-13 19:38:23