在PHP中扩展静态类 - 避免在扩展类中为多个类共享变量

时间:2012-06-24 02:14:05

标签: php oop static extends

我似乎无法在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类而不是简单地将它复制到AB,就像我之前对{{1}的理解一样}}。我尝试过使用关键字extends,但似乎没有任何区别。

如果有人能指导我在这里做错的正确方向,并且如何解决这个问题,那将是可爱的。

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

static::$className

4 个答案:

答案 0 :(得分:2)

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

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

请参阅PHP5手册上的Static KeywordClass Abstraction

答案 1 :(得分:0)

我认为对您的情况最好的答案是使用get_called_class()函数而不是当前的$className变量,它将返回后期静态绑定类名而不是__CLASS__或{ {1}}只返回当前的类名。

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

get_called_class()

答案 2 :(得分:0)

我希望基类保留子类实例的存储库,这样就可以正确地分离属于每个类的数据,而不是从静态基类变量中提取数据。

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

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

答案 3 :(得分:0)

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

使用__callStatic的答案似乎可行,但是对于一些应该很简单的事情来说却相当复杂。另外两个答案似乎并没有为该问题提供切实可行的解决方案,因此,我在此处提供了自己的解决方法。

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

<?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'
?>