API на PHP для взаимодействия с MySQL: практическое руководство
Построение API на PHP с MySQL
Как создать REST API на PHP, работающее с MySQL, без использования фреймворков?
Основной рекомендуемый подход - использование расширения PDO (PHP Data Objects). Оно обеспечивает безопасную работу с базой данных через подготовленные запросы и защиту от SQL-инъекций. Ниже приведен минимальный пример API, который принимает GET-запросы и возвращает список записей из таблицы users.
// api.php
header('Content-Type: application/json');
$host = 'localhost';
$db = 'test';
$user = 'root';
$pass = '';
$dsn = "mysql:host=$host;dbname=$db;charset=utf8mb4";
try {
$pdo = new PDO($dsn, $user, $pass, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
]);
} catch (PDOException $e) {
http_response_code(500);
echo json_encode(['error' => 'Database connection failed']);
exit;
}
$action = $_GET['action'] ?? 'list';
switch ($action) {
case 'list':
$stmt = $pdo->query('SELECT id, name, email FROM users');
$users = $stmt->fetchAll();
echo json_encode($users);
break;
default:
http_response_code(404);
echo json_encode(['error' => 'Action not found']);
}Table entry php (php: вставка записи в таблицу)
В этом коде создается PDO-объект с указанием DSN, имени пользователя и пароля. Устанавливается режим ошибок в исключения, что упрощает отладку. Параметр ATTR_DEFAULT_FETCH_MODE задает ассоциативный массив как формат результата. Затем по параметру action выбирается ветвление. Для действия list выполняется простой SELECT-запрос и результат выводится в JSON. В случае ошибки подключения возвращается код 500.
Типичная ошибка: забыть установить кодировку charset=utf8mb4 в DSN - тогда русские символы могут отображаться как знаки вопроса. Решение: всегда явно указывать charset.
Еще одна проблема - отсутствие обработки исключений в блоке catch. Если не вернуть корректный HTTP-статус, клиент может получить 200 OK с пустым телом, что вводит в заблуждение.
Как реализовать API с помощью расширения mysqli?
Альтернативный вариант - использование объектно-ориентированного стиля mysqli. Этот подход также использует подготовленные запросы, но требует больше ручного управления.
$mysqli = new mysqli('localhost', 'root', '', 'test');
if ($mysqli->connect_error) {
http_response_code(500);
echo json_encode(['error' => $mysqli->connect_error]);
exit;
}
$action = $_GET['action'] ?? 'list';
if ($action === 'list') {
$result = $mysqli->query('SELECT id, name, email FROM users');
$users = $result->fetch_all(MYSQLI_ASSOC);
echo json_encode($users);
}
$mysqli->close();Php mysql module (модуль mysql для php)
Здесь проверка соединения выполняется через свойство connect_error. Метод fetch_all(MYSQLI_ASSOC) возвращает все строки в виде ассоциативного массива. Обратите внимание, что нельзя использовать mysqli для динамических запросов без экранирования - для вставки данных следует применять подготовленные выражения через prepare и bind_param.
Распространенная ошибка: отсутствие закрытия соединения. При длительных скриптах это может привести к исчерпанию пула соединений. Всегда вызывайте $mysqli->close() после завершения работы.
Как упростить маршрутизацию с помощью микрофреймворка Slim?
Для более сложных проектов удобно использовать микрофреймворки, например Slim. Он предоставляет удобную маршрутизацию, middleware и встроенную работу с PSR-7.
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Slim\Factory\AppFactory;
require __DIR__ . '/vendor/autoload.php';
$app = AppFactory::create();
$app->get('/api/users', function (Request $request, Response $response) {
// Подключение к БД (можно вынести в middleware)
$pdo = new PDO(/* ... */);
$stmt = $pdo->query('SELECT id, name FROM users');
$users = $stmt->fetchAll();
$response->getBody()->write(json_encode($users));
return $response->withHeader('Content-Type', 'application/json');
});
$app->run();Php mysql примеры (примеры работы с mysql в php)
В данном примере используется маршрут GET /api/users. Ответ формируется через объект Response, что позволяет легко менять статус и заголовки. Slim также поддерживает группы маршрутов, middleware для проверки CORS и т.д.
Ошибка: если не добавить withHeader, по умолчанию Content-Type может быть text/html. Клиент, ожидающий JSON, не сможет корректно обработать ответ. Всегда явно устанавливайте заголовок.
Как использовать ORM Eloquent вне Laravel?
Для работы с базой данных на более высоком уровне абстракции применяется ORM Eloquent (часть Laravel). Его можно установить отдельно через Composer и использовать через Capsule Manager.
use Illuminate\Database\Capsule\Manager as Capsule;
$capsule = new Capsule;
$capsule->addConnection([
'driver' => 'mysql',
'host' => 'localhost',
'database' => 'test',
'username' => 'root',
'password' => '',
'charset' => 'utf8mb4',
]);
$capsule->setAsGlobal();
$capsule->bootEloquent();
$users = Capsule::table('users')->get();
echo $users->toJson();После настройки Capsule можно использовать фасад Capsule::table() для построения запросов. ORM позволяет работать с моделями, но в данном примере показан сырой Query Builder. Для API это часто удобнее, так как не требует создания классов моделей.
Проблема: при использовании Eloquent вне Laravel могут возникнуть конфликты с другими библиотеками из-за автозагрузки. Рекомендуется тщательно тестировать.
Расширенные примеры реализации API
Пример 1. Полный CRUD API на PDO с маршрутизацией по методу и параметрам
<?php
// file: api_users.php
header('Content-Type: application/json');
$dsn = 'mysql:host=localhost;dbname=test;charset=utf8mb4';
$pdo = new PDO($dsn, 'root', '', [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
]);
$method = $_SERVER['REQUEST_METHOD'];
$id = $_GET['id'] ?? null;
try {
switch ($method) {
case 'GET':
if ($id) {
$stmt = $pdo->prepare('SELECT id, name, email FROM users WHERE id = ?');
$stmt->execute([$id]);
$user = $stmt->fetch();
if (!$user) {
http_response_code(404);
echo json_encode(['error' => 'User not found']);
} else {
echo json_encode($user);
}
} else {
$stmt = $pdo->query('SELECT id, name, email FROM users');
echo json_encode($stmt->fetchAll());
}
break;
case 'POST':
$input = json_decode(file_get_contents('php://input'), true);
$stmt = $pdo->prepare('INSERT INTO users (name, email) VALUES (?, ?)');
$stmt->execute([$input['name'], $input['email']]);
http_response_code(201);
echo json_encode(['id' => $pdo->lastInsertId()]);
break;
case 'PUT':
if (!$id) {
http_response_code(400);
echo json_encode(['error' => 'ID required']);
break;
}
$input = json_decode(file_get_contents('php://input'), true);
$stmt = $pdo->prepare('UPDATE users SET name=?, email=? WHERE id=?');
$stmt->execute([$input['name'], $input['email'], $id]);
if ($stmt->rowCount() === 0) {
http_response_code(404);
echo json_encode(['error' => 'User not found']);
} else {
echo json_encode(['updated' => true]);
}
break;
case 'DELETE':
if (!$id) {
http_response_code(400);
echo json_encode(['error' => 'ID required']);
break;
}
$stmt = $pdo->prepare('DELETE FROM users WHERE id=?');
$stmt->execute([$id]);
if ($stmt->rowCount() === 0) {
http_response_code(404);
echo json_encode(['error' => 'User not found']);
} else {
echo json_encode(['deleted' => true]);
}
break;
default:
http_response_code(405);
echo json_encode(['error' => 'Method not allowed']);
}
} catch (PDOException $e) {
http_response_code(500);
echo json_encode(['error' => $e->getMessage()]);
}Результаты тестов через curl:
# Создание пользователя
$ curl -X POST http://localhost/api_users.php -d '{"name":"Иван","email":"ivan@example.com"}'
{"id":1}
# Получение всех пользователей
$ curl http://localhost/api_users.php
[{"id":1,"name":"Иван","email":"ivan@example.com"}]
# Получение одного пользователя
$ curl http://localhost/api_users.php?id=1
{"id":1,"name":"Иван","email":"ivan@example.com"}
# Обновление
$ curl -X PUT http://localhost/api_users.php?id=1 -d '{"name":"Петр","email":"petr@example.com"}'
{"updated":true}
# Удаление
$ curl -X DELETE http://localhost/api_users.php?id=1
{"deleted":true}Пример 2. API с использованием mysqli и подготовленных выражений
<?php
header('Content-Type: application/json');
$mysqli = new mysqli('localhost', 'root', '', 'test');
if ($mysqli->connect_error) {
http_response_code(500);
echo json_encode(['error' => $mysqli->connect_error]);
exit;
}
$method = $_SERVER['REQUEST_METHOD'];
$id = $_GET['id'] ?? null;
if ($method === 'POST') {
$input = json_decode(file_get_contents('php://input'), true);
$stmt = $mysqli->prepare('INSERT INTO users (name, email) VALUES (?, ?)');
$stmt->bind_param('ss', $input['name'], $input['email']);
$stmt->execute();
http_response_code(201);
echo json_encode(['id' => $mysqli->insert_id]);
$stmt->close();
} elseif ($method === 'GET' && $id) {
$stmt = $mysqli->prepare('SELECT id, name, email FROM users WHERE id = ?');
$stmt->bind_param('i', $id);
$stmt->execute();
$result = $stmt->get_result();
$user = $result->fetch_assoc();
if ($user) {
echo json_encode($user);
} else {
http_response_code(404);
echo json_encode(['error' => 'User not found']);
}
$stmt->close();
} else {
http_response_code(400);
echo json_encode(['error' => 'Invalid request']);
}
$mysqli->close();Результаты:
$ curl -X POST http://localhost/api_mysqli.php -d '{"name":"Анна","email":"anna@test.com"}'
{"id":2}
$ curl http://localhost/api_mysqli.php?id=2
{"id":2,"name":"Анна","email":"anna@test.com"}Пример 3. Slim Framework с использованием PDO и группы маршрутов
<?php
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Slim\Factory\AppFactory;
require __DIR__ . '/vendor/autoload.php';
$app = AppFactory::create();
$container = $app->getContainer();
$container->set('db', function () {
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8mb4', 'root', '');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
return $pdo;
});
$app->group('/api/users', function () use ($app) {
$app->get('', function (Request $request, Response $response) {
$db = $this->get('db');
$stmt = $db->query('SELECT id, name, email FROM users');
$users = $stmt->fetchAll();
$response->getBody()->write(json_encode($users));
return $response->withHeader('Content-Type', 'application/json');
});
$app->get('/{id}', function (Request $request, Response $response, $args) {
$db = $this->get('db');
$stmt = $db->prepare('SELECT id, name, email FROM users WHERE id = ?');
$stmt->execute([$args['id']]);
$user = $stmt->fetch();
if (!$user) {
return $response->withStatus(404);
}
$response->getBody()->write(json_encode($user));
return $response->withHeader('Content-Type', 'application/json');
});
$app->post('', function (Request $request, Response $response) {
$db = $this->get('db');
$input = $request->getParsedBody();
$stmt = $db->prepare('INSERT INTO users (name, email) VALUES (?, ?)');
$stmt->execute([$input['name'], $input['email']]);
$response->getBody()->write(json_encode(['id' => $db->lastInsertId()]));
return $response->withStatus(201)->withHeader('Content-Type', 'application/json');
});
});
$app->run();Пример 4. Eloquent Query Builder вне Laravel для API
<?php
require __DIR__ . '/vendor/autoload.php';
use Illuminate\Database\Capsule\Manager as Capsule;
$capsule = new Capsule;
$capsule->addConnection([
'driver' => 'mysql',
'host' => 'localhost',
'database' => 'test',
'username' => 'root',
'password' => '',
'charset' => 'utf8mb4',
]);
$capsule->setAsGlobal();
$capsule->bootEloquent();
header('Content-Type: application/json');
$users = Capsule::table('users')->select('id', 'name', 'email')->get();
echo $users->toJson();
// Для одного пользователя по id
$id = $_GET['id'] ?? null;
if ($id) {
$user = Capsule::table('users')->find($id);
if ($user) {
echo json_encode($user);
} else {
http_response_code(404);
echo json_encode(['error' => 'User not found']);
}
}