【问题标题】:Abstract constants in PHP - Force a child class to define a constantPHP 中的抽象常量 - 强制子类定义常量
【发布时间】:2012-05-09 06:19:30
【问题描述】:

我注意到 PHP 中不能有抽象常量。

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

【问题讨论】:

  • 一个常量,一旦设置它在每个类、函数方法或其他东西中可用,它就可以在整个范围内使用。没有意义,请提供一些代码来解释一下。
  • 要么在抽象类中定义一个常量(因此每个子类都有一个常量,即使它没有定义自己的),或者使用抽象函数(它强制每个子类定义自己的)。编辑:您的问题的简短回答是否定的。
  • 如果你必须在运行时设置一个值,而不是根据定义,一个变量。

标签: php constants abstract


【解决方案1】:

这可能有点“黑客”,但工作量很小,但如果未在子类中声明常量,则会显示不同的错误消息。

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

在此示例中,抽象父类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;

【讨论】:

  • 这是我认为最优雅的解决方案 - 谢谢!
  • 仅供参考,我刚刚在 php 7.2 中尝试过,但它不起作用。抛出异常:致命错误:未捕获错误:无法声明自引用常量'self::RAW_DATA_CACHE_KEY'
  • 我刚刚用 PHP 7.2.19 进行了测试,它就像一个魅力。请注意,你不能在类中调用 self::NAME,而是 $this::NAME。
  • @ChrisFlannagan 尝试static::NAME
  • PhpStorm 2019.3.4 错误地将其标记为错误“无法声明自引用常量”,但是,这是一个错误,这是有效的语法。为错误报告投票:youtrack.jetbrains.com/issue/WI-58073
【解决方案2】:

constantconstant;据我所知,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());
    }
}

这样可以正常运行

class Foo extends Hello {
    const CONSTANT_1 = 'HELLO_A';
    const CONSTANT_2 = 'HELLO_B';
}
new Foo();

Bar 会返回错误

class Bar extends Hello {
    const CONSTANT_1 = 'BAR_A';
}
new Bar();

Songo 会返回错误

class Songo extends Hello {

}
new Songo();

执行器类

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

【讨论】:

  • 这不是一个常数,这是一个函数。
  • @Baba 很好的解决方法 :) 但这是否意味着对于每个常量都会有一个 if 语句?
  • @Songo .. 你可以看到更新的代码 .. 它已经适用于正常和抽象 .... 我更改了关键字,以便您可以理解
  • 虽然这可行,但我觉得这个解决方案相当麻烦、不可靠并且引入了不必要的复杂性。如果 Abstract 类依赖于运行时信息,@Alex 应该将 Template Method 模式与 Constant Methods 一起使用,或者检查处理方法内部是否存在默认常量值。此外,很容易忽略 ctor 中对 Enforcer 的硬编码依赖。子类可以覆盖 ctor,然后所有对反射 API 的肮脏摆弄将不再起作用。
  • 我的评论包括两个更好的解决方案的建议。此外,如果您知道您的解决方案只是一个概念验证并且不能在生产中使用,那么答案应该是这样的。来到 SO 的人们通常会为他们在生产中遇到的实际问题寻求帮助。但他们不一定知道某些事情是不好的做法、黑客行为或只是为了“教育目的”。除非这么说。这就是我指出这一点的原因。
【解决方案3】:

不幸的是,不是……常数就是它在锡上所说的那样,常数。一旦定义了就不能重新定义,这样就不可能通过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();

从您最初的描述来看,这是我能想到的最佳方式。

【讨论】:

  • 常量的值可以在子类中被覆盖。常量不能免于这种重新定义。
  • @Brilliand 是的,那不是一个常数。
  • @AlfonsoFernandez-Ocampo 如果它在不同的上下文中(即子类),那么它实际上是一个不同的常量,而不是对第一个常量的更改。拥有“常数”意味着不能在其他地方定义任何模糊常数的东西是相当极端的。
【解决方案4】:

不,但您可以尝试其他方式,例如抽象方法:

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
}  

或静态成员:

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
} 

Source

【讨论】:

  • 如果您从网络上的其他地方复制代码,请确保您也引用了源代码。以上代码取自sitepoint.com/forums/showthread.php?629565-Abstract-constants
  • 这比接受的答案更好。如果抽象类依赖子类,定义一个抽象方法来声明这个依赖,并使用该方法从实现类中获取值。
【解决方案5】:

在 php 7.2 中测试,但应该从 5.3 开始,您可以利用后期静态绑定来存档此行为。它将抛出与异常相同的致命错误,因为在大多数情况下,您不想在运行时处理 致命 错误。 如果您愿意,您可以轻松实现自定义错误处理程序。

所以以下对我有用:

<?php

abstract class Foo {

    public function __construct() {
        echo static::BAR;
    }

}


class Bar extends Foo {
    const BAR = "foo bar";
}

$bar = new Bar();    //foo bar

如果您删除 const,您将获得:

Fatal error: Uncaught Error: Undefined class constant 'BAR' in ...

【讨论】:

  • 不幸的是,您没有收到编译时错误 - 您只会在执行 echo static::BAR; 时收到错误。 IDE 或静态分析器不会告诉 Bar 类的作者他们必须定义常量。
【解决方案6】:

也许我遗漏了一些东西,但使用后期静态绑定对我有用。这对您的问题有用吗?

abstract class A{
    const NAME=null;

    static function f(){
        return static::NAME;
    }
}

class B extends A{
    const NAME='B';    
}

B::f();

【讨论】:

    【解决方案7】:

    PHP 接口支持常量。这并不理想,因为您必须记住在每个子类上实现接口,因此它会部分地达到目的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-04-15
      • 2021-07-18
      • 2011-08-19
      • 1970-01-01
      • 2012-12-04
      相关资源
      最近更新 更多