Отправка POST запросов с помощью php. Часть 1
Эта небольшая статья будет об отправке запросов POST на сервер. Далеко не все программисты знают как отправлять такие запросы. На самом деле есть целых три способа. Я расскажу обо всех трех.
Сокеты прежде всего
Самый первый и самый правильный, на мой взгляд, способ – это способ с сокетами.
Для начала разберемся с теорией. Когда пользователь заполняет форму на сайте и нажимает на submit-кнопку формы, происходит следующее:
- браузер открывает соединение с сервером, на который отсылается форма;
- отправляет серверу заголовки, с некоторой информацией о посылаемых данных
- отправляет серверу данные, которые ввел пользователь в форму;
- читает ответ сервера, чтобы узнать результат работы;
- закрывает соединение с сервером.
Именно в таком порядке происходит отправка формы с пост-запросом. Это и нужно повторить, только уже программными инструментами на php. Теперь давайте разберемся со всеми пунктами по порядку. Для того, чтобы открыть соединение с сервером, нужно использовать функцию fsockopen(). Вот ее сигнатура:
resource fsockopen ( string $target [, int $port [, int &$errno [, string &$errstr [, float $timeout]]]] )
В функцию fsockopen передаются следующие параметры:
- $target – это адрес (в нашем случае URL), к которому нужно открыть сокет;
- $port – номер порта (обычно для протокола HTTP это 80 или 8080);
- $errno – в эту переменную будет записан номер ошибки, если она произойдет. Заметьте символ & перед переменной в сигнатуре. В свое время я долго не мог понять, что значит такая запись. После изучения технологии ссылок все стало на свои места: на месте $errno в вызове функции должна быть передана переменная (значение не подойдет). Именно в эту переменную и будет записан номер ошибки;
- $errstr – в эту переменную будет записана строка с ошибкой, если она произойдет;
- $timeout – время ожидания соединении (таймаут) в секундах.
Функция вернет ресурс соединения или FALSE в случае ошибки. Если произошла ошибка, то в переменных $errno и $errstr можно посмотреть номер и текст ошибки соответственно.
После открытия соединения, нужно указать серверу метод запроса (POST), URI запроса, а также хост, с которого идет запрос.
Указывать будем с помощью обычной функции записи в файл fwrite().
int fwrite ( resource $handle, string $string [, int $length] )
Параметры:
- $handle – указатель на открытый файл или соединение;
- $string – строка, которую нужно записать в файл;
- $length – размер записываемых данных. Если этот параметр опущен, то запишется вся строка $string
Функция вернет количество записанных байт или FALSE в случае ошибки.
Теперь разберемся с заголовками. Что такое заголовки? Если в двух словах – это специальный текст, который указывает серверу то или иное значение некоторых параметров. Не очень понятно, да? Тогда вот пример.
При посылке запроса обязательно нужно указать серверу тип передаваемых данных. В post-запросах тип имеет название application/x-www-form-urlencoded. Для того чтобы указать серверу тип, есть заголовок Content-type. В комплексе, заголовок и значение заголовка записываются через двоеточие, вот так: Content-type: application/x-www-form-urlencoded.
После каждого заголовка ОБЯЗАТЕЛЬНО должен быть перевод строки.
Для посылки обычного post-запроса, понадобятся следующие заголовки:
- Content-type – заголовок, указывающий тип данных;
- Content-length – заголовок, указывающий размер отправляемых данных
- Accept – типы данных, которые может принимать браузер;
- User-agent – имя браузера.
После отправки всех заголовков, ставится еще один перевод строки.
Данные, которые отправляются серверу достаточно просты по виду: varname1=value1&varname2=value2&varname3=value3. Да, именно такие как при отправке GET-запроса. После отправки данных, ставятся два перевода строк.
Прочитать ответ сервера можно функцией fgets(). Сигнатура ее проста и наверное всем известна:
string fgets ( resource $handle [, int $length] )
Параметры:
- $handle – указатель на открытый файл или соединение;
- $length – количество байт, которые нужно прочитать из ресурса $handle.
Функция вернет строку, прочитанную из файла или FALSE в случае ошибки. Для того, чтобы узнать, когда закончатся данные, можно использовать функцию feof().
bool feof ( resource $handle )
Параметры:
- $handle – указатель на открытый файл или соединение.
Вернет TRUE, если достигнут конец файла или ресурса и FALSE если конец еще не достигнут.
С помощью этих двух функций мы сможем прочитать весь ответ примерно таким образом:
while(!feof($handle)){ $body.=fgets($handle, 4096); }
Закрыть соединение с сервером можно с помощью функции fclose().
bool fclose ( resource $handle )
Параметры:
- $handle – указатель на открытый файл или соединение.
Вернет TRUE, если файл или соединение закрыты и FALSE, если закрыть не удалось.
Теперь код. Для начала изготовим небольшой тестовый файл, который отобразит данные. Он будет приблизительно такого содержания:
test.php
<?php echo 'Name:'.$_POST['name'],"<br>",'Surname:'.$_POST['surname']; ?>
Этот код выведет переменные name и surname, которые были переданные по методу POST.
Теперь собственно код отправки запроса.
socket.php
<?php //открываем сокет к http://www.example.loc на 80-й порт с таймаутом в 30 секунд $socket = fsockopen('www.example.loc', 80, $errno, $errstr, 30); //если fsockopen вернула false, то завершаем работу скрипта и выводим текст и номер ошибки if(!$socket)die("$errstr($errno)"); //собираем данные $data = "name=".urlencode("John")."&surname=".urlencode("Smith"); //пишем в сокет метод, URI и протокол fwrite($socket, "POST /post/test.php HTTP/1.1\r\n"); //а также имя хоста fwrite($socket, "Host: www.example.loc\r\n"); //теперь отправляем заголовки //Content-type должен быть applicaion/x-www-form-urlencoded fwrite($socket,"Content-type: application/x-www-form-urlencoded\r\n"); //размер передаваемых данных передаем в заголовке Content-length fwrite($socket,"Content-length:".strlen($data)."\r\n"); //типы принимаемых данных. */* означает, что принимаем все типы данных fwrite($socket,"Accept:*/*\r\n"); //представимся оперой fwrite($socket,"User-agent:Opera 10.00\r\n"); fwrite($socket,"Connection:Close\r\n"); fwrite($socket,"\r\n"); //теперь передаем данные fwrite($socket,"$data\r\n"); fwrite($socket,"\r\n"); //теперь читаем и выводим ответ $answer = ''; while(!feof($socket)){ $answer.= fgets($socket, 4096); } echo $answer; //закрываем сокет fclose($socket); ?>
Теперь если обратиться к файлу socket.php, то сервер выведет, приблизительно такой ответ
HTTP/1.1 200 OK
Date: Mon, 19 Oct 2009 09:26:53 GMT
Server: Apache/2.2.9 (Win32) PHP/5.2.11
X-Powered-By: PHP/5.2.11
Content-Length: 26
Content-Type: text/html
Name:John<br>Surname:Smith
Сервер также ответил с заголовками.
HTTP/1.1 200 OK – здесь указана версия протокола, код ответа (200) и текст ответа (ОК)
Date: Mon, 19 Oct 2009 09:26:53 GMT – дата
Server: Apache/2.2.9 (Win32) PHP/5.2.11 – имя сервера
X-Powered-By: PHP/5.2.11 – эту строчку добавил php
Content-Length: 26 – размер передаваемых данных в байтах
Content-Type: text/html – тип данных
Далее пустая строка и тело ответа.
На сегодня все. В следующий раз разберем отправку запросов с помощью библиотеки curl.
Читайте все статьи цикла.
В первой части рассматривается работа с сокетами.
Во второй части описывается библиотека cURL.
В третьей части можно почитать о работе с Stream Function.
В четвертой части описывается отправка файла через POST.
В пятой части описывается отправка POST-запросов в Zend Framework.
Популярность: 39%
Интересное из других блогов:
2leep.comИ не забывайте комментировать статью.
Добавляйся в группу во вконтакте, чтобы самым первым узнавать все новости сайта
Автор: Rustyle, 24 марта 2010 в 20:49
используети если понадобится сайт media.mac-style.ru
Автор: Rustyle, 24 марта 2010 в 20:51
используети если интересено на каком сайте косяки с некорректным отображение он указан в инфе
Автор: Jackie, 24 апреля 2010 в 03:44
используетСпасибо за отличные статьи по теме.
У меня проблема в этом месте:
while(!feof($socket)){
$answer.= fgets($socket, 4096);
}
Я дебажу код, дебаггер заходит в этот цикл, а из него не выходит. При этом $answer заполняется полностью нужным ответом с сервера, но на последнем пробеге по циклу все подвисает. Подскажите в чем может быть проблема?
Автор: web-junior, 24 апреля 2010 в 09:35
используетскорее всего проблема в сервере, который не закрывает соединение.
Автор: FXIX, 29 мая 2010 в 22:21
используетПодскажите в чем тонкий момент отправки POST-запроса на HTTPS?
код:
не работает. (скрипт подключается к хосту https://www.google.com/accounts/ClientLogin отдает логин\пароль\итд и возвращает SID, LSID, Auth)
собственно вот:
http://code.google.com/intl/ru/apis/blogger/docs/2.0/developers_guide_protocol.html#ClientLogin
http://code.google.com/intl/ru/apis/accounts/docs/AuthForInstalledApps.html#Request
целый день убил – не пойму в чем дело.
Автор: web-junior, 30 мая 2010 в 12:03
используетСкорее всего вы некорректно сформировали запрос. А точнее неверно вставили переводы строк. Перевод строк в формате \n работает только в двойных кавычках. Если вы используете одинарные кавычки, то следует вызвать функцию chr(10)
Автор: SOAD, 1 августа 2010 в 23:10
используетА почему функция fsockopen() возвращает ошибку, когда ей передаёшь доменное имя третьего уровня ?
Автор: web-junior, 2 августа 2010 в 10:14
используетЭто скорее всего зависит не от самой функции, а от платформы, на которой она реализована.
Автор: Михаил, 25 ноября 2010 в 15:17
используетУ меня возникла такая проблема: страница открывается, но данные не передаются.Пишет просто Name: после заголовков.
В чём может быть ошибка?
Автор: web-junior, 25 ноября 2010 в 15:44
используетА какими заголовками ответил сервер?
Покажите пожалуйста ваш код, если он отличается от приведенного в статье.
Автор: Михаил, 25 ноября 2010 в 16:36
используетКод сценария:
Код страницы:
Ответ сервера:
HTTP/1.1 200 OK Date: Thu, 25 Nov 2010 14:26:53 GMT Server: Apache/2.2.14 (Ubuntu) X-Powered-By: PHP/5.3.2-1ubuntu4.5 Vary: Accept-Encoding Content-Length: 6 Content-Type: text/html Name:
Автор: web-junior, 25 ноября 2010 в 19:34
используетВы забыли про тип передаваемых данных. Если вы не отсылаете файлы, то нужно написать тип данных application/x-www-form-urlencoded так:
после
Если вы собираетесь отправлять файлы, то нужно передавать тип multipart/form-data, подробнее см. здесь
Автор: Михаил, 25 ноября 2010 в 20:14
используетУра, теперь всё рабтает! Большое спасибо за помощь и за статью!
Автор: web-junior, 25 ноября 2010 в 20:53
используетПожалуйста!
Заходите еще!
Автор: Евгений, 26 декабря 2010 в 23:24
используетЗдравствуйте, подскажите пожалуйста!
Как быть если сервер выставляет куки? их каким-то образом необходимо передать на сервер для отправки запроса?
Как можно решить эту проблему?
Заранее спасибо за ответ и за статьи в общем!
Автор: web-junior, 15 марта 2011 в 15:58
используетЗдравствуйте.
Когда сервер устанавливает куки, их нужно сохранить и передавать каждый раз обратно при отсылке запроса.
Сервер устанавливает их в заголовке ‘Set-Cookie’, а отсылать их нужно в заголовке ‘Cookie’
Автор: Дмитрий, 12 февраля 2012 в 20:45
используетВсе сделал как сказано выше и все с первого раза заработало при отправке на свой сайт, но мне надо отправлять на другой, вот сюда https://www.liqpay.com, в результате 400 ошибка. Что делать?
Автор: web-junior, 13 февраля 2012 в 11:33
используетЗдравствуйте.
Скорее всего вы передаете не все параметры, которые необходимы.
400 ошибка указывает на неправильный запрос, поэтому проверьте его внимательно.
Автор: Дмитрий, 13 февраля 2012 в 12:40
используетНе все заголовки или не все содержимое что надо, т.е. не весь XML?
Автор: web-junior, 13 февраля 2012 в 12:51
используетСкорее всего.