3

我似乎无法在 PHP 中扩展静态类。

PHP代码:

<?php
    class InstanceModule {
        public static $className = 'None';
        public static function PrintClassName() {
            echo self::$className . ' (' . __CLASS__ . ')<br />';
        }
    }

    class A extends InstanceModule {
        public static function Construct() {
            self::$className = "A";
        }
    }

    class B extends InstanceModule {
        public static function Construct() {
            self::$className = "B";
        }
    }
?>

我的调用代码,以及我的期望:

<?php
    //PHP Version 5.3.14

    A::PrintClassName(); //Expected 'None' - actual result: 'None'
    B::PrintClassName(); //Expected 'None' - actual result: 'None'

    A::Construct();

    A::PrintClassName(); //Expected 'A' - actual result: 'A'
    B::PrintClassName(); //Expected 'None' - actual result: 'A'

    B::Construct();

    A::PrintClassName(); //Expected 'A' - actual result: 'B'
    B::PrintClassName(); //Expected 'B' - actual result: 'B'

    A::Construct();

    A::PrintClassName(); //Expected 'A' - actual result: 'A'
    B::PrintClassName(); //Expected 'B' - actual result: 'A'
?>

实际完整输出:

None (InstanceModule)
None (InstanceModule)
A (InstanceModule)
A (InstanceModule)
B (InstanceModule)
B (InstanceModule)
A (InstanceModule)
A (InstanceModule)

所以这里发生的事情(从看起来)是,一旦我设置self::$className了任何一个扩展类,它就会覆盖另一个类的变量。我认为这是因为我使用静态类,并且只能有一个类,而InstanceModule不是像我之前对. 我试过改用关键字,但似乎没有什么区别。ABextendsstatic::$className

如果有人能指导我正确的方向,我在这里做错了什么,以及如何解决这个问题,那就太好了。

编辑:澄清一下,这段代码做了我想要的,但显然是一个可怕的解决方法,因为它会破坏扩展和重用函数的整个想法:

<?php
    class A {
        public static $className = 'None';
        public static function PrintClassName() {
            echo self::$className . ' (' . __CLASS__ . ')<br />';
        }
        public static function Construct() {
            self::$className = "A";
        }
    }

    class B {
        public static $className = 'None';
        public static function PrintClassName() {
            echo self::$className . ' (' . __CLASS__ . ')<br />';
        }
        public static function Construct() {
            self::$className = "B";
        }
    }
?>
4

4 回答 4

4

由于 $className 是静态的并且在父类中,当您在 A 或 B 中设置 className 时,它​​会更改父类中的变量,读取变量时也是如此。除非您在扩展类中覆盖 className,否则您将从最初在 InstanceModule 中定义的同一内存位置存储和检索信息。

如果在 A/B 中重新定义 className,则可以分别从 InstanceModule 或 A/B 使用 parent:: 或 self:: 访问 className。根据您要执行的操作,抽象类也可能发挥重要作用。

请参阅PHP5 手册中的静态关键字类抽象

于 2012-06-24T02:42:32.003 回答
3

这似乎仍然是 php 7.3 中的一个问题(在此处发布原始问题 7 年后)。

使用的答案__callStatic看起来可能有效,但对于应该简单的事情来说,它相当复杂。而其他 2 个答案似乎并没有真正为这个问题提供一个有效的解决方案,所以我在这里包括我自己的解决方法作为答案。

您可以用静态数组替换静态变量:

<?php
    class InstanceModule {
        public static $className = [];
        public static function PrintClassName() {
            $calledClass = get_called_class();
            if(empty(self::$className[$calledClass])){
              $thisClassName = "none";
            }else{
              $thisClassName = self::$className[$calledClass];
            }
            echo $thisClassName . ' (' . __CLASS__ . ')<br />';
        }
    }

    class A extends InstanceModule {
        public static function Construct() {
            $calledClass = get_called_class();
            self::$className[$calledClass] = "A";
        }
    }

    class B extends InstanceModule {
        public static function Construct() {
            $calledClass = get_called_class();
            self::$className[$calledClass] = "B";
        }
    }
?>

因此,每个子类的“静态”值InstanceModule都存储在一个键下,该键具有它所源自的类的名称。

对我来说,兄弟类共享静态属性似乎是一个 PHP 错误......这绝不应该发生,尤其是当父类是抽象的时。这种解决方法不是很漂亮,但它是我使用的唯一一种不太复杂的方法。

使用此解决方法,您可以获得所需的结果:

<?php   
    A::PrintClassName(); //Expected 'None' - actual result: 'None'
    B::PrintClassName(); //Expected 'None' - actual result: 'None'

    A::Construct();

    A::PrintClassName(); //Expected 'A' - actual result: 'A'
    B::PrintClassName(); //Expected 'None' - actual result: 'None'

    B::Construct();

    A::PrintClassName(); //Expected 'A' - actual result: 'A'
    B::PrintClassName(); //Expected 'B' - actual result: 'B'

    A::Construct();

    A::PrintClassName(); //Expected 'A' - actual result: 'A'
    B::PrintClassName(); //Expected 'B' - actual result: 'B'
?>
于 2019-06-25T05:36:44.687 回答
2

我会让基类保留一个子类实例的存储库,以便您可以正确分离属于每个类的数据,而不是从静态基类变量中提取该数据。

您可以使用__callStatic()基类上的魔术方法来完成对不存在的子类的调用方法,如下所示。不幸的是,由于该魔术方法的可见性,需要将静态存储库变量声明为 public。

abstract class Base
{
    public static $repo = array();

    public static function __callStatic($name, $args)
    {
        $class = get_called_class();

        if (!isset(self::$repo[$class])) {
                echo "Creating instance of $class\n";
                self::$repo[$class] = new $class();
        }

        return call_user_func_array(array(self::$repo[$class], $name), $args);
    }

        protected function PrintClassName()
        {
                echo __CLASS__, " (", get_called_class(), ")\n";
        }

        protected abstract function Construct($a);
}

class A extends Base
{
        protected function Construct($a)
        {
                echo __CLASS__, ": setting x := $a\n";
        }
}

class B extends Base
{
        protected function Construct($a)
        {
                echo __CLASS__, ": setting y := $a\n";
        }
}

A::PrintClassName();
B::PrintClassName();

A::Construct('X');
B::Construct('Y');

输出:

Creating instance of A
Base (A)
Creating instance of B
Base (B)
A: setting x := X
B: setting y := Y
于 2012-06-25T00:28:25.737 回答
1

我认为针对您的情况的最佳答案是使用get_called_class()函数而不是当前$className变量,这将返回后期静态绑定类名,而不是返回当前类名__CLASS__get_class()仅返回当前类名。

如果您将PrintClassName()函数更改为仅输出get_called_class()返回的内容,您的输出将如下所示。现在你只需要合并一个默认值,它当然会在类之间共享,所以如果你要继续使用静态方法,你必须在两个类中都有这个标志。

A
B
A
B
A
B
A
B
于 2012-06-24T02:47:22.567 回答