PHP:子类静态继承 - 子类共享静态变量?

7

如下所示,我有一个超类(Article)和两个子类。 我希望每个子类都有一个静态数组,用于保存其所有对象。

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{}

-创建两个Report对象并将它们添加到它们的数组中:
$tmp = new Report();
$tmp->add_Object_To_Array();

$tmp = new Report();
$tmp->add_Object_To_Array();

-创建两个Interview对象并将它们添加到它们的数组中:
$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背景。


抽象类Article缺少其结束的}。在该上下文中,$this指的是抽象类Article。您将如何将其推送到数组中? - StackSlave
谢谢,但我认为: $tmp = new Report(); $tmp->add_Object_To_Array(); 指的是“报告对象的函数”(面向对象思维)。我知道抽象类不能有对象。 - MathiasCiarlo
õ¢┐þö¿static::$articlesÞÇîõ©ìµÿ»self::$articlesÒÇé - AlexP
1
谢谢,但我已经尝试过了,它没有起作用。 - MathiasCiarlo
@AlexP static::是必需的,但它只是解决方案的一部分。您还需要单独定义变量。 - jcsanyi
太好了!问题解决了,谢谢!就像jcsanyi说的那样! - MathiasCiarlo
3个回答

16

所有数据都放在一个数组中有两个原因:

  1. The $articles property is only defined in the Article class.

    Static class properties do not get inherited the same way you might expect if you're used to non-static properties. While they are available to the child classes, they're still referencing a single variable on the parent class - resulting in the behavior you're seeing here where both child classes are sharing the same array.

    The only way to prevent this is to define a separate array in each of your child classes, like this:

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

    This actually makes sense if you think of the static variable declarations as code that gets run when the class is defined. Creating a static variable and assigning an empty array to it happens when the Article class is defined. It doesn't happen again when the Interview and Report classes are defined. There's only one time that an empty array is getting assigned - there's only one shared variable.

  2. You're using self in your add_Object_To_Array() method instead of static.

    • self:: refers to the class it is defined in, so since your add_Object_To_Array() method is defined in the Article class, it'll refer to the Article::$articles array.

    • static:: Is available starting in PHP 5.3, and refers to the class it is called on. This is known as Late Static Binding, and will result in add_Object_To_Array() referring to either Report::$articles or Interview::$articles depending on the type of the object you're calling it on.

    This code will reference the two arrays that we declared in the first step:

    public function add_Object_To_Array() {
        array_push(static::$articles, $this);
    }
    

非常感谢!非常好的答案,既有教育性又解决了问题! :) - MathiasCiarlo

3

我认为我可以提供一个备选方案,稍微改变您的设计,但不需要在每个子类中进行静态定义。根据您的使用情况,这可能是比第一种解决方案更好的选择。

这个解决方案使用get_class()来查找我们正在存储的对象类型,然后将其存储在父类中 - 在由类名键控的子数组中:

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());

不错!我没有完全理解你的扩展方法,更确切地说是return isset(self::$articles[$class]) ? self::$articles[$class] : array();,我对这种类型的if语句不是很熟悉。如果语句为“false”,它是否返回一个空数组? - MathiasCiarlo
@MathiasCiarlo 我在那里使用了三元运算符。我已经将其重写为更清晰但本质上等效的版本。 - jcsanyi

1
对于寻找替代方案的任何人来说,除了上述的好方法之外,你还可以使用PHP的后期静态绑定(static::),但你还需要在每个子类中单独定义变量(这将使它们独立)。你可以这样使用:
abstract class Article
{
    public static $articles = array();

    public function add_Object_To_Array()
    {
        array_push(static::$articles, $this);
    }
}

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

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

然后当从每个子类调用add_Object_To_Array()时,它将把文章存储在不同的位置。
请参阅延迟静态绑定

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接