Основы взаимодействия PHP с MySQL: от подключения к продвинутым техникам
Работа с MySQL в PHP: обзор подходов
Как наиболее эффективно взаимодействовать с MySQL в современном PHP?
Основное решение: PDO (PHP Data Objects)
PDO предоставляет единый интерфейс для работы с разными базами данных, поддерживает подготовленные запросы, которые защищают от SQL-инъекций, и удобную обработку ошибок через исключения.
// Подключение к базе данных с PDO
$host = '127.0.0.1';
$dbname = 'test';
$user = 'root';
$pass = '';
$charset = 'utf8mb4';
try {
$dsn = "mysql:host=$host;dbname=$dbname;charset=$charset";
$pdo = new PDO($dsn, $user, $pass);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
echo "Подключение установлено";
} catch (PDOException $e) {
die("Ошибка подключения: " . $e->getMessage());
}
Php работа с базой mysql (работа с базой данных mysql в php)
Подключение установлено
Типичные проблемы при использовании PDO:
- Отсутствует расширение PDO и драйвер pdo_mysql. Решение: установить через пакетный менеджер (apt install php-pdo php-mysql) или раскомментировать строки в php.ini.
- Неверные учётные данные или хост. Решение: проверить параметры подключения, права пользователя.
- Ошибки кодировки – символы отображаются как '?'. Решение: установить charset utf8mb4 в DSN и в таблицах.
После подключения выполняются запросы. Пример вставки данных с подготовленным запросом:
$sql = "INSERT INTO users (name, email) VALUES (:name, :email)";
$stmt = $pdo->prepare($sql);
$stmt->execute([':name' => 'Иван', ':email' => 'ivan@example.com']);
echo "Добавлена запись с ID: " . $pdo->lastInsertId();
Добавлена запись с ID: 1
Выборка данных:
$stmt = $pdo->query("SELECT * FROM users");
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
echo $row['name'] . ' - ' . $row['email'] . "<br>";
}
Иван - ivan@example.com
PDO также поддерживает транзакции, что удобно для атомарных операций.
$pdo->beginTransaction();
try {
$pdo->exec("UPDATE accounts SET balance = balance - 100 WHERE id = 1");
$pdo->exec("UPDATE accounts SET balance = balance + 100 WHERE id = 2");
$pdo->commit();
} catch (Exception $e) {
$pdo->rollBack();
echo "Ошибка транзакции: " . $e->getMessage();
}
Как подключиться к MySQL с помощью расширения mysqli?
Вариант: mysqli (объектно-ориентированный стиль)
mysqli – расширение, ориентированное только на MySQL, но оно также поддерживает подготовленные запросы и может быть удобнее для проектов, работающих исключительно с этой СУБД.
$mysqli = new mysqli('127.0.0.1', 'root', '', 'test');
if ($mysqli->connect_error) {
die("Ошибка: " . $mysqli->connect_error);
}
echo "Подключение успешно";
Подключение успешно
Проблемы при использовании mysqli:
- Не установлено расширение mysqli. Решение: аналогично PDO, установить php-mysqli.
- Разница между объектным и процедурным стилем может вызывать путаницу. Рекомендуется придерживаться объектного для лучшей читаемости.
Пример подготовленного запроса в mysqli:
$stmt = $mysqli->prepare("INSERT INTO users (name, email) VALUES (?, ?)");
$stmt->bind_param('ss', $name, $email);
$name = 'Петр';
$email = 'petr@example.com';
$stmt->execute();
echo "Добавлена запись с ID: " . $stmt->insert_id;
Добавлена запись с ID: 2
Выборка:
$result = $mysqli->query("SELECT * FROM users");
while ($row = $result->fetch_assoc()) {
echo $row['name'] . ' - ' . $row['email'] . "<br>";
}
Как использовать устаревшее расширение mysql_* и почему его не стоит применять?
Вариант: функции mysql_* (устарело с PHP 5.5, удалено в PHP 7.0)
Этот подход считается устаревшим и небезопасным. Его использование может привести к уязвимостям и отсутствию поддержки в современных версиях PHP.
$connection = mysql_connect('127.0.0.1', 'root', '');
if (!$connection) {
die("Ошибка: " . mysql_error());
}
mysql_select_db('test', $connection);
echo "Подключение (устаревшее)";
Подключение (устаревшее)
Основные проблемы mysql_* :
- Функции устарели и удалены – код не будет работать на PHP 7+.
- Отсутствие поддержки подготовленных запросов – высокий риск SQL-инъекций.
- Нет объектного интерфейса, код плохо читается.
Если проект ещё использует mysql_*, его необходимо мигрировать на PDO или mysqli. Пример простой выборки (не рекомендуется):
$result = mysql_query("SELECT * FROM users", $connection);
while ($row = mysql_fetch_assoc($result)) {
echo $row['name'] . "<br>";
}
Какие ещё подходы существуют для работы с MySQL в PHP?
Вариант: использование ORM (например, Doctrine, Eloquent)
ORM (Object-Relational Mapping) скрывает детали SQL и позволяет работать с базой данных через объекты. Это удобно для крупных проектов, но добавляет overhead и требует изучения.
Пример (с использованием Eloquent из Laravel, но можно использовать standalone):
// После настройки Capsule Manager
$users = User::where('active', 1)->get();
foreach ($users as $user) {
echo $user->name;
}
Проблемы ORM:
- Сложность настройки для простых задач.
- Потеря производительности при сложных запросах.
- Необходимость изучения синтаксиса ORM.
Расширенные примеры работы с MySQL в PHP
1. Использование PDO с именованными и позиционными плейсхолдерами
// Именованные плейсхолдеры
$stmt = $pdo->prepare("SELECT * FROM users WHERE name = :name AND email = :email");
$stmt->execute([':name' => 'Иван', ':email' => 'ivan@example.com']);
$user = $stmt->fetch();
// Позиционные плейсхолдеры (знак вопроса)
$stmt = $pdo->prepare("SELECT * FROM users WHERE name = ? AND email = ?");
$stmt->execute(['Иван', 'ivan@example.com']);
$user = $stmt->fetch();
Array
(
[id] => 1
[name] => Иван
[email] => ivan@example.com
)
Разница: именованные читаемее, позиционные компактнее. Выбор зависит от стиля кода.
2. Пакетная вставка множества записей
$data = [
['name' => 'Анна', 'email' => 'anna@test.com'],
['name' => 'Борис', 'email' => 'boris@test.com'],
// ... много записей
];
$pdo->beginTransaction();
try {
$stmt = $pdo->prepare("INSERT INTO users (name, email) VALUES (:name, :email)");
foreach ($data as $row) {
$stmt->execute($row);
}
$pdo->commit();
} catch (Exception $e) {
$pdo->rollBack();
echo "Ошибка: " . $e->getMessage();
}
echo "Вставлено " . count($data) . " записей";
Вставлено 3 записей (если в массиве 3 элемента)
3. Получение результатов в разных форматах
PDO::FETCH_ASSOC, FETCH_NUM, FETCH_OBJ, FETCH_CLASS и др.
// FETCH_OBJ - объект
$stmt = $pdo->query("SELECT * FROM users LIMIT 1");
$user = $stmt->fetch(PDO::FETCH_OBJ);
echo $user->name;
// FETCH_CLASS - в объект указанного класса
class User {
public $name;
public $email;
}
$stmt->setFetchMode(PDO::FETCH_CLASS, 'User');
$user = $stmt->fetch();
echo $user->name;
Иван (в обоих случаях)
4. Работа с хранимыми процедурами
// MySQL хранимая процедура
// CREATE PROCEDURE GetUser(IN uid INT) SELECT * FROM users WHERE id = uid;
$stmt = $pdo->prepare("CALL GetUser(:uid)");
$stmt->execute([':uid' => 1]);
$user = $stmt->fetch();
print_r($user);
Array ( [id] => 1 [name] => Иван ... )
Важно: процедуры могут возвращать несколько результирующих наборов. Для работы с ними используется nextRowset().
5. Запрос с объединением таблиц (JOIN) и группировкой
$sql = "
SELECT u.name, COUNT(o.id) as order_count
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id
HAVING order_count > 0
ORDER BY order_count DESC
";
$stmt = $pdo->query($sql);
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($result as $row) {
echo $row['name'] . ' сделал ' . $row['order_count'] . ' заказов<br>';
}
Иван сделал 3 заказов Петр сделал 1 заказов
6. Обработка ошибок: получение информации об исключении
try {
$pdo->exec("INSERT INTO wrong_table (id) VALUES (1)");
} catch (PDOException $e) {
echo "Код ошибки: " . $e->getCode() . "<br>";
echo "Сообщение: " . $e->getMessage() . "<br>";
echo "Файл: " . $e->getFile() . " строка " . $e->getLine();
}
Код ошибки: 42S02 Сообщение: SQLSTATE[42S02]: Base table or view not found: 1146 Table 'test.wrong_table' doesn't exist Файл: /var/www/example.php строка 5
7. Работа с большими объёмами данных: использование unbuffered query
// Для mysqli можно отключить буферизацию
$mysqli->real_query("SELECT * FROM big_table");
$result = $mysqli->use_result();
while ($row = $result->fetch_assoc()) {
// обработка каждой строки без загрузки всего в память
}
// Для PDO аналог - использование MYSQL_ATTR_USE_BUFFERED_QUERY = false при создании соединения
Этот подход уменьшает потребление памяти при больших выборках, но блокирует таблицу на время чтения.