问题
Consider the example below. Class a has private const SOMETHING
, but class b has 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();
Output:
This is b!
But now if I comment out the definition for SOMETHING
in class b, an error is thrown:
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();
Output:
Fatal error: Uncaught Error: Cannot access private const b::SOMETHING in {file}.php:7
However, changing the visibility from private const SOMETHING
to protected const SOMETHING
in class a fixes this.
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();
Now the output is as expected:
This is a!
I don't understand why php is evaluating b::SOMETHING prior to applying the null coalescing operator, which according to 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.
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?
回答1:
Thanks to @Devon and @Dormilich for their responses.
TL;DR: You can't use the null coalescing operator (??
) with constants. You have to use defined()
instead.
According to 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.
Meaning that $x ?? $y
is shorthand for isset($x) ? $x : $y
. And this is where the problem lies, because the documentation for isset explicitly states:
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.
That's what throws the fatal php error I describe in the question. Instead, a solution would be to do away with the null coalescing operator and replace it with 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();
A second solution is to change how the code works in the first place. As @Devon correctly points out, the private
visibility of a::SOMETHING
prevents class b from seeing it, so b::SOMETHING
is not defined. However, when the visibility of a::SOMETHING
is changed to protected
, class b can see it and b::SOMETHING
references it. This code doesn't need the null coalescing operator at all, and can just use static::SOMETHING
without any conditional:
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();
回答2:
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
is set. It's set because B
extends A
and you've defined SOMETHING
as a constant of A
. The problem isn't that it is not set, the problem is that you haven't granted B
access to it, so it really doesn't fit into the null coalescing format.
This really comes down to the improper use of private
visibility.
回答3:
As you admitted before :
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.
This mean that this operator works correct in your second code block because in class b const SOMETHING exist! but it unaccessible. You can't check existent nor 'NULL value state' of existed encapsulated fields with ?? or with isset().
I guess the check logic of encapsulated field works in manner of :
Exists? ->yes-> is Null? -> Error(Can't access it to check value)
Other code block block works as they should
来源:https://stackoverflow.com/questions/50971530/why-doesnt-phps-null-coalescing-operator-work-on-class-constants-with-dif