为什么 PHP 的空合并运算符 (??) 不能处理具有不同可见性的类常量?

Ant*_*ton 6 php visibility constants null-coalescing-operator php-7

考虑下面的例子。a类有private const SOMETHING,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();
Run Code Online (Sandbox Code Playgroud)

输出:

This is b!
Run Code Online (Sandbox Code Playgroud)

但是现在如果我注释掉SOMETHING类 b 中的定义,则会引发错误:

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();
Run Code Online (Sandbox Code Playgroud)

输出:

Fatal error: Uncaught Error: Cannot access private const b::SOMETHING in {file}.php:7
Run Code Online (Sandbox Code Playgroud)

但是,将a 类中的可见性更改为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();
Run Code Online (Sandbox Code Playgroud)

现在输出符合预期:

This is a!
Run Code Online (Sandbox Code Playgroud)

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

空合并运算符 (??) 已被添加为语法糖,用于需要将三元与 isset() 结合使用的常见情况。如果存在且不为 NULL,则返回其第一个操作数;否则它返回它的第二个操作数。

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

Ant*_*ton 7

感谢@Devon 和@Dormilich 的回应。

TL;DR:您不能将空合并运算符 ( ??) 与常量一起使用。你必须defined()改用。

根据空合并运算符 (??) 的文档

空合并运算符 (??) 已被添加为语法糖,用于需要将三元与 isset() 结合使用的常见情况。如果存在且不为 NULL,则返回其第一个操作数;否则它返回它的第二个操作数。

意思$x ?? $y是 的简写isset($x) ? $x : $y。这就是问题所在,因为isset文档明确指出:

警告:isset() 仅适用于变量,因为传递任何其他内容都会导致解析错误。要检查是否设置了常量,请使用 defined() 函数。

这就是我在问题中描述的致命 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();
Run Code Online (Sandbox Code Playgroud)

第二种解决方案是首先更改代码的工作方式。正如@Devon 正确指出的那样, 的private可见性a::SOMETHING阻止了 b 类看到它,因此b::SOMETHING没有定义。但是,当 的可见性a::SOMETHING更改为 时protected,类 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();
Run Code Online (Sandbox Code Playgroud)

  • 但是为什么 PHP 开发团队不简单地将这个非常有用的 `??` 运算符也扩展到常量。 (3认同)