Выполнение POST запроса к внешнему API на PHP
Основные методы отправки POST запросов к API из PHP
Самый надёжный и гибкий подход - использование расширения cURL.
Оно поддерживает все необходимые опции: установку метода, заголовков, передачу данных, обработку сертификатов и таймауты. Ниже приведён базовый пример отправки JSON-данных и чтения ответа.
<?php
$url = 'https://api.example.com/v1/items';
$data = ['name' => 'Новый товар', 'price' => 99.99];
$json = json_encode($data);
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $json);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'Accept: application/json'
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
// Для продакшена отключать проверку SSL не рекомендуется
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if(curl_error($ch)) {
$error = curl_error($ch);
// обработка ошибки
}
curl_close($ch);
// Обработка ответа
if($httpCode === 201) {
$result = json_decode($response, true);
}
?>Пояснения:
- CURLOPT_POST - переводит запрос в метод POST.
- CURLOPT_POSTFIELDS - тело запроса. Для JSON передаётся строка, для формы - массив.
- CURLOPT_HTTPHEADER - заголовки для указания типа данных.
- CURLOPT_RETURNTRANSFER - возвращает ответ как строку вместо вывода.
Проблема: неверный заголовок Content-Type может привести к ошибке 415. Решение - всегда указывать его соответственно передаваемому формату.
Как выполнить POST запрос без использования cURL?
Встроенная функция file_get_contents с контекстом потоков подходит для простых запросов, когда не требуется детальная настройка. Пример:
<?php
$url = 'https://api.example.com/v1/items';
$data = ['name' => 'Товар'];
$options = [
'http' => [
'method' => 'POST',
'header' => "Content-Type: application/x-www-form-urlencoded\r\n",
'content' => http_build_query($data)
]
];
$context = stream_context_create($options);
$response = file_get_contents($url, false, $context);
// $http_response_header содержит заголовки ответа
?>Ограничения: сложнее обрабатывать ошибки, нет прямого контроля таймаутов (кроме default_socket_timeout). Подходит для быстрых интеграций без внешних зависимостей.
Типичная ошибка - неправильное указание заголовков (пропуск \r\n в конце каждой строки). Решение - формировать заголовки строго по спецификации HTTP.
Как отправить POST запрос с помощью Composer пакета Guzzle?
Guzzle - современная библиотека для работы с HTTP. Устанавливается через Composer: composer require guzzlehttp/guzzle. Пример отправки JSON:
<?php
require 'vendor/autoload.php';
use GuzzleHttp\Client;
$client = new Client(['base_uri' => 'https://api.example.com']);
try {
$response = $client->post('/v1/items', [
'json' => ['name' => 'Товар'],
'headers' => ['Authorization' => 'Bearer token123']
]);
$body = $response->getBody()->getContents();
$status = $response->getStatusCode();
} catch (\GuzzleHttp\Exception\ClientException $e) {
// 4xx ошибки
}
?>Guzzle автоматически обрабатывает JSON, поддерживает повторные попытки, асинхронные запросы. Рекомендуется для сложных проектов.
Ошибка при неверной конфигурации SSL - можно отключить проверку на этапе разработки: ['verify' => false]. В продакшене это недопустимо.
Как принять и обработать POST данные на PHP сервере?
Когда необходимо создать собственное API, принимающее POST запросы, используется суперглобальный массив $_POST для формы или php://input для JSON. Пример обработчика:
<?php
// Устанавливаем заголовки ответа
header('Content-Type: application/json');
// Получаем тело запроса
$input = file_get_contents('php://input');
$data = json_decode($input, true);
if (json_last_error() !== JSON_ERROR_NONE) {
http_response_code(400);
echo json_encode(['error' => 'Невалидный JSON']);
exit;
}
// Валидация и работа с данными
if (empty($data['name'])) {
http_response_code(422);
echo json_encode(['error' => 'Поле name обязательно']);
exit;
}
// Сохранение в БД...
http_response_code(201);
echo json_encode(['id' => 1, 'message' => 'Создано']);
?>Важно проверять метод запроса: $_SERVER['REQUEST_METHOD'] === 'POST'. Типичная ошибка - забыть декодировать JSON или неправильно установить заголовок ответа.
Если данные приходят как multipart/form-data (с файлами), используйте $_POST и $_FILES. Для JSON всегда php://input.
Расширенные примеры использования POST запросов в PHP
Пример 1: POST запрос с авторизацией Bearer токеном и обработкой ошибок через cURL
<?php
function apiPost($url, $data, $token) {
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'Authorization: Bearer ' . $token,
'Accept: application/json'
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
if ($error) {
return ['error' => "cURL error: $error"];
}
return ['code' => $httpCode, 'body' => json_decode($response, true)];
}
$result = apiPost('https://api.example.com/data', ['key' => 'value'], 'myToken123');
print_r($result);
?>Array
(
[code] => 200
[body] => Array
(
[success] => true
[id] => 42
)
)Пояснение: функция возвращает структурированный ответ с кодом и телом. Таймауты соединения и выполнения защищают от зависания.
Пример 2: POST запрос с загрузкой файла (multipart/form-data) через cURL
<?php
$url = 'https://api.example.com/upload';
$filePath = '/path/to/file.pdf';
$cfile = new CURLFile($filePath, 'application/pdf', 'document.pdf');
$postData = [
'file' => $cfile,
'description' => 'Отчёт за март'
];
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postData); // массив, не json!
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
?>При использовании CURLFile не нужно вручную устанавливать Content-Type - cURL задаст multipart/form-data автоматически.
Пример 3: Асинхронный POST запрос с помощью curl_multi (выполнение нескольких запросов параллельно)
<?php
$urls = [
'https://api1.com/endpoint',
'https://api2.com/endpoint'
];
$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, json_encode(['index' => $i]));
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_multi_add_handle($multiHandle, $ch);
$handles[$i] = $ch;
}
$active = null;
do {
$mrc = curl_multi_exec($multiHandle, $active);
} while ($mrc == CURLM_CALL_MULTI_PERFORM);
while ($active && $mrc == CURLM_OK) {
if (curl_multi_select($multiHandle) !== -1) {
do {
$mrc = curl_multi_exec($multiHandle, $active);
} while ($mrc == CURLM_CALL_MULTI_PERFORM);
}
}
foreach ($handles as $i => $ch) {
$response = curl_multi_getcontent($ch);
$info = curl_getinfo($ch);
echo "Запрос $i: HTTP {$info['http_code']} - $response\n";
curl_multi_remove_handle($multiHandle, $ch);
curl_close($ch);
}
curl_multi_close($multiHandle);
?>Результат (пример):
Запрос 0: HTTP 200 - {"status":"ok"}
Запрос 1: HTTP 200 - {"status":"ok"}Такой подход сокращает общее время ожидания при работе с несколькими API.