Рассмотрим пример ниже. Класс А имеет private const SOMETHING
, но класс б имеет 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!
Но теперь, если я закомментирую определение для 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();
Выход:
Fatal error: Uncaught Error: Cannot access private const b::SOMETHING in {file}.php:7
Тем не менее, изменение видимости от 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 до применения оператора объединения нулей, который в соответствии с документация:
Нулевой оператор объединения (??) был добавлен как синтаксический сахар
для общего случая необходимости использовать троичный в сочетании с
Исеть (). Возвращает свой первый операнд, если он существует и не равен NULL;
в противном случае он возвращает свой второй операнд.
Поскольку b :: SOMETHING не задано, почему не работает первый пример, и для константы в базовом классе требуется согласованная видимость?
Поскольку b :: SOMETHING не задано, почему не работает первый пример, и для константы в базовом классе требуется согласованная видимость?
B::SOMETHING
установлено. Это установлено, потому что B
продолжается A
и вы определили SOMETHING
как константа A
, Проблема не в том, что он не установлен, проблема в том, что вы не предоставили B
доступ к нему, так что он действительно не вписывается в нулевой формат объединения.
Это действительно сводится к неправильному использованию private
видимость.
Спасибо @Devon и @Dormilich за их ответы.
TL; DR: Вы не можете использовать оператор объединения нулей (??
с константами. Вы должны использовать defined()
вместо.
В соответствии с документация для оператора объединения нулей (??):
Нулевой оператор объединения (??) был добавлен как синтаксический сахар
для общего случая необходимости использовать троичный в сочетании с
Исеть (). Возвращает свой первый операнд, если он существует и не равен NULL;
в противном случае он возвращает свой второй операнд.
Означающий, что $x ?? $y
это сокращение для isset($x) ? $x : $y
, И здесь кроется проблема, потому что документация для isset прямо заявляет:
Предупреждение: isset () работает только с переменными, передавая что-либо еще
приведет к ошибке разбора. Для проверки, установлены ли константы, используйте
определенная () функция.
Вот что выдает роковую ошибку 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, private
видимость a::SOMETHING
препятствует тому, чтобы класс b видел это, так b::SOMETHING
не определено. Тем не менее, когда видимость a::SOMETHING
изменено на protected
, класс В может видеть это и b::SOMETHING
ссылается на это. Этот код вообще не нуждается в операторе слияния null и может просто использовать 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();
Как вы признались раньше:
Нулевой оператор объединения (??) был добавлен как синтаксический сахар для общего случая необходимости использования троичного в сочетании с isset (). Возвращает свой первый операнд, если он существует и не равен NULL; в противном случае он возвращает свой второй операнд.
Это означает, что этот оператор работает правильно во втором блоке кода, потому что в классе b const ЧТО-ТО существует! но это недоступно. Вы не можете проверить ни существующие, ни ‘NULL-значения состояния’ существующих инкапсулированных полей с помощью ?? или с isset ().
Я предполагаю, что логика проверки инкапсулированного поля работает следующим образом:
Существует? ->да-> нулевой? -> Ошибка (не могу получить доступ к нему, чтобы проверить значение)
Другой блок кода работает как надо