Хорошие вопросы.
Разумеется, увеличивать количество запросов к БД ради красивой типизации - это абсолютно не вариант.
Как можно увидеть, в приведенной цитате написано не однозначно, а "скорее всего".
И поэтому надо конечно стараться, чтобы функция возвращала какое-то одно значение, но не делать этого любой ценой. Поэтому
getUserDataFromDatabase
вполне может возвращать
array|false
, а
getUserIdFromDatabase
- int|false, хотя я бы с ней не заморочивался, первой вполне достаточно. А
userIsPresentInDb
вообще не нужна, вместо неё можно использовать одну их предыдущих.
Другое дело, что нужного результата можно добиться и другим способом.
Ведь вполне можно вернуть
пустой массив. И таким образом
формально соблюсти типизацию.
return $result ?: []; // дёшево и сердито
Но это будет всё равно не очень красиво. В одной стороны - да, для проверки, вернула ли функция непустое значение, такой вариант вполне сгодится. Но с другой стороны все равно как-то неаккуратненько. Ведь мы на самом деле ждём не абы какой массив, а вполне определённый, с конкретным набором полей. То есть, если эта функция вернет массив вида [0, 42, 100500], то это будет явно не то, что нам нужно, но при этом типизация и слова поперёк не скажет.
Если думать от типизации, то функция
getUserDataFromDatabase
, возвращающая какой-то абстрактный массив - это нонсенс, бессмыслица. Эта функция должна возвращать
юзера. Поэтому в идеале надо придумать способ указать, что функция возвращает не абстрактный, а
конкретный массив определенного формата. Тем более, что такой способ как раз есть, ведь типизованный массив - это же объект! И для данного случая даже специальный паттерн есть - ValueObject (хотя в случае с пользователем лучше будет все-таки делать полноценный класс, содержащий не только данные, но и методы, например auth(), который сравнивает хэш пароля с введенным).
Соответственно, в идеале функция должна возвращать объект, представляющий пользователя.
И уже в этом объекте поле id может либо либо быть нулём, либо иметь какое-то положительное значение. Или даже объект может содержать отдельное свойство, заполнен/не заполнен.
И вот в этом случае типизация заиграет совсем новыми красками, и будет использоваться на 100%
function getUserFromDb( int $userId ) : UserObject {
return $result ? UserObject::fillFromArray($result) : new UserObject();
};
$user = getUserFromDb();
if (!$user->id) {
// нинашли :'(
}
Но опять же, как пишет ниже
Сергей delphinpro, зависит от задачи.
И решив проблему правильной типизации значения, которое функция вернет, если нашла пользователя, можно вернуться к вопросу о том, что возвращать, если функция ничего не нашла. И в этом случае вполне подходящим вариантом будет вернуть null
function getUserFromDb( int $userId ) : ?UserObject {
return $result ? UserObject::fillFromArray($result) : null;
};
if ($user = getUserFromDb()) {
// нашли :)
}