Http build query php post
cURL в PHP: примеры POST, GET запросов с headers, cookie, JSON и многопоточностью
В этой статье мы рассмотрим эффективные приемы работы с cURL, отправление POST, GET и т.д. запросов, работу с cookie, заголовки, JSON а также в конце статьи будут некоторые полезные инструменты, которые могут значительно облегчить вам работу с HTTP запросами.
GET запрос при помощи cURL
Для того, чтобы отправить запрос, нужно создать объект при помощи функции curl_init(), а затем следует настроить его.
Все настройки, которые вы можете найти по этой ссылке. Там вы найдете опции, которые мы будем устанавливать функцией curl_setopt, в дальнейших примерах.
Пример простого GET запроса при помощи cURL:
Если в результате сервер вернет нам редирект, то мы по нему автоматически не перейдем. А иногда это бывает полезно. Чтобы cURL автоматически шел по редиректу нужно установить опцию CURLOPT_FOLLOWLOCATION.
С установленной опцией скрипт автоматически перейдет по вернувшемуся редиректу и вернет ответ уже с итоговой страницы.
POST запрос при помощи cURL
Теперь давайте отправим post запрос на адрес https://httpbin.org/anything
Отлично, с GET и POST запросами в cURL мы немного освоились. Теперь разберемся с заголовками, которые мы можем отсылать в запросе.
Заголовки устанавливаются при помощи опции CURLOPT_HTTPHEADER Чтобы получше узнать, для чего нужна эта опция давайте попробуем отправить POST запрос в формате JSON
cURL: POST запрос в формате JSON
Отличия конфигурации JSON запроса от обычного POST запроса заключается в том, что мы кодируем поля при помощи json_encode() И добавляем заголовок Content-Type: application/json
cURL: GET запрос в формате JSON
GET запрос в формате JSON отправляется так же как и POST запрос, просто нужно CURLOPT_CUSTOMREQUEST установить в ‘GET’
cURL и другие виды HTTP запросов: PUT, DELETE, HEAD, PATCH, OPTIONS, CONNECT и т.д.
Стоп, Дмитрий, прекрати выдумывать виды запросов!
Ничего я не выдумываю: HTTP протокол предполагает множество типов HTTP запросов просто POST и GET являются более распространенными.
Чтобы отправить PUT запрос, нужно установить опцию CURLOPT_PUT таким образом:
Это делается по тому же принципу, как и CURLOPT_POST. Но что делать с остальным зоопарком запросов? Разве у cURL есть CURLOPT_DELETE или CURLOPT_HEAD? Нет.
Для того, чтобы отправлять другие виды запросов есть другая опция: CURLOPT_CUSTOMREQUEST
Вместо строки curl_setopt($curl, CURLOPT_POST, true); мы явно задаем имя запроса опцией CURLOPT_CUSTOMREQUEST:
Замечание: Не используйте эту возможность пока не убедитесь, что сервер поддерживает данный тип запроса.
Как получить заголовки ответа
В предыдущем примере мы научились посылать заголовки. Самый правильный способ принять заголовки:
Иногда можно встретить другой вариант получения заголовков ответа. К сожалению, они не совсем правильные и могут работать некорректно в некоторых случаях.
Рассмотрим такой пример:
Мы сначала определяем размер заголовка, с помощью CURLINFO_HEADER_SIZE затем вырезаем его из ответа. К сожалению, это может не срабатывать, когда используется прокси или в некоторых случаях редиректа.
Скачивание больших файлов с помощью cURL
Для того, чтобы скачать большой файл пригодится этот способ:
Обратите внимание, если вы будете использовать file_get_contents для скачивания файлов, то файл сначала загружается в оперативную память, а потом сохраняется на диск. Поэтому если файл действительно большой, то скорее всего вашему серверу не хватит памяти. Также к памяти будет требователен следующий код:
Здесь мы скачиваем файл при помощи cURL в оперативную память, а затем сохраняем его на диск. Не смотря на то, что этот способ не годится для скачивания больших файлов, с помощью него можно вполне сохранить простую веб страницу.
Параллельные cURL запросы в PHP
Для чего могут потребоваться многопоточные запросы? Например у нас есть много URL адресов:
И если мы будем по очереди отправлять запросы, то второй запрос начнется только после того, как закончился первый и так далее, а это существенно увеличивает время работы скрипта.
Как выполнить 3 запроса одновременно? В этом нам поможет curl_multi_
Давайте решим конкретную задачу при помощи параллельных curl запросов. Нам нужно отправить одновременно 3 запроса.
Такие параллельные запросы выполняются значительно быстрее чем поочередные.
cURL запросы с сохранением и загрузкой cookie из файла
cURL позволяет нам установить cookie при передачи запросов, а также автоматически принимать и устанавливать cookie, которые нам возвращает сервер, сохраняя их между запросами.
Давайте рассмотрим такой пример:
Теперь cookie у нас хранятся в файле cookie.txt в директории со скриптом (если вы ничего не меняли). Если мы совершаем повторные запросы, то cURL автоматически берет и отправляет cookie на сервер, как и обычный браузер. Таким образом мы можем авторизироваться на сайте и сохранить сеанс между запросами.
Передача cookie без файлов
Иммитация браузера с помощью cURL
Иногда сайт, к которому мы обращаемся может фильтровать запросы, защищаясь от парсинга. Если для этого используются упрощенные способы защиты, например проверка User-Agent, то мы можем легко притвориться, что являемся реальным польователем, который взаимодействует с сайтом через браузер, мы можем послать заголовки и cookie, которые обычно посылает браузер.
В данном примере установлены заголовки, которые посылает Chrome.
В простых ситуациях этого хватает. Но если используется защита при помощи javascript или что-то более продвинутое, то здесь cURL бессилен, и следует использовать либо BAS либо Zennoposter. Либо если вы хотите попытать счастье с PHP, то Selenium.
Не используйте эти знания в противоправных целях.
cURL запросы через прокси
Простой пример для отправки запросов через proxy. Если ваш прокси предполагает авторизацию, то раскомментируйте соответствующие строчки.
Отправка файлов
Авторизация с помощью cURL
HTTP Авторизация
Чтобы с помощью cURL авторизироваться на сайте, который использует Basic HTTP-аутентификацию нужно установить опцию CURLOPT_USERPWD, в которой будет наш логин и пароль.
OAuth авторизация
Авторизация через форму
Давайте применим полученные нами знания и авторизируемся на каком-нибудь сайте. Для этого нужно посмотреть куда форма отправляет данные и отправить туда то же самое.
Допустим на сайте есть такая форма:
Тогда наш cURL запрос должен быть сформирован так:
Автоматическое построение запросов
Перевод консольной команды curl в PHP
И вот еще один сервис, который переводит консольную команду curl в PHP: https://incarnate.github.io/curl-to-php/
Так вы можете создать простые запросы на cURL в PHP не создавая их вручную.
Лайфхак
В консоли браузера, во вкладке сеть, вы можете кликнуть правой кнопкой мыши и скопировать любой запрос в виде команды cURL, а потом с помощью сервиса curl-to-php перевести запрос в PHP. Теперь вы вообще можете сконвертировать в cURL абсолютно любой запрос, который посылает ваш браузер.
Как работать с cURL гораздо проще
Вы можете спросить: почему у cURL такие кривые и страшные методы? У вас может возникнуть желание взять и создать обертку для работы с cURL, чтобы вы могли не писать каждый раз большие куски некрасивого кода, а писать все проще, например так:
К счастью, такая обертка уже написана и найти ее можно здесь: https://github.com/php-curl-class/php-curl-class
Просто установите ее при помощи: composer require php-curl-class/php-curl-class и не работайте с кривыми кусками кода, которые таковы вероятно потому, что cURL изначально консольное приложение.
POST и GET запросы без cURL
С помощью PHP мы можем отправить простой GET запрос используя функцию file_get_contents.
При помощи file_get_contents мы также можем отправить POST запрос.
Подробнее о том, какие опции можно передавать в stream_context_create, вы можете изучить здесь: http://docs.php.net/manual/ru/context.http.php
Другие инструменты для работы с запросами в PHP
Для работы с запросами есть еще более мощный инструмент: Guzzle
Несколько примеров на Guzzle
GET запросы на Guzzle
Разные типы запросов на Guzzle
Асинхронные запросы на Guzzle
Если интересно, то читайте: Guzzle Quick Start
Пишите комментарии, если что-то осталось не понятно.
Протокол HTTP и работа с заголовками
Протокол HTTP
Как работает WWW (всемирная паутина, веб) в двух словах:
Иными словами, весь современный веб построен на модели клиент-серверного взаимодействия. И чтобы весь этот процесс оказался возможным, необходим универсальный язык-протокол, который будет понимать и сервер, и браузер. Такой протокол есть, а называется он HTTP.
Как работает HTTP, и зачем нам это знать
Программировать на PHP можно и без знания протокола HTTP, но есть ряд ситуаций, когда для решения задач нужно знать, как именно работает веб-сервер. Ведь PHP — это, в первую очередь, серверный язык программирования.
Протокол HTTP очень прост и состоит, по сути, из двух частей:
Сначала идёт список заголовков, затем пустая строка, а затем (если есть) тело запроса/ответа.
И клиент, и сервер могут посылать друг другу заголовки и тело ответа, но в случае с клиентом доступные заголовки будут одни, а с сервером — другие. Рассмотрим пошагово, как будет выглядеть работа по протоколу HTTP в случае, когда пользователь хочет загрузить главную страницу социальной сети «Вконтакте».
1. Браузер пользователя устанавливает соединение с сервером vk.com и отправляет следующий запрос:
2. Сервер принимает запрос и отправляет ответ:
3. Браузер принимает ответ и показывает готовую страницу
Больше всего нам интересен самый первый шаг, где браузер инициирует запрос к серверу vk.com
Рассмотрим подробнее, что там происходит. Первая строка запроса определяет несколько важных параметров, а именно:
GET — это метод (глагол), который мы применяем для доступа к указанной странице.
GET является самым часто используемым методом, потому что он говорит серверу о том, что клиент всего лишь хочет прочитать указанный документ. Но помимо GET есть и другие методы, один из них мы рассмотрим уже в следующем разделе.
Больше всего здесь интересен именно код состояния, он же код ответа сервера.
В этом примере код ответа — 200, что означает: сервер работает, документ найден и будет передан клиенту. Но не всегда всё идет гладко.
Например, запрошенный документ может отсутствовать или сервер будет перегружен, в таком случае клиент не получит контент, а код ответа будет отличным от 200.
Спецификация HTTP 1.1 определяет 40 различных кодов HTTP.
После стартовой строки следуют заголовки, а затем тело ответа.
Работа с заголовками в PHP
В PHP есть все возможности для взаимодействия с протоколом HTTP:
Разберём всё по порядку.
Получение тела запроса
Получение заголовков запроса
Пример, как получить предыдущую страницу, с которой перешёл пользователь:
Добавление/изменение заголовков ответа
В PHP-сценарии можно управлять всеми заголовками ответа, которые попадут к пользователю вместе с контентом страницы. Это возможно, потому что PHP работает на стороне веб-сервера и имеет с ним очень тесную интеграцию.
Вот примеры сценариев, когда пригодится управление заголовками ответа:
Управление телом ответа
Параметры запроса
На сайте дневника наблюдений за погодой мы сделали отдельную страницу, чтобы показывать на ней информацию о погоде из истории за один конкретный день. То есть страница одна, но показывает разные данные, в зависимости от выбранного дня.
Также пользователи хотят добавить в закладки адреса страниц с нужными им днями. Получается, что имея только один сценарий сделать страницу, способную показывать дневник погоды за любой день невозможно? Вовсе нет!
Из чего состоит URI
URI — это уникальный идентификатор ресурса. Ресурс в нашем случае — это полный путь до страницы сайта. И вот как может выглядеть ресурс для показа погоды за конкретный день:
http://weather-diary.ru/day.php?date=2017-10-15
В примере выше указывается два аргумента: дата и единица измерения температуры.
Параметры запроса как внешние переменные
Теперь в адресе страницы используются параметры запроса, но какая нам от этого польза? Она состоит в том, что если имя страницы вызывает к исполнению соответствующий PHP-сценарий, то параметры запроса превращаются в специальные внешние переменные в этом сценарии. То есть, если в адресе присутствуют такие параметры, то их легко получить внутри кода сценария и выполнить с ними какие-нибудь действия. Например, показать погоду за конкретный день в выбранных единицах измерения.
Получение параметров запроса
Формирование URI с параметрами запроса
Иногда нужно совершить обратную операцию: сформировать адрес страницы, включив туда нужные параметры запроса из массива.
Скажем, на странице погодного дневника надо поставить ссылку на следующий и предыдущий день. Нужно также сохранить выбранную единицу измерений. То есть необходимо сохранить текущие параметры запроса, поменять значение одного из них (день), и сформировать новую ссылку.
Вот как это можно сделать:
Здесь мы использовали две функции:
Особенности http_build_query в PHP
Казалось бы http_build_query — простая функция, однако, имеет некоторые особенности. Нельзя однозначно сказать что это баг, скорее просто недокументированная фича, которую стоит учитывать при разработке.
Допустим, есть массив [‘page’ => 2, ‘hide’ => null]. Что же вернёт http_build_query в результате? Это будет просто page=2, аргумент hide был отброшен, т.к. он имеет значение null! Что будет, если передать массив, где все значения будут null? Функция вернёт строку нулевой длины.
Взаимосвязь parse_str и http_build_query
В документации PHP по функции http_build_query есть отсылка к другой функции — parse_str, которая делает обратное действие: возвращает массив [‘page’ => ‘2’, ‘hide’ => »] на основе строк ‘page=2&hide’ или ‘page=2&hide=’. Если этот массив прогнать обратно через http_build_query, то результат получится ‘page=2&hide=’, что для некоторых ситуаций будет детерминировано.
Влияние http_build_query на фреймворк Laravel
Однако, некоторые фреймворки, например, Laravel и, скорее всего Symfony, строку с параметрами ‘page=2&hide’ или ‘page=2&hide=’ парсят [‘page’ => ‘2’, ‘hide’ => null]. Самое странное, что обратно этот массив параметров они собирают с помощью всё той же http_build_query, в результате чего в урле теряется параметр hide.
Ещё одна ошибка, которая часть встречается в скриптах при формировании полного url. Вот пример из Laravel 5.6 из класса \Illuminate\Http\Request:
Что вернёт данный метод, если в query все значения будут null? Что-то вроде: «http://127.0.0.1:8000/?». Как видите, на конце появился не нужный знак вопроса, т.к. после него нет ни одного параметра. Это происходит из-за того, что считается количество элементов в массиве вместе с null значениями.
Однако, соседний метод \Illuminate\Http\Request::fullUrl() вернёт корректное значение: «http://127.0.0.1:8000/?hide=»
Проблема потери параметров в http_build_query
Казалось бы, нет проблемы, теряется параметр без значения. Однако, для внешних систем, параметр без значения может быть значимым и его потеря приведёт к непредвиденному результату. Например, это может быть какая-нибудь JS-библиотека, для которой параметр без значения так же важен как и параметр со значением. Проблемы также могут возникнуть в аналитических системах, при интеграции с другими сайтами и системами.
Не все веб-приложения работают на PHP, не все соблюдают стандарты и спецификации, особенно в других странах. И иногда потеря get-параметра без значения может обернуться серьёзной ошибкой для бизнес-логики.
А что, если не баг, а фича?
Однако, у этой особенности работы http_build_query есть и приятная сторона. Можно достаточно просто удалять ненужные параметры из строки запроса таким способом:
В результате чего получится строка: page=2§ion=22.
Читайте также
Очень крутая задача, в которой не всё так просто, как кажется на первый взгляд. Попробуйте найти в ней 2 логические…
Если в кратце, то суть такая: file_put_contents() не атомарен и не гарантирует записи данных файл, а может вообще очистить его…
Стандартные библиотеки PHP умеют генерировать только целые случайные числа. Однако, возникают задачи где нужно не целое рандомное число с максимально…
http_build_query
http_build_query — Генерирует URL-кодированную строку запроса
Описание
Генерирует URL-кодированную строку запроса из предоставленного ассоциативного (или индексированного) массива.
Список параметров
Может быть массив или объект, содержащий свойства.
Если data массив, то он может быть простой одномерной структурой или массивом массивов (который, в свою очередь, может содержать другие массивы).
Если data объект, тогда только общедоступные свойства будут включены в результат.
Если числовые индексы используются в базовом массиве и этот параметр указан, то он будет добавлен к числовому индексу для элементов только в базовом массиве.
Это позволяет обеспечить допустимые имена переменных, в которые позже данные будут декодированы PHP или другим CGI-приложением.
arg_separator.output используется в качестве разделителя аргументов, но может быть переопределён путём указания этого параметра.
Возвращаемые значения
Возвращает URL-кодированную строку.
Примеры
Пример #1 Простой пример использования http_build_query()
Результат выполнения данного примера:
Пример #2 Пример использования http_build_query() с числовыми индексами элементов.
Результат выполнения данного примера:
Пример #3 Пример использования http_build_query() с многомерными массивами
Результат выполнения данных примеров: (символы перенесены для удобства чтения)
Только числовой индексированный элемент «CEO» в базовом массиве получил префикс. Другие числовые индексы, найденные в pastimes, не требуют строкового префикса, чтобы быть допустимыми именами переменных.
Пример #4 Пример использования http_build_query() с объектом
$parent = new parentClass ();
Результат выполнения данного примера:
Смотрите также
User Contributed Notes 24 notes
Params with null value do not present in result string.
If you need to change the enc_type, use this:
http_build_query($query, null, ini_get(‘arg_separator.output’), PHP_QUERY_RFC3986);
// BAD CODE!
http_build_query($query, null, null, PHP_QUERY_RFC3986);
if you send boolean values it transform in integer :
$a = [teste1= true,teste2=false];
echo http_build_query($a)
//result will be teste1=1&teste2=0
This function makes like this
To do it like this:
As noted before, with php5.3 the separator is & on some servers it seems. Normally if posting to another php5.3 machine this will not be a problem.
But if you post to a tomcat java server or something else the & might not be handled properly.
To overcome this specify:
http_build_query($array); //gives & to some servers
It’s not mentioned in the documentation, but when calling http_build_query on an object, public null fields are ignored.
Is it worth noting that if query_data is an associative array and a value is itself an empty array, or an array of nothing but empty array (or arrays containing only empty arrays etc.), the corresponding key will not appear in the resulting query string?
E.g.
$post_data = array(‘name’=>’miller’, ‘address’=>array(‘address_lines’=>array()), ‘age’=>23);
echo http_build_query($post_data);
Instead you can make your own simple function if you simply want to pass along the data:
If you need the inverse functionality, and (like me) you cannot use pecl_http, you may want to use something akin to the following.
http — Как я могу отправить запрос POST с PHP?
На самом деле я хочу прочитать содержимое, которое идет после поискового запроса, когда это будет сделано. Проблема в том, что URL принимает только POST методы, и он не предпринимает никаких действий с GET метод …
Решение
CURL-менее метод с PHP5:
См. Руководство по PHP для получения дополнительной информации о методе и о том, как добавить заголовки, например:
Другие решения
Вы можете использовать cURL:
@Edward упоминает, что http_build_query может быть опущен, так как curl будет правильно кодировать массив, переданный параметру CURLOPT_POSTFIELDS, но имейте в виду, что в этом случае данные будут кодироваться с использованием multipart / form-data.
Я использую эту функцию с API, которые ожидают, что данные будут закодированы с использованием application / x-www-form-urlencoded. Вот почему я использую http_build_query ().
Я рекомендую вам использовать пакет с открытым исходным кодом пропивать это полностью модульное тестирование и использует новейшие практики кодирования.
Установка жрет
Использование Guzzle для отправки запроса POST
Использование Guzzle очень прямолинейно, так как он использует легкий объектно-ориентированный API:
Есть другой метод CURL, если вы идете по этому пути.
Если вы случайно используете WordPress для разработки своего приложения (на самом деле это удобный способ получить авторизацию, информационные страницы и т. Д. Даже для очень простых вещей), вы можете использовать следующий фрагмент:
Если вы не хотите разрабатывать собственную тему или плагин для запуска движка WordPress, вы можете просто сделать следующее в изолированном файле PHP в корне WordPress:
Он не отображает темы и не выводит HTML, просто взломайте API WordPress!
Я хотел бы добавить некоторые мысли об основанном на завитках ответе Фреда Танрикута. Я знаю, что большинство из них уже написаны в ответах выше, но я думаю, что это хорошая идея, чтобы показать ответ, который включает в себя все из них вместе.
Вот класс, который я написал для выполнения запросов HTTP-GET / POST / PUT / DELETE на основе curl, касающихся только тела ответа:
улучшения
Пример использования
ПОЛУЧИТЬ
СООБЩЕНИЕ
ПОЛОЖИЛ
УДАЛЯТЬ
тестирование
Вы также можете сделать несколько крутых тестов сервиса с помощью этого простого класса.
Еще одна альтернатива завиток меньше метод выше это использовать родной поток функции:
Создает и возвращает контекст потока с любыми опциями, предоставленными в опции предустановки.