为什么 PHP 的空合并运算符 (??) 不能对具有不同可见性的 class 常量起作用?

Why doesn't PHP's null coalescing operator (??) work on class constants with different visibilities?

考虑下面的例子。 Class a 有 private const SOMETHING,但 class b 有 protected const SOMETHING.

class a {
    private const SOMETHING = 'This is a!';

    public static function outputSomething() {
        return static::SOMETHING ?? self::SOMETHING;
    }
}

class b extends a {
    protected const SOMETHING = 'This is b!';
}

echo (new b())::outputSomething();

输出:

This is b!

但是现在如果我在class b中注释掉SOMETHING的定义,就会抛出一个错误:

class a {
    private const SOMETHING = 'This is a!';

    public static function outputSomething() {
        return static::SOMETHING ?? self::SOMETHING;
    }
}

class b extends a {
    //protected const SOMETHING = 'This is b!';
}

echo (new b())::outputSomething();

输出:

Fatal error: Uncaught Error: Cannot access private const b::SOMETHING in {file}.php:7

但是,在 class 中将可见性从 private const SOMETHING 更改为 protected const SOMETHING 可以解决此问题。

class a {
    protected const SOMETHING = 'This is a!';

    public static function outputSomething() {
        return static::SOMETHING ?? self::SOMETHING;
    }
}

class b extends a {
    //protected const SOMETHING = 'This is b!';
}

echo (new b())::outputSomething();

现在输出符合预期:

This is a!

我不明白为什么 php 在应用空合并运算符之前评估 b::SOMETHING,根据 the documentation:

The null coalescing operator (??) has been added as syntactic sugar for the common case of needing to use a ternary in conjunction with isset(). It returns its first operand if it exists and is not NULL; otherwise it returns its second operand.

由于未设置 b::SOMETHING,为什么第一个示例不起作用,并且需要对基数 class 中的常量保持一致的可见性?

Since b::SOMETHING is not set, why doesn't the first example work and a consistent visibility is required for the constant in the base class?

B::SOMETHING 已设置。设置它是因为 B 扩展了 A 并且您已将 SOMETHING 定义为 A 的常量。问题不是它没有设置,问题是你没有授予 B 访问它的权限,所以它真的不适合 null 合并格式。

这实际上归结为 private 可见性使用不当。

正如你之前承认的那样:

The null coalescing operator (??) has been added as syntactic sugar for the common case of needing to use a ternary in conjunction with isset(). It returns its first operand if it exists and is not NULL; otherwise it returns its second operand.


这意味着此运算符在您的第二个代码块中工作正常,因为在 class b const 中存在一些东西!但它无法访问。您无法使用 ?? 检查现有封装字段的存在或 'NULL value state'或者使用 isset()。

我猜封装字段的检查逻辑以 :
的方式工作 存在? ->-> 是否为空? -> 错误(可以'访问它以检查值)

其他代码块块正常工作

感谢@Devon 和@Dormilich 的回复。

TL;DR:您不能对常量使用 null 合并运算符 (??)。您必须改用 defined()

根据the documentation for the null coalescing operator (??)

The null coalescing operator (??) has been added as syntactic sugar for the common case of needing to use a ternary in conjunction with isset(). It returns its first operand if it exists and is not NULL; otherwise it returns its second operand.

意味着 $x ?? $y 对于 isset($x) ? $x : $y 是 shorthand。这就是问题所在,因为 documentation for isset 明确指出:

Warning: isset() only works with variables as passing anything else will result in a parse error. For checking if constants are set use the defined() function.

这就是引发我在问题中描述的致命 php 错误的原因。相反,解决方案是取消空合并运算符并将其替换为 defined():

class a {
    private const SOMETHING = 'This is a!';

    public static function outputSomething() {
        return defined('static::SOMETHING') ? static::SOMETHING : self::SOMETHING;
    }
}

class b extends a {
    //protected const SOMETHING = 'This is b!';
}

echo (new b())::outputSomething();

第二种解决方案是首先更改代码的工作方式。正如@Devon 正确指出的那样, a::SOMETHINGprivate 可见性阻止 class b 看到它,因此未定义 b::SOMETHING 。但是,当 a::SOMETHING 的可见性更改为 protected 时, class b 可以看到它并且 b::SOMETHING 可以引用它。此代码根本不需要空合并运算符,可以只使用 static::SOMETHING 而无需任何条件:

class a {
    protected const SOMETHING = 'This is a!';

    public static function outputSomething() {
        return static::SOMETHING;
    }
}

class b extends a {
    //protected const SOMETHING = 'This is b!';
}

echo (new b())::outputSomething();