Использование null значений в методах PHP
В интернете часто можно встретить вопрос, нужно ли передавать NULL в параметрах методов, а также возвращать NULL из методов. Если нет, то почему и как писать код в таких случаях? Об этом и пойдёт речь в статье.
В чём вообще проблема использования null значений? Дело в том, что NULL можно трактовать по разному — это когда значение неизвестно, его используют в качестве значений по умолчанию для параметров методов, возвращают когда не смогли получить успешный результат или когда что-то произошло не так. Есть куча способов и причин использовать NULL. Однако это не всегда приемлемо.
Передача null в параметры
Передавать NULL в параметры метода может быть полезно, если нужно указать отсутствие значения или инициализировать параметр с нулевым значением по умолчанию.
Например, предположим, что у вас есть метод, который принимает параметр $name:
Если вы хотите обрабатывать случаи, когда он не указан, вы можете установить для параметра $name значение по умолчанию NULL:
В этом случае, если вы вызываете greet () метод без передачи каких-либо аргументов, он примет для параметра $name значение NULL по умолчанию.
Причина отказа от NULL заключается в том, что не нужно постоянно его проверять, если вы ожидаете в значении какое-то другое содержимое. Вместо этого вы можете вернуть, пустой массив, если ожидаемый результат — это массив. Если это целое число, возможно, будет достаточно 0, если это строка, то пустая строка и так далее.
Но есть и другие способы передачи и проверки аргументов метода. К примеру, можно передавать аргументы в виде массива:
$buttonParameters['col'],
'rows' => $params['count']
'count' => $params['count']
];
}
Другой вариант, не указывать аргументы функции явно, а воспользоваться динамическими аргументами. В этом поможет функция func_get_args (). И в теле уже самой функции просто валидировать получаемые функцией аргументы:
Дополнительно можно использовать функции func_num_args () и func_get_arg ().
Однако эти два способа сильно ухудшают понимание кода и требует множество дополнительных проверок каждого параметра. Поэтому эти способы создают ещё больше хлопот, чем использование NULL по умолчанию. Тем не менее эти способы частенько можно встретить.
В PHP 8 появились именованные аргументы. Теперь передать параметры можно следующим образом:
В этом случае аргументы функции передаются по имени параметра, а не по его позиции. Аргумент не зависит от порядка и указанного значения по умолчанию. Именованный аргумент передаётся с помощью добавления двоеточия перед значением имени параметра. Для имени параметра можно использовать зарезервированные слова. Однако имя не может быть создано динамически и должно быть идентификатором.
Возврат null из метода
Обычно из функции возвращают false, если что-то пошло не так. Аналогичное делают и стандартные функции PHP. Для этих же целей возвращают NULL, пустую строку, пустой массив или вообще ничего. В общем, у каждого свой поход и причины возвращать то или иное значение. Многие даже как-то не сильно об этом задумываются. Однако в некоторых случаях, возврат NULL как и возврат других значений, которые изначально не подразумевались для возврата, могут приводить к ошибкам и непонятным замешательствам. Здесь конечно же серьёзную роль играет сам PHP, поскольку типы данных в нём слабо типизированы и мы можем вертеть данными как угодно.
К примеру, возьмем такой метод:
mysqli—>query($query);
if ($result) {
return false;
}
$data = [];
while ($row = $result->fetch_assoc())
{
$data[] = $row;
}
return $data;
}
В нём мы собираемся получить список клиентов из базы данных. Если клиенты успешно получены, мы возвращаем массив, а если что-то пошло не так, то метод возвращает false. Затем мы используем данный метод, чтобы далее проводить операции с клиентами:
getClients();
Вот здесь и начинаются первые сложности, поскольку метод возвращает несколько типов данных: array и false. Поэтому нужно дополнительно определить, что именно было возвращено. Вроде бы здесь нет ничего такого необычного и проверка данных может показаться обычным делом, но здесь как раз возможно ошибиться на проверке типа данных.
getClients();
if (!$result) {
// что-то произошло не так
}
// работаем с массивом клиентов
В этом примере идёт неявная проверка данных, поэтому даже если функция getClients () возвращает пустой массив, то условие сработает. Поэтому лучше проверять на конкретный тип данных:
getClients();
if ($result === false) {
// что-то произошло не так
}
if (count($result) === 0) {
// пустой результат
}
// а если не false и array не пустой, то работаем с массивом клиентов
Если мы используем вместо false другое значение, пустую строку или NULL, то ситуация может возникнуть практически такая же. Как в таком случае лучше изменить код и что использовать?
Вместо возврата false, пустой строки или NULL можно использовать исключения. Это позволит сразу же определить, что именно и на каком моменте произошло.
mysqli—>query($query);
if ($result) {
throw new ErrorException('Произошла такая-то ошибка');
}
$data = [];
while ($row = $result->fetch_assoc())
{
$data[] = $row;
}
return $data;
}
Затем при использовании метода обработать это исключение:
getClients();
// все норм, двигаемся дальше
} catch (ErrorException $e) {
// схватили ошибку!
}
Здесь уже не нужно дополнительно проверять на всякие false или NULL и можно быть точно уверенным, почему метод не отдал правильные данные. Важно лишь правильно перехватывать и обрабатывать такие исключения.
exists()) {
return null;
}
return $file—>getLength();
}
В этом простом примере метод возвращает размер файла. Если файла не существует, то возвращается NULL, 0 или пустая строка — неважно. Вот здесь и начинается проблема, что файла не существует, он был перемещён или удалён, но вместо того чтобы узнать об этом, подобная проверка маскирует проблему. В этом случае можно использовать исключение:
exists()) {
throw new Exception(
"Нельзя получить размер файла. Файл не найден!"
);
}
return $file—>getLength();
}
Начиная с PHP7 для функций можно установить тип возвращаемого значения. Например:
Результат:
int(777)
Если указать цифру строкой:
То она автоматически будет преобразована в int.
Результат:
int(777)
Чтобы включить строгую типизацию, нужно установить:
declare(strict_types=1);
Тогда если попытаться задействовать в этой функции строку, будет выдана ошибка:
Uncaught TypeError: Return value of getValue () must be of the type integer, string returned
Начиная с PHP 7.1 можно указать тип возвращаемого значения, допускающий значение NULL. Для этого нужно поставить вопросительный знак перед типом возвращаемого значения. Таким образом, функция может вернуть целое или нулевое значение.
Кроме этого, если в методе вообще не использовать return или просто указать его без всякого значения, метод вернёт NULL. Например:
Поскольку return; и return null; эквивалентны в PHP, то если возвращаемое значение не указано, PHP выполнит работу NULL за вас. То же самое касается и свойств объектов. По умолчанию они имеют значение NULL до тех пор, пока не будут заданы. Поскольку это поведение по умолчанию, лучше помнить об этом и проверять свойства, которые имеет ваш класс, прежде чем совершать вызовы или что-то ещё.
В большинстве случаев рекомендуется возвращать Null-object который предоставляет поведение по умолчанию в зависимости от того как реагирует ваш код на ситуацию когда ничего не найдено.
Так что же в итоге возвращать?
Все индивидуально, зависит от проекта. Главное следовать установленной спецификации в рамках проекта, чтобы соблюдался единый стиль. Проверка нулевых значений — обычная практика, иначе вы рискуете повсюду получить исключения. Лучше корректно обработать ошибку, чем генерировать исключения, когда в этом нет необходимости. NULL это не обязательно плохо, как и в случае со многими другими решениями, это зависит от ситуации.
Использование NULL может быть допустимо, когда это целесообразно и не противоречит остальным частям проекта. В большинстве случаев null следует либо заменить выдачей исключения, либо предоставлением нулевого объекта.
Плохое использование NULL, это когда идет попытка заменить или скрыть исключительные ситуации, такие как: ошибка добавления записи в бд, подключение к какому-то API, неверные параметры и так далее. В этих случаях создание исключения более адекватно, поскольку значение NULL не дает значимой информации об ошибке и любые проверки игнорируют проблему.