Отправка POST данных через cURL в PHP: особенности реализации
Основной способ отправки POST запроса через cURL
Базовый алгоритм включает инициализацию сеанса, установку параметров и выполнение запроса. Ниже приведён минимальный рабочий пример.
<?php
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://example.com/api');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query(['name' => 'John', 'email' => 'john@example.com']));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
if (curl_errno($ch)) {
$error = curl_error($ch);
}
curl_close($ch);
?>
Пояснение шагов
- curl_init() создаёт новый сеанс.
- CURLOPT_URL задаёт адрес назначения.
- CURLOPT_POST включает метод POST.
- CURLOPT_POSTFIELDS принимает строку или массив (рекомендуется использовать http_build_query для правильного кодирования).
- CURLOPT_RETURNTRANSFER гарантирует, что результат будет возвращён в переменную, а не выведен напрямую.
- curl_exec() выполняет запрос.
- curl_errno() и curl_error() проверяют наличие ошибок.
- curl_close() закрывает сеанс.
Типичные ошибки и их решения
- Ошибка SSL: Если сервер использует самоподписанный сертификат, может возникнуть ошибка проверки. Решение - отключить проверку
CURLOPT_SSL_VERIFYPEER => false(не рекомендуется для продакшена). - Таймаут: Долгий ответ можно обработать, установив
CURLOPT_TIMEOUT => 30(секунды). - Некорректный Content-Type: Если сервер ожидает
application/x-www-form-urlencoded, то передача строки через http_build_query - правильный подход. Для других типов данных нужно указывать заголовки явно.
Варианты отправки POST запросов
Как отправить POST запрос с JSON данными?
Для отправки JSON необходимо установить заголовок Content-Type: application/json и передать строку JSON в CURLOPT_POSTFIELDS.
<?php
$data = ['username' => 'admin', 'password' => 'secret'];
$ch = curl_init('https://example.com/auth');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
?>
Возможные проблемы
- Если забыть установить заголовок Content-Type, сервер может не интерпретировать данные как JSON.
- При работе с кириллицей необходимо убедиться, что данные корректно кодируются в UTF-8 перед json_encode.
Как загрузить файл через POST с помощью cURL?
Для отправки файлов используется класс CURLFile или устаревший синтаксис с префиксом @ (начиная с PHP 5.5 предпочтительнее CURLFile).
<?php
$file = new CURLFile('/path/to/image.jpg', 'image/jpeg', 'image.jpg');
$postData = ['file' => $file, 'description' => 'Photo'];
$ch = curl_init('https://example.com/upload');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData); // массив автоматически устанавливает multipart/form-data
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
?>
Частые ошибки
- Если не указать mime-тип, может использоваться
application/octet-stream. - Путь к файлу должен быть абсолютным или корректным относительным относительно рабочей директории скрипта.
- Большие файлы могут превысить лимиты памяти или времени выполнения - настройте
memory_limitиmax_execution_time.
Как отправить POST запрос с авторизацией Basic?
Для базовой аутентификации достаточно задать параметры CURLOPT_USERPWD и CURLOPT_HTTPAUTH.
<?php
$ch = curl_init('https://example.com/protected');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, 'data=value');
curl_setopt($ch, CURLOPT_USERPWD, 'user:pass');
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
?>
Проблемы
- Если сервер ожидает другой тип аутентификации (Digest, NTLM), нужно указать соответствующую константу.
- Пароль в открытом виде в коде - не лучшая практика; используйте переменные окружения или файлы конфигурации.
Как обработать ответ с кодом ошибки HTTP?
После выполнения запроса полезно проверить HTTP-статус ответа через curl_getinfo($ch, CURLINFO_HTTP_CODE).
<?php
$ch = curl_init('https://example.com/api');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, ['key' => 'value']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($httpCode >= 400) {
// Обработка ошибки
} else {
// Успешный ответ
}
curl_close($ch);
?>
Ошибки
- CURLOPT_RETURNTRANSFER не установлен - тогда curl_exec вернёт true/false, а тело ответа будет выведено напрямую.
- При редиректах код 3xx не считается ошибкой, если не включён CURLOPT_FOLLOWLOCATION.
Как добавить пользовательские заголовки и cookie?
Дополнительные заголовки задаются через CURLOPT_HTTPHEADER, а cookie - через CURLOPT_COOKIE или CURLOPT_COOKIEFILE.
<?php
$headers = [
'User-Agent: MyApp/1.0',
'Accept: application/json'
];
$ch = curl_init('https://example.com');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, 'data=test');
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_COOKIE, 'session_id=abc123');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
?>
Типичные сложности
- Заголовки должны быть строками в формате
Name: Valueбез лишних пробелов. - Если используется несколько cookie, их можно объединить через точку с запятой.
Как настроить таймауты и ограничение скорости?
Таймаут на весь запрос задаётся CURLOPT_TIMEOUT, на соединение - CURLOPT_CONNECTTIMEOUT. Для ограничения скорости используется CURLOPT_MAX_RECV_SPEED_LARGE.
<?php
$ch = curl_init('https://example.com');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, 'x=1');
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
curl_setopt($ch, CURLOPT_MAX_RECV_SPEED_LARGE, 1024); // байт в секунду
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
?>
Ошибки при настройках
- При слишком малом таймауте запрос может завершиться с ошибкой 28 (CURLE_OPERATION_TIMEDOUT).
- Ограничение скорости может привести к увеличению времени выполнения, необходимо следить за max_execution_time в PHP.
Расширенные примеры использования cURL для POST запросов
Параллельная отправка нескольких POST запросов (curl_multi)
Когда требуется отправить несколько запросов одновременно, используется мульти-сеанс. Это ускоряет работу при обращении к разным серверам или эндпоинтам.
<?php
$urls = [
'https://api1.example.com/post',
'https://api2.example.com/post'
];
$multiHandle = curl_multi_init();
$handles = [];
foreach ($urls as $i => $url) {
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, "data=$i");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_multi_add_handle($multiHandle, $ch);
$handles[] = $ch;
}
$active = null;
do {
$status = curl_multi_exec($multiHandle, $active);
} while ($active > 0 && $status === CURLM_OK);
foreach ($handles as $ch) {
$response = curl_multi_getcontent($ch);
echo $response . PHP_EOL;
curl_multi_remove_handle($multiHandle, $ch);
curl_close($ch);
}
curl_multi_close($multiHandle);
?>
Результат: содержимое ответов от обоих серверов будет выведено последовательно.
Отправка POST запроса с автоматическими повторными попытками при неудаче
Для повышения надёжности можно реализовать логику повторных попыток с экспоненциальной задержкой.
<?php
function curlPostWithRetry($url, $data, $maxRetries = 3, $delay = 1) {
$attempt = 0;
while ($attempt < $maxRetries) {
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
if ($httpCode === 200 && !$error) {
return $response;
}
$attempt++;
sleep($delay * $attempt);
}
return false;
}
$result = curlPostWithRetry('https://example.com/api', ['key' => 'value']);
?>
Функция возвращает ответ при успехе или false после исчерпания попыток.
Отправка POST запроса через SOCKS5 прокси
Для обхода ограничений или использования анонимности можно настроить cURL на работу через прокси-сервер.
<?php
$ch = curl_init('https://httpbin.org/post');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, 'test=proxy');
curl_setopt($ch, CURLOPT_PROXY, 'socks5://127.0.0.1:1080');
curl_setopt($ch, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
?>
В ответе от httpbin.org будет видно, что запрос пришёл через указанный прокси.
Сохранение и повторное использование cookie между запросами
Для имитации сессии можно сохранить cookie в файл и загружать его при последующих запросах.
<?php
$cookieFile = '/tmp/cookies.txt';
$ch = curl_init('https://example.com/login');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, 'user=admin&pass=123');
curl_setopt($ch, CURLOPT_COOKIEJAR, $cookieFile);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
// Второй запрос с теми же cookie
$ch2 = curl_init('https://example.com/profile');
curl_setopt($ch2, CURLOPT_COOKIEFILE, $cookieFile);
curl_setopt($ch2, CURLOPT_RETURNTRANSFER, true);
$profile = curl_exec($ch2);
curl_close($ch2);
?>
Первый запрос сохраняет куки, второй использует их, имитируя аутентифицированное состояние.
Альтернативный метод POST без cURL: stream_context_create
В некоторых окружениях cURL может быть отключён. Тогда можно воспользоваться обёрткой file_get_contents с контекстом потока.
<?php
$data = http_build_query(['name' => 'John']);
$options = [
'http' => [
'method' => 'POST',
'header' => "Content-Type: application/x-www-form-urlencoded\r\n",
'content' => $data
]
];
$context = stream_context_create($options);
$response = file_get_contents('https://example.com/api', false, $context);
?>
Этот способ проще, но имеет меньше возможностей (таймауты, прокси, работа с файлами).