Другая сторона API
PHP-скрипты бывают не только серверами — они и сами вызывают API: получают данные, общаются с сервисами, интегрируют сторонние системы. Встроенный клиент — это file_get_contents вместе с контекстом потока: один URL даёт обычный GET, а контекст добавляет методы, заголовки и тело. Три привычки:
1. укажите ignore_errors в контексте, чтобы ответы 4xx и 5xx всё равно отдавали вам своё тело,
2. проверьте код состояния — первую строку $http_response_header — прежде чем доверять телу,
3. декодируйте тело через json_decode и проверьте результат на null, прежде чем пользоваться данными.
(В PHP есть и расширение cURL для более сложной клиентской работы; здесь же всё построено только на встроенных потоках.) Примеры ниже обращаются к локальному тестовому серверу, поэтому вывод стабилен — клиентский код в точности такой же, какой вы направили бы на реальный URL.
GET и декодирование
Повседневная задача: выполнить GET по URL, проверить код состояния, декодировать JSON-тело в массивы с помощью json_decode($body, true) — а дальше это обычные данные PHP.
Получаем список в массивы
Тело ответа декодируется в список пользователей, а дальше это обычные данные PHP — их обходят в цикле и печатают поле за полем.
Вывод: PHP-скрипт
<?php
// Сервер, который отвечает JSON-списком пользователей.
$router = <<<'PHP'
<?php
header("Content-Type: application/json");
echo '[{"id": 1, "name": "Ana"}, {"id": 2, "name": "Ben"}]';
PHP;
$routerFile = sys_get_temp_dir() . "/pc-get-decode.php";
file_put_contents($routerFile, $router);
// Свободный порт, затем встроенный веб-сервер PHP как дочерний процесс.
$probe = stream_socket_server("tcp://127.0.0.1:0");
$port = (int) explode(":", stream_socket_get_name($probe, false))[1];
fclose($probe);
$server = proc_open(["php", "-S", "127.0.0.1:$port", $routerFile],
[1 => ["file", "/dev/null", "w"], 2 => ["file", "/dev/null", "w"]], $pipes);
do {
usleep(20000);
$up = @fsockopen("127.0.0.1", $port);
} while ($up === false);
fclose($up);
$base = "http://127.0.0.1:$port";
$context = stream_context_create(["http" => ["ignore_errors" => true]]);
$body = file_get_contents("$base/users", false, $context);
$status = (int) explode(" ", $http_response_header[0])[1];
if ($status !== 200) {
echo "неожиданный код состояния: $status\n";
} elseif (($users = json_decode($body, true)) === null) {
echo "некорректный JSON: " . json_last_error_msg() . "\n";
} else {
foreach ($users as $user) {
echo "пользователь {$user['id']}: {$user['name']}\n";
}
}
proc_terminate($server);
proc_close($server);
unlink($routerFile);
POST с JSON-телом
Отправка данных — зеркальное отражение: закодируйте массив, положите его в контекст как content с нужным заголовком Content-Type, отправьте POST — а затем декодируйте ответ сервера, который здесь несёт присвоенный сервером id.
Отправляем массив, читаем ответ
Массив уходит в виде JSON, а созданная запись возвращается со своим новым id — полный путь записи API-клиента в одной небольшой программе.
Вывод: PHP-скрипт
<?php
// Сервер присваивает id и отвечает 201.
$router = <<<'PHP'
<?php
$input = json_decode(file_get_contents("php://input"), true);
header("Content-Type: application/json");
http_response_code(201);
echo json_encode(["id" => 7, "name" => $input["name"]]);
PHP;
$routerFile = sys_get_temp_dir() . "/pc-post-json.php";
file_put_contents($routerFile, $router);
// Свободный порт, затем встроенный веб-сервер PHP как дочерний процесс.
$probe = stream_socket_server("tcp://127.0.0.1:0");
$port = (int) explode(":", stream_socket_get_name($probe, false))[1];
fclose($probe);
$server = proc_open(["php", "-S", "127.0.0.1:$port", $routerFile],
[1 => ["file", "/dev/null", "w"], 2 => ["file", "/dev/null", "w"]], $pipes);
do {
usleep(20000);
$up = @fsockopen("127.0.0.1", $port);
} while ($up === false);
fclose($up);
$base = "http://127.0.0.1:$port";
$payload = json_encode(["name" => "Cleo"]);
$context = stream_context_create(["http" => [
"method" => "POST",
"header" => "Content-Type: application/json",
"content" => $payload,
"ignore_errors" => true,
]]);
$body = file_get_contents("$base/users", false, $context);
[, $status] = explode(" ", $http_response_header[0], 2);
$created = json_decode($body, true);
echo "состояние: $status\n";
echo "создано: id {$created['id']}, name {$created['name']}\n";
proc_terminate($server);
proc_close($server);
unlink($routerFile);