#php #generics #phpdoc
#php #универсальные классы #phpdoc
Вопрос:
Я пытаюсь реализовать класс результатов, который обрабатывает запросы. Итак, проще говоря, у вас были бы такие функции, как:
function all();
function first();
function paginate(int $perPage, int $pageNo = 1);
Это работает довольно хорошо, проблема в том, что IDE не имеет возможности узнать тип возвращаемого значения, когда этот же класс результатов используется в нескольких разных классах запросов. Пример:
UserQuery->results()->all()
вернет массив пользовательских объектов.
UserQuery->results()->first()
вернет единственную пользовательскую сущность.
В некоторых языках у вас есть общие классы, что означает, что я мог бы просто использовать Results<User>
в классе UserQuery, и тогда мой класс результатов мог бы возвращать T[]
и T
соответственно.
Одна из моих идей заключалась в том, чтобы передать пустую сущность в качестве конструктора классу Results, а затем попытаться использовать это свойство в качестве возвращаемого типа, но я не смог этого понять. Есть ли какой-либо обходной путь для этого? Основная проблема, которую я пытаюсь решить, — это автозаполнение и анализ IDE, поэтому чистое решение PHPDoc идеально подходит для моего варианта использования.
Единственное другое решение, которое я могу придумать, — это написать отдельный класс результатов для каждого типа объекта, что оказалось бы утомительным.
Комментарии:
1. В Eclipse и PHP storm вы можете вручную создать подсказку типа, подобную этой,
/* @var $foo Users */
например, прямо перед циклом, чтобыas $foo
часть имела автозаполнение.2. Да, это то, что я обычно делаю, когда IDE не определяет правильный тип. К сожалению, это приводит к написанию почти такого же объема кода, как и в последнем варианте, поскольку мне приходится документировать возвращаемое значение при каждом использовании, а не только там, где оно определено. Это также не очень удобно для рефакторинга.
Ответ №1:
Я не думаю, что есть способ сделать именно то, что вы описали, но в подобных случаях я бы предложил использовать прокси-класс для каждого типа результатов и документировать правильные типы возвращаемых данных, используя @метод phpDocumentor. Это решение имеет дополнительную ценность в виде наличия отличного места для любых модификаций и расширений результатов, специфичных для конкретного типа. Вот пример:
abstract class Results
{
function all(): array
{
}
function first()
{
}
function paginate(int $perPage, int $pageNo = 1): array
{
}
}
class User { }
/**
* @method User[] all()
* @method User first()
* @method User[] paginate(int $perPage, int $pageNo = 1)
*/
class UserResults extends Results { }
class UserQuery
{
/**
* @var UserResults
*/
private $results;
public function __construct()
{
$this->results = new UserResults();
}
public function results(): UserResults
{
return $this->results;
}
}
$userQuery = new UserQuery();
$test = $userQuery->results()->all();
Комментарии:
1. Только сейчас я заметил ваш комментарий о «отдельном классе результатов для каждого типа объекта, который оказался бы утомительным», но имхо в предлагаемой форме эти классы реализации результатов могут быть легко сгенерированы автоматически и не будут стоить никакой дополнительной работы.
2. Да, проблема в том, что когда изменяется абстрактный класс или вводится новый метод, мне пришлось бы переписать PHPDoc для ~ 30 дочерних классов, спасибо, что собрали это вместе, я уверен, что это может помочь другим, которые могут столкнуться с этим.