PHP中的抽象常量 - 强制子类定义常量

Ale*_*lex 44 php constants abstract

我注意到你不能在PHP中使用抽象常量.

有没有办法可以强制子类定义一个常量(我需要在其中一个抽象类内部方法中使用)?

Web*_*ery 46

这可能是一个"黑客",但只需很少的努力就可以完成工作,但如果未在子类中声明常量,则只需使用不同的错误消息.

自引用常量声明在语法上是正确的并且解析没有问题,只有在运行时实际执行该声明时才抛出错误,因此必须在子类中重写抽象类中的自引用声明否则会出现致命错误:Cannot declare self-referencing constant.

在此示例中,abstract,parent类Foo强制其所有子节点声明变量NAME.此代码运行正常,输出Donald.但是,如果孩子上课Fooling没有声明变量,会触发致命的错误.

<?php

abstract class Foo {

    // Self-referential 'abstract' declaration
    const NAME = self::NAME;

}

class Fooling extends Foo {

    // Overrides definition from parent class
    // Without this declaration, an error will be triggered
    const NAME = 'Donald';

}

$fooling = new Fooling();

echo $fooling::NAME;
Run Code Online (Sandbox Code Playgroud)

  • 仅供参考,我刚刚在php 7.2中尝试了此操作,但无法正常工作。引发异常:致命错误:未捕获错误:无法声明自引用常量'self :: RAW_DATA_CACHE_KEY' (6认同)
  • 此解决方案在 PHP 7.4 中不起作用,在运行时会抛出异常“无法声明自引用常量 'self::MY_VAR'”。 (4认同)
  • PhpStorm 2019.3.4 错误地将其标记为错误“无法声明自引用常量”,但是,这是一个错误,这是有效的语法。为错误报告投票:https://youtrack.jetbrains.com/issue/WI-58073 (3认同)
  • 这是我认为最优雅的解决方案 - 谢谢! (2认同)
  • 我刚刚使用 PHP 7.2.19 进行了测试,它的效果非常好。请注意,您不能在类中调用 self::NAME,但可以调用 $this::NAME。 (2认同)
  • @ChrisFlannagan 尝试 `static::NAME` (2认同)

Bab*_*aba 28

A constant是一个constant; 据我所知,PHP中没有abstractprivate常量,但你可以有一个解决方法:

样本抽象类

abstract class Hello {
    const CONSTANT_1 = 'abstract'; // Make Abstract
    const CONSTANT_2 = 'abstract'; // Make Abstract
    const CONSTANT_3 = 'Hello World'; // Normal Constant
    function __construct() {
        Enforcer::__add(__CLASS__, get_called_class());
    }
}
Run Code Online (Sandbox Code Playgroud)

这样运行正常

class Foo extends Hello {
    const CONSTANT_1 = 'HELLO_A';
    const CONSTANT_2 = 'HELLO_B';
}
new Foo();
Run Code Online (Sandbox Code Playgroud)

Bar会返回Error

class Bar extends Hello {
    const CONSTANT_1 = 'BAR_A';
}
new Bar();
Run Code Online (Sandbox Code Playgroud)

Songo 将返回Error

class Songo extends Hello {

}
new Songo();
Run Code Online (Sandbox Code Playgroud)

执行者类

class Enforcer {
    public static function __add($class, $c) {
        $reflection = new ReflectionClass($class);
        $constantsForced = $reflection->getConstants();
        foreach ($constantsForced as $constant => $value) {
            if (constant("$c::$constant") == "abstract") {
                throw new Exception("Undefined $constant in " . (string) $c);
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 虽然这有效,但我觉得这个解决方案相当麻烦,不可靠并且引入了不必要的复杂性.如果Abstract类依赖于运行时信息,则@Alex应该使用*Template Method*模式和*Constant Methods*,或者在处理方法中检查是否存在默认常量值.此外,很容易忽视ctor中对Enforcer的硬编码依赖性.子类可以覆盖ctor,然后使用Reflection API进行的所有脏操作将不再起作用. (9认同)
  • 我的评论包括两个更好的解决方案的建议.此外,如果你知道你的解决方案只是一个概念验证而*不能用于生产*,答案应该这样说.来到SO的人通常会寻求帮助解决他们在生产中遇到的实际问题.但他们并不一定知道一些不好的做法,一个黑客或只是为了"教育目的".除非这么说.这就是我指出它的原因. (4认同)
  • 这不是一个常数,这是一个功能. (2认同)

Jam*_*low 19

不幸的是,不是......一个常数正是它在锡上所说的,不变的.一旦定义,就无法重新定义,因此以这种方式,不可能通过PHP的抽象继承或接口来定义它的定义.

但是......您可以检查是否在父类的构造函数中定义了常量.如果没有,则抛出异常.

abstract class A
{
    public function __construct()
    {
        if (!defined('static::BLAH'))
        {
            throw new Exception('Constant BLAH is not defined on subclass ' . get_class($this));
        }
    }
}

class B extends A
{
    const BLAH = 'here';
}

$b = new B();
Run Code Online (Sandbox Code Playgroud)

这是我从最初的描述中想到的最佳方式.

  • 可以在子类中重写常量的值.常数并不能免于那种重新定义. (6认同)

Son*_*ngo 7

不,但你可以尝试其他方法,如抽象方法:

abstract class Fruit
{
    abstract function getName();
    abstract function getColor();

    public function printInfo()
    {
        echo "The {$this->getName()} is {$this->getColor()}";
    }
}

class Apple extends Fruit
{
    function getName() { return 'apple'; }
    function getColor() { return 'red'; }

    //other apple methods
}

class Banana extends Fruit
{
    function getName() { return 'banana'; }
    function getColor() { return 'yellow'; }

    //other banana methods
}  
Run Code Online (Sandbox Code Playgroud)

或静态成员:

abstract class Fruit
{
    protected static $name;
    protected static $color;

    public function printInfo()
    {
        echo "The {static::$name} is {static::$color}";
    }
}

class Apple extends Fruit
{
    protected static $name = 'apple';
    protected static $color = 'red';

    //other apple methods
}

class Banana extends Fruit
{
    protected static $name = 'banana';
    protected static $color = 'yellow';

    //other banana methods
} 
Run Code Online (Sandbox Code Playgroud)

资源

  • 如果您从Web上的其他位置复制代码,请确保您也引用了源代码.以上代码摘自http://www.sitepoint.com/forums/showthread.php?629565-Abstract-constants (5认同)
  • 这比接受的答案更好。如果抽象类依赖于子类,则定义一个抽象方法来声明此依赖项,并使用该方法从实现类中获取值。 (3认同)