Что не так с функцией http_build_query в PHP

Казалось бы http_build_query — простая функция, однако, имеет некоторые особенности. Нельзя однозначно сказать что это баг, скорее просто недокументированная фича, которую стоит учитывать при разработке.

Допустим, есть массив [‘page’ => 2, ‘hide’ => null]. Что же вернёт http_build_query в результате? Это будет просто page=2, аргумент hide был отброшен, т.к. он имеет значение null! Что будет, если передать массив, где все значения будут null? Функция вернёт строку нулевой длины.

На официальной странице документации PHP по функции http_build_query есть отсылка к другой функции — parse_str, которая делает обратное действие: возвращает массив [‘page’ => ‘2’, ‘hide’ => »] на основе строк ‘page=2&hide’ или ‘page=2&hide=’. Если этот массив прогнать обратно через http_build_query, то результат получится ‘page=2&hide=’, что для некоторых ситуаций будет детерминировано.

Однако, некоторые фреймворки, например, Laravel и, скорее всего Symfony, строку с параметрами ‘page=2&hide’ или ‘page=2&hide=’  парсят [‘page’ => ‘2’, ‘hide’ => null]. Самое странное, что обратно этот массив параметров они собирают с помощью всё той же http_build_query, в результате чего в урле теряется параметр hide.

Казалось бы, нет проблемы, теряется параметр без значения. Однако, для внешних систем, параметр без значения может быть значимым и его потеря приведёт к непредвиденному результату. Например, это может быть какая-нибудь JS-библиотека, для которой параметр без значения так же важен как и параметр со значением. Проблемы также могут возникнуть в аналитических системах, при интеграции с другими сайтами и системами.

Не все веб-приложения работают на PHP, не все соблюдают стандарты и спецификации, особенно в других странах. И иногда потеря get-параметра без значения может обернуться серьёзной ошибкой для бизнес-логики.

Однако, у этой особенности http_build_query есть и приятная сторона. Можно достаточно просто удалять ненужные параметры из строки запроса таким способом:

$query = ['page' => 2, 'hide' => 6, 'user' => 4, 'section' => 22];
$except = ['hide' => null, 'user' => null];
$new = http_build_query(array_merge($query, $except) );

В результате чего получится строка: page=2&section=22.

Ещё одна ошибка, которая часть встречается в скриптах при формировании полного url. Вот пример из Laravel 5.6 из класса \Illuminate\Http\Request:

public function fullUrlWithQuery(array $query)
{
    $question = $this->getBaseUrl().$this->getPathInfo() == '/' ? '/?' : '?';

    return count($this->query()) > 0
        ? $this->url().$question.http_build_query(array_merge($this->query(), $query))
        : $this->fullUrl().$question.http_build_query($query);
}

Что вернёт данный метод, если в query все значения будут null? Что-то вроде: «http://127.0.0.1:8000/?». Как видите, на конце появился не нужный знак вопроса, т.к. после него нет ни одного параметра. Это происходит из-за того, что считается количество элементов в массиве вместе с null значениями.

Однако, соседний метод \Illuminate\Http\Request::fullUrl() вернёт корректное значение: «http://127.0.0.1:8000/?hide=»

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *