cURL – мощный инструмент для отправки запросов. Только взгляните сколько он всего умеет.
Для того, чтобы отправить запрос, нужно создать объект при помощи функции curl_init(), а затем следует настроить его.
Все настройки, которые вы можете найти по этой ссылке. Там вы найдете опции, которые мы будем устанавливать функцией curl_setopt, в дальнейших примерах.
Пример простого GET запроса при помощи cURL:
$url = 'https://phpstack.ru/'; $headers = []; // создаем заголовки $curl = curl_init(); // создаем экземпляр curl curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); curl_setopt($curl, CURLOPT_VERBOSE, 1); curl_setopt($curl, CURLOPT_POST, false); // curl_setopt($curl, CURLOPT_URL, $url); $result = curl_exec($curl);
В итоге, переменная $result снова содержит html код страницы этого сайта.
Если в результате сервер вернет нам редирект, то мы по нему автоматически не перейдем. А иногда это бывает полезно. Чтобы cURL автоматически шел по редиректу нужно установить опцию CURLOPT_FOLLOWLOCATION.
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
С установленной опцией скрипт автоматически перейдет по вернувшемуся редиректу и вернет ответ уже с итоговой страницы.
Теперь давайте отправим post запрос на адрес https://httpbin.org/anything
$url = 'https://httpbin.org/anything'; // url, на который отправляется запрос $post_data = [ // поля нашего запроса 'field1' => 'val_1', 'field2' => 'val_2', ]; $headers = []; // заголовки запроса $post_data = http_build_query($post_data); $curl = curl_init(); curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); curl_setopt($curl, CURLOPT_VERBOSE, 1); curl_setopt($curl, CURLOPT_POSTFIELDS, $post_data); curl_setopt($curl, CURLOPT_URL, $url); curl_setopt($curl, CURLOPT_POST, true); // true - означает, что отправляется POST запрос $result = curl_exec($curl);
Отлично, с GET и POST запросами в cURL мы немного освоились. Теперь разберемся с заголовками, которые мы можем отсылать в запросе.
Заголовки устанавливаются при помощи опции CURLOPT_HTTPHEADER Чтобы получше узнать, для чего нужна эта опция давайте попробуем отправить POST запрос в формате JSON
Подробнее о работе с JSON в PHP
Отличия конфигурации JSON запроса от обычного POST запроса заключается в том, что мы кодируем поля при помощи json_encode() И добавляем заголовок Content-Type: application/json
$url = 'https://httpbin.org/anything'; $headers = ['Content-Type: application/json']; // заголовки нашего запроса $post_data = [ // поля нашего запроса 'field1' => 'val_1', 'field2' => 'val_2', ]; $data_json = json_encode($post_data); // переводим поля в формат JSON $curl = curl_init(); curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); curl_setopt($curl, CURLOPT_VERBOSE, 1); curl_setopt($curl, CURLOPT_POSTFIELDS, $data_json); curl_setopt($curl, CURLOPT_URL, $url); curl_setopt($curl, CURLOPT_POST, true); $result = curl_exec($curl); // результат POST запроса
GET запрос в формате JSON отправляется так же как и POST запрос, просто нужно CURLOPT_CUSTOMREQUEST установить в ‘GET’
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'GET');
А в остальном код идентичен предыдущему примеру. Хотя, надо признать, GET запросы с телом – это нонсенс. Обычно для этих целей используется POST, PUT или PATCH, но был у меня один случай… Поэтому вот GET запрос в формате JSON.
Стоп, Дмитрий, прекрати выдумывать виды запросов!
Ничего я не выдумываю: HTTP протокол предполагает множество типов HTTP запросов просто POST и GET являются более распространенными.
Чтобы отправить PUT запрос, нужно установить опцию CURLOPT_PUT таким образом:
curl_setopt($curl, CURLOPT_PUT, true);
Это делается по тому же принципу, как и CURLOPT_POST. Но что делать с остальным зоопарком запросов? Разве у cURL есть CURLOPT_DELETE или CURLOPT_HEAD? Нет.
Для того, чтобы отправлять другие виды запросов есть другая опция: CURLOPT_CUSTOMREQUEST
Вместо строки curl_setopt($curl, CURLOPT_POST, true);
мы явно задаем имя запроса опцией CURLOPT_CUSTOMREQUEST:
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'HEAD'); // или curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'DELETE'); // или curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'PATCH'); // или curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'OPTIONS');
Замечание: Не используйте эту возможность пока не убедитесь, что сервер поддерживает данный тип запроса.
В предыдущем примере мы научились посылать заголовки. Самый правильный способ принять заголовки:
$ch = curl_init(); $headers = []; curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); // ... остальные опции // this function is called by curl for each header received curl_setopt($ch, CURLOPT_HEADERFUNCTION, function($curl, $header) use (&$response_headers) { $len = strlen($header); $header = explode(':', $header, 2); if (count($header) < 2) { // ignore invalid headers return $len; } $headers[strtolower(trim($header[0]))][] = trim($header[1]); return $len; } ); $data = curl_exec($ch); print_r($response_headers); // выводим заголовки ответа
Иногда можно встретить другой вариант получения заголовков ответа. К сожалению, они не совсем правильные и могут работать некорректно в некоторых случаях.
Рассмотрим такой пример:
$ch = curl_init(); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_HEADER, 1); // ... $response = curl_exec($ch); // Then, after your curl_exec call: $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE); $header = substr($response, 0, $header_size); $body = substr($response, $header_size);
Мы сначала определяем размер заголовка, с помощью CURLINFO_HEADER_SIZE затем вырезаем его из ответа. К сожалению, это может не срабатывать, когда используется прокси или в некоторых случаях редиректа.
Для того, чтобы скачать большой файл пригодится этот способ:
$url = 'https://example.com/big_file.zip'; // откуда скачиваем файл $path = __DIR__ . '/big_file.zip'; // куда сохраняем файл $fp = fopen($path, 'w'); $ch = curl_init($url); curl_setopt($ch, CURLOPT_FILE, $fp); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_exec($ch); curl_close($ch); fclose($fp);
Обратите внимание, если вы будете использовать file_get_contents для скачивания файлов, то файл сначала загружается в оперативную память, а потом сохраняется на диск. Поэтому если файл действительно большой, то скорее всего вашему серверу не хватит памяти. Также к памяти будет требователен следующий код:
$ch = curl_init('https://example.ru/big_file.zip'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_HEADER, false); $data = curl_exec($ch); curl_close($ch); file_put_contents(__DIR__ . '/big_file.zip', $data);
Здесь мы скачиваем файл при помощи cURL в оперативную память, а затем сохраняем его на диск. Не смотря на то, что этот способ не годится для скачивания больших файлов, с помощью него можно вполне сохранить простую веб страницу.
Для чего могут потребоваться многопоточные запросы? Например у нас есть много URL адресов:
$urls = [ 'https://httpbin.org/anything?1', 'https://httpbin.org/anything?2', 'https://httpbin.org/anything?3', ];
И если мы будем по очереди отправлять запросы, то второй запрос начнется только после того, как закончился первый и так далее, а это существенно увеличивает время работы скрипта.
Выглядит это так:
$results = []; foreach ($urls as $url) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_HEADER, false); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); $results[$url] = curl_exec($ch); curl_close($ch); } var_dump($results);
Теперь в $results у нас содержится массив, где ключи – это url адреса, а значения – результаты запросов. Однако запросы выполняются долго. Но мы можем это ускорить.
Как выполнить 3 запроса одновременно? В этом нам поможет curl_multi_
Давайте решим конкретную задачу при помощи параллельных curl запросов. Нам нужно отправить одновременно 3 запроса.
$urls = [ 'https://httpbin.org/anything?1', 'https://httpbin.org/anything?2', 'https://httpbin.org/anything?3', ]; // array of curl handles $multiCurl = []; // data to be returned $results = []; // multi handle $mh = curl_multi_init(); foreach ($urls as $url) { $multiCurl[$url] = curl_init(); curl_setopt($multiCurl[$url], CURLOPT_URL, $url); curl_setopt($multiCurl[$url], CURLOPT_HEADER, 0); curl_setopt($multiCurl[$url], CURLOPT_RETURNTRANSFER, 1); curl_multi_add_handle($mh, $multiCurl[$url]); } $index = null; do { curl_multi_exec($mh, $index); } while ($index > 0); // get content and remove handles foreach ($multiCurl as $k => $ch) { $results[$k] = curl_multi_getcontent($ch); curl_multi_remove_handle($mh, $ch); } // close curl_multi_close($mh); var_dump($results); // в $results у нас содержатся ответы на наши 3 запроса
Такие параллельные запросы выполняются значительно быстрее чем поочередные.
cURL позволяет нам установить cookie при передачи запросов, а также автоматически принимать и устанавливать cookie, которые нам возвращает сервер, сохраняя их между запросами.
Давайте рассмотрим такой пример:
$cookiePath = __DIR__ . '/cookie.txt'; $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, "https://yandex.ru/"); curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.125 Safari/537.36'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_HEADER, 0); //CURLOPT_COOKIEJAR - файл, куда пишутся куки после закрытия коннекта, например после curl_close() curl_setopt($ch, CURLOPT_COOKIEJAR, $cookiePath); //CURLOPT_COOKIEFILE - файл, откуда читаются куки. curl_setopt($ch, CURLOPT_COOKIEFILE, $cookiePath); $result = curl_exec($ch); // теперь в этой переменной будут содержаться cookie, которые установил сервер $cookies = curl_getinfo($ch, CURLINFO_COOKIELIST); curl_close($ch); // выводим на экран содержимое файла cookie.txt, которое установил нам Yandex. // можете изучить сколько всего он сохраняет на ваш компьютер при первом же заходе. echo file_get_contents($cookiePath);
Теперь cookie у нас хранятся в файле cookie.txt в директории со скриптом (если вы ничего не меняли). Если мы совершаем повторные запросы, то cURL автоматически берет и отправляет cookie на сервер, как и обычный браузер. Таким образом мы можем авторизироваться на сайте и сохранить сеанс между запросами.
$ch = curl_init(); curl_setopt($ch, CURLOPT_URL, "https://example.com/"); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_HEADER, 0); // мы напрямую указываем cookie, которые хотим передать на сервера curl_setopt($ch, CURLOPT_COOKIE, 'PHPSESSID=123456'); $result = curl_exec($ch); curl_close($ch);
Иногда сайт, к которому мы обращаемся может фильтровать запросы, защищаясь от парсинга. Если для этого используются упрощенные способы защиты, например проверка User-Agent, то мы можем легко притвориться, что являемся реальным польователем, который взаимодействует с сайтом через браузер, мы можем послать заголовки и cookie, которые обычно посылает браузер.
В данном примере установлены заголовки, которые посылает Chrome.
$url = 'https://phpstack.ru/'; $headers = [ 'Connection: keep-alive', 'Upgrade-Insecure-Requests: 1', 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36', 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9', 'Accept-Encoding: gzip, deflate', 'Accept-Language: ru,en-US;q=0.9,en;q=0.8', ]; $cookieFile = __DIR__ . '/cookie.txt'; $curl = curl_init(); // создаем экземпляр curl curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); curl_setopt($curl, CURLOPT_VERBOSE, 1); curl_setopt($curl, CURLOPT_COOKIEFILE, $cookieFile); curl_setopt($curl, CURLOPT_COOKIEJAR, $cookieFile); curl_setopt($curl, CURLOPT_POST, false); // curl_setopt($curl, CURLOPT_URL, $url); $result = curl_exec($curl);
В простых ситуациях этого хватает. Но если используется защита при помощи javascript или что-то более продвинутое, то здесь cURL бессилен, и следует использовать либо BAS либо Zennoposter. Либо если вы хотите попытать счастье с PHP, то Selenium.
Не используйте эти знания в противоправных целях.
Простой пример для отправки запросов через proxy. Если ваш прокси предполагает авторизацию, то раскомментируйте соответствующие строчки.
$url = 'http://dynupdate.no-ip.com/ip.php'; // тут мы можем узнать свой IP адрес $proxy = '127.0.0.1:8888'; //$proxyauth = 'user:password'; // если прокси с авторизацией, то раскомметируйте $ch = curl_init(); curl_setopt($ch, CURLOPT_URL,$url); curl_setopt($ch, CURLOPT_PROXY, $proxy); //curl_setopt($ch, CURLOPT_PROXYUSERPWD, $proxyauth); // если прокси с авторизацией, то раскомметируйте curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_HEADER, 1); $html = curl_exec($ch); curl_close($ch); echo $html;
Для того, чтобы отправить файлы на сервер, мы просто заполняем поля POST запроса, указывая там специальный класс CURLFile. На сервере вы можете получить отправленные файлы, при помощи суперглобального массива $_FILES.
$url = 'https://phpstack.ru/'; $postFields = [ 'photo1' => new \CURLFile( __DIR__ . '/img1.jpg' ), 'photo2' => new \CURLFile( __DIR__ . '/img2.jpg' ), ]; $ch = curl_init($url); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $postFields); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_HEADER, false); $html = curl_exec($ch); curl_close($ch);
Чтобы с помощью cURL авторизироваться на сайте, который использует Basic HTTP-аутентификацию нужно установить опцию CURLOPT_USERPWD, в которой будет наш логин и пароль.
Пример:
$login = 'test_login'; // наш логин $password = 'test_password'; // наш пароль $url = 'https://phpstack.ru/'; $ch = curl_init($url); curl_setopt($ch, CURLOPT_USERPWD, "$login:$password"); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_HEADER, false); $result = curl_exec($ch); curl_close($ch);
$url = 'https://phpstack.ru/'; $oauthToken = 'Bearer dsfgdsfgdsfgdsfgdsfg'; // наш токен $ch = curl_init($url); curl_setopt($ch, CURLOPT_HTTPHEADER, array("Authorization: $oauthToken")); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_HEADER, false); $html = curl_exec($ch); curl_close($ch);
Давайте применим полученные нами знания и авторизируемся на каком-нибудь сайте. Для этого нужно посмотреть куда форма отправляет данные и отправить туда то же самое.
Допустим на сайте есть такая форма:
<html> <body> <form method = "POST" action="https://phpstack.ru/admin/' > <input name="login" type="text"> <input name="password" type="text"> <input type="submit" name="submit" value="Отправить" > </form> </body> </html>
Тогда наш cURL запрос должен быть сформирован так:
$url = 'http://phpstack.ru/admin/'; // url, на который отправляется запрос $postData = [ // поля нашего запроса 'login' => 'our_login', // наш логин 'password' => 'our_password', // наш пароль ]; $cookieFile = __DIR__ . '/cookie.txt'; // притворяемся браузером $headers = [ 'Connection: keep-alive', 'Upgrade-Insecure-Requests: 1', 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36', 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9', 'Accept-Encoding: gzip, deflate', 'Accept-Language: ru,en-US;q=0.9,en;q=0.8', ]; $post_data = http_build_query($post_data); $curl = curl_init(); curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); curl_setopt($curl, CURLOPT_VERBOSE, 1); curl_setopt($curl, CURLOPT_POSTFIELDS, $postData); curl_setopt($curl, CURLOPT_COOKIEFILE, $cookieFile); curl_setopt($curl, CURLOPT_COOKIEJAR, $cookieFile); curl_setopt($curl, CURLOPT_URL, $url); curl_setopt($curl, CURLOPT_POST, true); // true $result = curl_exec($curl);
В $result у нас ответ сервера, мы можем проверить, что на странице находится сообщение об успешной авторизации и дальше гулять по личному кабинету сайта. Да, кстати, не используйте эти знания в противоправных целях.
Есть очень интересный сервис: https://curlbuilder.com/ – он поможет вам построить запрос для консольного приложения.
И вот еще один сервис, который переводит консольную команду curl в PHP: https://incarnate.github.io/curl-to-php/
Так вы можете создать простые запросы на cURL в PHP не создавая их вручную.
В консоли браузера, во вкладке сеть, вы можете кликнуть правой кнопкой мыши и скопировать любой запрос в виде команды cURL, а потом с помощью сервиса curl-to-php перевести запрос в PHP. Теперь вы вообще можете сконвертировать в cURL абсолютно любой запрос, который посылает ваш браузер.
Вы можете спросить: почему у cURL такие кривые и страшные методы? У вас может возникнуть желание взять и создать обертку для работы с cURL, чтобы вы могли не писать каждый раз большие куски некрасивого кода, а писать все проще, например так:
$curl = new Curl(); $curl->get('https://www.example.com/search', [ 'q' => 'keyword', ]);
Или так:
$curl = new Curl(); $curl->post('https://www.example.com/login/', [ 'username' => 'myusername', 'password' => 'mypassword', ]);
К счастью, такая обертка уже написана и найти ее можно здесь: https://github.com/php-curl-class/php-curl-class
Просто установите ее при помощи: composer require php-curl-class/php-curl-class
и не работайте с кривыми кусками кода, которые таковы вероятно потому, что cURL изначально консольное приложение.
С помощью PHP мы можем отправить простой GET запрос используя функцию file_get_contents.
Пример:
$result = file_get_contents('https://phpstack.ru/');
Теперь у нас в переменной $result записан весь html код главной страницы этого сайта. Мы совершили GET запрос, а html код – это ответ на него.
При помощи file_get_contents мы также можем отправить POST запрос.
Пример:
$postData = http_build_query([ 'var1' => 'текст 1', 'var2' => 'текст 2' ]); $opts = [ 'http' => [ 'method' => 'POST', 'header' => 'Content-type: application/x-www-form-urlencoded', 'content' => $postData ] ]; $context = stream_context_create($opts); $result = file_get_contents('https://httpbin.org/anything', false, $context);
Подробнее о том, какие опции можно передавать в stream_context_create, вы можете изучить здесь: http://docs.php.net/manual/ru/context.http.php
В $result мы получили ответ на POST запрос. httpbin.org – это сторонний сервис, который вы можете использовать для отладки запросов. Он возвращает нам наш собственный запрос в формате JSON и еще некоторую информацию. Так мы можем увидеть, что мы отправляем в своих запросах.
Как видите file_get_contents – полезная функция, которая не только позволяет читать файлы на нашем сервере, но еще и отправлять запросы.
Подробнее о ней вы можете прочитать здесь: https://www.php.net/manual/ru/function.file-get-contents.php
Для работы с запросами есть еще более мощный инструмент: Guzzle
// Создаем клиента с базовым URL $client = new GuzzleHttp\Client(['base_uri' => 'https://foo.com/api/']); // Посылаем запрос на https://foo.com/api/test $response = $client->request('GET', 'test'); // Посылаем запрос на https://foo.com/root $response = $client->request('GET', '/root');
$response = $client->get('http://httpbin.org/get'); $response = $client->delete('http://httpbin.org/delete'); $response = $client->head('http://httpbin.org/get'); $response = $client->options('http://httpbin.org/get'); $response = $client->patch('http://httpbin.org/patch'); $response = $client->post('http://httpbin.org/post'); $response = $client->put('http://httpbin.org/put');
$promise = $client->getAsync('http://httpbin.org/get'); $promise = $client->deleteAsync('http://httpbin.org/delete'); $promise = $client->headAsync('http://httpbin.org/get'); $promise = $client->optionsAsync('http://httpbin.org/get'); $promise = $client->patchAsync('http://httpbin.org/patch'); $promise = $client->postAsync('http://httpbin.org/post'); $promise = $client->putAsync('http://httpbin.org/put');