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?
In PHP 7, a new feature, null coalescing operator (??) has been introduced. It is used to replace the ternary operation in conjunction with isset() function. The Null coalescing operator returns its first operand if it exists and is not NULL; otherwise it returns its second operand.
The null-coalescing operator ?? returns the value of its left-hand operand if it isn't null ; otherwise, it evaluates the right-hand operand and returns its result.
Ternary Operator throws e-notice if left operand is null, while null coalescing operator does not throws e-notice if left operand does not exist. Ternary Operator checks whether the value is true, but Null coalescing operator checks if the value is not null.
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();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With