Недавно я работал с классами, которые инициализируют себя из массива данных и имеют типизированные свойства, которые устанавливаются со значениями, извлеченными из массива.
Свойства типизированы, что означает, что PHP выдаст ошибку TypeError, если будет передано что-либо, кроме указанного типа.
Однако PhpStorm, похоже, распространяет тип mixed
из доступа к массиву в свойство класса.
Я изо всех сил пытаюсь объяснить это словами, поэтому вот пример сценария:
<?php
class TestClass
{
public readonly string $myString;
public readonly int $myInt;
public readonly array $myArray;
public function __construct(array $src)
{
$this->myString = $src['stringVal'];
$this->myInt = $src['intVal'];
$this->myArray = $src['array'];
}
}
$arr = [
'stringVal' => 123,
'intVal' => "I am a string",
'array' => null,
];
$x = new TestClass($arr); // throws TypeError from within the constructor
$y = new TestClass($x->myInt); // PhpStorm doesn't see anything wrong with this???
Вы можете видеть, что есть типы для всех трех свойств. Вы можете посмотреть на сценарий и увидеть, что он потерпит неудачу. Сценарий в том виде, в котором он написан, выдает ошибку TypeError точно так, как вы ожидаете, когда пытается установить $this->myString
.
Однако, если я наведу курсор на любое из свойств класса, PhpStorm добавит к свойству тип mixed
:
Поскольку PhpStorm обрабатывает все свойства как «x или смешанные», он не видит ничего плохого в явно некорректной последней строке скрипта, где мы пытаемся передать int в конструктор, для которого требуется массив:
Как я могу заставить PhpStorm прекратить вывод mixed
этих свойств?
Обновление: это не только mixed
исходит из массива, кажется, что PhpStorm просто предполагает, что все, что происходит в конструкторе, является допустимым, независимо от типов свойств (?)
Другой пример:
class TestClass2
{
public readonly int $myInt;
public function __construct()
{
$this->myInt = "hello world";
}
}
Конструктор снова выдает ошибку TypeError, как и следовало ожидать. Но PhpStorm, кажется, согласен предположить, что свойство будет принимать строку:
Не объясняет поведение PhpStorm, о котором вы должны сообщить JetBrains в отчете об ошибке, но вы можете сделать что-то очень похожее и без отображения: класс, подобный приведенному выше коду, скрывает важные детали и не должен беспокоить класс, чтобы взять на себя это.
Вы можете отправить отчет об ошибке PHPStorm здесь
@Martin Я проделывал этот трюк в некоторых местах, но я не думаю, что собираюсь изменить привычки всех остальных, чтобы обойти (очевидную) проблему с ошибкой / конфигурацией в моей среде IDE :)
@JaredFarrish Я не знал об этом синтаксисе инициализации, он выглядит очень полезным!
Я немного сомневаюсь, что вещи слишком милые. Например, в этом примере свойства (которые являются общедоступными) сопоставляются напрямую, без посредничества. Когда в него войдет массив? Возможно, хранится в контексте чего-то другого. В конечном итоге вам может быть трудно изменить эти свойства, если то, что инициализирует объект, хранится в состоянии и его нелегко или практически увидеть/изменить (особенно с помощью инструментов рефакторинга). Если вы собираетесь использовать массивы таким образом, я бы предложил создать статические методы with<>()
, которые отображают этот массив для вас, и дать им соответствующие имена.
Пример использования инициализатора with
: 3v4l.org/mUqSd#v8.1.17
@JaredFarrish Это, по общему признанию, надуманный пример, иллюстрирующий проблему IDE, которую я пытаюсь решить - я бы не рекомендовал писать такой класс обычно :) Реальные сценарии, с которыми я работаю, немного сложнее их, но сводится к тому, что «среда IDE делает что-то неправильно, когда я назначаю что-то неправильное/подозрительное в конструкторе».
Верно, и об этом следует сообщить JetBrains.
Попался. Я надеялся, что там будет какая-то настройка или что-то, чего мне не хватало, но я отправлю отчет об ошибке.
Я подал отчет об ошибке в JetBrains, и они дали мне следующий обходной путь:
В качестве обходного пути вы можете либо пометить файл
declare(strict_types=1);
, либо включить PHP | Совместимость типов | Нарушение правил строгой проверки типов: Включить для всех файлов.
В моем случае настройка проверки кода была наиболее подходящей и решила проблему для меня.
Вы можете помочь подсказке типов PHPStorm следующим образом:
$this->myInt = (int)$src['intVal'];
принудительное приведение типов. Лично я считаю, что принудительное приведение очень хорошо подходит для обеспечения того, чтобы0
распознавалось как0
, даже если оно ошибочно сохранено как'0'
и т. д.