Отправка 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

И не забывайте комментировать статью.

Добавляйся в группу во вконтакте, чтобы самым первым узнавать все новости сайта

Отзывов: 43 на «Отправка POST запросов с помощью php. Часть 1»

  1. Автор: Rustyle, 24 марта 2010 в 20:49

    использует Google Chrome 4.1.249.1036 Google Chrome 4.1.249.1036 на Windows 7 Windows 7

    и если понадобится сайт media.mac-style.ru

  2. Автор: Rustyle, 24 марта 2010 в 20:51

    использует Google Chrome 4.1.249.1036 Google Chrome 4.1.249.1036 на Windows 7 Windows 7

    и если интересено на каком сайте косяки с некорректным отображение он указан в инфе

  3. Автор: Jackie, 24 апреля 2010 в 03:44

    использует Firefox 3.6.3 Firefox 3.6.3 на Windows Vista Windows Vista

    Спасибо за отличные статьи по теме.

    У меня проблема в этом месте:

    while(!feof($socket)){
    $answer.= fgets($socket, 4096);
    }

    Я дебажу код, дебаггер заходит в этот цикл, а из него не выходит. При этом $answer заполняется полностью нужным ответом с сервера, но на последнем пробеге по циклу все подвисает. Подскажите в чем может быть проблема?

    • Автор: web-junior, 24 апреля 2010 в 09:35

      использует Firefox 3.5.8 Firefox 3.5.8 на Windows XP Windows XP

      скорее всего проблема в сервере, который не закрывает соединение.

  4. Автор: FXIX, 29 мая 2010 в 22:21

    использует Firefox 3.6.3 Firefox 3.6.3 на Windows XP Windows XP

    Подскажите в чем тонкий момент отправки POST-запроса на HTTPS?

    код:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    
    $username='логин';
    $password='пароль';
     
    $data=urlencode(
    "accountType=HOSTED_OR_GOOGLE
    &Email=$username
    &Passwd=$password
    &source=CoExample
    &service=blogger");
     
    $header ='POST /accounts/ClientLogin HTTP/1.1\n';
    $header.='Host: google.com\n';
    $header.='Content-Type: application/x-www-form-urlencoded\n';
    $header.='Content-Length: '.strlen($data).'\n\n';
    $header.=$data.'\n\n';
    $connect=fsockopen('ssl://www.google.com', 443, 
    $errno, $errstr) or die ($errno.' : '.$errstr);
     
    $out=fwrite($connect,$header);
     
    while($in=fgets($connect,2048)) {
        echo $in;
    }
    fclose ($connect);

    не работает. (скрипт подключается к хосту 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

      использует Firefox 3.5.9 Firefox 3.5.9 на Windows XP Windows XP

      Скорее всего вы некорректно сформировали запрос. А точнее неверно вставили переводы строк. Перевод строк в формате \n работает только в двойных кавычках. Если вы используете одинарные кавычки, то следует вызвать функцию chr(10)

  5. Автор: SOAD, 1 августа 2010 в 23:10

    использует Firefox 3.5.6 Firefox 3.5.6 на Windows XP Windows XP

    А почему функция fsockopen() возвращает ошибку, когда ей передаёшь доменное имя третьего уровня ?

    • Автор: web-junior, 2 августа 2010 в 10:14

      использует Firefox 3.5.9 Firefox 3.5.9 на Windows XP Windows XP

      Это скорее всего зависит не от самой функции, а от платформы, на которой она реализована.

  6. Автор: Михаил, 25 ноября 2010 в 15:17

    использует Firefox 3.6.12 Firefox 3.6.12 на Ubuntu 10.04 Ubuntu 10.04

    У меня возникла такая проблема: страница открывается, но данные не передаются.Пишет просто Name: после заголовков.
    В чём может быть ошибка?

    • Автор: web-junior, 25 ноября 2010 в 15:44

      использует Firefox 3.6.12 Firefox 3.6.12 на Windows 7 Windows 7

      А какими заголовками ответил сервер?
      Покажите пожалуйста ваш код, если он отличается от приведенного в статье.

      • Автор: Михаил, 25 ноября 2010 в 16:36

        использует Firefox 3.6.12 Firefox 3.6.12 на Ubuntu 10.04 Ubuntu 10.04

        Код сценария:

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        
        $socket = fsockopen('misha.host', 80, $errno, $errstr);
         
        if(!$socket)die("$errstr($errno)");
         
        $data = "name=".urlencode("Misha");
         
        fwrite($socket, "POST /test.php HTTP/1.1\r\n");
        fwrite($socket, "Host: misha.host\r\n");
        fwrite($socket,"Content-length:".strlen($data)."\r\n");
        fwrite($socket,"Accept:*/*\r\n");
        fwrite($socket,"User-agent:Opera 10.00\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);

        Код страницы:

        1
        
         echo "Name: ".$_POST['name'];

        Ответ сервера:
        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

          использует Firefox 3.6.12 Firefox 3.6.12 на Windows 7 Windows 7

          Вы забыли про тип передаваемых данных. Если вы не отсылаете файлы, то нужно написать тип данных application/x-www-form-urlencoded так:

          fwrite($socket,
          "Content-type: application/x-www-form-urlencoded\r\n");

          после

          fwrite($socket, "Host: misha.host\r\n");

          Если вы собираетесь отправлять файлы, то нужно передавать тип multipart/form-data, подробнее см. здесь

          • Автор: Михаил, 25 ноября 2010 в 20:14

            использует Firefox 3.6.12 Firefox 3.6.12 на Ubuntu 10.04 Ubuntu 10.04

            Ура, теперь всё рабтает! Большое спасибо за помощь и за статью!

          • Автор: web-junior, 25 ноября 2010 в 20:53

            использует Firefox 3.6.12 Firefox 3.6.12 на Windows 7 Windows 7

            Пожалуйста!
            Заходите еще!

  7. Автор: Евгений, 26 декабря 2010 в 23:24

    использует Firefox 3.6.13 Firefox 3.6.13 на Windows XP Windows XP

    Здравствуйте, подскажите пожалуйста!
    Как быть если сервер выставляет куки? их каким-то образом необходимо передать на сервер для отправки запроса?
    Как можно решить эту проблему?
    Заранее спасибо за ответ и за статьи в общем!

    • Автор: web-junior, 15 марта 2011 в 15:58

      использует Google Chrome 10.0.648.133 Google Chrome 10.0.648.133 на Windows 7 Windows 7

      Здравствуйте.
      Когда сервер устанавливает куки, их нужно сохранить и передавать каждый раз обратно при отсылке запроса.
      Сервер устанавливает их в заголовке ‘Set-Cookie’, а отсылать их нужно в заголовке ‘Cookie’

  8. Автор: Дмитрий, 12 февраля 2012 в 20:45

    использует Firefox 4.0 Firefox 4.0 на Windows 7 Windows 7

    Все сделал как сказано выше и все с первого раза заработало при отправке на свой сайт, но мне надо отправлять на другой, вот сюда https://www.liqpay.com, в результате 400 ошибка. Что делать?

    • Автор: web-junior, 13 февраля 2012 в 11:33

      использует Firefox 10.0 Firefox 10.0 на GNU/Linux x64 GNU/Linux x64

      Здравствуйте.
      Скорее всего вы передаете не все параметры, которые необходимы.
      400 ошибка указывает на неправильный запрос, поэтому проверьте его внимательно.

      • Автор: Дмитрий, 13 февраля 2012 в 12:40

        использует Firefox 4.0 Firefox 4.0 на Windows 7 Windows 7

        Не все заголовки или не все содержимое что надо, т.е. не весь XML?

RSS-лента комментариев. Адрес для трекбека

Ваш отзыв

Вы можете использовать следующие теги: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="" highlight="">

Нажимая на кнопку "Добавить" вы принимаете правила комментирования