Отправка HTTPS запросов на PHP: способы и примеры
Основные методы отправки HTTPS запросов в PHP
Решение с помощью cURL (наиболее эффективное)
Библиотека cURL
cURL предоставляет гибкий и надёжный способ выполнения HTTPS запросов в PHP. Она поддерживает GET, POST, любые методы, заголовки, таймауты, сертификаты SSL и многое другое. Рекомендуется как основной инструмент для работы с удалёнными ресурсами.
<?php
// Инициализация сеанса cURL
$ch = curl_init('https://api.example.com/data');
// Настройка параметров
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // возвращать результат, а не выводить
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); // проверка SSL сертификата
curl_setopt($ch, CURLOPT_CAINFO, '/path/to/cacert.pem'); // свой файл сертификатов (опционально)
curl_setopt($ch, CURLOPT_TIMEOUT, 30); // таймаут в секундах
// Выполнение запроса
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
// Обработка ошибок
if (curl_errno($ch)) {
$error = curl_error($ch);
// запись в лог, исключение и т.д.
}
curl_close($ch);
?>
Приведённый код отправляет GET запрос на HTTPS endpoint. После выполнения переменная $response содержит тело ответа, а $httpCode – код состояния HTTP. Ошибки, такие как недоступность хоста или проблемы с SSL, обрабатываются через curl_errno().
Типичные ошибки и их решения
- SSL certificate problem – возникает при неверной цепочке сертификатов. Решение: скачать актуальный cacert.pem с curl.haxx.se и указать путь через
CURLOPT_CAINFO. Либо временно отключить проверку черезCURLOPT_SSL_VERIFYPEER = false(не рекомендуется для продакшена). - Timeout – увеличить значение
CURLOPT_TIMEOUTилиCURLOPT_CONNECTTIMEOUT. - Could not resolve host – проверить правильность URL и состояние DNS.
Как выполнить HTTPS запрос без cURL, используя встроенные функции PHP?
Для простых запросов подходит связка file_get_contents и stream_context_create. Этот метод не требует установки дополнительных расширений, но имеет ограничения.
<?php
$url = 'https://api.example.com/items';
$data = json_encode(['name' => 'test']);
$opts = [
'http' => [
'method' => 'POST',
'header' => "Content-Type: application/json\r\n" .
"Accept: application/json\r\n",
'content' => $data,
'timeout' => 10,
'ignore_errors' => true // получать тело ответа даже при ошибках HTTP
],
'ssl' => [
'verify_peer' => true,
'cafile' => '/path/to/cacert.pem',
'verify_peer_name' => true
]
];
$context = stream_context_create($opts);
$response = @file_get_contents($url, false, $context);
if ($response === false) {
$error = error_get_last();
// обработка ошибки
}
?>
Настройки SSL передаются в ключе 'ssl'. Таймаут задаётся через 'timeout'. С помощью 'ignore_errors' можно получать тело ответа даже при коде 4xx/5xx. Основной недостаток – сложность управления заголовками запроса и невозможность работы с большими объёмами данных в потоковом режиме.
Распространённые проблемы
- allow_url_fopen запрещён – в php.ini должна быть директива
allow_url_fopen = On. Если отключена, единственный выход – cURL. - SSL ошибки – те же, что и для cURL: необходимо указать cacert.pem или временно отключить проверку (
'verify_peer' => false). - Невозможно отправить DELETE, PUT и другие методы – метод указывается в ключе
'method', всё корректно работает.
Как использовать современный HTTP клиент Guzzle для сложных запросов?
Guzzle – это мощная библиотека, устанавливаемая через Composer. Она предоставляет объектно-ориентированный интерфейс, поддержку асинхронных запросов, мидлвары и встроенную работу с PSR-7. Идеально подходит для сложных API-интеграций.
<?php
require 'vendor/autoload.php';
use GuzzleHttp\Client;
$client = new Client([
'base_uri' => 'https://api.example.com/',
'timeout' => 30.0,
'verify' => '/path/to/cacert.pem'
]);
try {
$response = $client->request('POST', 'items', [
'headers' => [
'Accept' => 'application/json',
'X-API-Key' => 'your-key'
],
'json' => ['name' => 'test']
]);
$body = $response->getBody()->getContents();
$statusCode = $response->getStatusCode();
} catch (\GuzzleHttp\Exception\ConnectException $e) {
// ошибка соединения
} catch (\GuzzleHttp\Exception\ClientException $e) {
// ошибка HTTP (4xx)
}
?>
Guzzle автоматически преобразует переданные json и устанавливает Content-Type. Обработка ошибок вынесена в исключения. Для простых скриптов установка Composer может быть избыточной, но при активном использовании HTTP-запросов библиотека окупается.
Возможные сложности
- Composer не установлен – необходима его установка глобально или в проекте.
- Версия PHP – Guzzle требует PHP 7.2.5+ для последних версий. Старые проекты могут использовать Guzzle 6.
- Конфликт с другими библиотеками – управляется через composer.json.
Как вручную сформировать HTTPS запрос через сокеты?
Техника для глубокого понимания протокола. Используется редко, но может пригодиться в случаях, когда отсутствуют cURL и allow_url_fopen. Требует ручного формирования запроса и работы с OpenSSL.
<?php
$host = 'api.example.com';
$port = 443;
$path = '/data';
$socket = fsockopen("ssl://$host", $port, $errno, $errstr, 30);
if (!$socket) {
die("Ошибка: $errstr ($errno)");
}
$request = "GET $path HTTP/1.1\r\n" .
"Host: $host\r\n" .
"Connection: close\r\n\r\n";
fwrite($socket, $request);
$response = '';
while (!feof($socket)) {
$response .= fgets($socket, 4096);
}
fclose($socket);
// Разделяем заголовки и тело
list($headers, $body) = explode("\r\n\r\n", $response, 2);
echo $body;
?>
В этом примере используется поток ssl://, который автоматически устанавливает TLS. Такой подход даёт полный контроль, но требует ручной обработки заголовков и передачи данных, а также не поддерживает прокси, куки и другие удобства. Практическое применение ограничено отладкой или встраиванием в системы без cURL.
Типичные ошибки
- Unknown protocol – если PHP собран без поддержки OpenSSL,
ssl://не работает. Проверить черезextension_loaded('openssl'). - Chunked transfer encoding – сокет не обрабатывает автоматически, поэтому для больших ответов потребуется парсинг
Transfer-Encoding: chunked. - Блокирующие вызовы – при медленных соединениях скрипт может зависнуть. Рекомендуется задавать таймауты через
stream_set_timeout.
Расширенные примеры и нестандартные случаи
Пример 1: cURL с отправкой JSON и получением ответа
<?php
$ch = curl_init('https://httpbin.org/post');
$payload = json_encode(['key' => 'value', 'number' => 42]);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $payload,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Accept: application/json'
],
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_CAINFO => '/etc/ssl/certs/ca-certificates.crt',
CURLOPT_TIMEOUT => 10
]);
$response = curl_exec($ch);
$info = curl_getinfo($ch);
curl_close($ch);
echo "HTTP Code: " . $info['http_code'] . "\n";
echo "Response Body:\n";
echo $response;
?>
Результат выполнения (сокращённый):
HTTP Code: 200
Response Body:
{
"args": {},
"data": "{\"key\":\"value\",\"number\":42}",
"files": {},
"form": {},
"headers": {
"Content-Type": "application/json",
...
},
"json": {
"key": "value",
"number": 42
},
"url": "https://httpbin.org/post"
}
Пример 2: file_get_contents для отправки PUT запроса
<?php
$url = 'https://httpbin.org/put';
$data = json_encode(['updated' => true]);
$opts = [
'http' => [
'method' => 'PUT',
'header' => "Content-Type: application/json\r\n" .
"Accept: application/json\r\n",
'content' => $data,
'timeout' => 5
],
'ssl' => [
'verify_peer' => true,
'cafile' => '/etc/ssl/certs/ca-certificates.crt'
]
];
$context = stream_context_create($opts);
$result = file_get_contents($url, false, $context);
if ($result !== false) {
echo $result;
} else {
echo "Ошибка: " . print_r(error_get_last(), true);
}
?>
Результат (фрагмент):
{
"args": {},
"data": "{\"updated\":true}",
"form": {},
"json": {
"updated": true
},
"method": "PUT",
"url": "https://httpbin.org/put"
}
Пример 3: Guzzle с параллельными асинхронными запросами
<?php
require 'vendor/autoload.php';
use GuzzleHttp\Client;
use GuzzleHttp\Promise;
$client = new Client(['timeout' => 5, 'verify' => '/etc/ssl/certs/ca-certificates.crt']);
$promises = [
'users' => $client->getAsync('https://api.example.com/users'),
'posts' => $client->getAsync('https://api.example.com/posts')
];
$results = Promise\Utils::settle($promises)->wait();
foreach ($results as $key => $result) {
if ($result['state'] === 'fulfilled') {
echo "$key: успех, код " . $result['value']->getStatusCode() . "\n";
} else {
echo "$key: ошибка - " . $result['reason'] . "\n";
}
}
?>
Результат (примерный вывод):
users: успех, код 200 posts: успех, код 200
Пример 4: cURL с загрузкой файла (multipart/form-data)
<?php
$file = new CURLFile('/path/to/image.jpg', 'image/jpeg', 'image.jpg');
$ch = curl_init('https://httpbin.org/post');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => ['file' => $file],
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_CAINFO => '/etc/ssl/certs/ca-certificates.crt'
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
echo "HTTP $httpCode\n";
echo $response;
?>
Результат (часть JSON, где отображаются файлы):
HTTP 200
{
"files": {
"file": "data:image/jpeg;base64,..."
},
"form": {},
...
}