为啥 PHP 的空合并运算符 (??) 不能处理具有不同可见性的类常量?
Posted
技术标签:
【中文标题】为啥 PHP 的空合并运算符 (??) 不能处理具有不同可见性的类常量?【英文标题】:Why doesn't PHP's null coalescing operator (??) work on class constants with different visibilities?为什么 PHP 的空合并运算符 (??) 不能处理具有不同可见性的类常量? 【发布时间】:2018-11-30 23:06:51 【问题描述】:考虑下面的例子。 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();
输出:
This is b!
但是现在如果我在 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
但是,将 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();
现在输出如预期:
This is a!
我不明白为什么 php 在应用空合并运算符之前评估 b::SOMETHING,根据 the documentation:
已添加空合并运算符 (??) 作为语法糖 对于需要结合使用三元的常见情况 伊塞特()。如果存在且不为 NULL,则返回其第一个操作数; 否则返回第二个操作数。
由于未设置 b::SOMETHING,为什么第一个示例不起作用并且基类中的常量需要一致的可见性?
【问题讨论】:
self和static的区别在***.com/questions/5197300/new-self-vs-new-static中有描述 另外一个问题是isset()不能检查static::SOMETHING
(致命错误:不能在表达式的结果上使用isset())
如果您希望能够在子类中定义,为什么要将 SOMETHING 设置为私有或常量?似乎是一个糟糕的定义选择。
@Dormilich,这就是我最初的想法,但是当类常量具有一致的可见性时它会起作用。文档说要使用defined
作为常量,所以我想我的问题的后续行动是,如果它不应该在任何情况下工作,为什么它可以工作?
@Devon 这样做不是很好的做法,但在我的用例中有性能考虑。事实证明,以这种方式直接从常量加载数据(尤其是数组数据)比使用在子类中需要时覆盖父函数的函数的“正确”继承要快得多。
【参考方案1】:
感谢@Devon 和@Dormilich 的回复。
TL;DR:您不能将空合并运算符 (??
) 与常量一起使用。您必须改用defined()
。
根据the documentation for the null coalescing operator (??):
已添加空合并运算符 (??) 作为语法糖 对于需要结合使用三元的常见情况 伊塞特()。如果存在且不为 NULL,则返回其第一个操作数; 否则返回第二个操作数。
意思是$x ?? $y
是isset($x) ? $x : $y
的简写。这就是问题所在,因为documentation for 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();
第二种解决方案是首先更改代码的工作方式。正如@Devon 正确指出的那样,a::SOMETHING
的private
可见性阻止了 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();
【讨论】:
但是 PHP 开发团队为什么不简单地将这个非常有用的??
运算符也扩展到常量。【参考方案2】:
正如你之前承认的那样:
已添加空合并运算符 (??) 作为语法糖,用于需要将三元组与 isset() 结合使用的常见情况。如果存在且不为 NULL,则返回其第一个操作数;否则返回第二个操作数。这意味着该运算符在您的第二个代码块中正确工作,因为在 b 类中存在 const 一些东西!但它无法访问。您不能使用 ?? 检查现有封装字段的存在或“NULL 值状态”。或使用 isset()。 我猜封装字段的检查逻辑的工作方式是:Exists? ->yes-> is Null? -> 错误(无法访问它来检查值) 其他代码块可以正常工作
【讨论】:
【参考方案3】:由于未设置 b::SOMETHING,为什么第一个示例不起作用并且基类中的常量需要一致的可见性?
B::SOMETHING
已设置。之所以设置它是因为 B
扩展了 A
并且您已将 SOMETHING
定义为 A
的常量。问题不在于它没有设置,问题在于您没有授予B
对其的访问权限,因此它确实不适合空合并格式。
这真的归结为private
可见性的不当使用。
【讨论】:
以上是关于为啥 PHP 的空合并运算符 (??) 不能处理具有不同可见性的类常量?的主要内容,如果未能解决你的问题,请参考以下文章
为啥我不能在 Wordpress 中创建新帖子?警告:从第 716 行 /public_html/wp-admin/includes/post.php 中的空值创建默认对象