MySQL Improved Extension (mysqli)

Расширение mysqli, или как его еще называют улучшенное (improved) MySQL расширение, было разработано, чтобы дать возможность программистам в полной мере воспользоваться функционалом MySQL сервера версий 4.1.3 и выше. Расширение mysqli включается в поставку PHP версий 5 и выше.

mysqli имеет ряд преимуществ и усовершенствований по сравнению с mysql, которые заключаются в следующем:

  • Объектно-ориентированный интерфейс
  • Поддержка подготавливаемых запросов
  • Поддержка мультизапросов
  • Поддержка транзакций
  • Улучшенные возможности отладки
  • Поддержка встроенного сервера

Наравне с объектно-ориентированным расширение предоставляет и процедурный интерфейс.

Соединения

Сервер MySQL поддерживает различные способы передачи данных. Соединения могут использовать TCP/IP протоколы, сокеты Unix доменов или именованные пайпы Windows.

Имя хоста localhost имеет специальное назначение. Оно используется только в сокетах Unix доменов. Невозможно открыть TCP/IP соединение, используя в качестве имени хоста localhost. Вместо него нужно задать 127.0.0.1.

ООП подход:

$mysqli = new mysqli(
    'localhost', 'dbuser', 'dbpass', 'library'
);

if (mysqli_connect_errno()) {
    echo 'Connect failed: ' . mysqli_connect_error();
    exit;
}

// All other database calls go here

$mysqli->close();

Процедурный подход:

$dbh = mysqli_connect(
    'localhost', 'dbuser', 'dbpass', 'library'
);

if (!$dbh) {
    echo 'Connect failed: ' . mysqli_connect_error();
    exit;
}

// All other database calls go here

mysqli_close($dbh);

Так как mysqli не выбрасывает исключения, то в блоках try/catch нет необходимости. Вместо этого после подключения к базе следует воспользоваться функцией mysqli_connect_error(). Она проверяет была ли какая-нибудь ошибка при попытке соединения с базой.

Выполнение запросов

За выполнение запросов отвечают функции mysqli_query(), mysqli_real_query() и mysqli_multi_query(). Чаще всего применяется функция mysqli_query(), так как она выполняет сразу две задачи: выполняет запрос и буферизует на клиенте результат этого запроса (если он есть). Вызов mysqli_query() идентичен последовательному вызову функций mysqli_real_query() и mysqli_store_result().

Буферизация результатов запроса

После выполнения запроса его результаты можно целиком буферизовать на клиенте, либо читать построчно с сервера. Буферизация на клиенте позволяет серверу как можно быстрее освобождать занятые запросом ресурсы. Построчное же чтение и дальнейшая обработка результатов клиентом довольно медленный процесс. Поэтому рекомендуется использовать буферизацию результирующих наборов. Функция mysqli_query() совмещает в себе операции выполнения запроса и буферизации результирующего набора.

PHP приложения могут свободно оперировать данными внутри буферизованных результирующих наборов. Быстрая навигация по строкам наборов обусловлена тем, что наборы полностью располагаются в памяти клиента. Следует помнить, что зачастую обработка результатов на клиенте проще, нежели средствами сервера.

Подготавливаемые запросы

СУБД MySQL поддерживает подготавливаемые запросы. Подготавливаемые (или параметризованные) запросы используются для повышения эффективности, когда один запрос выполняется многократно.

Принцип работы

Выполнение подготавливаемого запроса проводится в два этапа: подготовка и исполнение. На этапе подготовки на сервер посылается шаблон запроса. Сервер выполняет синтаксическую проверку этого шаблона, строит план выполнения запроса и выделяет под него ресурсы.

MySQL сервер поддерживает неименованные, или позиционные, псевдопеременные ?.

Пример #1 Первый этап: подготовка

<?php
$mysqli = new mysqli("example.com", "user", "password", "database");
if ($mysqli->connect_errno) {
    echo "Не удалось подключиться к MySQL: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error;
}

/* обычный запрос */
if (!$mysqli->query("DROP TABLE IF EXISTS test") || !$mysqli->query("CREATE TABLE test(id INT)")) {
    echo "Не удалось создать таблицу: (" . $mysqli->errno . ") " . $mysqli->error;
}

/* подготавливаемый запрос, первая стадия: подготовка */
if (!($stmt = $mysqli->prepare("INSERT INTO test(id) VALUES (?)"))) {
    echo "Не удалось подготовить запрос: (" . $mysqli->errno . ") " . $mysqli->error;
}
?>

За подготовкой идет выполнение. Во время запуска запроса клиент привязывает к псевдопеременным реальные значения и посылает их на сервер. Сервер, в свою очередь, подставляет их в шаблон и запускает уже готовый запрос на выполнение.

Пример #2 Второй этап: привязка параметров и выполнение

<?php
/* подготавливаемый запрос, вторая стадия: привязка и выполнение */
$id = 1;
if (!$stmt->bind_param("i", $id)) {
    echo "Не удалось привязать параметры: (" . $stmt->errno . ") " . $stmt->error;
}

if (!$stmt->execute()) {
    echo "Не удалось выполнить запрос: (" . $stmt->errno . ") " . $stmt->error;
}
?>

Повторное выполнение запроса

Подготовленный запрос можно запускать многократно. Перед каждым запуском значения привязанных переменных будут передаваться на сервер и подставляться в текст запроса. Сам текст запроса повторно не анализируется, равно как и не отсылается повторно шаблон.

Пример #3 Выражение INSERT один раз подготавливается, а затем многократно выполняется

<?php
$mysqli = new mysqli("example.com", "user", "password", "database");
if ($mysqli->connect_errno) {
    echo "Не удалось подключиться к MySQL: (" . $mysqli->connect_errno . ") " . $mysqli->connect_error;
}

/* обычный запрос */
if (!$mysqli->query("DROP TABLE IF EXISTS test") || !$mysqli->query("CREATE TABLE test(id INT)")) {
    echo "Не удалось создать таблицу: (" . $mysqli->errno . ") " . $mysqli->error;
}

/* подготавливаемый запрос, первая стадия: подготовка */
if (!($stmt = $mysqli->prepare("INSERT INTO test(id) VALUES (?)"))) {
     echo "Не удалось подготовить запрос: (" . $mysqli->errno . ") " . $mysqli->error;
}

/* подготавливаемый запрос, вторая стадия: привязка и выполнение */
$id = 1;
if (!$stmt->bind_param("i", $id)) {
    echo "Не удалось привязать параметры: (" . $stmt->errno . ") " . $stmt->error;
}

if (!$stmt->execute()) {
    echo "Не удалось выполнить запрос: (" . $stmt->errno . ") " . $stmt->error;
}

/* подготавливаемый запрос: повторные выполнения, на сервер передаются только значения переменных */
for ($id = 2; $id < 5; $id++) {
    if (!$stmt->execute()) {
        echo "Не удалось выполнить запрос: (" . $stmt->errno . ") " . $stmt->error;
    }
}

/* рекомендуется явно закрывать запросы */
$stmt->close();

/* обычный запрос */
$res = $mysqli->query("SELECT id FROM test");
var_dump($res->fetch_all());
?>

Каждый подготавливаемый запрос использует ресурсы сервера. Если запрос больше не нужен, его необходимо сразу закрыть. Если не сделать этого явно, запрос закроется сам, но только когда PHP освободит его дескриптор, как правило это происходит при выходе запроса из области видимости или при завершении работы скрипта.

API поддержка транзакций

Фиксация и откат

<?php
$mysqli = new mysqli("example.com", "user", "password", "database");
$mysqli->autocommit(false);

$mysqli->query("INSERT INTO test(id) VALUES (1)");
$mysqli->rollback();

$mysqli->query("INSERT INTO test(id) VALUES (2)");
$mysqli->commit();
?>