我似乎无法在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
类而不是简单地将它复制到A
和B
,就像我之前对{{1}的理解一样}}。我尝试过使用关键字extends
,但似乎没有任何区别。
如果有人能指导我在这里做错的正确方向,并且如何解决这个问题,那将是可爱的。
修改 的: 为了澄清,这段代码做了我想要的,但显然是一个可怕的解决方法,因为它会破坏扩展和重用函数的整个想法:
static::$className
答案 0 :(得分:2)
由于$ className是静态的并且在父类中,因此当您在A或B中设置className时,它会更改父级中的变量,并且在读取变量时也会这样做。除非在扩展类中覆盖className,否则您将从最初在InstanceModule中定义的相同内存位置存储和检索信息。
如果在A / B中重新定义className,则可以分别使用parent ::或::来自InstanceModule或A / B访问className。根据您的尝试,抽象类也可能发挥重要作用。
请参阅PHP5手册上的Static Keyword或Class 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'
?>