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 могут возникнуть конфликты с другими библиотеками из-за автозагрузки. Рекомендуется тщательно тестировать.

- удаленная php mysql (удаленное подключение к mysql в php)
- подключения базы данных php (подключение к базе данных php)

Расширенные примеры реализации 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']);
    }
}

API с MySQL в PHP - comments

En
Php api mysql (php)