Загрузка данных


<?php
header('Content-Type: application/json; charset=utf-8');
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type, Authorization');

if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
    http_response_code(204);
    exit;
}

$host = 'web.edu';
$db   = 'db_name';
$user = 'user';
$pass = 'password';
$charset = 'utf8mb4';

$dsn = "mysql:host=$host;dbname=$db;charset=$charset";
$options = [
    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    PDO::ATTR_EMULATE_PREPARES   => false,
];

try {
    $pdo = new PDO($dsn, $user, $pass, $options);
} catch (\PDOException $e) {
    http_response_code(500);
    echo json_encode(['error' => 'Не удалось подключиться к базе данных']);
    exit;
}

/* ---------- Вспомогательные функции ---------- */

function jsonResponse($data, int $code = 200): void {
    http_response_code($code);
    echo json_encode($data, JSON_UNESCAPED_UNICODE);
    exit;
}

function requireAuth(): void {
    $header = $_SERVER['HTTP_AUTHORIZATION']
        ?? $_SERVER['REDIRECT_HTTP_AUTHORIZATION']
        ?? '';
    if (!preg_match('/Bearer\s+(\S+)/i', $header, $m)) {
        jsonResponse(['error' => 'Требуется Bearer-токен'], 401);
    }
    if ($m[1] !== 'test-bearer-token-12345') {
        jsonResponse(['error' => 'Невалидный токен'], 403);
    }
}

function readJsonBody(): array {
    $data = json_decode(file_get_contents('php://input'), true);
    if (!is_array($data)) {
        jsonResponse(['error' => 'Некорректный JSON в теле запроса'], 400);
    }
    return $data;
}

/**
 * Валидация полей пользователя.
 * $requireAll = true — создание (все поля обязательны),
 *                false — обновление (проверяются только переданные).
 */
function validateUserPayload(array $data, bool $requireAll): void {
    $errors = [];

    if ($requireAll || array_key_exists('name', $data)) {
        if (!isset($data['name']) || !is_string($data['name']) || trim($data['name']) === '') {
            $errors['name'] = 'Поле name обязательно и не может быть пустым';
        } elseif (mb_strlen($data['name']) > 255) {
            $errors['name'] = 'Длина name не должна превышать 255 символов';
        }
    }

    if ($requireAll || array_key_exists('email', $data)) {
        if (!isset($data['email']) || !is_string($data['email']) || trim($data['email']) === '') {
            $errors['email'] = 'Поле email обязательно';
        } elseif (!filter_var($data['email'], FILTER_VALIDATE_EMAIL)) {
            $errors['email'] = 'Некорректный формат email';
        }
    }

    if ($requireAll || array_key_exists('age', $data)) {
        if (!isset($data['age']) || !is_int($data['age'])) {
            $errors['age'] = 'Поле age обязательно и должно быть целым числом';
        } elseif ($data['age'] < 0 || $data['age'] > 120) {
            $errors['age'] = 'age должен быть в диапазоне 0..120';
        }
    }

    if ($errors) {
        jsonResponse(['error' => 'Ошибка валидации', 'fields' => $errors], 400);
    }
}

function formatUser(array $row): array {
    return [
        'id'        => (int)$row['idusers'],
        'name'      => $row['name'],
        'email'     => $row['email'],
        'age'       => (int)$row['age'],
        'createdAt' => $row['created_at'],
        'updatedAt' => $row['updated_at'],
    ];
}

function fetchUser(PDO $pdo, int $id): array {
    $stmt = $pdo->prepare("SELECT * FROM users WHERE idusers = :id");
    $stmt->execute([':id' => $id]);
    $row = $stmt->fetch();
    if (!$row) {
        jsonResponse(['error' => 'Пользователь не найден'], 404);
    }
    return $row;
}

/* ---------- Маршрутизация ---------- */
// Поддержка: ?action=...&id=...   и   /users/{id}

$method = $_SERVER['REQUEST_METHOD'];
$action = $_GET['action'] ?? '';

// ID из query или из пути /users/{id}
$userId = null;
if (isset($_GET['id']) && ctype_digit((string)$_GET['id'])) {
    $userId = (int)$_GET['id'];
} elseif (preg_match('#/users/(\d+)#', $_SERVER['REQUEST_URI'] ?? '', $m)) {
    $userId = (int)$m[1];
}

// Определяем действие, если не задано явно
if ($action === '') {
    $action = match (true) {
        $method === 'POST'                          => 'create_user',
        $method === 'GET'  && $userId !== null      => 'get_user',
        $method === 'GET'                           => 'list_users',
        $method === 'PUT'  && $userId !== null      => 'update_user',
        $method === 'DELETE' && $userId !== null    => 'delete_user',
        default                                     => '',
    };
}

/* ---- 1. Создание пользователя ---- */
if ($action === 'create_user' && $method === 'POST') {
    requireAuth();
    $data = readJsonBody();
    validateUserPayload($data, true);

    try {
        $stmt = $pdo->prepare(
            "INSERT INTO users (name, email, age, created_at, updated_at)
             VALUES (:name, :email, :age, NOW(), NOW())"
        );
        $stmt->execute([
            ':name'  => $data['name'],
            ':email' => $data['email'],
            ':age'   => $data['age'],
        ]);
        $row = fetchUser($pdo, (int)$pdo->lastInsertId());
        jsonResponse(formatUser($row), 201);
    } catch (\PDOException $e) {
        error_log('SQL error: ' . $e->getMessage());
        jsonResponse(['error' => 'Ошибка при создании пользователя'], 500);
    }
}

/* ---- 2. Список с пагинацией ---- */
if ($action === 'list_users' && $method === 'GET') {
    requireAuth();

    $page     = max(1, (int)($_GET['page'] ?? 1));
    $pageSize = min(100, max(1, (int)($_GET['pageSize'] ?? 10)));

    try {
        $total = (int)$pdo->query("SELECT COUNT(*) FROM users")->fetchColumn();

        $stmt = $pdo->prepare(
            "SELECT * FROM users ORDER BY idusers ASC LIMIT :limit OFFSET :offset"
        );
        $stmt->bindValue(':limit',  $pageSize, PDO::PARAM_INT);
        $stmt->bindValue(':offset', ($page - 1) * $pageSize, PDO::PARAM_INT);
        $stmt->execute();

        jsonResponse([
            'page'     => $page,
            'pageSize' => $pageSize,
            'total'    => $total,
            'items'    => array_map('formatUser', $stmt->fetchAll()),
        ]);
    } catch (\PDOException $e) {
        error_log('SQL error: ' . $e->getMessage());
        jsonResponse(['error' => 'Ошибка при получении списка'], 500);
    }
}

/* ---- 3. Получение одного пользователя ---- */
if ($action === 'get_user' && $method === 'GET') {
    requireAuth();
    if ($userId === null) {
        jsonResponse(['error' => 'Не указан id пользователя'], 400);
    }
    try {
        jsonResponse(formatUser(fetchUser($pdo, $userId)));
    } catch (\PDOException $e) {
        error_log('SQL error: ' . $e->getMessage());
        jsonResponse(['error' => 'Ошибка при получении пользователя'], 500);
    }
}

/* ---- 4. Обновление пользователя ---- */
if ($action === 'update_user' && $method === 'PUT') {
    requireAuth();
    if ($userId === null) {
        jsonResponse(['error' => 'Не указан id пользователя'], 400);
    }
    $data = readJsonBody();
    validateUserPayload($data, false);

    try {
        $row = fetchUser($pdo, $userId);

        $stmt = $pdo->prepare(
            "UPDATE users
                SET name = :name, email = :email, age = :age, updated_at = NOW()
              WHERE idusers = :id"
        );
        $stmt->execute([
            ':name'  => $data['name']  ?? $row['name'],
            ':email' => $data['email'] ?? $row['email'],
            ':age'   => $data['age']   ?? (int)$row['age'],
            ':id'    => $userId,
        ]);

        jsonResponse(formatUser(fetchUser($pdo, $userId)));
    } catch (\PDOException $e) {
        error_log('SQL error: ' . $e->getMessage());
        jsonResponse(['error' => 'Ошибка при обновлении пользователя'], 500);
    }
}

/* ---- 5. Удаление пользователя ---- */
if ($action === 'delete_user' && $method === 'DELETE') {
    requireAuth();
    if ($userId === null) {
        jsonResponse(['error' => 'Не указан id пользователя'], 400);
    }
    try {
        fetchUser($pdo, $userId); // 404, если нет
        $pdo->prepare("DELETE FROM users WHERE idusers = :id")
            ->execute([':id' => $userId]);
        http_response_code(204);
        exit;
    } catch (\PDOException $e) {
        error_log('SQL error: ' . $e->getMessage());
        jsonResponse(['error' => 'Ошибка при удалении пользователя'], 500);
    }
}

http_response_code(400);
echo json_encode(['error' => 'Некорректный запрос'], JSON_UNESCAPED_UNICODE);