Un API mic, dar complet
E timpul să punem piesele laolaltă: un API de utilizatori cu cele cinci endpoint-uri clasice —
POST /users creare · GET /users listare · GET /users/{id} citirea unuia · PUT /users/{id} înlocuire · DELETE /users/{id} ștergere.
O decizie specifică PHP: fiecare cerere rulează scriptul de rutare de la zero, așa că datele nu pot trăi într-o variabilă între cereri. API-urile reale păstrează starea într-o bază de date; aici rolul acesta îl joacă un mic fișier JSON — routerul îl încarcă, îl modifică, îl salvează înapoi. Handlerele și-ar păstra forma dacă fișierul ar fi înlocuit cu o bază de date reală.
Creare și listare
POST decodează JSON-ul primit, dă utilizatorului un id, îl stochează și răspunde 201 cu înregistrarea creată — inclusiv id-ul, ca să îl afle clientul. GET întoarce toți utilizatorii ca tablou JSON.
POST /users și GET /users
Două creări, apoi listarea: fiecare POST răspunde 201 cu utilizatorul stocat, iar GET-ul final îi arată pe amândoi — starea API-ului a crescut cu fiecare cerere.
Ieșire: script PHP
<?php
// Depozitul se află într-un fișier JSON - fiecare cerere rulează scriptul de la zero.
$router = <<<'PHP'
<?php
$storeFile = sys_get_temp_dir() . "/pc-create-and-list-store.json";
$users = json_decode(file_get_contents($storeFile), true);
$method = $_SERVER["REQUEST_METHOD"];
$path = parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);
header("Content-Type: application/json");
if ($method === "POST" && $path === "/users") {
$input = json_decode(file_get_contents("php://input"), true);
if (!is_array($input) || !isset($input["name"])) {
http_response_code(400);
echo '{"error": "invalid JSON"}' . "\n";
return;
}
$user = ["id" => count($users) + 1, "name" => $input["name"]];
$users[] = $user;
file_put_contents($storeFile, json_encode($users));
http_response_code(201);
echo json_encode($user) . "\n";
} elseif ($method === "GET" && $path === "/users") {
echo json_encode($users) . "\n";
} else {
http_response_code(404);
}
PHP;
function httpRequest(string $url, string $method = "GET", array $headers = [], ?string $body = null): array {
$options = ["method" => $method, "ignore_errors" => true];
if ($headers !== []) {
$options["header"] = implode("\r\n", $headers);
}
if ($body !== null) {
$options["content"] = $body;
}
$responseBody = file_get_contents($url, false, stream_context_create(["http" => $options]));
return [(int) explode(" ", $http_response_header[0])[1], $responseBody, $http_response_header];
}
$storeFile = sys_get_temp_dir() . "/pc-create-and-list-store.json";
file_put_contents($storeFile, "[]"); // pornim de la un depozit gol
$routerFile = sys_get_temp_dir() . "/pc-create-and-list.php";
file_put_contents($routerFile, $router);
// Un port liber, apoi serverul web încorporat în PHP ca proces-copil.
$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";
foreach (["Ana", "Ben"] as $name) {
[$status, $body] = httpRequest("$base/users", "POST",
["Content-Type: application/json"], json_encode(["name" => $name]));
echo "POST /users -> $status " . trim($body) . "\n";
}
[$status, $body] = httpRequest("$base/users");
echo "GET /users -> $status " . trim($body) . "\n";
proc_terminate($server);
proc_close($server);
unlink($routerFile);
unlink($storeFile);
Citirea unuia, înlocuire, ștergere
Rutele /users/{id} completează setul: citirea răspunde 200 sau 404, înlocuirea decodează datele noi în id-ul existent, ștergerea răspunde 204. După ștergere, același GET care funcționa înainte întoarce cinstit 404 — resursa nu mai există.
GET, PUT și DELETE pe /users/{id}
Viața completă a unei resurse: o citim, o înlocuim (răspunsul conține numele nou), o ștergem și nu mai reușim să o citim — fiecare pas cu codul de status corect.
Ieșire: script PHP
<?php
// GET, PUT și DELETE pentru o singură resursă, peste un depozit de tip fișier JSON.
$router = <<<'PHP'
<?php
$storeFile = sys_get_temp_dir() . "/pc-one-user-lifecycle-store.json";
$users = json_decode(file_get_contents($storeFile), true);
$method = $_SERVER["REQUEST_METHOD"];
$segments = explode("/", trim(parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH), "/"));
$id = $segments[1] ?? "";
if ($method === "GET") {
header("Content-Type: application/json");
if (!isset($users[$id])) {
http_response_code(404);
echo '{"error": "user not found"}' . "\n";
return;
}
echo json_encode($users[$id]) . "\n";
} elseif ($method === "PUT") {
$input = json_decode(file_get_contents("php://input"), true);
if (!is_array($input) || !isset($input["name"])) {
http_response_code(400);
echo '{"error": "invalid JSON"}' . "\n";
return;
}
$users[$id] = ["id" => (int) $id, "name" => $input["name"]];
file_put_contents($storeFile, json_encode($users));
header("Content-Type: application/json");
echo json_encode($users[$id]) . "\n";
} elseif ($method === "DELETE") {
unset($users[$id]);
file_put_contents($storeFile, json_encode($users));
http_response_code(204);
}
PHP;
function httpRequest(string $url, string $method = "GET", array $headers = [], ?string $body = null): array {
$options = ["method" => $method, "ignore_errors" => true];
if ($headers !== []) {
$options["header"] = implode("\r\n", $headers);
}
if ($body !== null) {
$options["content"] = $body;
}
$responseBody = file_get_contents($url, false, stream_context_create(["http" => $options]));
return [(int) explode(" ", $http_response_header[0])[1], $responseBody, $http_response_header];
}
$storeFile = sys_get_temp_dir() . "/pc-one-user-lifecycle-store.json";
file_put_contents($storeFile, '{"1": {"id": 1, "name": "Ana"}}');
$routerFile = sys_get_temp_dir() . "/pc-one-user-lifecycle.php";
file_put_contents($routerFile, $router);
// Un port liber, apoi serverul web încorporat în PHP ca proces-copil.
$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";
$steps = [
["GET", "/users/1", null],
["PUT", "/users/1", '{"name": "Ana Maria"}'],
["DELETE", "/users/1", null],
["GET", "/users/1", null],
];
foreach ($steps as [$method, $path, $payload]) {
$requestHeaders = $payload === null ? [] : ["Content-Type: application/json"];
[$status, $body] = httpRequest("$base$path", $method, $requestHeaders, $payload);
printf("%-6s %s -> %d %s\n", $method, $path, $status, trim($body));
}
proc_terminate($server);
proc_close($server);
unlink($routerFile);
unlink($storeFile);