Глава 141 · С нуля → справочник
SQLite3: полный справочник локальной базы, команд и результатов
От первого значения в базе до всех классов, методов, типов, настроек, транзакций, callback, потоков BLOB, ошибок и резервных копий.
Как запустить любую карточку: файлы, схема и каталог
Все новые карточки используют один небольшой каталог мастерской. Пример можно запускать отдельно: его архив содержит example.php, catalog.php, schema.sql и README. Молоток имеет остаток 8, Перчатки — 15, Кисть — 0. Ноль здесь означает известное отсутствие товара, а не пропавшую строку.
- schema.sql: структура и три известные строки
- catalog.php: подключение и создание учебных данных
- example.php: один изучаемый механизм
- stdout: название товара, остаток или понятный отказ
require выполняет подключаемый PHP-файл. __DIR__ — папка текущего файла, . склеивает строки. SQL в schema.sql описывает таблицу: id отличает товары, name хранит название, stock — целое неотрицательное количество. INSERT заполняет строки. Код подключения показан полностью: никаких скрытых таблиц урок не предполагает.
Нужно расширение sqlite3. SQLite работает внутри PHP; установка MySQL для этих примеров не требуется. :memory: означает временную базу одного соединения. Чтобы изучать сохранение между запусками, используйте проект с файлом catalog.sqlite.
schema.sql
CREATE TABLE products (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
stock INTEGER NOT NULL CHECK (stock >= 0)
);
INSERT INTO products VALUES
(1, 'Молоток', 8),
(2, 'Перчатки', 15),
(3, 'Кисть', 0);catalog.php
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec(file_get_contents(__DIR__ . '/schema.sql'));Команда php example.php выводит результат в терминал. Для HTML нужен отдельный обработчик и веб-сервер — переход от строк базы к странице каталога. В рабочем сайте создание постоянной схемы выполняют отдельно от посещения страницы; здесь временные исходные данные нужны для воспроизводимого урока.
Добавить полку, проверив схему, а не угадывая причину ошибки
Склад начал хранить номер полки. Для учебной базы надо понять, есть ли колонка shelf, добавить её при необходимости и записать известное место A2 для Молотка.
- table_xinfo(products) → список колонок id/name/stock
- shelf не найден → ALTER TABLE добавляет колонку с DEFAULT
- bindValue A2/id1 → UPDATE
- SELECT → Молоток 8, полкаA2
PRAGMA table_xinfo возвращает описание колонок, включая скрытые/вычисляемые. Переменная $exists меняется на true только при совпадении имени. Константы имён схемы доверенные; строку посетителя в ALTER TABLE не подставляем.
| Запись в этом примере | Как читать |
|---|---|
require __DIR__ . ... | Выполняет указанный PHP-файл из папки примера. Точка склеивает путь. Файл включён в архив; он создаёт соединение и исходные данные. |
$db->method(...) | Стрелка вызывает действие у конкретного соединения, команды или результата. Скобки содержат входные значения; результат можно сохранить через =. |
while (...) | Повторяет блок, пока условие истинно. В чтении результата проверка конца связана с конкретным методом: false или null указаны в его контракте. |
[...] | Квадратные скобки задают массив либо читают его элемент. Ключ имени колонки и номер позиции — разные способы адресации. |
<?php
require __DIR__ . '/catalog.php';
$result = $db->query("PRAGMA table_xinfo('products')");
$exists = false;
while (($column = $result->fetchArray(SQLITE3_ASSOC)) !== false) {
if ($column['name'] === 'shelf') { $exists = true; }
}
$result->finalize();
if (!$exists) {
$db->exec("ALTER TABLE products ADD COLUMN shelf TEXT NOT NULL DEFAULT 'Не назначена'");
}
$statement = $db->prepare('UPDATE products SET shelf = :shelf WHERE id = :id');
$statement->bindValue(':shelf', 'A2', SQLITE3_TEXT);
$statement->bindValue(':id', 1, SQLITE3_INTEGER);
$statement->execute()->finalize();
$row = $db->querySingle('SELECT name, stock, shelf FROM products WHERE id = 1', true);
echo $row['name'], ' — ', $row['stock'], " шт.
";
echo 'Полка: ', $row['shelf'], "
";
Ожидаемый результат
Молоток — 8 шт.
Полка: A2
Это собственное решение реальной проблемы из обсуждения: неудачный SELECT может иметь разные причины, поэтому его false не доказывает отсутствие колонки. Проверка схемы отвечает прямо на вопрос о shelf.
Эти же строки в табличном виде. CLI-пример выше печатает текст; таблица помогает увидеть соответствие названия и остатка. Как получить такую таблицу настоящим HTML-обработчиком.
| Товар / строка | Наличие |
|---|---|
| Молоток | 8 шт. |
Проверьте изменение требования. На постоянной базе миграции запускают отдельно от HTTP-запроса, фиксируют их версию и исключают конкурентный запуск. Проверка и ALTER без координации могут состязаться; пример использует изолированную базу одного процесса. Для нужного SQLite table_xinfo проверьте библиотеку; полный список правил ALTER TABLE смотрите в официальной документации.
Сделать HTML-отчёт, который можно открыть отдельным файлом
Нужен небольшой читаемый снимок остатков для коллеги. PHP в терминале прочитает SQLite и сохранит report.html рядом с примером; браузер сможет открыть готовый файл без подключения к базе.
- SELECT → три массива name/stock
- fetchArray по одному → имя экранируется для HTML
- строки tr/td добавляются в $html
- file_put_contents → report.html → браузер показывает таблицу
.= дописывает текст к строке. htmlspecialchars превращает специальные символы имени товара в безопасный HTML-текст. Остаток получен из заданной целочисленной схемы; произвольный текст во всех ячейках также нужно экранировать. Это генерация статического файла, а не HTTP-обработчик.
| Запись в этом примере | Как читать |
|---|---|
require __DIR__ . ... | Выполняет указанный PHP-файл из папки примера. Точка склеивает путь. Файл включён в архив; он создаёт соединение и исходные данные. |
$db->method(...) | Стрелка вызывает действие у конкретного соединения, команды или результата. Скобки содержат входные значения; результат можно сохранить через =. |
while (...) | Повторяет блок, пока условие истинно. В чтении результата проверка конца связана с конкретным методом: false или null указаны в его контракте. |
[...] | Квадратные скобки задают массив либо читают его элемент. Ключ имени колонки и номер позиции — разные способы адресации. |
<?php
require __DIR__ . '/catalog.php';
$result = $db->query('SELECT name, stock FROM products ORDER BY id');
$html = '<!doctype html><html lang="ru"><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Остатки</title><h1>Остатки</h1><table><tr><th>Товар</th><th>Штук</th></tr>';
while (($row = $result->fetchArray(SQLITE3_ASSOC)) !== false) {
$name = htmlspecialchars($row['name'], ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
$html .= '<tr><td>'.$name.'</td><td>'.$row['stock'].'</td></tr>';
}
$result->finalize();
$html .= '</table></html>';
$path = __DIR__ . '/report.html';
if (file_put_contents($path, $html) === false) { throw new RuntimeException('Не удалось записать report.html'); }
echo $html, "
";
Ожидаемый результат
<!doctype html><html lang="ru"><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Остатки</title><h1>Остатки</h1><table><tr><th>Товар</th><th>Штук</th></tr><tr><td>Молоток</td><td>8</td></tr><tr><td>Перчатки</td><td>15</td></tr><tr><td>Кисть</td><td>0</td></tr></table></html>
Терминал показывает точные байты, записанные в report.html. Откройте этот файл браузером: заголовок «Остатки», две колонки, три товара. Код не предполагает существующей внешней таблицы: catalog.php/schema.sql находятся в архиве.
Проверьте изменение требования. Для огромного отчёта не накапливайте весь HTML в строке: пишите частями с проверкой ошибок. Отчёт — снимок, он не обновится при смене остатка. Учебный файл минимален; оформление и доступ к актуальной странице рассматривает полный HTTP-проект каталога.
Видимый результат открытия report.html:
| Товар | Штук |
|---|---|
| Молоток | 8 |
| Перчатки | 15 |
| Кисть | 0 |
Сначала работающий каталог, затем справочник
Если база данных для вас новая тема, начните с последовательного каталога товаров. Там показаны создание файла и таблицы, добавление записей, чтение в массив, foreach, готовая HTML-страница и ошибки каждого этапа. Здесь — подробные возможности SQLite3; их удобнее изучать, когда уже видна общая задача.
Зачем нужна SQLite: сохранить данные сайта в одном месте
Есть небольшая локальная очередь заданий, справочник доставки или каталог программы. Массив PHP исчезает после завершения запроса. JSON-файл позволяет сохранить данные, но при одновременной записи, поиске и связанных изменениях придётся самим строить блокировки, индексы и восстановление. SQLite — встроенный движок базы: он выполняется внутри PHP-процесса, хранит таблицы в файле и даёт SQL, ограничения и транзакции без отдельного сервера.
Таблица похожа на лист с именованными колонками: products содержит id, name, price. Строка — один товар, поле — одно его значение. Это полезная первая модель, но база не является обычной таблицей на экране: она поддерживает типы хранения, индексы, связи и конкурентные операции. SQL — отдельный язык команд: CREATE TABLE создаёт структуру, INSERT добавляет строку, SELECT получает данные, UPDATE меняет, DELETE удаляет. PHP вызывает SQLite, затем получает число, массив строки или объект чтения.
- PHP $db — открытое соединение
- SQL SELECT 40+2 внутри SQLite
- SQLite возвращает число42
- PHP $answer получает42
- echo выводит42
Сохраните пример в отдельный файл example.php и выполните php example.php в терминале с настроенным расширением; начало <?php открывает PHP-код. Начнём с полезного минимального результата: получить число из работающей базы, без файла и настроек сервера. Знак $ обозначает переменную, = присваивает результат, new создаёт объект; $db->querySingle(...) вызывает действие этого объекта. Текст в кавычках — SQL-команда. Круглые скобки содержат аргумент, точка с запятой завершает PHP-инструкцию. Подробнее переменные, вызов функции и объекты.
Как читать этот вариант. new создаёт объект соединения; :memory: означает отдельную временную базу этого соединения. $db хранит объект, стрелка вызывает его метод, строка SELECT — команда SQLite, $answer получает число. ; завершает PHP-инструкцию. Никакой сервер и файл для этого опыта не нужны.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$answer = $db->querySingle('SELECT 40 + 2');
echo $answer, "\n";
$db->close();
Ожидаемый результат
42
Теперь сохраним значение в таблицу: имя summary обозначает набор строк, amount INTEGER — колонку чисел. exec выполняет команду без выдачи строк, querySingle берёт одно значение. Пустое имя базы создаёт приватный временный файл, удаляемый при закрытии.
Как читать этот вариант. Пустое имя создаёт приватную временную дисковую базу (PHP 7.0.10+). После закрытия SQLite удаляет её. CREATE задаёт таблицу, INSERT сохраняет строку, SELECT получает итог; сумма остаётся только до закрытия.
<?php
$db = new SQLite3('');
$db->enableExceptions(true);
$db->exec('CREATE TABLE summary (amount INTEGER)');
$db->exec('INSERT INTO summary VALUES (250)');
echo $db->querySingle('SELECT SUM(amount) FROM summary'), "\n";
$db->close();
Ожидаемый результат
250
Для постоянного хранения выбирают путь вне публичной папки сайта, ограничивают файловые права и делают копии. Два независимых :memory:-соединения получают две разные базы. SQLite подходит многим локальным и небольшим серверным задачам; при множестве одновременно пишущих процессов и распределённом доступе стоит рассмотреть серверную БД. Выбор зависит от нагрузки и эксплуатации, не от обещания «SQLite всегда быстрее». Книга ниже описывает прямое расширение sqlite3; PDO SQLite предоставляет иной интерфейс к тому же движку.
Установка, требования и проверка окружения
SQLite3 доступно с PHP 5.3, расширение обычно включено при стандартной сборке; --without-sqlite3 исключает его. Начиная с PHP 7.4 требуется внешняя libsqlite не ниже 3.7.4, до этого могла использоваться bundled-библиотека. Это минимальное требование расширения, а более новые SQL/API-функции требуют более новой SQLite. Версия библиотеки не равна версии PHP.
В Windows в используемом php.ini включают extension=sqlite3, соответствующий php_sqlite3.dll находится в поставке PHP. С PHP 7.4 также нужен libsqlite3.dll в пути поиска DLL/PATH, обычно рядом с PHP в корректной установке; копировать DLL в системный каталог Windows не рекомендуется. DLL должна соответствовать версии, архитектуре и варианту сборки PHP. В Linux пакет/сборка должны подключать sqlite3 именно в нужном SAPI; после изменения конфигурации веб-workers обычно перезапускают штатным способом. Это описание действий администратора, примеры здесь глобальную конфигурацию не меняют.
Как читать этот вариант. Вывод соответствует проверочной PHP 8.5.11. Веб-сервер и CLI могут читать разные php.ini и загружать разные расширения; проверяйте среду фактического обработчика, не только удобную консоль.
<?php
echo extension_loaded('sqlite3') ? "sqlite3 подключён\n" : "sqlite3 отсутствует\n";
echo class_exists(SQLite3::class) ? "Класс SQLite3 доступен\n" : "Нужна настройка PHP\n";
echo method_exists(SQLite3Result::class, 'fetchAll') ? "fetchAll доступен\n" : "Используйте fetchArray\n";
Ожидаемый результат
sqlite3 подключён
Класс SQLite3 доступен
fetchAll доступен
Как читать этот вариант. PRAGMA compile_options показывает compile-time настройки (например ENABLE_FTS5, если доступен). JSON в проверенной SQLite встроен; наличие одной функции не обещает все возможности другой версии. Для рабочей возможности проверяют её точную версию и smoke-пример на отдельной базе.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$options = [];
$result = $db->query('PRAGMA compile_options');
while (($row = $result->fetchArray(SQLITE3_NUM)) !== false) { $options[] = $row[0]; }
$result->finalize();
echo count($options) > 0 ? "Параметры сборки получены\n" : "Список пуст\n";
echo $db->querySingle("SELECT json_valid('{" . '"ok":true' . "}')"), "\n";
Ожидаемый результат
Параметры сборки получены
1
Сообщение «Class SQLite3 not found» означает отсутствие расширения в текущем процессе; ошибка «unable to open database» чаще связана с путём/правами, а не отсутствующей таблицей. sqlite3 и pdo_sqlite независимы: установленный PDO-драйвер сам по себе не гарантирует класс SQLite3. Для чтения уже существующего файла предпочтите READONLY или READWRITE без CREATE, чтобы ошибка пути не создала пустую новую базу.
Настройки php.ini: что защищает файл и где находятся SQLite-модули
| Параметр | Default и доступность | Смысл |
|---|---|---|
| sqlite3.extension_dir | Пустая строка; INI_SYSTEM | Каталог нативных библиотек для SQLite3::loadExtension. Не путать с PHP extension_dir. |
| sqlite3.defensive | 1; INI_USER с PHP8.2, ранее INI_SYSTEM | Запрещает SQL-возможности преднамеренной порчи файла: запись в schema/shadow tables/sqlite_dbpage. Доступна с PHP7.2.17/7.3.4 и эффективна при SQLite3.26+. |
Defensive не является read-only: обычное приложение продолжает менять собственные таблицы. Эта настройка не заменяет права файлов, проверку SQL, резервную копию и ограничения authorizer. Новую конфигурацию defensive задают до открытия соединения; в PHP8.5.11 значение применяется при open. Загрузка дополнительного нативного модуля — решение доверенного администратора.
Как читать этот вариант. Это ожидаемые значения проверочной среды, не универсальная гарантия для чужого сервера. Конфигурация расширения читается отдельно от общего extension_dir PHP.
<?php
echo ini_get('sqlite3.defensive') ? "Defensive включён\n" : "Defensive выключен\n";
echo ini_get('sqlite3.extension_dir') === '' ? "Каталог дополнительных SQLite-модулей не задан\n" : "Каталог SQLite-модулей задан\n";
Ожидаемый результат
Defensive включён
Каталог дополнительных SQLite-модулей не задан
Как читать этот вариант. В PHP 8.2+ настройку можно включать в пределах запроса. Она не мешает обычным CREATE/INSERT; не нужно отключать её ради сохранения каталога. Пример меняет только собственный процесс до создания соединения.
<?php
ini_set('sqlite3.defensive', '1');
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE products (id INTEGER PRIMARY KEY)');
$db->exec('INSERT INTO products DEFAULT VALUES');
echo $db->querySingle('SELECT COUNT(*) FROM products'), "\n";
Ожидаемый результат
1
SQLite3: один объект хранит связь с одной базой
SQLite3 управляет соединением, SQL-командами, регистрацией callback и ошибками. Конструктор открывает базу; последующие $db->... используют эту связь. Два объекта имеют отдельные настройки и транзакции, даже если работают с одним файлом. Это конкретная польза ООП: не передавать путь и режим ошибки заново в каждую функцию, а держать согласованное состояние клиента. Соединение не является массивом данных и не сериализуется для хранения в сессии.
| Нужен результат | Метод |
|---|---|
| Команда без строк | SQLite3::exec |
| Одно значение / первая строка | SQLite3::querySingle |
| Последовательность строк | SQLite3::query |
| SQL с данными приложения | SQLite3::prepare → bindings → execute |
| Согласованная копия | SQLite3::backup |
Как читать этот вариант. query возвращает объект чтения, а не PHP-массив всех товаров. fetchArray извлекает текущую строку; ASSOC выбирает подписи name и price.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT NOT NULL, price INTEGER NOT NULL)');
$db->exec("INSERT INTO products VALUES (1, 'Pen', 120), (2, 'Notebook', 350), (3, 'Box', 200)");
$result = $db->query('SELECT name, price FROM products WHERE id = 1');
$row = $result->fetchArray(SQLITE3_ASSOC);
echo $row['name'], ':', $row['price'], "\n";
$result->finalize();
Ожидаемый результат
Pen:120
Как читать этот вариант. Создаётся только наш уникальный временный файл; удаляется именно он. READONLY защищает это соединение от записи, но не отменяет права другого процесса на сам файл.
<?php
$path = tempnam(sys_get_temp_dir(), 'sql3-');
$writer = new SQLite3($path);
$writer->exec('CREATE TABLE totals (n INTEGER)');
$writer->exec('INSERT INTO totals VALUES (7)');
$writer->close();
try {
$reader = new SQLite3(':memory:');
$reader->close();
$reader->open($path, SQLITE3_OPEN_READONLY);
$reader->enableExceptions(true);
echo $reader->querySingle('SELECT n FROM totals'), "\n";
try { $reader->exec('DELETE FROM totals'); }
catch (Exception $error) { echo "Запись запрещена\n"; }
$reader->close();
} finally { unlink($path); }
Ожидаемый результат
7
Запись запрещена
Общие методы собраны ниже в справочнике, а константы класса задают действия authorizer. Объект не проверяет автоматически право текущего пользователя просматривать заказ: такая бизнес-проверка живёт выше SQL-клиента.
SQLite3Stmt: сохранённый шаблон и отдельные данные
Statement содержит подготовленную SQL-команду, её bindings и состояние выполнения. prepare не выполняет запрос; bindValue фиксирует значение, bindParam связывает переменную; execute запускает. После ответа сначала finalize, затем следующий execute. private-конструктор означает, что такой объект выдаёт база, а не пользовательский new. На один объект нельзя повесить несколько независимо читаемых результатов.
- prepare WHERE id=:id
- bindValue :id=2
- execute→SQLite3Result
- fetchArray→Notebook
- finalize→повторный execute
Как читать этот вариант. prepare переводит шаблон SQL в готовую команду. :id — место для значения, без кавычек. bindValue кладёт туда число 2; execute выполняет команду, fetchArray даёт строку. Данные не меняют структуру SQL.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT NOT NULL, price INTEGER NOT NULL)');
$db->exec("INSERT INTO products VALUES (1, 'Pen', 120), (2, 'Notebook', 350), (3, 'Box', 200)");
$statement = $db->prepare('SELECT name FROM products WHERE id = :id');
$statement->bindValue(':id', 2, SQLITE3_INTEGER);
$result = $statement->execute();
echo $result->fetchArray(SQLITE3_ASSOC)['name'], "\n";
$result->finalize();
$statement->close();
Ожидаемый результат
Notebook
Как читать этот вариант. Результаты разных execute одного statement разделяют внутреннюю структуру. Поэтому сначала finalize, затем новая привязка/execute. Не храните старые SQLite3Result в массиве как якобы независимые ответы.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT NOT NULL, price INTEGER NOT NULL)');
$db->exec("INSERT INTO products VALUES (1, 'Pen', 120), (2, 'Notebook', 350), (3, 'Box', 200)");
$stmt = $db->prepare('SELECT name FROM products WHERE id = ?');
foreach ([1, 3] as $id) {
$stmt->bindValue(1, $id, SQLITE3_INTEGER);
$result = $stmt->execute();
echo $result->fetchArray(SQLITE3_ASSOC)['name'], "\n";
$result->finalize();
}
$stmt->close();
Ожидаемый результат
Pen
Box
Повторная подготовка может стоить дороже повторного выполнения, но затраты часто определяются SQL-планом, индексами и дисковой синхронизацией. Переход к объектам сам по себе не ускоряет плохой запрос. Все методы и три EXPLAIN_MODE-константы присутствуют в указателе.
SQLite3Result: положение в ответе, а не вся таблица
Result хранит состояние чтения одной команды. fetchArray извлекает очередную строку, false сообщает конец; fetchAll PHP8.5+ материализует остаток. columnName/numColumns описывают колонки; columnType относится к значению текущей строки. reset снова запускает проход, finalize освобождает курсор. Прямой new закрыт, объекты результатов не сериализуются.
Как читать этот вариант. Одна строка — один массив. Второй fetchArray возвращает false, потому что подходящая строка была одна. Не путать false окончания и null в отдельной колонке.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT NOT NULL, price INTEGER NOT NULL)');
$db->exec("INSERT INTO products VALUES (1, 'Pen', 120), (2, 'Notebook', 350), (3, 'Box', 200)");
$result = $db->query('SELECT name, price FROM products WHERE id = 1');
$row = $result->fetchArray(SQLITE3_ASSOC);
echo $row['name'], ':', $row['price'], "\n";
var_dump($result->fetchArray(SQLITE3_ASSOC));
$result->finalize();
Ожидаемый результат
Pen:120
bool(false)
Как читать этот вариант. У результата есть текущая позиция. fetchAll не возвращает автоматически уже прочитанную Pen; для нового независимого просмотра выполните SELECT заново.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT NOT NULL, price INTEGER NOT NULL)');
$db->exec("INSERT INTO products VALUES (1, 'Pen', 120), (2, 'Notebook', 350), (3, 'Box', 200)");
$result = $db->query('SELECT name FROM products ORDER BY id');
echo $result->fetchArray(SQLITE3_ASSOC)['name'], "\n";
$rest = $result->fetchAll(SQLITE3_ASSOC);
echo count($rest), ':', $rest[0]['name'], "\n";
$result->finalize();
Ожидаемый результат
Pen
2:Notebook
Для передачи данных другому слою можно вернуть небольшой array, но это отдельный выбор расхода памяти. Сам Result не даёт numRows: точное количество требует COUNT(*) либо чтения; не подменяйте его numColumns. После finalize и после повторного execute исходного statement старый результат использовать нельзя.
SQLite3Exception: как сохранить причину и объяснить отказ
SQLite3Exception появился в PHP8.3 и наследует Exception без дополнительных публичных методов. Включённый enableExceptions(true) переводит ошибки SQL в этот класс; ошибки открытия конструктора в PHP8.5.11 могут оставаться обычным Exception, поэтому границу подключения ловят совместимым catch(Exception). Конкретный класс исключения помогает отделить отказ базы от ошибок приложения.
| Унаследованная часть | Что означает |
|---|---|
| __construct(message="", code=0, previous=null) | Сообщение, числовой код, предыдущее Throwable при обёртывании. |
| getMessage / getCode | Описание и код; не использовать текст как стабильный протокол. |
| getFile / getLine | Где возникло исключение; не путь к повреждённой строке SQL. |
| getTrace / getTraceAsString | Трассировка как array/string; предназначена разработчику, может содержать чувствительный контекст. |
| getPrevious | Причина в цепочке либо null. |
| __toString | Текст исключения с диагностикой; не безопасный пользовательский ответ. |
| __clone | Закрыт: исключения не копируют клонированием. |
| message, code, file, line, trace, previous, string | Внутренние protected/private свойства Exception, читаются через методы, а не как произвольный публичный массив. |
Как читать этот вариант. SQLite3Exception существует с PHP8.3. Для совместимости с 8.2 используют catch(Exception); TypeError от неверного типа параметра — отдельная проблема PHP-кода, не штатная SQL-ошибка.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
try { $db->exec('CREATE TABLE incomplete'); }
catch (SQLite3Exception $error) {
echo $error instanceof Exception ? "SQLite-ошибка наследует Exception\n" : "Другой тип\n";
echo is_int($error->getCode()) ? "Код доступен\n" : "Нет кода\n";
}
Ожидаемый результат
SQLite-ошибка наследует Exception
Код доступен
Как читать этот вариант. previous сохраняет исходный stack trace и код для закрытого журнала, а сообщение верхнего слоя объясняет операцию. Не печатайте $error целиком в HTTP-ответ: __toString и trace могут раскрыть детали.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
try {
try { $db->exec('INSERT INTO missing_queue VALUES (1)'); }
catch (SQLite3Exception $cause) { throw new RuntimeException('Не удалось добавить задачу', 0, $cause); }
} catch (RuntimeException $error) {
echo $error->getMessage(), "\n";
echo $error->getPrevious() instanceof SQLite3Exception ? "Техническая причина сохранена\n" : "Причины нет\n";
}
Ожидаемый результат
Не удалось добавить задачу
Техническая причина сохранена
Общий механизм исключений, catch и finally объясняет возврат управления. После SQL-ошибки нельзя считать транзакцию успешно завершённой; применяют выбранную стратегию отката. Для некоторых тяжёлых ошибок SQLite может сам отменить транзакцию, и дополнительный ROLLBACK тоже может отказать — сохраните первоначальную причину, не замените её ошибкой уборки.
Все глобальные константы и режимы EXPLAIN
| Константа | Смысл |
|---|---|
| SQLITE3_ASSOC / SQLITE3_NUM / SQLITE3_BOTH | Имена колонок / индексы с0 / оба вида ключей результата. |
| SQLITE3_INTEGER | Целое signed SQLite, хранение1/2/3/4/6/8байт по величине. |
| SQLITE3_FLOAT | SQLite REAL: 8-байт IEEE число, не точное представление денежных дробей. |
| SQLITE3_TEXT | Текст в кодировке базы; не бинарный BLOB. |
| SQLITE3_BLOB | Байты без текстового преобразования, включая NUL. |
| SQLITE3_NULL | SQL NULL, отсутствие значения; не 0 и не пустая строка. |
| SQLITE3_OPEN_READONLY | Открытие только для чтения. |
| SQLITE3_OPEN_READWRITE | Открытие существующей базы для чтения/записи. |
| SQLITE3_OPEN_CREATE | Вместе с READWRITE разрешает создать отсутствующий файл. |
| SQLITE3_DETERMINISTIC | С PHP7.1.4: обещание стабильного scalar callback при одинаковых входах. |
| SQLite3Stmt::EXPLAIN_MODE_PREPARED = 0 | Обычное выполнение SQL. |
| SQLite3Stmt::EXPLAIN_MODE_EXPLAIN = 1 | Инструкции виртуальной машины SQLite. |
| SQLite3Stmt::EXPLAIN_MODE_EXPLAIN_QUERY_PLAN = 2 | Высокоуровневое описание стратегии запроса. |
Глобальные SQLITE3_* доступны только при загруженном расширении. EXPLAIN_MODE-константы требуют PHP8.5 и SQLite3.43+. Не заменяйте понятное имя числом: INTEGER и ASSOC могут иметь одинаковый код1, но отвечают на разные вопросы. Другие C-константы SQLITE_* из документации SQLite не обязательно экспортированы в PHP; это особенно важно для кодов ошибок.
Как читать этот вариант. INTEGER определяет тип привязки, ASSOC — ключи возвращённого массива. Одинаковые числовые значения из разных групп не означают взаимозаменяемые назначения.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$stmt = $db->prepare('SELECT :n AS quantity');
$stmt->bindValue(':n', 5, SQLITE3_INTEGER);
$row = $stmt->execute()->fetchArray(SQLITE3_ASSOC);
echo $row['quantity'], "\n";
echo SQLITE3_BOTH === (SQLITE3_ASSOC | SQLITE3_NUM) ? "BOTH объединяет два режима ключей\n" : "Иная сборка\n";
Ожидаемый результат
5
BOTH объединяет два режима ключей
Как читать этот вариант. При пропущенном type PHP 7.0.7+ определяет int/bool→INTEGER, float→FLOAT, null→NULL, остальное→TEXT. Явный BLOB нужен для бинарных байтов; null всегда NULL, даже с INTEGER. Документированный default TEXT в сигнатуре не отменяет автоопределение при пропуске аргумента.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$stmt = $db->prepare('SELECT typeof(:count), typeof(:ratio), typeof(:missing), length(:bytes)');
$stmt->bindValue(':count', 3);
$stmt->bindValue(':ratio', 1.5);
$stmt->bindValue(':missing', null, SQLITE3_INTEGER);
$stmt->bindValue(':bytes', "A\0B", SQLITE3_BLOB);
echo implode('|', $stmt->execute()->fetchArray(SQLITE3_NUM)), "\n";
Ожидаемый результат
integer|real|null|3
Константы SQLite3 для контроля доступа: все действия и ответы
Ответы callback: SQLite3::OK разрешает действие; SQLite3::DENY отвергает; SQLite3::IGNORE имеет специальный смысл. При READ это подстановка NULL вместо колонки, при DELETE — отключение truncate-оптимизации с сохранением удаления. Ни IGNORE, ни readOnly не являются универсальной «безопасной песочницей».
| Действие SQLite3:: | Первое уточнение | Второе уточнение |
|---|---|---|
| CREATE_INDEX / DROP_INDEX | Имя индекса | Имя таблицы |
| CREATE_TABLE / DROP_TABLE | Имя таблицы | null |
| CREATE_TEMP_INDEX / DROP_TEMP_INDEX | Имя индекса | Имя таблицы |
| CREATE_TEMP_TABLE / DROP_TEMP_TABLE | Имя таблицы | null |
| CREATE_TEMP_TRIGGER / DROP_TEMP_TRIGGER | Имя триггера | Имя таблицы |
| CREATE_TEMP_VIEW / DROP_TEMP_VIEW | Имя view | null |
| CREATE_TRIGGER / DROP_TRIGGER | Имя триггера | Имя таблицы |
| CREATE_VIEW / DROP_VIEW | Имя view | null |
| DELETE / INSERT | Имя таблицы | null |
| PRAGMA | Имя pragma | Первый аргумент либо null |
| READ / UPDATE | Имя таблицы | Имя колонки |
| SELECT | null | null |
| TRANSACTION | Операция BEGIN/COMMIT/ROLLBACK | null |
| ATTACH | Имя файла | null |
| DETACH | Имя схемы | null |
| ALTER_TABLE | Имя схемы | Имя таблицы |
| REINDEX | Имя индекса | null |
| ANALYZE | Имя таблицы | null |
| CREATE_VTABLE / DROP_VTABLE | Имя таблицы | Имя модуля |
| FUNCTION | null | Имя функции |
| SAVEPOINT | Операция | Имя savepoint |
| RECURSIVE | null | null |
| COPY | Исторический код, не используется современным SQLite | Не строить новую политику вокруг него |
COPY сохранён как историческая константа старого SQLite, современный authorizer его не выдаёт. RECURSIVE описывает рекурсивный SELECT, не рекурсию PHP. Полная сигнатура из пяти аргументов и ограничения callback — в SQLite3::setAuthorizer.
Как читать этот вариант. Политика установлена до подготовки запроса. Разрешены только нужные actions/table; остальные запрещены. Это пример механизма, не готовая песочница произвольного SQL: также ограничивают функции, объём работы, память, файлы и время.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT NOT NULL, price INTEGER NOT NULL)');
$db->exec("INSERT INTO products VALUES (1, 'Pen', 120), (2, 'Notebook', 350), (3, 'Box', 200)");
$db->setAuthorizer(function ($action, $first = null, $second = null, $schema = null, $origin = null) {
if ($action === SQLite3::SELECT) { return SQLite3::OK; }
if ($action === SQLite3::READ && $first === 'products') { return SQLite3::OK; }
return SQLite3::DENY;
});
echo $db->querySingle('SELECT name FROM products WHERE id = 1'), "\n";
try { $db->exec('DELETE FROM products'); }
catch (Exception $error) { echo "Изменение запрещено\n"; }
$db->setAuthorizer(null);
Ожидаемый результат
Pen
Изменение запрещено
Как читать этот вариант. IGNORE для READ заменяет значение этой колонки на NULL. Это не то же самое, что DENY всей команды. Не путать с IGNORE для DELETE: там удаление продолжается, только отключается truncate-оптимизация.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE contacts (name TEXT, private_note TEXT)');
$db->exec("INSERT INTO contacts VALUES ('Ann', 'Internal note')");
$db->setAuthorizer(function ($action, $table = null, $column = null) {
if ($action === SQLite3::SELECT) { return SQLite3::OK; }
if ($action === SQLite3::READ && $table === 'contacts') {
return $column === 'name' ? SQLite3::OK : SQLite3::IGNORE;
}
return SQLite3::DENY;
});
$row = $db->querySingle('SELECT name, private_note FROM contacts', true);
echo $row['name'], "\n";
var_dump($row['private_note']);
$db->setAuthorizer(null);
Ожидаемый результат
Ann
NULL
Транзакции, конкурентная запись и WAL без ложных обещаний
SQLite3 не предоставляет отдельных PHP-методов commit/beginTransaction: команды BEGIN, COMMIT, ROLLBACK выполняются через exec. BEGIN DEFERRED (default) откладывает реальное получение чтения/записи до обращения; IMMEDIATE пытается получить право записи сразу; EXCLUSIVE вне WAL дополнительно блокирует читателей. В WAL IMMEDIATE и EXCLUSIVE ведут себя одинаково в этой части. Несколько читателей могут работать одновременно, писатель один.
Как читать этот вариант. BEGIN IMMEDIATE заранее получает право записи. COMMIT подтверждает обе команды; catch отменяет их вместе. Реальное списание также проверяет существование обеих сторон и число затронутых строк. Во время транзакции не ждут ответ внешнего HTTP API.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE wallets (id INTEGER PRIMARY KEY, balance INTEGER CHECK(balance >= 0))');
$db->exec('INSERT INTO wallets VALUES (1, 100), (2, 0)');
$db->exec('BEGIN IMMEDIATE');
try {
$db->exec('UPDATE wallets SET balance = balance - 30 WHERE id = 1');
$db->exec('UPDATE wallets SET balance = balance + 30 WHERE id = 2');
$db->exec('COMMIT');
} catch (Exception $error) {
$db->exec('ROLLBACK');
throw $error;
}
echo $db->querySingle('SELECT balance FROM wallets WHERE id = 1'), ':', $db->querySingle('SELECT balance FROM wallets WHERE id = 2'), "\n";
Ожидаемый результат
70:30
Как читать этот вариант. Без транзакции пополнение второй записи сохранилось бы, хотя списание первой отказало. Здесь CHECK провоцирует ожидаемый отказ и ROLLBACK отменяет оба шага. Реальный перевод требует проверки количества обновлённых строк, валюты, авторизации и уникального идентификатора операции.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE balances (id INTEGER PRIMARY KEY, amount INTEGER CHECK(amount >= 0))');
$db->exec('INSERT INTO balances VALUES (1, 20), (2, 0)');
$db->exec('BEGIN IMMEDIATE');
try {
$db->exec('UPDATE balances SET amount = amount + 50 WHERE id = 2');
$db->exec('UPDATE balances SET amount = amount - 50 WHERE id = 1');
$db->exec('COMMIT');
} catch (Exception $error) {
$db->exec('ROLLBACK');
echo "Перевод отменён\n";
}
echo $db->querySingle('SELECT amount FROM balances WHERE id = 2'), "\n";
Ожидаемый результат
Перевод отменён
0
Как читать этот вариант. Второй BEGIN не создаёт вложенную транзакцию. SAVEPOINT — именованная точка, ROLLBACK TO возвращает к ней, RELEASE снимает её; внешний COMMIT фиксирует base. Договорённость о допустимом частичном успехе должна быть явной.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE labels (name TEXT UNIQUE)');
$db->exec('BEGIN');
$db->exec("INSERT INTO labels VALUES ('base')");
$db->exec('SAVEPOINT optional');
try {
$db->exec("INSERT INTO labels VALUES ('extra')");
$db->exec("INSERT INTO labels VALUES ('base')");
} catch (Exception $error) { $db->exec('ROLLBACK TO optional'); }
$db->exec('RELEASE optional');
$db->exec('COMMIT');
echo $db->querySingle('SELECT COUNT(*) FROM labels'), "\n";
Ожидаемый результат
1
WAL улучшает совместную работу читателей и писателя, но не создаёт несколько одновременных writers. Длинный читатель может мешать продвижению checkpoint и раздувать WAL; база использует файлы -wal/-shm, поэтому каталог должен поддерживать нужные права и операции. WAL рассчитан на процессы одной машины с согласованной shared-memory/locking-средой, а не на произвольную сетевую папку. Смена режима journal_mode — эксплуатационное решение после теста, а не универсальная строка для исправления любой ошибки.
Предел ожидания задаёт SQLite3::busyTimeout. Если операция отказала из-за конкуренции, безопасно повторяют всю бизнес-операцию только при понятной идемпотентности и конечном числе попыток. При COMMIT→BUSY транзакция может оставаться открытой: не объявляйте её автоматически откатанной. BEGIN/COMMIT не вкладываются: используйте savepoints. За время открытой транзакции не ждите банк, Telegram или sleep; внешний запрос и подтверждение состояния организуют отдельно.
От HTTP-формы к таблице: границы ответственности
Для формы HTML имя поля quantity становится ключом входного массива PHP; это ещё непроверенная строка/массив, не гарантированное целое. Сначала проверяют HTTP-метод, форму данных, право пользователя и CSRF для нужного сценария, затем допустимый диапазон. После этого bindValue передаёт проверенное число в SQL. Подготовленные параметры защищают структуру SQL, но не отвечают, имеет ли посетитель право покупать этот товар и допустимо ли количество.
- форма quantity="3"
- проверка метода/структуры/права/диапазона
- PHP int3
- bindValue :n=3
- условный UPDATE
- changes=1 или отказ
- commit→пользовательский ответ
Полная HTML/PHP-связка показана в обработке форм, JSON-вход — в работе с JSON, карта входных данных — в суперглобальных массивах. Здесь следующий понятный шаг — получить товар по числу и списать остаток только при его наличии.
Как читать этот вариант. prepare переводит шаблон SQL в готовую команду. :id — место для значения, без кавычек. bindValue кладёт туда число 2; execute выполняет команду, fetchArray даёт строку. Данные не меняют структуру SQL.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT NOT NULL, price INTEGER NOT NULL)');
$db->exec("INSERT INTO products VALUES (1, 'Pen', 120), (2, 'Notebook', 350), (3, 'Box', 200)");
$statement = $db->prepare('SELECT name FROM products WHERE id = :id');
$statement->bindValue(':id', 2, SQLITE3_INTEGER);
$result = $statement->execute();
echo $result->fetchArray(SQLITE3_ASSOC)['name'], "\n";
$result->finalize();
$statement->close();
Ожидаемый результат
Notebook
Как читать этот вариант. Условие и изменение находятся внутри одной SQL-команды. Это устраняет окно между отдельными SELECT остатка и UPDATE; полномасштабный заказ дополнительно требует транзакции и уникального бизнес-ключа.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE stock (id INTEGER PRIMARY KEY, quantity INTEGER)');
$db->exec('INSERT INTO stock VALUES (1, 2)');
$buy = $db->prepare('UPDATE stock SET quantity = quantity - :n WHERE id = 1 AND quantity >= :n');
$buy->bindValue(':n', 3, SQLITE3_INTEGER);
$buy->execute()->finalize();
echo $db->changes() === 1 ? "Списано\n" : "Недостаточно товара\n";
Ожидаемый результат
Недостаточно товара
Для вывода в HTML имя товара экранируют в месте вывода, для JSON кодируют сериализатором. SQL-экранирование не заменяет ни одно из них. При передаче Result между слоями заранее определите, кто завершает курсор; для небольшого каталога можно вернуть обычный массив, для длинного экспорта — построчный итератор с finally.
Все классы и методы SQLite3 по назначению
Здесь полный API книги SQLite3: соединение, подготовленная команда, результат и исключение. PDO SQLite — отдельный API и отдельная ветка; названия методов и значения аргументов у них не обязаны совпадать. Каждая карточка содержит контракт и последовательные примеры, ссылки ведут к точному объяснению.
SQLite3
SQLite3::__constructSQLite3::openSQLite3::closeSQLite3::execSQLite3::querySQLite3::querySingleSQLite3::changesSQLite3::lastInsertRowIDSQLite3::prepareSQLite3::lastErrorCodeSQLite3::lastExtendedErrorCodeSQLite3::lastErrorMsgSQLite3::enableExceptionsSQLite3::enableExtendedResultCodesSQLite3::busyTimeoutSQLite3::escapeStringSQLite3::backupSQLite3::versionSQLite3::loadExtensionSQLite3::createFunctionSQLite3::createAggregateSQLite3::createCollationSQLite3::setAuthorizerSQLite3::openBlob
SQLite3Stmt
SQLite3Stmt::bindValueSQLite3Stmt::bindParamSQLite3Stmt::executeSQLite3Stmt::resetSQLite3Stmt::clearSQLite3Stmt::paramCountSQLite3Stmt::getSQLSQLite3Stmt::readOnlySQLite3Stmt::closeSQLite3Stmt::busySQLite3Stmt::explainSQLite3Stmt::setExplainSQLite3Stmt::__construct
SQLite3Result
SQLite3Result::fetchArraySQLite3Result::fetchAllSQLite3Result::numColumnsSQLite3Result::columnNameSQLite3Result::columnTypeSQLite3Result::resetSQLite3Result::finalizeSQLite3Result::__construct
Класс SQLite3Exception использует унаследованный контракт Exception.
Разобрать ошибку, выбрать следующий инструмент и закрепить практикой
| Наблюдение / задача | Что проверить |
|---|---|
| Дважды видны колонки в PHP-массиве | SQLite3Result::fetchArray: BOTH содержит два вида ключей. |
| Все параметры в цикле равны последнему значению | SQLite3Stmt::bindParam: несколько ссылок привязаны к одной $value; обычно нужен bindValue. |
| Маркер не связывается | SQLite3::prepare: :id пишется без SQL-кавычек, имя совпадает, позиция начинается с1. |
| database is locked | SQLite3::busyTimeout, короткая транзакция, освобождение курсоров, права каталога. |
| После INSERT появились лишние записи | SQLite3Stmt::execute: обычный INSERT не читают fetchArray; проверьте повторный HTTP-запрос и идемпотентность. |
| Память растёт на экспорте | SQLite3Result::fetchArray вместо накопления всех строк; проверить и SQL-план. |
| Нужна копия живой базы | SQLite3::backup и отдельная проверка восстановления. |
| Неясно, почему SELECT медленный | SQLite3Stmt::setExplain, индексы под фильтры и измерение на реалистичных данных; сам PHP-класс не устраняет полный scan. |
Упражнение: создайте в :memory: таблицу отправлений tracking TEXT UNIQUE, status TEXT. Подготовьте INSERT, передайте два разных номера через bindValue, прочитайте их в порядке tracking, затем попробуйте повторить первый. Ожидается две строки и обработанный UNIQUE-отказ, не третье отправление. Решение собирается из SQLite3::prepare, SQLite3Stmt::bindValue, SQLite3Result::fetchArray и SQLite3::lastExtendedErrorCode. Если импорт должен быть атомарным, оберните группу в транзакцию; если повторы допустимы, явно определите UPSERT/ON CONFLICT-политику.
Как читать этот вариант. Начинаем с объявления таблицы, затем один шаблон обслуживает оба отправления. Третья попытка конфликтует с UNIQUE и обрабатывается только как дубликат; неизвестная ошибка не скрывается. status остаётся привязанным к created. Если новое выполнение должно получать полностью новый набор параметров, задайте все bindings заново.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE parcels (tracking TEXT UNIQUE, status TEXT NOT NULL)');
$stmt = $db->prepare('INSERT INTO parcels VALUES (:tracking, :status)');
foreach (['PK-101', 'PK-102'] as $tracking) {
$stmt->bindValue(':tracking', $tracking, SQLITE3_TEXT);
$stmt->bindValue(':status', 'created', SQLITE3_TEXT);
$stmt->execute()->finalize();
}
$stmt->bindValue(':tracking', 'PK-101');
try { $stmt->execute()->finalize(); }
catch (Exception $error) {
if ($db->lastExtendedErrorCode() !== 2067) { throw $error; }
echo "Номер уже существует\n";
}
$stmt->close();
$result = $db->query('SELECT tracking, status FROM parcels ORDER BY tracking');
while (($row = $result->fetchArray(SQLITE3_ASSOC)) !== false) { echo $row['tracking'], ':', $row['status'], "\n"; }
$result->finalize();
Ожидаемый результат
Номер уже существует
PK-101:created
PK-102:created
- Обсуждение практического затруднения: Почему маркер :name нельзя заключать в SQL-кавычки.
- Обсуждение практического затруднения: Почему несколько bindParam с одной переменной цикла получают последнее значение.
- Обсуждение практического затруднения: Историческая повторная вставка при чтении результата обычного INSERT; разграничение exec и fetch.
- Обсуждение практического затруднения: Перенос кода PDO в SQLite3: execute не принимает массив параметров.
- Обсуждение практического затруднения: BOTH выдаёт два ключа одной колонки, это не две строки.
- Обсуждение практического затруднения: Конкурирующие соединения: короткая транзакция, завершение курсора, busyTimeout и осмысленный WAL.
- Обсуждение практического затруднения: Права на каталог journal/WAL и задержка при конкуренции, а не только права файла.
Обсуждения использованы для выбора реально встречающихся проблем, не как нормативный контракт. Старые ответы могут советовать неверные кавычки, повторные fetch или устаревшие настройки. Правила выше сверены с PHP.net, исходниками PHP8.5.11 и SQLite; код примеров написан отдельно. Два примера loadExtension требуют отсутствующий у нас нативный spellfix и проверены только на синтаксис. Остальные SQL-примеры выполняются на собственных временных базах без доступа к рабочим данным.
Функции и методы этой темы
Откройте карточку, чтобы увидеть договор функции, примеры, ограничения и связанные инструменты.
SQLite3::__constructНужно хранить каталог или локальную очередь, не поднимая сервер базы. Конструктор создаёт соединение с файлом либо временной базой.
new SQLite3(string $filename, int $flags = SQLITE3_OPEN_READWRITE | SQLITE3_OPEN_CREATE, string $encryptionKey = "")Открыть собственную базу и прочитать товар. Объект SQLite3 открывает базу. Строка :memory: означает отдельное временное хранилище этого соединения; она не выбирает существующий файл.
- new SQLite3(':memory:')
- Схема и три исходные строки лежат в schema.sql. Сначала создаём базу, затем таблицу, только после этого SELECT имеет откуда читать товар.
- После выполнения: Молоток — 8 шт.
В этом самостоятельном примере подготовка указана непосредственно в коде и дополнительных файлах архива. Запуск: php example.php; требования и внешние зависимости отмечены перед кодом.
| Запись в этом примере | Как читать |
|---|---|
$db->method(...) | Стрелка вызывает действие у конкретного соединения, команды или результата. Скобки содержат входные значения; результат можно сохранить через =. |
[...] | Квадратные скобки задают массив либо читают его элемент. Ключ имени колонки и номер позиции — разные способы адресации. |
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec(file_get_contents(__DIR__ . '/schema.sql'));
$result = $db->query('SELECT name, stock FROM products WHERE id = 1');
$row = $result->fetchArray(SQLITE3_ASSOC);
echo $row['name'], ' — ', $row['stock'], " шт.\n";
Ожидаемый результат
Молоток — 8 шт.
Схема и три исходные строки лежат в schema.sql. Сначала создаём базу, затем таблицу, только после этого SELECT имеет откуда читать товар.
Эти же строки в табличном виде. CLI-пример выше печатает текст; таблица помогает увидеть соответствие названия и остатка. Как получить такую таблицу настоящим HTML-обработчиком.
| Товар / строка | Наличие |
|---|---|
| Молоток | 8 шт. |
Если требование изменится. Для файла задайте абсолютный путь и режим открытия. SQLITE3_OPEN_READONLY не создаёт отсутствующий файл; запись требует прав и на каталог для вспомогательных файлов.
Полный контракт, ограничения и следующие варианты. Ниже сохранены параметры, возвращаемые значения, ошибки, версии и дополнительные задачи этой операции. Перед каждым более длинным примером сначала прочитайте его подготовку и ход действий.
Нужно хранить каталог или локальную очередь, не поднимая сервер базы. Конструктор создаёт соединение с файлом либо временной базой.
| Вход / параметр | Смысл |
|---|---|
| filename | Путь, :memory: для памяти, пустая строка для приватной временной дисковой базы. |
| flags | READONLY; READWRITE; READWRITE|CREATE по умолчанию. CREATE разрешает создать отсутствующий файл. |
| encryptionKey | Ключ только для сборки с поддержкой шифрования. Обычная сборка его игнорирует: пароль не делает файл зашифрованным. |
Возвращаемый объект хранит соединение. При невозможности открыть базу возникает Exception/SQLite3Exception независимо от последующего включения ошибок. Относительный путь зависит от текущего каталога; в приложении используют явно выбранный абсолютный путь вне web-root. Права нужны и на каталог для journal/WAL, не только на файл.
- new SQLite3(":memory:")
- открытая база принадлежит $db
- querySingle возвращает 42
- close освобождает базу
Как читать этот вариант. new создаёт объект соединения; :memory: означает отдельную временную базу этого соединения. $db хранит объект, стрелка вызывает его метод, строка SELECT — команда SQLite, $answer получает число. ; завершает PHP-инструкцию. Никакой сервер и файл для этого опыта не нужны.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$answer = $db->querySingle('SELECT 40 + 2');
echo $answer, "\n";
$db->close();
Ожидаемый результат
42
Как читать этот вариант. Пустое имя создаёт приватную временную дисковую базу (PHP 7.0.10+). После закрытия SQLite удаляет её. CREATE задаёт таблицу, INSERT сохраняет строку, SELECT получает итог; сумма остаётся только до закрытия.
<?php
$db = new SQLite3('');
$db->enableExceptions(true);
$db->exec('CREATE TABLE summary (amount INTEGER)');
$db->exec('INSERT INTO summary VALUES (250)');
echo $db->querySingle('SELECT SUM(amount) FROM summary'), "\n";
$db->close();
Ожидаемый результат
250
Следующий шаг и альтернативы: SQLite3::open, SQLite3::close, SQLite3::querySingle.
SQLite3::openПереподключить уже существующий объект после close или управлять открытием в наследнике.
SQLite3::open(string $filename, int $flags = SQLITE3_OPEN_READWRITE | SQLITE3_OPEN_CREATE, string $encryptionKey = ""): voidПереключить закрытый объект на сохранённый каталог. open открывает базу у уже созданного, но закрытого объекта. Сам конструктор обычного SQLite3 уже вызывает открытие.
- $db->open($path, SQLITE3_OPEN_READONLY)
- Первое открытие создаёт учебный файл, затем close освобождает связь. Повторное открытие читает тот же файл в режиме без записи.
- После выполнения: Перчатки — 15 шт.
В этом самостоятельном примере подготовка указана непосредственно в коде и дополнительных файлах архива. Запуск: php example.php; требования и внешние зависимости отмечены перед кодом.
| Запись в этом примере | Как читать |
|---|---|
$db->method(...) | Стрелка вызывает действие у конкретного соединения, команды или результата. Скобки содержат входные значения; результат можно сохранить через =. |
[...] | Квадратные скобки задают массив либо читают его элемент. Ключ имени колонки и номер позиции — разные способы адресации. |
<?php
$path = __DIR__ . '/catalog.sqlite';
$db = new SQLite3($path);
$db->enableExceptions(true);
$db->exec(file_get_contents(__DIR__ . '/schema.sql'));
$db->close();
$db->open($path, SQLITE3_OPEN_READONLY);
$result = $db->query('SELECT name, stock FROM products WHERE id = 2');
$row = $result->fetchArray(SQLITE3_ASSOC);
echo $row['name'], ' — ', $row['stock'], " шт.\n";
Ожидаемый результат
Перчатки — 15 шт.
Первое открытие создаёт учебный файл, затем close освобождает связь. Повторное открытие читает тот же файл в режиме без записи.
Эти же строки в табличном виде. CLI-пример выше печатает текст; таблица помогает увидеть соответствие названия и остатка. Как получить такую таблицу настоящим HTML-обработчиком.
| Товар / строка | Наличие |
|---|---|
| Перчатки | 15 шт. |
Если требование изменится. Не вызывайте open на уже открытом объекте, ожидая незаметной смены базы. Повторный запуск этого конкретного создания схемы в той же папке потребует отдельного нового учебного каталога, чтобы не сталкиваться с существующей таблицей.
Полный контракт, ограничения и следующие варианты. Ниже сохранены параметры, возвращаемые значения, ошибки, версии и дополнительные задачи этой операции. Перед каждым более длинным примером сначала прочитайте его подготовку и ход действий.
Переподключить уже существующий объект после close или управлять открытием в наследнике.
| Вход / параметр | Смысл |
|---|---|
| filename / flags / encryptionKey | Те же значения и ограничения, что у конструктора; open вызывается на объекте, не статически. |
Успех не возвращает значения (void), ошибка открытия — исключение. Открытое соединение сначала закрывают, повторный open поверх него — ошибка. Если наследник переопределяет конструктор, он обязан вызвать parent::__construct либо open до запросов. Пример наследования не нужен для обычного первого подключения.
- close старой базы
- open выбранного файла
- READONLY проверяет операции на записи
Как читать этот вариант. open не создаёт второй PHP-объект. Здесь прежнее соединение закрыто; новая база в памяти не наследует старые таблицы.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE old_data (id INTEGER)');
$db->close();
$db->open(':memory:');
echo $db->querySingle("SELECT COUNT(*) FROM sqlite_schema WHERE name = 'old_data'"), "\n";
Ожидаемый результат
0
Как читать этот вариант. Создаётся только наш уникальный временный файл; удаляется именно он. READONLY защищает это соединение от записи, но не отменяет права другого процесса на сам файл.
<?php
$path = tempnam(sys_get_temp_dir(), 'sql3-');
$writer = new SQLite3($path);
$writer->exec('CREATE TABLE totals (n INTEGER)');
$writer->exec('INSERT INTO totals VALUES (7)');
$writer->close();
try {
$reader = new SQLite3(':memory:');
$reader->close();
$reader->open($path, SQLITE3_OPEN_READONLY);
$reader->enableExceptions(true);
echo $reader->querySingle('SELECT n FROM totals'), "\n";
try { $reader->exec('DELETE FROM totals'); }
catch (Exception $error) { echo "Запись запрещена\n"; }
$reader->close();
} finally { unlink($path); }
Ожидаемый результат
7
Запись запрещена
Следующий шаг и альтернативы: SQLite3::__construct, SQLite3::close.
SQLite3::closeОсвободить соединение и его блокировки сразу после локального отчёта.
SQLite3::close(): boolСохранить нужные значения и освободить соединение. close завершает работу объекта с базой. PHP-массив уже прочитанной строки существует отдельно; открытые результаты и потоки перед закрытием завершают.
- $db->close()
- Название и остаток сохранены в обычном массиве до закрытия. После close код не обращается к $db, но может оформить уже полученные данные.
- После выполнения: Молоток — 8 шт.
В архиве catalog.php открывает отдельную учебную базу и выполняет schema.sql: 1 — Молоток, 8 шт.; 2 — Перчатки, 15 шт.; 3 — Кисть, 0 шт. База находится в памяти и начинает работу с этих данных при каждом запуске. Распакуйте все файлы вместе и запустите php example.php. Полная подготовка и схема.
| Запись в этом примере | Как читать |
|---|---|
$db->method(...) | Стрелка вызывает действие у конкретного соединения, команды или результата. Скобки содержат входные значения; результат можно сохранить через =. |
[...] | Квадратные скобки задают массив либо читают его элемент. Ключ имени колонки и номер позиции — разные способы адресации. |
<?php
require __DIR__ . '/catalog.php';
$result = $db->query('SELECT name, stock FROM products WHERE id = 1');
$row = $result->fetchArray(SQLITE3_ASSOC);
$result->finalize();
$db->close();
echo $row['name'], ' — ', $row['stock'], " шт.\n";
Ожидаемый результат
Молоток — 8 шт.
Название и остаток сохранены в обычном массиве до закрытия. После close код не обращается к $db, но может оформить уже полученные данные.
Эти же строки в табличном виде. CLI-пример выше печатает текст; таблица помогает увидеть соответствие названия и остатка. Как получить такую таблицу настоящим HTML-обработчиком.
| Товар / строка | Наличие |
|---|---|
| Молоток | 8 шт. |
Если требование изменится. close не является delete файла. Если надо продолжить запросы, оставьте соединение открытым или создайте новое; файл сохраняется, а :memory: исчезает с последней связью.
Полный контракт, ограничения и следующие варианты. Ниже сохранены параметры, возвращаемые значения, ошибки, версии и дополнительные задачи этой операции. Перед каждым более длинным примером сначала прочитайте его подготовку и ход действий.
Освободить соединение и его блокировки сразу после локального отчёта.
| Вход / параметр | Смысл |
|---|---|
| Аргументов нет | Закрывается объект перед стрелкой. |
true — закрыто, false/исключение — закрыть не удалось. Подготовленные объекты и потоки имеют собственный жизненный цикл; не использовать их после закрытия. При завершении запроса PHP освободит ресурсы, но явное завершение важно в долгом worker.
- получили Pen
- finalize результата
- close statement
- close соединения
Как читать этот вариант. close освобождает ресурс SQLite, но PHP-переменная всё ещё указывает на объект. Его наличие не доказывает открытое соединение.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
var_dump($db->close());
try { $db->querySingle('SELECT 1'); }
catch (Error $error) { echo "Соединение закрыто\n"; }
Ожидаемый результат
bool(true)
Соединение закрыто
Как читать этот вариант. Результат → statement → соединение. Активный BLOB-поток нужно закрыть fclose раньше базы; оставшиеся ресурсы могут препятствовать закрытию. try/finally удобно для ранних выходов.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT NOT NULL, price INTEGER NOT NULL)');
$db->exec("INSERT INTO products VALUES (1, 'Pen', 120), (2, 'Notebook', 350), (3, 'Box', 200)");
$stmt = $db->prepare('SELECT name FROM products WHERE id = 1');
$result = $stmt->execute();
echo $result->fetchArray(SQLITE3_ASSOC)['name'], "\n";
$result->finalize();
$stmt->close();
var_dump($db->close());
Ожидаемый результат
Pen
bool(true)
Следующий шаг и альтернативы: SQLite3Result::finalize, SQLite3Stmt::close, SQLite3::openBlob.
SQLite3::execСоздать таблицу, изменить остаток или завершить транзакцию, когда набор строк не нужен.
SQLite3::exec(string $query): boolОбновить остаток без ожидания набора строк. exec исполняет SQL, которому не требуется читать строки ответа. Булев успех команды не содержит новый остаток; его проверяют отдельным SELECT.
- $db->exec('UPDATE products SET stock = 7 WHERE id = 1')
- Сначала меняем 8 на 7, затем читаем строку из базы и видим 7. enableExceptions(true) в catalog.php прекращает код при SQL-ошибке.
- После выполнения: Молоток — 7 шт.
В архиве catalog.php открывает отдельную учебную базу и выполняет schema.sql: 1 — Молоток, 8 шт.; 2 — Перчатки, 15 шт.; 3 — Кисть, 0 шт. База находится в памяти и начинает работу с этих данных при каждом запуске. Распакуйте все файлы вместе и запустите php example.php. Полная подготовка и схема.
| Запись в этом примере | Как читать |
|---|---|
$db->method(...) | Стрелка вызывает действие у конкретного соединения, команды или результата. Скобки содержат входные значения; результат можно сохранить через =. |
[...] | Квадратные скобки задают массив либо читают его элемент. Ключ имени колонки и номер позиции — разные способы адресации. |
<?php
require __DIR__ . '/catalog.php';
$db->exec('UPDATE products SET stock = 7 WHERE id = 1');
$result = $db->query('SELECT name, stock FROM products WHERE id = 1');
$row = $result->fetchArray(SQLITE3_ASSOC);
echo $row['name'], ' — ', $row['stock'], " шт.\n";
Ожидаемый результат
Молоток — 7 шт.
Сначала меняем 8 на 7, затем читаем строку из базы и видим 7. enableExceptions(true) в catalog.php прекращает код при SQL-ошибке.
Эти же строки в табличном виде. CLI-пример выше печатает текст; таблица помогает увидеть соответствие названия и остатка. Как получить такую таблицу настоящим HTML-обработчиком.
| Товар / строка | Наличие |
|---|---|
| Молоток | 7 шт. |
Если требование изменится. Для количества из формы используйте prepare и привязку. Здесь SQL фиксирован автором. exec не заменяет query при SELECT и не запускает внешнюю программу.
Полный контракт, ограничения и следующие варианты. Ниже сохранены параметры, возвращаемые значения, ошибки, версии и дополнительные задачи этой операции. Перед каждым более длинным примером сначала прочитайте его подготовку и ход действий.
Создать таблицу, изменить остаток или завершить транзакцию, когда набор строк не нужен.
| Вход / параметр | Смысл |
|---|---|
| query | Одна или несколько доверенных SQL-команд; данные посетителя передают через prepare/bindValue, не склеивают. |
Возвращает true при успехе, false при отказе в режиме warnings. Несколько команд не становятся атомарными автоматически; без транзакции ранние изменения могут сохраниться до поздней ошибки. В примерах enableExceptions(true) задан явно: ошибка SQL прерывает обычный путь исключением. В PHP 8.3+ используется SQLite3Exception (наследник Exception), раньше Exception; при устаревшем режиме warnings метод может вернуть false. Ошибки типов, аргументов и закрытого объекта могут быть TypeError, ValueError или Error. Сообщение базы сохраняют в закрытом журнале, посетителю дают понятный ответ без SQL и путей сервера.
- UPDATE stock
- SQLite изменяет quantity 5 → 4
- exec возвращает true
- SELECT отдельно получает 4
Как читать этот вариант. exec выполняет SQL без получения строк. Успех UPDATE и число изменённых строк — разные сведения: число даёт changes.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE stock (quantity INTEGER)');
$db->exec('INSERT INTO stock VALUES (5)');
var_dump($db->exec('UPDATE stock SET quantity = quantity - 1'));
echo $db->querySingle('SELECT quantity FROM stock'), "\n";
Ожидаемый результат
bool(true)
4
Как читать этот вариант. BEGIN IMMEDIATE заранее получает право записи. COMMIT подтверждает обе команды; catch отменяет их вместе. Реальное списание также проверяет существование обеих сторон и число затронутых строк. Во время транзакции не ждут ответ внешнего HTTP API.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE wallets (id INTEGER PRIMARY KEY, balance INTEGER CHECK(balance >= 0))');
$db->exec('INSERT INTO wallets VALUES (1, 100), (2, 0)');
$db->exec('BEGIN IMMEDIATE');
try {
$db->exec('UPDATE wallets SET balance = balance - 30 WHERE id = 1');
$db->exec('UPDATE wallets SET balance = balance + 30 WHERE id = 2');
$db->exec('COMMIT');
} catch (Exception $error) {
$db->exec('ROLLBACK');
throw $error;
}
echo $db->querySingle('SELECT balance FROM wallets WHERE id = 1'), ':', $db->querySingle('SELECT balance FROM wallets WHERE id = 2'), "\n";
Ожидаемый результат
70:30
Следующий шаг и альтернативы: SQLite3::changes, SQLite3::prepare, SQLite3::query.
SQLite3::queryПолучить таблицу строк каталога, чтобы прочитать её по одной.
SQLite3::query(string $query): SQLite3Result|falseПрочитать каталог по одной строке. query выполняет SQL и возвращает SQLite3Result — объект текущего ответа. fetchArray переносит из него следующую строку в PHP.
- $db->query('SELECT name, stock FROM products ORDER BY id')
- SQL задаёт колонки и порядок; while останавливается, когда fetchArray вернёт false. finalize освобождает завершённый ответ.
- После выполнения: Молоток — 8 шт. · Перчатки — 15 шт. · Кисть — 0 шт.
В архиве catalog.php открывает отдельную учебную базу и выполняет schema.sql: 1 — Молоток, 8 шт.; 2 — Перчатки, 15 шт.; 3 — Кисть, 0 шт. База находится в памяти и начинает работу с этих данных при каждом запуске. Распакуйте все файлы вместе и запустите php example.php. Полная подготовка и схема.
| Запись в этом примере | Как читать |
|---|---|
$db->method(...) | Стрелка вызывает действие у конкретного соединения, команды или результата. Скобки содержат входные значения; результат можно сохранить через =. |
while (...) | Повторяет блок, пока условие истинно. В чтении результата проверка конца связана с конкретным методом: false или null указаны в его контракте. |
[...] | Квадратные скобки задают массив либо читают его элемент. Ключ имени колонки и номер позиции — разные способы адресации. |
<?php
require __DIR__ . '/catalog.php';
$result = $db->query('SELECT name, stock FROM products ORDER BY id');
while (($row = $result->fetchArray(SQLITE3_ASSOC)) !== false) {
echo $row['name'], ' — ', $row['stock'], " шт.\n";
}
$result->finalize();
Ожидаемый результат
Молоток — 8 шт.
Перчатки — 15 шт.
Кисть — 0 шт.
SQL задаёт колонки и порядок; while останавливается, когда fetchArray вернёт false. finalize освобождает завершённый ответ.
Эти же строки в табличном виде. CLI-пример выше печатает текст; таблица помогает увидеть соответствие названия и остатка. Как получить такую таблицу настоящим HTML-обработчиком.
| Товар / строка | Наличие |
|---|---|
| Молоток | 8 шт. |
| Перчатки | 15 шт. |
| Кисть | 0 шт. |
Если требование изменится. Не вызывайте query ради INSERT с последующим fetchArray «для проверки»: повторное выполнение result при некоторых действиях способно повторить побочный эффект. Для изменения без строк используйте exec/подготовленную команду и finalize.
Полный контракт, ограничения и следующие варианты. Ниже сохранены параметры, возвращаемые значения, ошибки, версии и дополнительные задачи этой операции. Перед каждым более длинным примером сначала прочитайте его подготовку и ход действий.
Получить таблицу строк каталога, чтобы прочитать её по одной.
| Вход / параметр | Смысл |
|---|---|
| query | SQL-запрос; для изменяемых значений используют подготовленную команду. |
Возвращает SQLite3Result, false при ошибке без исключений. Наличие результата не означает наличие строк: первый fetchArray может вернуть false. Для INSERT/UPDATE без RETURNING используйте exec или execute с finalize, не запускайте fetch только ради проверки успеха. В примерах enableExceptions(true) задан явно: ошибка SQL прерывает обычный путь исключением. В PHP 8.3+ используется SQLite3Exception (наследник Exception), раньше Exception; при устаревшем режиме warnings метод может вернуть false. Ошибки типов, аргументов и закрытого объекта могут быть TypeError, ValueError или Error. Сообщение базы сохраняют в закрытом журнале, посетителю дают понятный ответ без SQL и путей сервера.
- SELECT выбирает Pen и Box
- query создаёт курсор
- fetchArray возвращает массив каждой строки
- false завершает while
Как читать этот вариант. query возвращает объект чтения, а не PHP-массив всех товаров. fetchArray извлекает текущую строку; ASSOC выбирает подписи name и price.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT NOT NULL, price INTEGER NOT NULL)');
$db->exec("INSERT INTO products VALUES (1, 'Pen', 120), (2, 'Notebook', 350), (3, 'Box', 200)");
$result = $db->query('SELECT name, price FROM products WHERE id = 1');
$row = $result->fetchArray(SQLITE3_ASSOC);
echo $row['name'], ':', $row['price'], "\n";
$result->finalize();
Ожидаемый результат
Pen:120
Как читать этот вариант. while запрашивает следующую строку, пока не получит false. В PHP не накоплен массив всех товаров; сортировка и сам запрос всё равно могут требовать памяти SQLite. ORDER BY нужен для определённого порядка.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT NOT NULL, price INTEGER NOT NULL)');
$db->exec("INSERT INTO products VALUES (1, 'Pen', 120), (2, 'Notebook', 350), (3, 'Box', 200)");
$result = $db->query('SELECT name FROM products WHERE price < 300 ORDER BY id');
while (($row = $result->fetchArray(SQLITE3_ASSOC)) !== false) {
echo $row['name'], "\n";
}
$result->finalize();
Ожидаемый результат
Pen
Box
Следующий шаг и альтернативы: SQLite3::querySingle, SQLite3::prepare, SQLite3Result::fetchArray.
SQLite3::querySingleБыстро получить сумму, количество либо одну карточку товара.
SQLite3::querySingle(string $query, bool $entireRow = false): mixedПоказать один товар без промежуточного результата. Второй аргумент true просит всю первую строку как массив. Без него querySingle возвращает только значение первой колонки.
- $db->querySingle($sql, true)
- Вся строка нужна, потому что выводим и название, и остаток. Нулевой остаток не равен пустому массиву отсутствующей строки.
- После выполнения: Кисть — 0 шт.
В архиве catalog.php открывает отдельную учебную базу и выполняет schema.sql: 1 — Молоток, 8 шт.; 2 — Перчатки, 15 шт.; 3 — Кисть, 0 шт. База находится в памяти и начинает работу с этих данных при каждом запуске. Распакуйте все файлы вместе и запустите php example.php. Полная подготовка и схема.
| Запись в этом примере | Как читать |
|---|---|
$db->method(...) | Стрелка вызывает действие у конкретного соединения, команды или результата. Скобки содержат входные значения; результат можно сохранить через =. |
[...] | Квадратные скобки задают массив либо читают его элемент. Ключ имени колонки и номер позиции — разные способы адресации. |
<?php
require __DIR__ . '/catalog.php';
$row = $db->querySingle('SELECT name, stock FROM products WHERE id = 3', true);
if ($row === []) { echo "Товар не найден\n"; } else {
echo $row['name'], ' — ', $row['stock'], " шт.\n";
}
Ожидаемый результат
Кисть — 0 шт.
Вся строка нужна, потому что выводим и название, и остаток. Нулевой остаток не равен пустому массиву отсутствующей строки.
Эти же строки в табличном виде. CLI-пример выше печатает текст; таблица помогает увидеть соответствие названия и остатка. Как получить такую таблицу настоящим HTML-обработчиком.
| Товар / строка | Наличие |
|---|---|
| Кисть | 0 шт. |
Если требование изменится. У режима одного значения отсутствие строки и SQL NULL могут совпасть в null. Для различения выбирайте всю строку. Внешние параметры требуют prepare, querySingle их не связывает.
Полный контракт, ограничения и следующие варианты. Ниже сохранены параметры, возвращаемые значения, ошибки, версии и дополнительные задачи этой операции. Перед каждым более длинным примером сначала прочитайте его подготовку и ход действий.
Быстро получить сумму, количество либо одну карточку товара.
| Вход / параметр | Смысл |
|---|---|
| query | SQL с ожидаемым первым значением/строкой; безопасные данные всё ещё требуют prepare. |
| entireRow | false — одно поле; true — массив первой строки. |
Тип первого поля становится PHP int/float/string/null; пустой результат — null либо []; невалидный запрос — false в warning-режиме. Метод не имеет аргумента параметров, поэтому не заменяет prepare для формы. В примерах enableExceptions(true) задан явно: ошибка SQL прерывает обычный путь исключением. В PHP 8.3+ используется SQLite3Exception (наследник Exception), раньше Exception; при устаревшем режиме warnings метод может вернуть false. Ошибки типов, аргументов и закрытого объекта могут быть TypeError, ValueError или Error. Сообщение базы сохраняют в закрытом журнале, посетителю дают понятный ответ без SQL и путей сервера.
- COUNT(*) в SQL
- одно число 3
- PHP выводит 3; весь каталог не переносится
Как читать этот вариант. Без true возвращается первое поле первой строки. true возвращает всю первую строку как ассоциативный массив; остальные строки не собираются.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT NOT NULL, price INTEGER NOT NULL)');
$db->exec("INSERT INTO products VALUES (1, 'Pen', 120), (2, 'Notebook', 350), (3, 'Box', 200)");
echo $db->querySingle('SELECT COUNT(*) FROM products'), "\n";
$row = $db->querySingle('SELECT name, price FROM products WHERE id = 2', true);
echo $row['name'], ':', $row['price'], "\n";
Ожидаемый результат
3
Notebook:350
Как читать этот вариант. Пустой scalar-результат — null, пустая строка-результат — []. SQL NULL тоже становится null, поэтому при необходимости различения выбирают entireRow и проверяют наличие ключа. Проверка if (!$value) потеряет допустимый 0.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT NOT NULL, price INTEGER NOT NULL)');
$db->exec("INSERT INTO products VALUES (1, 'Pen', 120), (2, 'Notebook', 350), (3, 'Box', 200)");
var_dump($db->querySingle('SELECT price FROM products WHERE id = 99'));
var_dump($db->querySingle('SELECT name FROM products WHERE id = 99', true));
var_dump($db->querySingle('SELECT 0'));
Ожидаемый результат
NULL
array(0) {
}
int(0)
Следующий шаг и альтернативы: SQLite3::query, SQLite3::prepare.
SQLite3::changesПонять, произошло ли ожидаемое изменение, и не назвать отсутствующий товар успешно обновлённым.
SQLite3::changes(): intПодтвердить условное резервирование товара. Метод сообщает число строк, изменённых последней SQL-командой соединения. Проверять надо сразу после интересующего UPDATE, до следующей записи.
- $db->changes()
- Условие и списание выполняются вместе. Счётчик помогает выбрать сообщение, а SELECT показывает фактический остаток.
- После выполнения: Резерв принят · Молоток — 6 шт.
В архиве catalog.php открывает отдельную учебную базу и выполняет schema.sql: 1 — Молоток, 8 шт.; 2 — Перчатки, 15 шт.; 3 — Кисть, 0 шт. База находится в памяти и начинает работу с этих данных при каждом запуске. Распакуйте все файлы вместе и запустите php example.php. Полная подготовка и схема.
| Запись в этом примере | Как читать |
|---|---|
$db->method(...) | Стрелка вызывает действие у конкретного соединения, команды или результата. Скобки содержат входные значения; результат можно сохранить через =. |
[...] | Квадратные скобки задают массив либо читают его элемент. Ключ имени колонки и номер позиции — разные способы адресации. |
<?php
require __DIR__ . '/catalog.php';
$db->exec('UPDATE products SET stock = stock - 2 WHERE id = 1 AND stock >= 2');
$changed = $db->changes();
echo $changed === 1 ? "Резерв принят\n" : "Товар недоступен\n";
$result = $db->query('SELECT name, stock FROM products WHERE id = 1');
$row = $result->fetchArray(SQLITE3_ASSOC);
echo $row['name'], ' — ', $row['stock'], " шт.\n";
Ожидаемый результат
Резерв принят
Молоток — 6 шт.
Условие и списание выполняются вместе. Счётчик помогает выбрать сообщение, а SELECT показывает фактический остаток.
Эти же строки в табличном виде. CLI-пример выше печатает текст; таблица помогает увидеть соответствие названия и остатка. Как получить такую таблицу настоящим HTML-обработчиком.
| Товар / строка | Наличие |
|---|---|
| Молоток | 6 шт. |
Если требование изменится. changes не считает строки SELECT и не является общим счётчиком всех каскадов/триггеров. Полноценный заказ и его резерв связывают транзакцией.
Полный контракт, ограничения и следующие варианты. Ниже сохранены параметры, возвращаемые значения, ошибки, версии и дополнительные задачи этой операции. Перед каждым более длинным примером сначала прочитайте его подготовку и ход действий.
Понять, произошло ли ожидаемое изменение, и не назвать отсутствующий товар успешно обновлённым.
| Вход / параметр | Смысл |
|---|---|
| Аргументов нет | Счётчик соединения после последнего INSERT/UPDATE/DELETE. |
Возвращает число изменённых строк, не общий накопленный итог и не количество найденных SELECT-строк. Вспомогательные изменения триггеров/каскадов не включаются в прямой счётчик команды; для view с INSTEAD OF trigger он может быть нулём. Читайте значение до следующей команды изменения. UPDATE тем же значением может учитываться: это не детектор изменения бизнес-смысла.
- UPDATE с id=99
- команда законна
- changes=0
- приложение сообщает «не найдено»
Как читать этот вариант. Успех UPDATE с отсутствующим id не означает изменение товара. changes относится к последней завершённой INSERT/UPDATE/DELETE данного соединения, не к числу строк SELECT.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT NOT NULL, price INTEGER NOT NULL)');
$db->exec("INSERT INTO products VALUES (1, 'Pen', 120), (2, 'Notebook', 350), (3, 'Box', 200)");
$db->exec('UPDATE products SET price = 150 WHERE id = 1');
echo $db->changes(), "\n";
$db->exec('UPDATE products SET price = 150 WHERE id = 99');
echo $db->changes(), "\n";
Ожидаемый результат
1
0
Как читать этот вариант. Условие и изменение находятся внутри одной SQL-команды. Это устраняет окно между отдельными SELECT остатка и UPDATE; полномасштабный заказ дополнительно требует транзакции и уникального бизнес-ключа.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE stock (id INTEGER PRIMARY KEY, quantity INTEGER)');
$db->exec('INSERT INTO stock VALUES (1, 2)');
$buy = $db->prepare('UPDATE stock SET quantity = quantity - :n WHERE id = 1 AND quantity >= :n');
$buy->bindValue(':n', 3, SQLITE3_INTEGER);
$buy->execute()->finalize();
echo $db->changes() === 1 ? "Списано\n" : "Недостаточно товара\n";
Ожидаемый результат
Недостаточно товара
Следующий шаг и альтернативы: SQLite3::exec, SQLite3::lastInsertRowID.
SQLite3::lastInsertRowIDСвязать позиции заказа с только что созданной записью.
SQLite3::lastInsertRowID(): intПолучить карточку только что добавленного товара. SQLite выдаёт rowid при вставке без номера. В нашей схеме INTEGER PRIMARY KEY id обозначает этот идентификатор.
- $db->lastInsertRowID()
- Номер 4 считывается на том же соединении после INSERT. По нему делаем параметризованный SELECT и показываем созданную запись.
- После выполнения: Товар #4: Отвёртка — 4 шт.
В архиве catalog.php открывает отдельную учебную базу и выполняет schema.sql: 1 — Молоток, 8 шт.; 2 — Перчатки, 15 шт.; 3 — Кисть, 0 шт. База находится в памяти и начинает работу с этих данных при каждом запуске. Распакуйте все файлы вместе и запустите php example.php. Полная подготовка и схема.
| Запись в этом примере | Как читать |
|---|---|
$db->method(...) | Стрелка вызывает действие у конкретного соединения, команды или результата. Скобки содержат входные значения; результат можно сохранить через =. |
[...] | Квадратные скобки задают массив либо читают его элемент. Ключ имени колонки и номер позиции — разные способы адресации. |
<?php
require __DIR__ . '/catalog.php';
$db->exec("INSERT INTO products (name, stock) VALUES ('Отвёртка', 4)");
$id = $db->lastInsertRowID();
$stmt = $db->prepare('SELECT name, stock FROM products WHERE id = :id');
$stmt->bindValue(':id', $id, SQLITE3_INTEGER);
$result = $stmt->execute();
$row = $result->fetchArray(SQLITE3_ASSOC);
echo 'Товар #', $id, ': ', $row['name'], ' — ', $row['stock'], " шт.\n";
Ожидаемый результат
Товар #4: Отвёртка — 4 шт.
Номер 4 считывается на том же соединении после INSERT. По нему делаем параметризованный SELECT и показываем созданную запись.
Эти же строки в табличном виде. CLI-пример выше печатает текст; таблица помогает увидеть соответствие названия и остатка. Как получить такую таблицу настоящим HTML-обработчиком.
| Товар / строка | Наличие |
|---|---|
| Товар #4: Отвёртка | 4 шт. |
Если требование изменится. Не подменяйте это SELECT MAX(id): он может выбрать чужую строку. WITHOUT ROWID и триггеры имеют особенности, перечисленные ниже; id не является счётчиком товаров.
Полный контракт, ограничения и следующие варианты. Ниже сохранены параметры, возвращаемые значения, ошибки, версии и дополнительные задачи этой операции. Перед каждым более длинным примером сначала прочитайте его подготовку и ход действий.
Связать позиции заказа с только что созданной записью.
| Вход / параметр | Смысл |
|---|---|
| Аргументов нет | Читается последний INSERT в rowid-таблицу на этом соединении. |
Возвращает rowid либо 0, если успешного INSERT ещё не было. Это signed 64-bit SQLite id; на 32-bit PHP следует учитывать диапазон int. WITHOUT ROWID не обновляет это значение. Неуспешная вставка обычно не меняет id, rollback успешной вставки его не обнуляет; REPLACE может создать другой id. После чужого INSERT на том же соединении значение уже другое.
- INSERT orders
- SQLite создаёт id=1
- lastInsertRowID→1
- позиции получают order_id=1
Как читать этот вариант. INTEGER PRIMARY KEY связан с rowid. SQLite выбирает id, а приложение читает его на том же соединении сразу после успешного INSERT; MAX(id) для этого не нужен.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE orders (id INTEGER PRIMARY KEY, total INTEGER)');
$insert = $db->prepare('INSERT INTO orders (total) VALUES (:total)');
$insert->bindValue(':total', 470, SQLITE3_INTEGER);
$insert->execute()->finalize();
echo $db->lastInsertRowID(), "\n";
Ожидаемый результат
1
Как читать этот вариант. Последний id остаётся историей соединения даже после rollback. Отправлять пользователю подтверждение заказа надо после commit, а не сразу после чтения id.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE orders (id INTEGER PRIMARY KEY)');
$db->exec('BEGIN');
$db->exec('INSERT INTO orders DEFAULT VALUES');
$id = $db->lastInsertRowID();
$db->exec('ROLLBACK');
echo $id, ':', $db->querySingle('SELECT COUNT(*) FROM orders'), "\n";
Ожидаемый результат
1:0
Следующий шаг и альтернативы: SQLite3::changes, SQLite3Stmt::execute.
SQLite3::prepareПринять id или имя из приложения, сохранив SQL-команду неизменной.
SQLite3::prepare(string $query): SQLite3Stmt|falseПодготовить поиск до передачи номера. SQLite3Stmt получают через prepare, а не самостоятельным new SQLite3Stmt. Объект хранит SQL-шаблон и привязки; execute возвращает отдельный объект результата.
- $db->prepare('SELECT name, stock FROM products WHERE id = :id')
- Маркер :id получает целое 2. После выполнения читаем строку, освобождаем результат, затем саму подготовленную команду.
- После выполнения: Перчатки — 15 шт.
В архиве catalog.php открывает отдельную учебную базу и выполняет schema.sql: 1 — Молоток, 8 шт.; 2 — Перчатки, 15 шт.; 3 — Кисть, 0 шт. База находится в памяти и начинает работу с этих данных при каждом запуске. Распакуйте все файлы вместе и запустите php example.php. Полная подготовка и схема.
| Запись в этом примере | Как читать |
|---|---|
$db->method(...) | Стрелка вызывает действие у конкретного соединения, команды или результата. Скобки содержат входные значения; результат можно сохранить через =. |
[...] | Квадратные скобки задают массив либо читают его элемент. Ключ имени колонки и номер позиции — разные способы адресации. |
<?php
require __DIR__ . '/catalog.php';
$stmt = $db->prepare('SELECT name, stock FROM products WHERE id = :id');
$stmt->bindValue(':id', 2, SQLITE3_INTEGER);
$result = $stmt->execute();
$row = $result->fetchArray(SQLITE3_ASSOC);
echo $row['name'], ' — ', $row['stock'], " шт.\n";
$result->finalize();
$stmt->close();
Ожидаемый результат
Перчатки — 15 шт.
Маркер :id получает целое 2. После выполнения читаем строку, освобождаем результат, затем саму подготовленную команду.
Эти же строки в табличном виде. CLI-пример выше печатает текст; таблица помогает увидеть соответствие названия и остатка. Как получить такую таблицу настоящим HTML-обработчиком.
| Товар / строка | Наличие |
|---|---|
| Перчатки | 15 шт. |
Если требование изменится. Маркер занимает место значения, не имени таблицы. Нельзя ожидать, что :ids с текстом «1,2» развернётся в два элемента IN; число маркеров задают отдельно.
Полный контракт, ограничения и следующие варианты. Ниже сохранены параметры, возвращаемые значения, ошибки, версии и дополнительные задачи этой операции. Перед каждым более длинным примером сначала прочитайте его подготовку и ход действий.
Принять id или имя из приложения, сохранив SQL-команду неизменной.
| Вход / параметр | Смысл |
|---|---|
| query | Одна SQL-команда с ? / :name / @name. Кавычки вокруг маркера превращают его в обычную строку. |
Возвращает SQLite3Stmt либо false при ошибке без исключений. Подготовка ещё не выполняет команду. В отличие от PDO, execute не принимает массив данных: все значения привязывают bindValue/bindParam. Повторный prepare каждый раз не обязателен — можно менять привязки и выполнять тот же statement. В примерах enableExceptions(true) задан явно: ошибка SQL прерывает обычный путь исключением. В PHP 8.3+ используется SQLite3Exception (наследник Exception), раньше Exception; при устаревшем режиме warnings метод может вернуть false. Ошибки типов, аргументов и закрытого объекта могут быть TypeError, ValueError или Error. Сообщение базы сохраняют в закрытом журнале, посетителю дают понятный ответ без SQL и путей сервера.
- SQL-шаблон WHERE id=:id
- bindValue :id=2
- execute
- строка Notebook
Как читать этот вариант. prepare переводит шаблон SQL в готовую команду. :id — место для значения, без кавычек. bindValue кладёт туда число 2; execute выполняет команду, fetchArray даёт строку. Данные не меняют структуру SQL.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT NOT NULL, price INTEGER NOT NULL)');
$db->exec("INSERT INTO products VALUES (1, 'Pen', 120), (2, 'Notebook', 350), (3, 'Box', 200)");
$statement = $db->prepare('SELECT name FROM products WHERE id = :id');
$statement->bindValue(':id', 2, SQLITE3_INTEGER);
$result = $statement->execute();
echo $result->fetchArray(SQLITE3_ASSOC)['name'], "\n";
$result->finalize();
$statement->close();
Ожидаемый результат
Notebook
Как читать этот вариант. Позиционный ? имеет индекс 1. Не нужно вручную добавлять SQL-кавычки или escapeString. Имя таблицы, ASC/DESC и SQL-оператор параметром заменить нельзя: для них нужен заранее разрешённый выбор.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE customers (name TEXT)');
$statement = $db->prepare('INSERT INTO customers (name) VALUES (?)');
$statement->bindValue(1, "O'Neil", SQLITE3_TEXT);
$statement->execute()->finalize();
echo $db->querySingle('SELECT name FROM customers'), "\n";
Ожидаемый результат
O'Neil
Следующий шаг и альтернативы: SQLite3Stmt::bindValue, SQLite3Stmt::execute, SQLite3Stmt::close.
SQLite3Stmt::bindValueЗафиксировать значение формы или массива для одного места подготовленного запроса.
SQLite3Stmt::bindValue(string|int $param, mixed $value, int $type = SQLITE3_TEXT): boolУвидеть момент чтения входного значения. Привязано текущее значение; позднее присваивание переменной не заменяет его. Тип SQLITE3_INTEGER выбран явно для номера.
- $stmt->bindValue(':id', $id, SQLITE3_INTEGER)
- Одна и та же смена 1 на 2 даёт разный результат из-за времени связывания. Это важно в цикле импорта, где одна переменная меняется много раз.
- После выполнения: Молоток — 8 шт.
В архиве catalog.php открывает отдельную учебную базу и выполняет schema.sql: 1 — Молоток, 8 шт.; 2 — Перчатки, 15 шт.; 3 — Кисть, 0 шт. База находится в памяти и начинает работу с этих данных при каждом запуске. Распакуйте все файлы вместе и запустите php example.php. Полная подготовка и схема.
| Запись в этом примере | Как читать |
|---|---|
$db->method(...) | Стрелка вызывает действие у конкретного соединения, команды или результата. Скобки содержат входные значения; результат можно сохранить через =. |
[...] | Квадратные скобки задают массив либо читают его элемент. Ключ имени колонки и номер позиции — разные способы адресации. |
<?php
require __DIR__ . '/catalog.php';
$stmt = $db->prepare('SELECT name, stock FROM products WHERE id = :id');
$id = 1;
$stmt->bindValue(':id', $id, SQLITE3_INTEGER);
$id = 2;
$result = $stmt->execute();
$row = $result->fetchArray(SQLITE3_ASSOC);
echo $row['name'], ' — ', $row['stock'], " шт.\n";
Ожидаемый результат
Молоток — 8 шт.
Одна и та же смена 1 на 2 даёт разный результат из-за времени связывания. Это важно в цикле импорта, где одна переменная меняется много раз.
Эти же строки в табличном виде. CLI-пример выше печатает текст; таблица помогает увидеть соответствие названия и остатка. Как получить такую таблицу настоящим HTML-обработчиком.
| Товар / строка | Наличие |
|---|---|
| Молоток | 8 шт. |
Если требование изменится. bindParam требует переменную, bindValue принимает готовое значение или выражение. Пропущенный параметр SQLite может стать NULL: проверяйте обязательные входные данные самостоятельно.
Полный контракт, ограничения и следующие варианты. Ниже сохранены параметры, возвращаемые значения, ошибки, версии и дополнительные задачи этой операции. Перед каждым более длинным примером сначала прочитайте его подготовку и ход действий.
Зафиксировать значение формы или массива для одного места подготовленного запроса.
| Вход / параметр | Смысл |
|---|---|
| param | Имя с : / @ (поддержка @ с PHP 7.4) либо позиция с 1; к имени без префикса добавляется :. |
| value / var | bindValue — готовое значение; bindParam — существующая PHP-переменная по ссылке. |
| type | INTEGER, FLOAT, TEXT, BLOB или NULL; при пропуске автоопределение с PHP 7.0.7. INTEGER — signed до 64 бит, FLOAT — IEEE 8 байт, TEXT — кодировка SQLite, BLOB — исходные байты. |
true — привязка принята; false/исключение — отказ. Тип SQLite при передаче не является проверкой бизнес-значения: количество -7 нужно запретить отдельно. До PHP 7.2.14/7.3.0 между выполнением и сменой привязок требовался reset; современный код всё равно завершает старый результат перед новым выполнением. NULL не означает пустую строку.
- $price=120
- bindValue копирует значение в привязку
- PHP $price=999
- SQL получает 120
Как читать этот вариант. bindValue сохраняет переданное значение 120. Позднейшая перемена $price не изменяет привязку. Это удобно при обходе входного массива: значение каждой ячейки фиксируется сразу.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$price = 120;
$stmt = $db->prepare('SELECT :price AS price');
$stmt->bindValue(':price', $price, SQLITE3_INTEGER);
$price = 999;
echo $stmt->execute()->fetchArray(SQLITE3_ASSOC)['price'], "\n";
Ожидаемый результат
120
Как читать этот вариант. При пропущенном type PHP 7.0.7+ определяет int/bool→INTEGER, float→FLOAT, null→NULL, остальное→TEXT. Явный BLOB нужен для бинарных байтов; null всегда NULL, даже с INTEGER. Документированный default TEXT в сигнатуре не отменяет автоопределение при пропуске аргумента.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$stmt = $db->prepare('SELECT typeof(:count), typeof(:ratio), typeof(:missing), length(:bytes)');
$stmt->bindValue(':count', 3);
$stmt->bindValue(':ratio', 1.5);
$stmt->bindValue(':missing', null, SQLITE3_INTEGER);
$stmt->bindValue(':bytes', "A\0B", SQLITE3_BLOB);
echo implode('|', $stmt->execute()->fetchArray(SQLITE3_NUM)), "\n";
Ожидаемый результат
integer|real|null|3
Следующий шаг и альтернативы: SQLite3Stmt::bindParam, SQLite3Stmt::clear, SQLite3Stmt::execute.
SQLite3Stmt::bindParamМного раз исполнить одну команду, меняя значение заранее привязанной переменной.
SQLite3Stmt::bindParam(string|int $param, mixed &$var, int $type = SQLITE3_TEXT): boolУвидеть момент чтения входного значения. Привязана переменная; execute возьмёт её значение на момент запуска. Тип SQLITE3_INTEGER выбран явно для номера.
- $stmt->bindParam(':id', $id, SQLITE3_INTEGER)
- Одна и та же смена 1 на 2 даёт разный результат из-за времени связывания. Это важно в цикле импорта, где одна переменная меняется много раз.
- После выполнения: Перчатки — 15 шт.
В архиве catalog.php открывает отдельную учебную базу и выполняет schema.sql: 1 — Молоток, 8 шт.; 2 — Перчатки, 15 шт.; 3 — Кисть, 0 шт. База находится в памяти и начинает работу с этих данных при каждом запуске. Распакуйте все файлы вместе и запустите php example.php. Полная подготовка и схема.
| Запись в этом примере | Как читать |
|---|---|
$db->method(...) | Стрелка вызывает действие у конкретного соединения, команды или результата. Скобки содержат входные значения; результат можно сохранить через =. |
[...] | Квадратные скобки задают массив либо читают его элемент. Ключ имени колонки и номер позиции — разные способы адресации. |
<?php
require __DIR__ . '/catalog.php';
$stmt = $db->prepare('SELECT name, stock FROM products WHERE id = :id');
$id = 1;
$stmt->bindParam(':id', $id, SQLITE3_INTEGER);
$id = 2;
$result = $stmt->execute();
$row = $result->fetchArray(SQLITE3_ASSOC);
echo $row['name'], ' — ', $row['stock'], " шт.\n";
Ожидаемый результат
Перчатки — 15 шт.
Одна и та же смена 1 на 2 даёт разный результат из-за времени связывания. Это важно в цикле импорта, где одна переменная меняется много раз.
Эти же строки в табличном виде. CLI-пример выше печатает текст; таблица помогает увидеть соответствие названия и остатка. Как получить такую таблицу настоящим HTML-обработчиком.
| Товар / строка | Наличие |
|---|---|
| Перчатки | 15 шт. |
Если требование изменится. bindParam требует переменную, bindValue принимает готовое значение или выражение. Пропущенный параметр SQLite может стать NULL: проверяйте обязательные входные данные самостоятельно.
Полный контракт, ограничения и следующие варианты. Ниже сохранены параметры, возвращаемые значения, ошибки, версии и дополнительные задачи этой операции. Перед каждым более длинным примером сначала прочитайте его подготовку и ход действий.
Много раз исполнить одну команду, меняя значение заранее привязанной переменной.
| Вход / параметр | Смысл |
|---|---|
| param | Имя с : / @ (поддержка @ с PHP 7.4) либо позиция с 1; к имени без префикса добавляется :. |
| value / var | bindValue — готовое значение; bindParam — существующая PHP-переменная по ссылке. |
| type | INTEGER, FLOAT, TEXT, BLOB или NULL; при пропуске автоопределение с PHP 7.0.7. INTEGER — signed до 64 бит, FLOAT — IEEE 8 байт, TEXT — кодировка SQLite, BLOB — исходные байты. |
true — связь создана, false/исключение — ошибка. Амперсанд & в сигнатуре означает передачу переменной по ссылке, но в месте вызова его не пишут. Самостоятельной валидации входа и SQL-идентификаторов здесь нет. Старым PHP до 7.2.14/7.3.0 требовался reset для обновления значения между выполнениями.
- $name связано с :name
- Delivery→execute→одна строка
- Pickup→execute→вторая строка
- commit сохраняет обе
Как читать этот вариант. bindParam связывает маркер с переменной, а не с её старым содержимым. При execute читается 999. Литерал 120 или выражение $price+1 нельзя передать по ссылке: для них нужен bindValue.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$price = 120;
$stmt = $db->prepare('SELECT :price AS price');
$stmt->bindParam(':price', $price, SQLITE3_INTEGER);
$price = 999;
echo $stmt->execute()->fetchArray(SQLITE3_ASSOC)['price'], "\n";
Ожидаемый результат
999
Как читать этот вариант. Связь одна, $name меняется на каждом шаге foreach. Для разных маркеров нельзя в цикле bindParam каждый раз использовать одну и ту же $value: все ссылки будут читать её последнее значение. В таком обходе обычно нужен bindValue.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE labels (name TEXT)');
$stmt = $db->prepare('INSERT INTO labels VALUES (:name)');
$name = '';
$stmt->bindParam(':name', $name, SQLITE3_TEXT);
$db->exec('BEGIN');
try {
foreach (['Delivery', 'Pickup'] as $name) { $stmt->execute()->finalize(); }
$db->exec('COMMIT');
} catch (Exception $error) { $db->exec('ROLLBACK'); throw $error; }
echo $db->querySingle('SELECT COUNT(*) FROM labels'), "\n";
Ожидаемый результат
2
Следующий шаг и альтернативы: SQLite3Stmt::bindValue, SQLite3Stmt::reset, SQLite3Stmt::execute.
SQLite3Stmt::executeЗапустить подготовленную команду после передачи значений.
SQLite3Stmt::execute(): SQLite3Result|falseЗапустить фильтр и прочитать найденные строки. execute использует уже связанные значения. SQLite3Result не содержит «готовую HTML-таблицу»: строки ещё нужно читать и выводить.
- $stmt->execute()
- Фильтр оставляет товары с остатком не меньше 8. Конец ответа определяется fetchArray === false, а не остатком последнего товара.
- После выполнения: Молоток — 8 шт. · Перчатки — 15 шт.
В архиве catalog.php открывает отдельную учебную базу и выполняет schema.sql: 1 — Молоток, 8 шт.; 2 — Перчатки, 15 шт.; 3 — Кисть, 0 шт. База находится в памяти и начинает работу с этих данных при каждом запуске. Распакуйте все файлы вместе и запустите php example.php. Полная подготовка и схема.
| Запись в этом примере | Как читать |
|---|---|
$db->method(...) | Стрелка вызывает действие у конкретного соединения, команды или результата. Скобки содержат входные значения; результат можно сохранить через =. |
while (...) | Повторяет блок, пока условие истинно. В чтении результата проверка конца связана с конкретным методом: false или null указаны в его контракте. |
[...] | Квадратные скобки задают массив либо читают его элемент. Ключ имени колонки и номер позиции — разные способы адресации. |
<?php
require __DIR__ . '/catalog.php';
$stmt = $db->prepare('SELECT name, stock FROM products WHERE stock >= :minimum ORDER BY id');
$stmt->bindValue(':minimum', 8, SQLITE3_INTEGER);
$result = $stmt->execute();
while (($row = $result->fetchArray(SQLITE3_ASSOC)) !== false) {
echo $row['name'], ' — ', $row['stock'], " шт.\n";
}
$result->finalize();
Ожидаемый результат
Молоток — 8 шт.
Перчатки — 15 шт.
Фильтр оставляет товары с остатком не меньше 8. Конец ответа определяется fetchArray === false, а не остатком последнего товара.
Эти же строки в табличном виде. CLI-пример выше печатает текст; таблица помогает увидеть соответствие названия и остатка. Как получить такую таблицу настоящим HTML-обработчиком.
| Товар / строка | Наличие |
|---|---|
| Молоток | 8 шт. |
| Перчатки | 15 шт. |
Если требование изменится. Перед повторным execute завершите прежний result. Даже команда INSERT может дать объект результата без строк: не пытайтесь прочитать его как SELECT.
Полный контракт, ограничения и следующие варианты. Ниже сохранены параметры, возвращаемые значения, ошибки, версии и дополнительные задачи этой операции. Перед каждым более длинным примером сначала прочитайте его подготовку и ход действий.
Запустить подготовленную команду после передачи значений.
| Вход / параметр | Смысл |
|---|---|
| Аргументов нет | Массив данных, привычный по PDO, здесь недопустим. Используются привязки statement. |
Успех возвращает SQLite3Result, в том числе для INSERT без строк. Такой результат просто finalize, не fetchArray «для проверки». Ошибка без исключений возвращает false. Один statement нельзя одновременно читать через несколько результатов: finalize перед повторным execute. В примерах enableExceptions(true) задан явно: ошибка SQL прерывает обычный путь исключением. В PHP 8.3+ используется SQLite3Exception (наследник Exception), раньше Exception; при устаревшем режиме warnings метод может вернуть false. Ошибки типов, аргументов и закрытого объекта могут быть TypeError, ValueError или Error. Сообщение базы сохраняют в закрытом журнале, посетителю дают понятный ответ без SQL и путей сервера.
- prepare задаёт фильтр
- bindValue limit=250
- execute запускает
- fetch возвращает Pen и Box
Как читать этот вариант. Метод без аргументов использует сделанные привязки. SQLite3Result — поток результата данной команды, а не независимая копия таблицы.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT NOT NULL, price INTEGER NOT NULL)');
$db->exec("INSERT INTO products VALUES (1, 'Pen', 120), (2, 'Notebook', 350), (3, 'Box', 200)");
$stmt = $db->prepare('SELECT name FROM products WHERE price < :limit ORDER BY id');
$stmt->bindValue(':limit', 250, SQLITE3_INTEGER);
$result = $stmt->execute();
while (($row = $result->fetchArray(SQLITE3_ASSOC)) !== false) { echo $row['name'], "\n"; }
$result->finalize();
Ожидаемый результат
Pen
Box
Как читать этот вариант. Результаты разных execute одного statement разделяют внутреннюю структуру. Поэтому сначала finalize, затем новая привязка/execute. Не храните старые SQLite3Result в массиве как якобы независимые ответы.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT NOT NULL, price INTEGER NOT NULL)');
$db->exec("INSERT INTO products VALUES (1, 'Pen', 120), (2, 'Notebook', 350), (3, 'Box', 200)");
$stmt = $db->prepare('SELECT name FROM products WHERE id = ?');
foreach ([1, 3] as $id) {
$stmt->bindValue(1, $id, SQLITE3_INTEGER);
$result = $stmt->execute();
echo $result->fetchArray(SQLITE3_ASSOC)['name'], "\n";
$result->finalize();
}
$stmt->close();
Ожидаемый результат
Pen
Box
Следующий шаг и альтернативы: SQLite3::prepare, SQLite3Stmt::bindValue, SQLite3Result::finalize.
SQLite3Stmt::resetПодготовить команду к повторному выполнению, сохранив её SQL и параметры.
SQLite3Stmt::reset(): boolПовторить прежний поиск с сохранённым параметром. reset возвращает statement к запуску, сохраняя SQL и привязки. Он не откатывает уже сделанные изменения таблицы.
- $stmt->reset()
- После reset параметр 2 остаётся; поэтому повторный запуск находит тот же товар. Если надо заменить параметры, их связывают заново.
- После выполнения: Перчатки — 15 шт. · Перчатки — 15 шт.
В архиве catalog.php открывает отдельную учебную базу и выполняет schema.sql: 1 — Молоток, 8 шт.; 2 — Перчатки, 15 шт.; 3 — Кисть, 0 шт. База находится в памяти и начинает работу с этих данных при каждом запуске. Распакуйте все файлы вместе и запустите php example.php. Полная подготовка и схема.
| Запись в этом примере | Как читать |
|---|---|
$db->method(...) | Стрелка вызывает действие у конкретного соединения, команды или результата. Скобки содержат входные значения; результат можно сохранить через =. |
[...] | Квадратные скобки задают массив либо читают его элемент. Ключ имени колонки и номер позиции — разные способы адресации. |
<?php
require __DIR__ . '/catalog.php';
$stmt = $db->prepare('SELECT name, stock FROM products WHERE id = :id');
$stmt->bindValue(':id', 2, SQLITE3_INTEGER);
$result = $stmt->execute();
$row = $result->fetchArray(SQLITE3_ASSOC);
echo $row['name'], ' — ', $row['stock'], " шт.\n";
$result->finalize();
$stmt->reset();
$result = $stmt->execute();
$row = $result->fetchArray(SQLITE3_ASSOC);
echo $row['name'], ' — ', $row['stock'], " шт.\n";
Ожидаемый результат
Перчатки — 15 шт.
Перчатки — 15 шт.
После reset параметр 2 остаётся; поэтому повторный запуск находит тот же товар. Если надо заменить параметры, их связывают заново.
Эти же строки в табличном виде. CLI-пример выше печатает текст; таблица помогает увидеть соответствие названия и остатка. Как получить такую таблицу настоящим HTML-обработчиком.
| Товар / строка | Наличие |
|---|---|
| Перчатки | 15 шт. |
| Перчатки | 15 шт. |
Если требование изменится. Не путайте reset, clear и rollback: первый сбрасывает выполнение, второй удаляет привязки, третий отменяет транзакцию. Повтор DML может изменить данные ещё раз.
Полный контракт, ограничения и следующие варианты. Ниже сохранены параметры, возвращаемые значения, ошибки, версии и дополнительные задачи этой операции. Перед каждым более длинным примером сначала прочитайте его подготовку и ход действий.
Подготовить команду к повторному выполнению, сохранив её SQL и параметры.
| Вход / параметр | Смысл |
|---|---|
| Аргументов нет | Действует на один statement. |
true при успехе, false/исключение при отказе. Привязки остаются, удаления данных не происходит. Это состояние выполнения, а не откат транзакции. Если предыдущий шаг завершился ошибкой SQLite, reset также может сообщить её.
- statement с :id=1
- reset прекращает текущее выполнение
- привязка 1 остаётся
- execute снова читает Pen
Как читать этот вариант. reset возвращает подготовленную команду к состоянию перед выполнением, сохраняя привязку :id=1. Это не rollback, не удаление строк и не clear.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT NOT NULL, price INTEGER NOT NULL)');
$db->exec("INSERT INTO products VALUES (1, 'Pen', 120), (2, 'Notebook', 350), (3, 'Box', 200)");
$stmt = $db->prepare('SELECT name FROM products WHERE id = :id');
$stmt->bindValue(':id', 1, SQLITE3_INTEGER);
$result = $stmt->execute();
echo $result->fetchArray(SQLITE3_ASSOC)['name'], "\n";
$result->finalize();
$stmt->reset();
$result = $stmt->execute();
echo $result->fetchArray(SQLITE3_ASSOC)['name'], "\n";
$result->finalize();
Ожидаемый результат
Pen
Pen
Как читать этот вариант. Старый курсор завершён до нового диапазона. Возврат к началу SQL-команды может повторить работу и побочные действия; не используйте reset DML как безобидную перемотку массива.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT NOT NULL, price INTEGER NOT NULL)');
$db->exec("INSERT INTO products VALUES (1, 'Pen', 120), (2, 'Notebook', 350), (3, 'Box', 200)");
$stmt = $db->prepare('SELECT name FROM products WHERE id >= :start ORDER BY id');
$stmt->bindValue(':start', 1);
$result = $stmt->execute();
echo $result->fetchArray(SQLITE3_ASSOC)['name'], "\n";
$result->finalize();
$stmt->reset();
$stmt->bindValue(':start', 3);
$result = $stmt->execute();
echo $result->fetchArray(SQLITE3_ASSOC)['name'], "\n";
$result->finalize();
Ожидаемый результат
Pen
Box
Следующий шаг и альтернативы: SQLite3Stmt::clear, SQLite3Result::reset, SQLite3Stmt::execute.
SQLite3Stmt::clearУбрать все старые значения перед новым набором параметров.
SQLite3Stmt::clear(): boolНе перенести номер прошлого поиска в следующий. clear удаляет связанные значения; SQL-шаблон остаётся. Обязательный новый параметр надо заполнить перед execute.
- $stmt->clear()
- После завершения первого ответа очищаем прежнее значение 1 и задаём 3. Второй поиск не наследует старое состояние.
- После выполнения: Кисть — 0 шт.
В архиве catalog.php открывает отдельную учебную базу и выполняет schema.sql: 1 — Молоток, 8 шт.; 2 — Перчатки, 15 шт.; 3 — Кисть, 0 шт. База находится в памяти и начинает работу с этих данных при каждом запуске. Распакуйте все файлы вместе и запустите php example.php. Полная подготовка и схема.
| Запись в этом примере | Как читать |
|---|---|
$db->method(...) | Стрелка вызывает действие у конкретного соединения, команды или результата. Скобки содержат входные значения; результат можно сохранить через =. |
[...] | Квадратные скобки задают массив либо читают его элемент. Ключ имени колонки и номер позиции — разные способы адресации. |
<?php
require __DIR__ . '/catalog.php';
$stmt = $db->prepare('SELECT name, stock FROM products WHERE id = :id');
$stmt->bindValue(':id', 1, SQLITE3_INTEGER);
$stmt->execute()->finalize();
$stmt->reset();
$stmt->clear();
$stmt->bindValue(':id', 3, SQLITE3_INTEGER);
$result = $stmt->execute();
$row = $result->fetchArray(SQLITE3_ASSOC);
echo $row['name'], ' — ', $row['stock'], " шт.\n";
Ожидаемый результат
Кисть — 0 шт.
После завершения первого ответа очищаем прежнее значение 1 и задаём 3. Второй поиск не наследует старое состояние.
Эти же строки в табличном виде. CLI-пример выше печатает текст; таблица помогает увидеть соответствие названия и остатка. Как получить такую таблицу настоящим HTML-обработчиком.
| Товар / строка | Наличие |
|---|---|
| Кисть | 0 шт. |
Если требование изменится. Без новой привязки маркер может оказаться SQL NULL. Это не автоматическая ошибка «забыли поле»; приложение должно перечислить и проверить обязательные параметры.
Полный контракт, ограничения и следующие варианты. Ниже сохранены параметры, возвращаемые значения, ошибки, версии и дополнительные задачи этой операции. Перед каждым более длинным примером сначала прочитайте его подготовку и ход действий.
Убрать все старые значения перед новым набором параметров.
| Вход / параметр | Смысл |
|---|---|
| Аргументов нет | Очищаются все bindings, не только один. |
true — очистка успешна, false/исключение — ошибка. Все места становятся NULL. Используйте вместе с reset: manual предупреждает, что отдельный clear может оставить новые bindValue/bindParam без эффекта. SQL-шаблон и строки базы не удаляются.
- старые city=Moscow/limit=5
- finalize→reset→clear
- новые city=Tula/limit=2
- новый результат Tula:2
Как читать этот вариант. После reset+clear прежнее 7 больше не передаётся; незаполненное место имеет SQL NULL. Пропущенный обязательный параметр надо проверять в приложении, не принимать NULL за успех.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$stmt = $db->prepare('SELECT :value IS NULL AS missing');
$stmt->bindValue(':value', 7);
$stmt->execute()->finalize();
$stmt->reset();
$stmt->clear();
$result = $stmt->execute();
echo $result->fetchArray(SQLITE3_ASSOC)['missing'], "\n";
$result->finalize();
Ожидаемый результат
1
Как читать этот вариант. Полный набор новых параметров предотвращает тихое использование остатка старого запроса. По контракту clear применяют вместе с reset; один clear без сброса состояния может помешать новым привязкам.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$stmt = $db->prepare('SELECT :city AS city, :limit AS amount');
$stmt->bindValue(':city', 'Moscow');
$stmt->bindValue(':limit', 5);
$stmt->execute()->finalize();
$stmt->reset();
$stmt->clear();
$stmt->bindValue(':city', 'Tula');
$stmt->bindValue(':limit', 2);
$row = $stmt->execute()->fetchArray(SQLITE3_ASSOC);
echo $row['city'], ':', $row['amount'], "\n";
Ожидаемый результат
Tula:2
Следующий шаг и альтернативы: SQLite3Stmt::reset, SQLite3Stmt::bindValue.
SQLite3Stmt::paramCountПроверить устройство подготовленного запроса при построении обёртки.
SQLite3Stmt::paramCount(): intПроверить форму шаблона перед связыванием. Число относится к слотам параметров statement. Для обычных последовательных ? здесь их два: номер и минимальный остаток.
- $stmt->paramCount()
- Позиции привязки начинаются с 1. Пять — нижняя граница остатка; Молоток с 8 проходит оба условия.
- После выполнения: Молоток — 8 шт.
В архиве catalog.php открывает отдельную учебную базу и выполняет schema.sql: 1 — Молоток, 8 шт.; 2 — Перчатки, 15 шт.; 3 — Кисть, 0 шт. База находится в памяти и начинает работу с этих данных при каждом запуске. Распакуйте все файлы вместе и запустите php example.php. Полная подготовка и схема.
| Запись в этом примере | Как читать |
|---|---|
$db->method(...) | Стрелка вызывает действие у конкретного соединения, команды или результата. Скобки содержат входные значения; результат можно сохранить через =. |
[...] | Квадратные скобки задают массив либо читают его элемент. Ключ имени колонки и номер позиции — разные способы адресации. |
<?php
require __DIR__ . '/catalog.php';
$stmt = $db->prepare('SELECT name, stock FROM products WHERE id = ? AND stock >= ?');
if ($stmt->paramCount() !== 2) { throw new RuntimeException('Изменился шаблон фильтра'); }
$stmt->bindValue(1, 1, SQLITE3_INTEGER);
$stmt->bindValue(2, 5, SQLITE3_INTEGER);
$result = $stmt->execute();
$row = $result->fetchArray(SQLITE3_ASSOC);
echo $row['name'], ' — ', $row['stock'], " шт.\n";
Ожидаемый результат
Молоток — 8 шт.
Позиции привязки начинаются с 1. Пять — нижняя граница остатка; Молоток с 8 проходит оба условия.
Эти же строки в табличном виде. CLI-пример выше печатает текст; таблица помогает увидеть соответствие названия и остатка. Как получить такую таблицу настоящим HTML-обработчиком.
| Товар / строка | Наличие |
|---|---|
| Молоток | 8 шт. |
Если требование изменится. Нумерованные ?NNN и повторяющиеся именованные маркеры имеют свои правила индексов. paramCount не проверяет качество самих данных и не заменяет валидацию.
Полный контракт, ограничения и следующие варианты. Ниже сохранены параметры, возвращаемые значения, ошибки, версии и дополнительные задачи этой операции. Перед каждым более длинным примером сначала прочитайте его подготовку и ход действий.
Проверить устройство подготовленного запроса при построении обёртки.
| Вход / параметр | Смысл |
|---|---|
| Аргументов нет | Возвращается число слотов параметров команды. |
Возвращает int, 0 если маркеров нет. Для ?NNN значение соответствует максимальному индексу, повторное именованное место использует один слот. Подсчёт ? регулярным выражением ненадёжен: кавычки, комментарии и именованные параметры меняют смысл.
- SELECT :min,:max,:min
- два уникальных слота
- paramCount→2
Как читать этот вариант. Повторное имя :min обозначает один параметр. Количество вхождений в тексте SQL и количество binding slots различаются.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$stmt = $db->prepare('SELECT :min, :max, :min');
echo $stmt->paramCount(), "\n";
Ожидаемый результат
2
Как читать этот вариант. SQLite нумерует слоты до самого большого индекса; ?5 даёт 5, даже если один маркер виден в SQL. Для универсального binder проще последовательные ? либо явные уникальные имена.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$stmt = $db->prepare('SELECT ?5');
echo $stmt->paramCount(), "\n";
$stmt->bindValue(5, 42);
echo $stmt->execute()->fetchArray(SQLITE3_NUM)[0], "\n";
Ожидаемый результат
5
42
Следующий шаг и альтернативы: SQLite3Stmt::bindValue, SQLite3Stmt::getSQL.
SQLite3Stmt::getSQLПонять, какой шаблон выполняет обёртка, и найти забытое значение.
SQLite3Stmt::getSQL(bool $expand = false): string|falseРазделить шаблон SQL и наблюдаемые данные. Без expanded метод возвращает исходный текст команды. Расширенная форма может показать значения параметров; её оставляют для закрытой локальной диагностики.
- $stmt->getSQL(false)
- В шаблоне видно :id, а в результате — найденные Перчатки. SQL-текст не является результатом SELECT.
- После выполнения: Шаблон: SELECT name, stock FROM products WHERE id = :id · Перчатки — 15 шт.
В архиве catalog.php открывает отдельную учебную базу и выполняет schema.sql: 1 — Молоток, 8 шт.; 2 — Перчатки, 15 шт.; 3 — Кисть, 0 шт. База находится в памяти и начинает работу с этих данных при каждом запуске. Распакуйте все файлы вместе и запустите php example.php. Полная подготовка и схема.
| Запись в этом примере | Как читать |
|---|---|
$db->method(...) | Стрелка вызывает действие у конкретного соединения, команды или результата. Скобки содержат входные значения; результат можно сохранить через =. |
[...] | Квадратные скобки задают массив либо читают его элемент. Ключ имени колонки и номер позиции — разные способы адресации. |
<?php
require __DIR__ . '/catalog.php';
$stmt = $db->prepare('SELECT name, stock FROM products WHERE id = :id');
$stmt->bindValue(':id', 2, SQLITE3_INTEGER);
echo 'Шаблон: ', $stmt->getSQL(false), "\n";
$result = $stmt->execute();
$row = $result->fetchArray(SQLITE3_ASSOC);
echo $row['name'], ' — ', $row['stock'], " шт.\n";
Ожидаемый результат
Шаблон: SELECT name, stock FROM products WHERE id = :id
Перчатки — 15 шт.
В шаблоне видно :id, а в результате — найденные Перчатки. SQL-текст не является результатом SELECT.
Эти же строки в табличном виде. CLI-пример выше печатает текст; таблица помогает увидеть соответствие названия и остатка. Как получить такую таблицу настоящим HTML-обработчиком.
| Товар / строка | Наличие |
|---|---|
| Перчатки | 15 шт. |
Если требование изменится. Не публикуйте expanded SQL с личными данными. getSQL не предназначен для получения безопасной строки, которую затем склеят с другой пользовательской SQL-командой.
Полный контракт, ограничения и следующие варианты. Ниже сохранены параметры, возвращаемые значения, ошибки, версии и дополнительные задачи этой операции. Перед каждым более длинным примером сначала прочитайте его подготовку и ход действий.
Понять, какой шаблон выполняет обёртка, и найти забытое значение.
| Вход / параметр | Смысл |
|---|---|
| expand | false — исходный SQL; true — заменить маркеры привязанными значениями или NULL. |
Возвращает строку либо false/исключение при ошибке. expand=true требует SQLite 3.14+; без этой поддержки warning/исключение. getSQL может привязать текущее значение bindParam для представления; изменение переменной к execute ещё может изменить исполняемые данные. Не использовать вывод как секретобезопасный журнал.
- SQL :count/:label
- count=3, label не связан
- expand→SELECT 3, NULL
Как читать этот вариант. false оставляет маркеры, true показывает SQL-представление bindings. Расширенный текст может содержать email, токены и бинарные данные — его нельзя бездумно писать в общий лог.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$stmt = $db->prepare('SELECT :name AS name');
$stmt->bindValue(':name', 'Pen');
echo $stmt->getSQL(), "\n";
echo $stmt->getSQL(true), "\n";
Ожидаемый результат
SELECT :name AS name
SELECT 'Pen' AS name
Как читать этот вариант. Незаполненный :label отображается как NULL. Это диагностическое представление, не предложение повторно исполнять склеенную строку вместо безопасных bindings.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$stmt = $db->prepare('SELECT :count, :label');
$stmt->bindValue(':count', 3, SQLITE3_INTEGER);
echo $stmt->getSQL(true), "\n";
Ожидаемый результат
SELECT 3, NULL
Следующий шаг и альтернативы: SQLite3Stmt::bindParam, SQLite3Stmt::paramCount.
SQLite3Stmt::readOnlyКлассифицировать подготовленную команду для диагностики или маршрутизации.
SQLite3Stmt::readOnly(): boolПроверить назначение подготовленной команды. readOnly описывает, может ли statement непосредственно менять файл базы. Это полезная диагностика известного отчёта, но не разрешение запускать произвольный SQL посетителя.
- $stmt->readOnly()
- Перед запуском проверяем ожидание: это чтение каталога. Затем выполняем и выводим товар.
- После выполнения: Молоток — 8 шт.
В архиве catalog.php открывает отдельную учебную базу и выполняет schema.sql: 1 — Молоток, 8 шт.; 2 — Перчатки, 15 шт.; 3 — Кисть, 0 шт. База находится в памяти и начинает работу с этих данных при каждом запуске. Распакуйте все файлы вместе и запустите php example.php. Полная подготовка и схема.
| Запись в этом примере | Как читать |
|---|---|
$db->method(...) | Стрелка вызывает действие у конкретного соединения, команды или результата. Скобки содержат входные значения; результат можно сохранить через =. |
[...] | Квадратные скобки задают массив либо читают его элемент. Ключ имени колонки и номер позиции — разные способы адресации. |
<?php
require __DIR__ . '/catalog.php';
$stmt = $db->prepare('SELECT name, stock FROM products WHERE id = 1');
if (!$stmt->readOnly()) { throw new RuntimeException('Отчёт не должен записывать данные'); }
$result = $stmt->execute();
$row = $result->fetchArray(SQLITE3_ASSOC);
echo $row['name'], ' — ', $row['stock'], " шт.\n";
Ожидаемый результат
Молоток — 8 шт.
Перед запуском проверяем ожидание: это чтение каталога. Затем выполняем и выводим товар.
Эти же строки в табличном виде. CLI-пример выше печатает текст; таблица помогает увидеть соответствие названия и остатка. Как получить такую таблицу настоящим HTML-обработчиком.
| Товар / строка | Наличие |
|---|---|
| Молоток | 8 шт. |
Если требование изменится. SELECT может вызывать пользовательские функции с внешними побочными эффектами. readOnly не ограничивает CPU, файловые операции PHP callback и доступ к чувствительным данным.
Полный контракт, ограничения и следующие варианты. Ниже сохранены параметры, возвращаемые значения, ошибки, версии и дополнительные задачи этой операции. Перед каждым более длинным примером сначала прочитайте его подготовку и ход действий.
Классифицировать подготовленную команду для диагностики или маршрутизации.
| Вход / параметр | Смысл |
|---|---|
| Аргументов нет | Проверяется уже подготовленная SQL-команда. |
true означает отсутствие прямых изменений файла; false — такой гарантии нет. Пользовательские SQL-функции могут менять что-то косвенно. PRAGMA, транзакционные команды, временные объекты имеют нюансы SQLite, поэтому readOnly не заменяет READONLY-соединение, authorizer и права файлов.
- SELECT mark_call()
- readOnly=true
- SQL вызывает PHP
- PHP меняет $calls
Как читать этот вариант. readOnly относится к прямому изменению файла этой SQL-командой, а не к настройке всего соединения.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT NOT NULL, price INTEGER NOT NULL)');
$db->exec("INSERT INTO products VALUES (1, 'Pen', 120), (2, 'Notebook', 350), (3, 'Box', 200)");
var_dump($db->prepare('SELECT name FROM products')->readOnly());
var_dump($db->prepare('UPDATE products SET price = price + 1')->readOnly());
Ожидаемый результат
bool(true)
bool(false)
Как читать этот вариант. Функция изменила PHP-переменную, хотя SQL не пишет файл. Поэтому readOnly не является изоляцией произвольного SQL от внешних эффектов callback.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$calls = 0;
$db->createFunction('mark_call', function () use (&$calls) { ++$calls; return 1; }, 0);
$stmt = $db->prepare('SELECT mark_call()');
var_dump($stmt->readOnly());
$stmt->execute()->finalize();
echo $calls > 0 ? "PHP callback был вызван\n" : "Нет вызова\n";
Ожидаемый результат
bool(true)
PHP callback был вызван
Следующий шаг и альтернативы: SQLite3::setAuthorizer, SQLite3::createFunction, SQLite3::open.
SQLite3Stmt::closeУбрать больше не нужную подготовленную команду из долгого процесса.
SQLite3Stmt::close(): trueОсвободить поиск после чтения выбранного товара. close уничтожает ресурсы подготовленной команды. До этого надо завершить её активный результат; дальнейший execute закрытого объекта недопустим.
- $stmt->close()
- Данные сохранены в $row, поэтому их можно вывести после освобождения поиска. Соединение $db при этом ещё открыто.
- После выполнения: Молоток — 8 шт.
В архиве catalog.php открывает отдельную учебную базу и выполняет schema.sql: 1 — Молоток, 8 шт.; 2 — Перчатки, 15 шт.; 3 — Кисть, 0 шт. База находится в памяти и начинает работу с этих данных при каждом запуске. Распакуйте все файлы вместе и запустите php example.php. Полная подготовка и схема.
| Запись в этом примере | Как читать |
|---|---|
$db->method(...) | Стрелка вызывает действие у конкретного соединения, команды или результата. Скобки содержат входные значения; результат можно сохранить через =. |
[...] | Квадратные скобки задают массив либо читают его элемент. Ключ имени колонки и номер позиции — разные способы адресации. |
<?php
require __DIR__ . '/catalog.php';
$stmt = $db->prepare('SELECT name, stock FROM products WHERE id = 1');
$result = $stmt->execute();
$row = $result->fetchArray(SQLITE3_ASSOC);
$result->finalize();
$stmt->close();
echo $row['name'], ' — ', $row['stock'], " шт.\n";
Ожидаемый результат
Молоток — 8 шт.
Данные сохранены в $row, поэтому их можно вывести после освобождения поиска. Соединение $db при этом ещё открыто.
Эти же строки в табличном виде. CLI-пример выше печатает текст; таблица помогает увидеть соответствие названия и остатка. Как получить такую таблицу настоящим HTML-обработчиком.
| Товар / строка | Наличие |
|---|---|
| Молоток | 8 шт. |
Если требование изменится. Не закрывайте statement в цикле, если хотите повторять один шаблон. Но не оставляйте ненужные долгоживущие statements и результаты в сервисе.
Полный контракт, ограничения и следующие варианты. Ниже сохранены параметры, возвращаемые значения, ошибки, версии и дополнительные задачи этой операции. Перед каждым более длинным примером сначала прочитайте его подготовку и ход действий.
Убрать больше не нужную подготовленную команду из долгого процесса.
| Вход / параметр | Смысл |
|---|---|
| Аргументов нет | Освобождается statement перед стрелкой. |
Современная сигнатура возвращает true при нормальном завершении. Результаты этой команды больше использовать нельзя. close не является commit; транзакцию завершают отдельно. Для повторного выполнения существующего statement нужен reset, не close.
- execute→result
- finalize
- close
- для новой команды снова prepare
Как читать этот вариант. finalize заканчивает результат, close окончательно освобождает prepared statement; повторный execute после close не разрешён.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT NOT NULL, price INTEGER NOT NULL)');
$db->exec("INSERT INTO products VALUES (1, 'Pen', 120), (2, 'Notebook', 350), (3, 'Box', 200)");
$stmt = $db->prepare('SELECT COUNT(*) FROM products');
$result = $stmt->execute();
echo $result->fetchArray(SQLITE3_NUM)[0], "\n";
$result->finalize();
var_dump($stmt->close());
Ожидаемый результат
3
bool(true)
Как читать этот вариант. Хранить объект в переменной можно и после закрытия, но это не делает его работоспособным. Если нужен новый запрос, снова prepare.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$stmt = $db->prepare('SELECT 1');
$stmt->close();
try { $stmt->execute(); }
catch (Error $error) { echo "Команда закрыта\n"; }
Ожидаемый результат
Команда закрыта
Следующий шаг и альтернативы: SQLite3Stmt::reset, SQLite3Result::finalize, SQLite3::close.
SQLite3Stmt::busyНайти statement, который ещё держит незаконченный просмотр.
SQLite3Stmt::busy(): boolНе принять незавершённый ответ за свободный statement. PHP 8.5+: busy сообщает, есть ли начатое выполнение, ещё не доведённое до конца или reset. Это состояние команды, а не блокировка всей базы другим процессом.
- $stmt->busy()
- Мы прочитали только первый товар из трёх и осознанно освобождаем остаток ответа. Для locked другой базы предназначены обработка ошибки и busyTimeout.
- После выполнения: Молоток — 8 шт. · В ответе ещё могут быть строки; завершаем просмотр
В архиве catalog.php открывает отдельную учебную базу и выполняет schema.sql: 1 — Молоток, 8 шт.; 2 — Перчатки, 15 шт.; 3 — Кисть, 0 шт. База находится в памяти и начинает работу с этих данных при каждом запуске. Распакуйте все файлы вместе и запустите php example.php. Полная подготовка и схема.
| Запись в этом примере | Как читать |
|---|---|
$db->method(...) | Стрелка вызывает действие у конкретного соединения, команды или результата. Скобки содержат входные значения; результат можно сохранить через =. |
[...] | Квадратные скобки задают массив либо читают его элемент. Ключ имени колонки и номер позиции — разные способы адресации. |
<?php
require __DIR__ . '/catalog.php';
$stmt = $db->prepare('SELECT name, stock FROM products ORDER BY id');
$result = $stmt->execute();
$row = $result->fetchArray(SQLITE3_ASSOC);
echo $row['name'], ' — ', $row['stock'], " шт.\n";
if ($stmt->busy()) { echo "В ответе ещё могут быть строки; завершаем просмотр\n"; }
$result->finalize();
Ожидаемый результат
Молоток — 8 шт.
В ответе ещё могут быть строки; завершаем просмотр
Мы прочитали только первый товар из трёх и осознанно освобождаем остаток ответа. Для locked другой базы предназначены обработка ошибки и busyTimeout.
Эти же строки в табличном виде. CLI-пример выше печатает текст; таблица помогает увидеть соответствие названия и остатка. Как получить такую таблицу настоящим HTML-обработчиком.
| Товар / строка | Наличие |
|---|---|
| Молоток | 8 шт. |
Если требование изменится. Требуется PHP 8.5+. Не используйте busy для подсчёта оставшихся строк и не запускайте новое чтение поверх активного результата.
Полный контракт, ограничения и следующие варианты. Ниже сохранены параметры, возвращаемые значения, ошибки, версии и дополнительные задачи этой операции. Перед каждым более длинным примером сначала прочитайте его подготовку и ход действий.
Найти statement, который ещё держит незаконченный просмотр.
| Вход / параметр | Смысл |
|---|---|
| Аргументов нет | Проверяется состояние statement, не блокировка файла. |
true, когда внутреннее выполнение начато и ещё не завершено/сброшено; false до чтения, после конца или reset. PHP8.5.11 делает предварительный step внутри execute и затем reset: сразу после execute busy может быть false, а после первого fetchArray становится true. Это подтверждено отдельным реальным примером. Не используйте результат как проверку чужих писателей: для них есть busyTimeout и коды ошибок.
- execute подготавливает результат
- первый fetchArray начинает чтение
- busy=true
- все строки прочитаны→busy=false
Как читать этот вариант. busy — состояние курсора, а не SQLITE_BUSY от блокировки другим процессом. Здесь первый fetchArray действительно начал чтение: реализация PHP8.5.11 сбрасывает внутренний statement после предварительного шага execute, поэтому сразу после execute busy ещё может быть false. Полный обход или reset прекращает активное выполнение.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT NOT NULL, price INTEGER NOT NULL)');
$db->exec("INSERT INTO products VALUES (1, 'Pen', 120), (2, 'Notebook', 350), (3, 'Box', 200)");
$stmt = $db->prepare('SELECT id FROM products ORDER BY id');
var_dump($stmt->busy());
$result = $stmt->execute();
$result->fetchArray(SQLITE3_NUM);
var_dump($stmt->busy());
while ($result->fetchArray(SQLITE3_NUM) !== false) {}
var_dump($stmt->busy());
$result->finalize();
Ожидаемый результат
bool(false)
bool(true)
bool(false)
Как читать этот вариант. Читатель ушёл со страницы до конца списка: reset завершает текущую работу. При дальнейшем чтении надо осознанно начать новую выборку; старый курсор не хранит независимый снимок.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT NOT NULL, price INTEGER NOT NULL)');
$db->exec("INSERT INTO products VALUES (1, 'Pen', 120), (2, 'Notebook', 350), (3, 'Box', 200)");
$stmt = $db->prepare('SELECT id FROM products');
$result = $stmt->execute();
$result->fetchArray(SQLITE3_NUM);
$stmt->reset();
var_dump($stmt->busy());
$result->finalize();
Ожидаемый результат
bool(false)
Следующий шаг и альтернативы: SQLite3Stmt::reset, SQLite3::busyTimeout.
SQLite3Stmt::explainПроверить, почему команда сейчас возвращает диагностические строки вместо товара.
SQLite3Stmt::explain(): intПосмотреть план и затем получить сам товар. В PHP 8.5+ план запроса возвращает описание работы SQLite. Метод explain читает выбранный режим, setExplain меняет его. В режиме плана колонки отличаются от name и stock.
- $stmt->setExplain(SQLite3Stmt::EXPLAIN_MODE_EXPLAIN_QUERY_PLAN)
- Сначала наблюдаем поиск по ключу id, затем возвращаем обычный режим и читаем Молоток. Нельзя показывать план как строку товара.
- После выполнения: Поиск по ключу · Молоток — 8 шт.
В архиве catalog.php открывает отдельную учебную базу и выполняет schema.sql: 1 — Молоток, 8 шт.; 2 — Перчатки, 15 шт.; 3 — Кисть, 0 шт. База находится в памяти и начинает работу с этих данных при каждом запуске. Распакуйте все файлы вместе и запустите php example.php. Полная подготовка и схема.
| Запись в этом примере | Как читать |
|---|---|
$db->method(...) | Стрелка вызывает действие у конкретного соединения, команды или результата. Скобки содержат входные значения; результат можно сохранить через =. |
Класс::ИМЯ | Двойное двоеточие обращается к константе режима либо статическому методу класса; это не выбор колонки SQL. |
[...] | Квадратные скобки задают массив либо читают его элемент. Ключ имени колонки и номер позиции — разные способы адресации. |
<?php
require __DIR__ . '/catalog.php';
$stmt = $db->prepare('SELECT name, stock FROM products WHERE id = 1');
$stmt->setExplain(SQLite3Stmt::EXPLAIN_MODE_EXPLAIN_QUERY_PLAN);
if ($stmt->explain() !== SQLite3Stmt::EXPLAIN_MODE_EXPLAIN_QUERY_PLAN) { throw new RuntimeException('План недоступен'); }
$plan = $stmt->execute();
$row = $plan->fetchArray(SQLITE3_ASSOC);
echo str_contains($row['detail'], 'SEARCH') ? "Поиск по ключу\n" : "Проверьте план чтения\n";
$plan->finalize();
$stmt->setExplain(SQLite3Stmt::EXPLAIN_MODE_PREPARED);
$result = $stmt->execute();
$row = $result->fetchArray(SQLITE3_ASSOC);
echo $row['name'], ' — ', $row['stock'], " шт.\n";
Ожидаемый результат
Поиск по ключу
Молоток — 8 шт.
Сначала наблюдаем поиск по ключу id, затем возвращаем обычный режим и читаем Молоток. Нельзя показывать план как строку товара.
Эти же строки в табличном виде. CLI-пример выше печатает текст; таблица помогает увидеть соответствие названия и остатка. Как получить такую таблицу настоящим HTML-обработчиком.
| Товар / строка | Наличие |
|---|---|
| Молоток | 8 шт. |
Если требование изменится. Нужны PHP 8.5 и SQLite3.43+. Текст плана зависит от версии SQLite; проверка SEARCH здесь демонстрационная, не устойчивый протокол автоматической оптимизации.
Полный контракт, ограничения и следующие варианты. Ниже сохранены параметры, возвращаемые значения, ошибки, версии и дополнительные задачи этой операции. Перед каждым более длинным примером сначала прочитайте его подготовку и ход действий.
Проверить, почему команда сейчас возвращает диагностические строки вместо товара.
| Вход / параметр | Смысл |
|---|---|
| mode для setExplain | EXPLAIN_MODE_PREPARED=0; EXPLAIN_MODE_EXPLAIN=1; EXPLAIN_MODE_EXPLAIN_QUERY_PLAN=2. explain параметров не принимает. |
explain возвращает одно из трёх значений, setExplain true/false при успешном/неуспешном переключении. Недопустимый mode вызывает ValueError. Нужны PHP 8.5+ и SQLite 3.43+; метод может отсутствовать при старой библиотеке сборки. Закончите текущий результат/reset до переключения режима.
- SQL SELECT name WHERE id=1
- mode=2→описание поиска
- mode=0→снова Pen
Как читать этот вариант. EXPLAIN QUERY PLAN возвращает описание выбранного плана вместо товаров. Точные формулировки и структура диагностики SQLite могут меняться; не строят бизнес-логику по тексту detail.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT NOT NULL, price INTEGER NOT NULL)');
$db->exec("INSERT INTO products VALUES (1, 'Pen', 120), (2, 'Notebook', 350), (3, 'Box', 200)");
$stmt = $db->prepare('SELECT name FROM products WHERE id = 1');
$stmt->setExplain(SQLite3Stmt::EXPLAIN_MODE_EXPLAIN_QUERY_PLAN);
echo $stmt->explain(), "\n";
$result = $stmt->execute();
$row = $result->fetchArray(SQLITE3_ASSOC);
echo str_contains($row['detail'], 'SEARCH') ? "Поиск по ключу\n" : "Другой план\n";
$result->finalize();
Ожидаемый результат
2
Поиск по ключу
Как читать этот вариант. Режим 1 показывает инструкции виртуальной машины, 2 — план на более высоком уровне, 0 — исходное действие. Объяснение SQL не равно замеру времени и не возвращает обычный товар.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT NOT NULL, price INTEGER NOT NULL)');
$db->exec("INSERT INTO products VALUES (1, 'Pen', 120), (2, 'Notebook', 350), (3, 'Box', 200)");
$stmt = $db->prepare('SELECT name FROM products WHERE id = 1');
$stmt->setExplain(SQLite3Stmt::EXPLAIN_MODE_EXPLAIN);
echo $stmt->explain(), "\n";
$stmt->setExplain(SQLite3Stmt::EXPLAIN_MODE_PREPARED);
echo $stmt->explain(), "\n";
$result = $stmt->execute();
echo $result->fetchArray(SQLITE3_ASSOC)['name'], "\n";
$result->finalize();
Ожидаемый результат
1
0
Pen
Следующий шаг и альтернативы: SQLite3Stmt::execute, SQLite3Stmt::reset.
SQLite3Stmt::setExplainПереключить ту же команду между обычным выполнением и исследованием плана.
SQLite3Stmt::setExplain(int $mode): boolПосмотреть план и затем получить сам товар. В PHP 8.5+ план запроса возвращает описание работы SQLite. Метод explain читает выбранный режим, setExplain меняет его. В режиме плана колонки отличаются от name и stock.
- $stmt->setExplain(SQLite3Stmt::EXPLAIN_MODE_EXPLAIN_QUERY_PLAN)
- Сначала наблюдаем поиск по ключу id, затем возвращаем обычный режим и читаем Молоток. Нельзя показывать план как строку товара.
- После выполнения: Поиск по ключу · Молоток — 8 шт.
В архиве catalog.php открывает отдельную учебную базу и выполняет schema.sql: 1 — Молоток, 8 шт.; 2 — Перчатки, 15 шт.; 3 — Кисть, 0 шт. База находится в памяти и начинает работу с этих данных при каждом запуске. Распакуйте все файлы вместе и запустите php example.php. Полная подготовка и схема.
| Запись в этом примере | Как читать |
|---|---|
$db->method(...) | Стрелка вызывает действие у конкретного соединения, команды или результата. Скобки содержат входные значения; результат можно сохранить через =. |
Класс::ИМЯ | Двойное двоеточие обращается к константе режима либо статическому методу класса; это не выбор колонки SQL. |
[...] | Квадратные скобки задают массив либо читают его элемент. Ключ имени колонки и номер позиции — разные способы адресации. |
<?php
require __DIR__ . '/catalog.php';
$stmt = $db->prepare('SELECT name, stock FROM products WHERE id = 1');
$stmt->setExplain(SQLite3Stmt::EXPLAIN_MODE_EXPLAIN_QUERY_PLAN);
if ($stmt->explain() !== SQLite3Stmt::EXPLAIN_MODE_EXPLAIN_QUERY_PLAN) { throw new RuntimeException('План недоступен'); }
$plan = $stmt->execute();
$row = $plan->fetchArray(SQLITE3_ASSOC);
echo str_contains($row['detail'], 'SEARCH') ? "Поиск по ключу\n" : "Проверьте план чтения\n";
$plan->finalize();
$stmt->setExplain(SQLite3Stmt::EXPLAIN_MODE_PREPARED);
$result = $stmt->execute();
$row = $result->fetchArray(SQLITE3_ASSOC);
echo $row['name'], ' — ', $row['stock'], " шт.\n";
Ожидаемый результат
Поиск по ключу
Молоток — 8 шт.
Сначала наблюдаем поиск по ключу id, затем возвращаем обычный режим и читаем Молоток. Нельзя показывать план как строку товара.
Эти же строки в табличном виде. CLI-пример выше печатает текст; таблица помогает увидеть соответствие названия и остатка. Как получить такую таблицу настоящим HTML-обработчиком.
| Товар / строка | Наличие |
|---|---|
| Молоток | 8 шт. |
Если требование изменится. Нужны PHP 8.5 и SQLite3.43+. Текст плана зависит от версии SQLite; проверка SEARCH здесь демонстрационная, не устойчивый протокол автоматической оптимизации.
Полный контракт, ограничения и следующие варианты. Ниже сохранены параметры, возвращаемые значения, ошибки, версии и дополнительные задачи этой операции. Перед каждым более длинным примером сначала прочитайте его подготовку и ход действий.
Переключить ту же команду между обычным выполнением и исследованием плана.
| Вход / параметр | Смысл |
|---|---|
| mode для setExplain | EXPLAIN_MODE_PREPARED=0; EXPLAIN_MODE_EXPLAIN=1; EXPLAIN_MODE_EXPLAIN_QUERY_PLAN=2. explain параметров не принимает. |
explain возвращает одно из трёх значений, setExplain true/false при успешном/неуспешном переключении. Недопустимый mode вызывает ValueError. Нужны PHP 8.5+ и SQLite 3.43+; метод может отсутствовать при старой библиотеке сборки. Закончите текущий результат/reset до переключения режима.
- SQL SELECT name WHERE id=1
- mode=2→описание поиска
- mode=0→снова Pen
Как читать этот вариант. EXPLAIN QUERY PLAN возвращает описание выбранного плана вместо товаров. Точные формулировки и структура диагностики SQLite могут меняться; не строят бизнес-логику по тексту detail.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT NOT NULL, price INTEGER NOT NULL)');
$db->exec("INSERT INTO products VALUES (1, 'Pen', 120), (2, 'Notebook', 350), (3, 'Box', 200)");
$stmt = $db->prepare('SELECT name FROM products WHERE id = 1');
$stmt->setExplain(SQLite3Stmt::EXPLAIN_MODE_EXPLAIN_QUERY_PLAN);
echo $stmt->explain(), "\n";
$result = $stmt->execute();
$row = $result->fetchArray(SQLITE3_ASSOC);
echo str_contains($row['detail'], 'SEARCH') ? "Поиск по ключу\n" : "Другой план\n";
$result->finalize();
Ожидаемый результат
2
Поиск по ключу
Как читать этот вариант. Режим 1 показывает инструкции виртуальной машины, 2 — план на более высоком уровне, 0 — исходное действие. Объяснение SQL не равно замеру времени и не возвращает обычный товар.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT NOT NULL, price INTEGER NOT NULL)');
$db->exec("INSERT INTO products VALUES (1, 'Pen', 120), (2, 'Notebook', 350), (3, 'Box', 200)");
$stmt = $db->prepare('SELECT name FROM products WHERE id = 1');
$stmt->setExplain(SQLite3Stmt::EXPLAIN_MODE_EXPLAIN);
echo $stmt->explain(), "\n";
$stmt->setExplain(SQLite3Stmt::EXPLAIN_MODE_PREPARED);
echo $stmt->explain(), "\n";
$result = $stmt->execute();
echo $result->fetchArray(SQLITE3_ASSOC)['name'], "\n";
$result->finalize();
Ожидаемый результат
1
0
Pen
Следующий шаг и альтернативы: SQLite3Stmt::execute, SQLite3Stmt::reset.
SQLite3Stmt::__constructПонять, откуда берётся объект команды и почему его не создают через new.
private SQLite3Stmt::__construct(SQLite3 $sqlite3, string $query)Подготовить поиск до передачи номера. SQLite3Stmt получают через prepare, а не самостоятельным new SQLite3Stmt. Объект хранит SQL-шаблон и привязки; execute возвращает отдельный объект результата.
- $db->prepare('SELECT name, stock FROM products WHERE id = :id')
- Маркер :id получает целое 2. После выполнения читаем строку, освобождаем результат, затем саму подготовленную команду.
- После выполнения: Перчатки — 15 шт.
В архиве catalog.php открывает отдельную учебную базу и выполняет schema.sql: 1 — Молоток, 8 шт.; 2 — Перчатки, 15 шт.; 3 — Кисть, 0 шт. База находится в памяти и начинает работу с этих данных при каждом запуске. Распакуйте все файлы вместе и запустите php example.php. Полная подготовка и схема.
| Запись в этом примере | Как читать |
|---|---|
$db->method(...) | Стрелка вызывает действие у конкретного соединения, команды или результата. Скобки содержат входные значения; результат можно сохранить через =. |
[...] | Квадратные скобки задают массив либо читают его элемент. Ключ имени колонки и номер позиции — разные способы адресации. |
<?php
require __DIR__ . '/catalog.php';
$stmt = $db->prepare('SELECT name, stock FROM products WHERE id = :id');
$stmt->bindValue(':id', 2, SQLITE3_INTEGER);
$result = $stmt->execute();
$row = $result->fetchArray(SQLITE3_ASSOC);
echo $row['name'], ' — ', $row['stock'], " шт.\n";
$result->finalize();
$stmt->close();
Ожидаемый результат
Перчатки — 15 шт.
Маркер :id получает целое 2. После выполнения читаем строку, освобождаем результат, затем саму подготовленную команду.
Эти же строки в табличном виде. CLI-пример выше печатает текст; таблица помогает увидеть соответствие названия и остатка. Как получить такую таблицу настоящим HTML-обработчиком.
| Товар / строка | Наличие |
|---|---|
| Перчатки | 15 шт. |
Если требование изменится. Маркер занимает место значения, не имени таблицы. Нельзя ожидать, что :ids с текстом «1,2» развернётся в два элемента IN; число маркеров задают отдельно.
Полный контракт, ограничения и следующие варианты. Ниже сохранены параметры, возвращаемые значения, ошибки, версии и дополнительные задачи этой операции. Перед каждым более длинным примером сначала прочитайте его подготовку и ход действий.
Понять, откуда берётся объект команды и почему его не создают через new.
| Вход / параметр | Смысл |
|---|---|
| sqlite3 / query | Внутренние аргументы связывают подготовленную команду с открытым соединением и SQL. |
Конструктор private: публичный вход — SQLite3::prepare. Он сообщает ошибку подготовки в установленном режиме и возвращает полностью связанный объект. Класс не сериализуется: нельзя положить рабочее соединение/statement в сессию между HTTP-запросами.
- открытый $db
- prepare(SQL)
- SQLite3Stmt связан с $db
- execute использует эту связь
Как читать этот вариант. prepare переводит шаблон SQL в готовую команду. :id — место для значения, без кавычек. bindValue кладёт туда число 2; execute выполняет команду, fetchArray даёт строку. Данные не меняют структуру SQL.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT NOT NULL, price INTEGER NOT NULL)');
$db->exec("INSERT INTO products VALUES (1, 'Pen', 120), (2, 'Notebook', 350), (3, 'Box', 200)");
$statement = $db->prepare('SELECT name FROM products WHERE id = :id');
$statement->bindValue(':id', 2, SQLITE3_INTEGER);
$result = $statement->execute();
echo $result->fetchArray(SQLITE3_ASSOC)['name'], "\n";
$result->finalize();
$statement->close();
Ожидаемый результат
Notebook
Как читать этот вариант. Позиционный ? имеет индекс 1. Не нужно вручную добавлять SQL-кавычки или escapeString. Имя таблицы, ASC/DESC и SQL-оператор параметром заменить нельзя: для них нужен заранее разрешённый выбор.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE customers (name TEXT)');
$statement = $db->prepare('INSERT INTO customers (name) VALUES (?)');
$statement->bindValue(1, "O'Neil", SQLITE3_TEXT);
$statement->execute()->finalize();
echo $db->querySingle('SELECT name FROM customers'), "\n";
Ожидаемый результат
O'Neil
Следующий шаг и альтернативы: SQLite3::prepare, SQLite3Stmt::execute.
SQLite3Result::fetchArrayПреобразовать следующую SQL-строку в PHP-массив для страницы или экспорта.
SQLite3Result::fetchArray(int $mode = SQLITE3_BOTH): array|falseПревратить ответ SQLite в поля товара. SQLite3Result получают через query или execute; его конструктор не является обычным способом загрузки строк. ASSOC выбирает ключи name и stock.
- $result->fetchArray(SQLITE3_ASSOC)
- Каждый вызов переносит одну строку и сдвигает позицию. Отсутствие строки проверено строго, поэтому stock0 не теряется.
- После выполнения: Кисть — 0 шт.
В архиве catalog.php открывает отдельную учебную базу и выполняет schema.sql: 1 — Молоток, 8 шт.; 2 — Перчатки, 15 шт.; 3 — Кисть, 0 шт. База находится в памяти и начинает работу с этих данных при каждом запуске. Распакуйте все файлы вместе и запустите php example.php. Полная подготовка и схема.
| Запись в этом примере | Как читать |
|---|---|
$db->method(...) | Стрелка вызывает действие у конкретного соединения, команды или результата. Скобки содержат входные значения; результат можно сохранить через =. |
[...] | Квадратные скобки задают массив либо читают его элемент. Ключ имени колонки и номер позиции — разные способы адресации. |
<?php
require __DIR__ . '/catalog.php';
$result = $db->query('SELECT name, stock FROM products WHERE id = 3');
$row = $result->fetchArray(SQLITE3_ASSOC);
if ($row === false) { echo "Товар не найден\n"; } else {
echo $row['name'], ' — ', $row['stock'], " шт.\n";
}
$result->finalize();
Ожидаемый результат
Кисть — 0 шт.
Каждый вызов переносит одну строку и сдвигает позицию. Отсутствие строки проверено строго, поэтому stock0 не теряется.
Эти же строки в табличном виде. CLI-пример выше печатает текст; таблица помогает увидеть соответствие названия и остатка. Как получить такую таблицу настоящим HTML-обработчиком.
| Товар / строка | Наличие |
|---|---|
| Кисть | 0 шт. |
Если требование изменится. SQLITE3_NUM даст позиции 0 и 1; SQLITE3_BOTH — оба набора ключей, но не два товара. Для одинаковых имён в JOIN задавайте AS.
Полный контракт, ограничения и следующие варианты. Ниже сохранены параметры, возвращаемые значения, ошибки, версии и дополнительные задачи этой операции. Перед каждым более длинным примером сначала прочитайте его подготовку и ход действий.
Преобразовать следующую SQL-строку в PHP-массив для страницы или экспорта.
| Вход / параметр | Смысл |
|---|---|
| mode | ASSOC — имена колонок; NUM — индексы с 0; BOTH — оба вида одновременно. |
Возвращает следующую строку либо false при конце/ошибке без исключений. SQL integer становится PHP int, если помещается в PHP_INT_MIN..PHP_INT_MAX, иначе string; float→float, text/blob→string, null→null. При одинаковых именах колонок ASSOC перезапишет одно значение: давайте уникальные AS-имена. Пустой SELECT — сразу false. Для обычного INSERT без RETURNING строки не читают: дополнительный шаг может повторно исполнить команду; используйте finalize. Не вызывайте fetch бесконечно после получения false.
- SQL-строка name=Pen,price=120
- ASSOC
- PHP [name=>Pen,price=>120]
- следующий fetch→false
Как читать этот вариант. Одна строка — один массив. Второй fetchArray возвращает false, потому что подходящая строка была одна. Не путать false окончания и null в отдельной колонке.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT NOT NULL, price INTEGER NOT NULL)');
$db->exec("INSERT INTO products VALUES (1, 'Pen', 120), (2, 'Notebook', 350), (3, 'Box', 200)");
$result = $db->query('SELECT name, price FROM products WHERE id = 1');
$row = $result->fetchArray(SQLITE3_ASSOC);
echo $row['name'], ':', $row['price'], "\n";
var_dump($result->fetchArray(SQLITE3_ASSOC));
$result->finalize();
Ожидаемый результат
Pen:120
bool(false)
Как читать этот вариант. Default BOTH даёт числовой ключ 0 и строковый name. Это одна колонка с двумя способами доступа, не дубликат строки базы. Для JSON API обычно удобнее ASSOC, для позиционного CSV — NUM.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$result = $db->query("SELECT 'Pen' AS name");
$row = $result->fetchArray();
echo count($row), ':', $row[0], ':', $row['name'], "\n";
$result->finalize();
$result = $db->query("SELECT 'Pen' AS name");
echo count($result->fetchArray(SQLITE3_NUM)), "\n";
$result->finalize();
Ожидаемый результат
2:Pen:Pen
1
Следующий шаг и альтернативы: SQLite3Result::fetchAll, SQLite3Result::numColumns, SQLite3Result::finalize.
SQLite3Result::fetchAllПолучить небольшой результат целиком для преобразования в JSON или шаблон.
SQLite3Result::fetchAll(int $mode = SQLITE3_BOTH): array|falseСобрать небольшой каталог в обычный массив. PHP 8.5+: метод читает все оставшиеся строки текущего ответа. Внутренние массивы — товары, внешний — список для шаблона страницы.
- $result->fetchAll(SQLITE3_ASSOC)
- После копирования данных в массив можно освободить result и передать список HTML-шаблону. Перед HTML-выводом название отдельно экранируют.
- После выполнения: Молоток — 8 шт. · Перчатки — 15 шт. · Кисть — 0 шт.
В архиве catalog.php открывает отдельную учебную базу и выполняет schema.sql: 1 — Молоток, 8 шт.; 2 — Перчатки, 15 шт.; 3 — Кисть, 0 шт. База находится в памяти и начинает работу с этих данных при каждом запуске. Распакуйте все файлы вместе и запустите php example.php. Полная подготовка и схема.
| Запись в этом примере | Как читать |
|---|---|
$db->method(...) | Стрелка вызывает действие у конкретного соединения, команды или результата. Скобки содержат входные значения; результат можно сохранить через =. |
foreach ($rows as $row) | Берёт по одной строке из набора. Блок между { и } повторяется для каждой строки; при пустом наборе не выполняется. |
[...] | Квадратные скобки задают массив либо читают его элемент. Ключ имени колонки и номер позиции — разные способы адресации. |
<?php
require __DIR__ . '/catalog.php';
$result = $db->query('SELECT name, stock FROM products ORDER BY id');
$products = $result->fetchAll(SQLITE3_ASSOC);
$result->finalize();
foreach ($products as $row) {
echo $row['name'], ' — ', $row['stock'], " шт.\n";
}
Ожидаемый результат
Молоток — 8 шт.
Перчатки — 15 шт.
Кисть — 0 шт.
После копирования данных в массив можно освободить result и передать список HTML-шаблону. Перед HTML-выводом название отдельно экранируют.
Эти же строки в табличном виде. CLI-пример выше печатает текст; таблица помогает увидеть соответствие названия и остатка. Как получить такую таблицу настоящим HTML-обработчиком.
| Товар / строка | Наличие |
|---|---|
| Молоток | 8 шт. |
| Перчатки | 15 шт. |
| Кисть | 0 шт. |
Если требование изменится. Для PHP 8.2–8.4 используйте цикл fetchArray. Большую выгрузку не превращают в один безразмерный массив; счётчик получают через COUNT(*).
Полный контракт, ограничения и следующие варианты. Ниже сохранены параметры, возвращаемые значения, ошибки, версии и дополнительные задачи этой операции. Перед каждым более длинным примером сначала прочитайте его подготовку и ход действий.
Получить небольшой результат целиком для преобразования в JSON или шаблон.
| Вход / параметр | Смысл |
|---|---|
| mode | ASSOC / NUM / BOTH, как у fetchArray. |
Возвращает массив оставшихся строк; при отсутствии строк [] и при отказе false в warning-режиме. Доступно в PHP 8.5 tagged API, даже если эта страница ещё отсутствовала в зафиксированном дереве. Потребление памяти растёт с числом и размером строк; BOTH добавляет два набора ключей. Это не дополнительный SQL SELECT, а чтение текущего результата.
- три строки SQL
- fetchArray забирает Pen
- fetchAll собирает Notebook и Box
- длина остатка 2
Как читать этот вариант. fetchAll (PHP 8.5+) получает оставшиеся строки. Подходит для небольшого списка, но размещает весь ответ в памяти PHP; для большого экспорта используйте построчный fetchArray.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT NOT NULL, price INTEGER NOT NULL)');
$db->exec("INSERT INTO products VALUES (1, 'Pen', 120), (2, 'Notebook', 350), (3, 'Box', 200)");
$result = $db->query('SELECT name FROM products ORDER BY id');
$rows = $result->fetchAll(SQLITE3_ASSOC);
echo count($rows), ':', $rows[1]['name'], "\n";
$result->finalize();
Ожидаемый результат
3:Notebook
Как читать этот вариант. У результата есть текущая позиция. fetchAll не возвращает автоматически уже прочитанную Pen; для нового независимого просмотра выполните SELECT заново.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT NOT NULL, price INTEGER NOT NULL)');
$db->exec("INSERT INTO products VALUES (1, 'Pen', 120), (2, 'Notebook', 350), (3, 'Box', 200)");
$result = $db->query('SELECT name FROM products ORDER BY id');
echo $result->fetchArray(SQLITE3_ASSOC)['name'], "\n";
$rest = $result->fetchAll(SQLITE3_ASSOC);
echo count($rest), ':', $rest[0]['name'], "\n";
$result->finalize();
Ожидаемый результат
Pen
2:Notebook
Следующий шаг и альтернативы: SQLite3Result::fetchArray, SQLite3Result::reset.
SQLite3Result::numColumnsСформировать ширину таблицы/экспорта из метаданных результата.
SQLite3Result::numColumns(): intПроверить две ожидаемые колонки отчёта. Метод возвращает ширину результата в колонках. Даже если товаров ноль, описание SELECT может содержать две колонки.
- $result->numColumns()
- Проверена форма строки, затем прочитаны значения. Число 2 — описание структуры, не количество найденных товаров.
- После выполнения: Молоток — 8 шт.
В архиве catalog.php открывает отдельную учебную базу и выполняет schema.sql: 1 — Молоток, 8 шт.; 2 — Перчатки, 15 шт.; 3 — Кисть, 0 шт. База находится в памяти и начинает работу с этих данных при каждом запуске. Распакуйте все файлы вместе и запустите php example.php. Полная подготовка и схема.
| Запись в этом примере | Как читать |
|---|---|
$db->method(...) | Стрелка вызывает действие у конкретного соединения, команды или результата. Скобки содержат входные значения; результат можно сохранить через =. |
[...] | Квадратные скобки задают массив либо читают его элемент. Ключ имени колонки и номер позиции — разные способы адресации. |
<?php
require __DIR__ . '/catalog.php';
$result = $db->query('SELECT name, stock FROM products WHERE id = 1');
if ($result->numColumns() !== 2) { throw new RuntimeException('Отчёт ожидает название и остаток'); }
$row = $result->fetchArray(SQLITE3_ASSOC);
echo $row['name'], ' — ', $row['stock'], " шт.\n";
Ожидаемый результат
Молоток — 8 шт.
Проверена форма строки, затем прочитаны значения. Число 2 — описание структуры, не количество найденных товаров.
Эти же строки в табличном виде. CLI-пример выше печатает текст; таблица помогает увидеть соответствие названия и остатка. Как получить такую таблицу настоящим HTML-обработчиком.
| Товар / строка | Наличие |
|---|---|
| Молоток | 8 шт. |
Если требование изменится. Для количества товаров используйте SELECT COUNT(*). Если меняете SELECT, обновляйте код чтения; позиционные индексы особенно чувствительны к порядку.
Полный контракт, ограничения и следующие варианты. Ниже сохранены параметры, возвращаемые значения, ошибки, версии и дополнительные задачи этой операции. Перед каждым более длинным примером сначала прочитайте его подготовку и ход действий.
Сформировать ширину таблицы/экспорта из метаданных результата.
| Вход / параметр | Смысл |
|---|---|
| Аргументов нет | Считаются колонки SELECT, не строки. |
Возвращает int; 0 у команды без колонок результата. Это дешевле обхода данных и сохраняет сведения даже при пустом наборе. Для количества строк выполняют COUNT(*) либо считают фактически обработанные строки, учитывая стоимость.
- SELECT name,price
- описание двух колонок
- numColumns→2
- даже если id=99 не найден
Как читать этот вариант. Колонки name и price — две, хотя товаров три. Число колонок не сообщает число строк.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT NOT NULL, price INTEGER NOT NULL)');
$db->exec("INSERT INTO products VALUES (1, 'Pen', 120), (2, 'Notebook', 350), (3, 'Box', 200)");
$result = $db->query('SELECT name, price FROM products');
echo $result->numColumns(), "\n";
$result->finalize();
Ожидаемый результат
2
Как читать этот вариант. Ноль товаров не уничтожает структуру SELECT. Можно показать заголовки Name/Price и сообщение о пустом фильтре.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT NOT NULL, price INTEGER NOT NULL)');
$db->exec("INSERT INTO products VALUES (1, 'Pen', 120), (2, 'Notebook', 350), (3, 'Box', 200)");
$result = $db->query('SELECT name, price FROM products WHERE id = 99');
echo $result->numColumns(), "\n";
var_dump($result->fetchArray(SQLITE3_ASSOC));
$result->finalize();
Ожидаемый результат
2
bool(false)
Следующий шаг и альтернативы: SQLite3Result::columnName, SQLite3Result::columnType, SQLite3::querySingle.
SQLite3Result::columnNameПостроить заголовок результата без знания схемы заранее.
SQLite3Result::columnName(int $column): string|falseПостроить заголовок экспорта из SQL-псевдонимов. Номер колонки начинается с 0. AS задаёт название поля результата и может отличаться от имени столбца хранилища.
- $result->columnName(0)
- Заголовок и данные берутся разными операциями. Это основа универсального экспорта; для произвольных значений используйте fputcsv.
- После выполнения: product;available · Молоток;8
В архиве catalog.php открывает отдельную учебную базу и выполняет schema.sql: 1 — Молоток, 8 шт.; 2 — Перчатки, 15 шт.; 3 — Кисть, 0 шт. База находится в памяти и начинает работу с этих данных при каждом запуске. Распакуйте все файлы вместе и запустите php example.php. Полная подготовка и схема.
| Запись в этом примере | Как читать |
|---|---|
$db->method(...) | Стрелка вызывает действие у конкретного соединения, команды или результата. Скобки содержат входные значения; результат можно сохранить через =. |
[...] | Квадратные скобки задают массив либо читают его элемент. Ключ имени колонки и номер позиции — разные способы адресации. |
<?php
require __DIR__ . '/catalog.php';
$result = $db->query('SELECT name AS product, stock AS available FROM products WHERE id = 1');
echo $result->columnName(0), ';', $result->columnName(1), "\n";
$row = $result->fetchArray(SQLITE3_NUM);
echo $row[0], ';', $row[1], "\n";
Ожидаемый результат
product;available
Молоток;8
Заголовок и данные берутся разными операциями. Это основа универсального экспорта; для произвольных значений используйте fputcsv.
Если требование изменится. Проверяйте индекс относительно numColumns. Имя результата не следует принимать за безопасный SQL-идентификатор для следующего запроса.
Полный контракт, ограничения и следующие варианты. Ниже сохранены параметры, возвращаемые значения, ошибки, версии и дополнительные задачи этой операции. Перед каждым более длинным примером сначала прочитайте его подготовку и ход действий.
Построить заголовок результата без знания схемы заранее.
| Вход / параметр | Смысл |
|---|---|
| column | Нулевой индекс: 0 — первая колонка; диапазон меньше numColumns. |
Возвращает имя/alias либо false для отсутствующей колонки. Имена выражений без AS могут быть неудобны и зависеть от текста SQL; для стабильного API явно задавайте aliases. Два одинаковых имени опасны при ASSOC.
- SELECT name AS product_name
- columnName(0)
- product_name становится заголовком
Как читать этот вариант. Индекс колонки начинается с 0. AS задаёт публичное имя результата и ключ ASSOC, не переименовывает столбец самой таблицы.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT NOT NULL, price INTEGER NOT NULL)');
$db->exec("INSERT INTO products VALUES (1, 'Pen', 120), (2, 'Notebook', 350), (3, 'Box', 200)");
$result = $db->query('SELECT name AS product_name, price AS price_kopecks FROM products');
for ($i = 0; $i < $result->numColumns(); ++$i) { echo $result->columnName($i), "\n"; }
$result->finalize();
Ожидаемый результат
product_name
price_kopecks
Как читать этот вариант. Индекс 1 отсутствует: контракт возвращает false. Не использовать имя из результата как разрешение выполнить произвольный пользовательский SQL-идентификатор.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$result = $db->query('SELECT 42 AS total');
echo $result->columnName(0), "\n";
var_dump($result->columnName(1));
$result->finalize();
Ожидаемый результат
total
bool(false)
Следующий шаг и альтернативы: SQLite3Result::numColumns, SQLite3Result::fetchArray.
SQLite3Result::columnTypeВыбрать способ отображения текущего значения или найти неожиданную строку вместо числа.
SQLite3Result::columnType(int $column): int|falseПонять фактический тип остатка текущей строки. SQLite имеет динамические типы значений. columnType описывает текущее значение после получения строки, а не заменяет ограничения схемы.
- $result->columnType(1)
- У второй колонки stock проверяется INTEGER. Тип имеет смысл вместе с конкретной строкой и известной схемой.
- После выполнения: Молоток — 8 шт.
В архиве catalog.php открывает отдельную учебную базу и выполняет schema.sql: 1 — Молоток, 8 шт.; 2 — Перчатки, 15 шт.; 3 — Кисть, 0 шт. База находится в памяти и начинает работу с этих данных при каждом запуске. Распакуйте все файлы вместе и запустите php example.php. Полная подготовка и схема.
| Запись в этом примере | Как читать |
|---|---|
$db->method(...) | Стрелка вызывает действие у конкретного соединения, команды или результата. Скобки содержат входные значения; результат можно сохранить через =. |
[...] | Квадратные скобки задают массив либо читают его элемент. Ключ имени колонки и номер позиции — разные способы адресации. |
<?php
require __DIR__ . '/catalog.php';
$result = $db->query('SELECT name, stock FROM products WHERE id = 1');
$row = $result->fetchArray(SQLITE3_ASSOC);
if ($result->columnType(1) !== SQLITE3_INTEGER) { throw new RuntimeException('Остаток должен быть целым'); }
echo $row['name'], ' — ', $row['stock'], " шт.\n";
Ожидаемый результат
Молоток — 8 шт.
У второй колонки stock проверяется INTEGER. Тип имеет смысл вместе с конкретной строкой и известной схемой.
Эти же строки в табличном виде. CLI-пример выше печатает текст; таблица помогает увидеть соответствие названия и остатка. Как получить такую таблицу настоящим HTML-обработчиком.
| Товар / строка | Наличие |
|---|---|
| Молоток | 8 шт. |
Если требование изменится. TEXT, BLOB, NULL и числовые значения различают явно. Обычная декларация INTEGER в нестрогой таблице не заменяет проверку входа или STRICT-схему.
Полный контракт, ограничения и следующие варианты. Ниже сохранены параметры, возвращаемые значения, ошибки, версии и дополнительные задачи этой операции. Перед каждым более длинным примером сначала прочитайте его подготовку и ход действий.
Выбрать способ отображения текущего значения или найти неожиданную строку вместо числа.
| Вход / параметр | Смысл |
|---|---|
| column | Нулевой индекс колонки текущей строки. |
Возвращает INTEGER/FLOAT/TEXT/BLOB/NULL либо false для отсутствующего индекса. Читайте после получения действительной строки, до следующего fetch; при конце результата действующей строки нет. Это не проверка SQL-схемы и не гарантия, что все строки имеют один тип. Для схемы отдельно используют PRAGMA table_info.
- fetch первой строки 7
- columnType=INTEGER
- fetch второй seven
- columnType=TEXT
Как читать этот вариант. INTEGER=1, FLOAT=2, TEXT=3, BLOB=4, NULL=5 — storage classes текущих значений. Это не текст декларации CREATE TABLE.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$result = $db->query("SELECT 42, 1.5, 'Pen', x'4100', NULL");
$result->fetchArray(SQLITE3_NUM);
for ($i = 0; $i < $result->numColumns(); ++$i) { echo $result->columnType($i), $i === 4 ? "\n" : ':'; }
$result->finalize();
Ожидаемый результат
1:2:3:4:5
Как читать этот вариант. Тип надо понимать на уровне строки. SQLite использует динамические типы и affinity; SQL STRICT-таблицы накладывают дополнительные ограничения, но columnType всё равно описывает значение результата.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$result = $db->query("SELECT 7 AS value UNION ALL SELECT 'seven'");
while (($row = $result->fetchArray(SQLITE3_NUM)) !== false) { echo $result->columnType(0), ':', $row[0], "\n"; }
$result->finalize();
Ожидаемый результат
1:7
3:seven
Следующий шаг и альтернативы: SQLite3Result::fetchArray, SQLite3Result::columnName.
SQLite3Result::resetПовторно прочитать простой SELECT, когда нужен второй проход.
SQLite3Result::reset(): boolПовторно прочитать небольшой известный отчёт. reset возвращает результат к началу исполнения команды. Это не перемотка уже готового независимого PHP-массива.
- $result->reset()
- Второй fetch снова получает первый товар. Для обычной страницы зачастую яснее прочитать небольшой набор один раз в массив.
- После выполнения: Молоток — 8 шт. · Молоток — 8 шт.
В архиве catalog.php открывает отдельную учебную базу и выполняет schema.sql: 1 — Молоток, 8 шт.; 2 — Перчатки, 15 шт.; 3 — Кисть, 0 шт. База находится в памяти и начинает работу с этих данных при каждом запуске. Распакуйте все файлы вместе и запустите php example.php. Полная подготовка и схема.
| Запись в этом примере | Как читать |
|---|---|
$db->method(...) | Стрелка вызывает действие у конкретного соединения, команды или результата. Скобки содержат входные значения; результат можно сохранить через =. |
[...] | Квадратные скобки задают массив либо читают его элемент. Ключ имени колонки и номер позиции — разные способы адресации. |
<?php
require __DIR__ . '/catalog.php';
$result = $db->query('SELECT name, stock FROM products ORDER BY id');
$row = $result->fetchArray(SQLITE3_ASSOC);
echo $row['name'], ' — ', $row['stock'], " шт.\n";
$result->reset();
$row = $result->fetchArray(SQLITE3_ASSOC);
echo $row['name'], ' — ', $row['stock'], " шт.\n";
$result->finalize();
Ожидаемый результат
Молоток — 8 шт.
Молоток — 8 шт.
Второй fetch снова получает первый товар. Для обычной страницы зачастую яснее прочитать небольшой набор один раз в массив.
Эти же строки в табличном виде. CLI-пример выше печатает текст; таблица помогает увидеть соответствие названия и остатка. Как получить такую таблицу настоящим HTML-обработчиком.
| Товар / строка | Наличие |
|---|---|
| Молоток | 8 шт. |
| Молоток | 8 шт. |
Если требование изменится. Команда может выполняться повторно и видеть новые данные. Не применяйте эту операцию к результату записи, рассчитывая на отсутствие повторных изменений.
Полный контракт, ограничения и следующие варианты. Ниже сохранены параметры, возвращаемые значения, ошибки, версии и дополнительные задачи этой операции. Перед каждым более длинным примером сначала прочитайте его подготовку и ход действий.
Повторно прочитать простой SELECT, когда нужен второй проход.
| Вход / параметр | Смысл |
|---|---|
| Аргументов нет | Сбрасывает текущий результат к началу выполнения. |
true — успешно, false/исключение — отказ. Это может заново исполнить SQL и PHP callbacks, а не бесплатно переставить указатель массива. Не применяйте к DML с побочными действиями как к обычной перемотке. Для независимых стабильных данных используйте небольшой материализованный массив либо соответствующую транзакцию.
- прочитали Pen
- reset statement
- следующий fetch снова Pen
- работа запроса может повториться
Как читать этот вариант. reset сбрасывает underlying SQLite statement; это не перемещение по независимому буферу PHP. SELECT начинает шагать заново.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT NOT NULL, price INTEGER NOT NULL)');
$db->exec("INSERT INTO products VALUES (1, 'Pen', 120), (2, 'Notebook', 350), (3, 'Box', 200)");
$result = $db->query('SELECT name FROM products ORDER BY id');
echo $result->fetchArray(SQLITE3_ASSOC)['name'], "\n";
$result->reset();
echo $result->fetchArray(SQLITE3_ASSOC)['name'], "\n";
$result->finalize();
Ожидаемый результат
Pen
Pen
Как читать этот вариант. Для маленького ответа два прохода допустимы. Для большого второй проход повторяет работу; часто лучше COUNT отдельно или один осмысленный проход. При изменениях базы между проходами результат может отличаться.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT NOT NULL, price INTEGER NOT NULL)');
$db->exec("INSERT INTO products VALUES (1, 'Pen', 120), (2, 'Notebook', 350), (3, 'Box', 200)");
$result = $db->query('SELECT name FROM products ORDER BY id');
$count = 0;
while ($result->fetchArray(SQLITE3_ASSOC) !== false) { ++$count; }
$result->reset();
echo $count, ':', $result->fetchArray(SQLITE3_ASSOC)['name'], "\n";
$result->finalize();
Ожидаемый результат
3:Pen
Следующий шаг и альтернативы: SQLite3Stmt::reset, SQLite3Result::fetchAll, SQLite3Result::finalize.
SQLite3Result::finalizeОсвободить курсор, чтобы он не держал ресурсы и не мешал следующему выполнению.
SQLite3Result::finalize(): trueОсвободить ответ и продолжить работу с базой. finalize завершает SQLite3Result. Уже прочитанные массивы остаются доступны; новый запрос создаст новый result.
- $result->finalize()
- Каждый ответ имеет собственный срок жизни. Мы завершаем первый до второго, не оставляя зависший курсор.
- После выполнения: Молоток — 8 шт. · Перчатки — 15 шт.
В архиве catalog.php открывает отдельную учебную базу и выполняет schema.sql: 1 — Молоток, 8 шт.; 2 — Перчатки, 15 шт.; 3 — Кисть, 0 шт. База находится в памяти и начинает работу с этих данных при каждом запуске. Распакуйте все файлы вместе и запустите php example.php. Полная подготовка и схема.
| Запись в этом примере | Как читать |
|---|---|
$db->method(...) | Стрелка вызывает действие у конкретного соединения, команды или результата. Скобки содержат входные значения; результат можно сохранить через =. |
[...] | Квадратные скобки задают массив либо читают его элемент. Ключ имени колонки и номер позиции — разные способы адресации. |
<?php
require __DIR__ . '/catalog.php';
$result = $db->query('SELECT name, stock FROM products WHERE id = 1');
$row = $result->fetchArray(SQLITE3_ASSOC);
echo $row['name'], ' — ', $row['stock'], " шт.\n";
$result->finalize();
$result = $db->query('SELECT name, stock FROM products WHERE id = 2');
$row = $result->fetchArray(SQLITE3_ASSOC);
echo $row['name'], ' — ', $row['stock'], " шт.\n";
$result->finalize();
Ожидаемый результат
Молоток — 8 шт.
Перчатки — 15 шт.
Каждый ответ имеет собственный срок жизни. Мы завершаем первый до второго, не оставляя зависший курсор.
Эти же строки в табличном виде. CLI-пример выше печатает текст; таблица помогает увидеть соответствие названия и остатка. Как получить такую таблицу настоящим HTML-обработчиком.
| Товар / строка | Наличие |
|---|---|
| Молоток | 8 шт. |
| Перчатки | 15 шт. |
Если требование изменится. После finalize читать закрытый result нельзя. Освобождение результата не удаляет строку и не заменяет COMMIT/ROLLBACK транзакции.
Полный контракт, ограничения и следующие варианты. Ниже сохранены параметры, возвращаемые значения, ошибки, версии и дополнительные задачи этой операции. Перед каждым более длинным примером сначала прочитайте его подготовку и ход действий.
Освободить курсор, чтобы он не держал ресурсы и не мешал следующему выполнению.
| Вход / параметр | Смысл |
|---|---|
| Аргументов нет | Завершается данный результат. |
В современном API возвращает true при нормальном завершении. После finalize результат больше не читают. Для результата из prepare исходный statement остаётся доступен; для query его внутреннее владение освобождается вместе с результатом. Завершайте в finally, если чтение может выйти по исключению.
- получили Pen
- finalize старого результата
- bindValue 2
- execute→Notebook
Как читать этот вариант. Даже если все строки не нужны, результат надо закончить. Завершение курсора уменьшает время удержания чтения; оно не заменяет commit явной транзакции.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT NOT NULL, price INTEGER NOT NULL)');
$db->exec("INSERT INTO products VALUES (1, 'Pen', 120), (2, 'Notebook', 350), (3, 'Box', 200)");
$result = $db->query('SELECT name FROM products ORDER BY id');
echo $result->fetchArray(SQLITE3_ASSOC)['name'], "\n";
var_dump($result->finalize());
$db->exec('UPDATE products SET price = 121 WHERE id = 1');
echo $db->querySingle('SELECT price FROM products WHERE id = 1'), "\n";
Ожидаемый результат
Pen
bool(true)
121
Как читать этот вариант. finalize результата prepared statement не означает close самого statement. Команду можно повторно исполнять, завершив прежний ответ.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT NOT NULL, price INTEGER NOT NULL)');
$db->exec("INSERT INTO products VALUES (1, 'Pen', 120), (2, 'Notebook', 350), (3, 'Box', 200)");
$stmt = $db->prepare('SELECT name FROM products WHERE id = ?');
$stmt->bindValue(1, 1);
$first = $stmt->execute();
echo $first->fetchArray(SQLITE3_ASSOC)['name'], "\n";
$first->finalize();
$stmt->bindValue(1, 2);
$second = $stmt->execute();
echo $second->fetchArray(SQLITE3_ASSOC)['name'], "\n";
$second->finalize();
$stmt->close();
Ожидаемый результат
Pen
Notebook
Следующий шаг и альтернативы: SQLite3Stmt::execute, SQLite3Stmt::close, SQLite3::close.
SQLite3Result::__constructРазобраться, как возникает объект результата, не пытаясь собирать его вручную.
private SQLite3Result::__construct()Превратить ответ SQLite в поля товара. SQLite3Result получают через query или execute; его конструктор не является обычным способом загрузки строк. ASSOC выбирает ключи name и stock.
- $result->fetchArray(SQLITE3_ASSOC)
- Каждый вызов переносит одну строку и сдвигает позицию. Отсутствие строки проверено строго, поэтому stock0 не теряется.
- После выполнения: Кисть — 0 шт.
В архиве catalog.php открывает отдельную учебную базу и выполняет schema.sql: 1 — Молоток, 8 шт.; 2 — Перчатки, 15 шт.; 3 — Кисть, 0 шт. База находится в памяти и начинает работу с этих данных при каждом запуске. Распакуйте все файлы вместе и запустите php example.php. Полная подготовка и схема.
| Запись в этом примере | Как читать |
|---|---|
$db->method(...) | Стрелка вызывает действие у конкретного соединения, команды или результата. Скобки содержат входные значения; результат можно сохранить через =. |
[...] | Квадратные скобки задают массив либо читают его элемент. Ключ имени колонки и номер позиции — разные способы адресации. |
<?php
require __DIR__ . '/catalog.php';
$result = $db->query('SELECT name, stock FROM products WHERE id = 3');
$row = $result->fetchArray(SQLITE3_ASSOC);
if ($row === false) { echo "Товар не найден\n"; } else {
echo $row['name'], ' — ', $row['stock'], " шт.\n";
}
$result->finalize();
Ожидаемый результат
Кисть — 0 шт.
Каждый вызов переносит одну строку и сдвигает позицию. Отсутствие строки проверено строго, поэтому stock0 не теряется.
Эти же строки в табличном виде. CLI-пример выше печатает текст; таблица помогает увидеть соответствие названия и остатка. Как получить такую таблицу настоящим HTML-обработчиком.
| Товар / строка | Наличие |
|---|---|
| Кисть | 0 шт. |
Если требование изменится. SQLITE3_NUM даст позиции 0 и 1; SQLITE3_BOTH — оба набора ключей, но не два товара. Для одинаковых имён в JOIN задавайте AS.
Полный контракт, ограничения и следующие варианты. Ниже сохранены параметры, возвращаемые значения, ошибки, версии и дополнительные задачи этой операции. Перед каждым более длинным примером сначала прочитайте его подготовку и ход действий.
Разобраться, как возникает объект результата, не пытаясь собирать его вручную.
| Вход / параметр | Смысл |
|---|---|
| Аргументов нет | Конструктор закрыт для приложения. |
SQLite3Result создают SQLite3::query и SQLite3Stmt::execute. Нельзя сделать new SQLite3Result и добавить туда строки. Для независимого собственного набора данных нужен обычный PHP-массив; для ответа базы — её result с ограниченным сроком жизни. Объект результата не сериализуется.
- query/execute
- SQLite создаёт привязанный результат
- fetchArray получает строку
- finalize завершает
Как читать этот вариант. Одна строка — один массив. Второй fetchArray возвращает false, потому что подходящая строка была одна. Не путать false окончания и null в отдельной колонке.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT NOT NULL, price INTEGER NOT NULL)');
$db->exec("INSERT INTO products VALUES (1, 'Pen', 120), (2, 'Notebook', 350), (3, 'Box', 200)");
$result = $db->query('SELECT name, price FROM products WHERE id = 1');
$row = $result->fetchArray(SQLITE3_ASSOC);
echo $row['name'], ':', $row['price'], "\n";
var_dump($result->fetchArray(SQLITE3_ASSOC));
$result->finalize();
Ожидаемый результат
Pen:120
bool(false)
Как читать этот вариант. finalize результата prepared statement не означает close самого statement. Команду можно повторно исполнять, завершив прежний ответ.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT NOT NULL, price INTEGER NOT NULL)');
$db->exec("INSERT INTO products VALUES (1, 'Pen', 120), (2, 'Notebook', 350), (3, 'Box', 200)");
$stmt = $db->prepare('SELECT name FROM products WHERE id = ?');
$stmt->bindValue(1, 1);
$first = $stmt->execute();
echo $first->fetchArray(SQLITE3_ASSOC)['name'], "\n";
$first->finalize();
$stmt->bindValue(1, 2);
$second = $stmt->execute();
echo $second->fetchArray(SQLITE3_ASSOC)['name'], "\n";
$second->finalize();
$stmt->close();
Ожидаемый результат
Pen
Notebook
Следующий шаг и альтернативы: SQLite3::query, SQLite3Stmt::execute, SQLite3Result::fetchArray.
SQLite3::lastErrorCodeПолучить машинный код последнего отказа, а не разбирать локализованный текст.
SQLite3::lastErrorCode(): intОбработать отказ записи без потери исходного товара. Основной код характеризует последнюю ошибку SQLite.
- $db->lastErrorCode(...)
- Первичный ключ 1 уже занят. Не скрываем отказ и не продолжаем считать импорт успешным; исходная строка остаётся Молотком с 8 штуками.
- После выполнения: Номер уже занят: товар не сохранён · Молоток — 8 шт.
В архиве catalog.php открывает отдельную учебную базу и выполняет schema.sql: 1 — Молоток, 8 шт.; 2 — Перчатки, 15 шт.; 3 — Кисть, 0 шт. База находится в памяти и начинает работу с этих данных при каждом запуске. Распакуйте все файлы вместе и запустите php example.php. Полная подготовка и схема.
| Запись в этом примере | Как читать |
|---|---|
$db->method(...) | Стрелка вызывает действие у конкретного соединения, команды или результата. Скобки содержат входные значения; результат можно сохранить через =. |
try / catch / finally | try содержит действие, catch обрабатывает отказ, finally выполняет освобождение ресурсов и при успехе, и при исключении. |
[...] | Квадратные скобки задают массив либо читают его элемент. Ключ имени колонки и номер позиции — разные способы адресации. |
<?php
require __DIR__ . '/catalog.php';
try {
$db->exec("INSERT INTO products VALUES (1, 'Повтор', 1)");
} catch (Exception $error) {
$diagnostic = $db->lastErrorCode();
if ($diagnostic === 0 || $diagnostic === '') { throw new RuntimeException('Причина отказа потеряна'); }
echo "Номер уже занят: товар не сохранён\n";
}
$result = $db->query('SELECT name, stock FROM products WHERE id = 1');
$row = $result->fetchArray(SQLITE3_ASSOC);
echo $row['name'], ' — ', $row['stock'], " шт.\n";
Ожидаемый результат
Номер уже занят: товар не сохранён
Молоток — 8 шт.
Первичный ключ 1 уже занят. Не скрываем отказ и не продолжаем считать импорт успешным; исходная строка остаётся Молотком с 8 штуками.
Эти же строки в табличном виде. CLI-пример выше печатает текст; таблица помогает увидеть соответствие названия и остатка. Как получить такую таблицу настоящим HTML-обработчиком.
| Товар / строка | Наличие |
|---|---|
| Молоток | 8 шт. |
Если требование изменится. Читайте диагностику сразу после отказа, прежде чем новый запрос перезапишет состояние. В рабочем интерфейсе обработайте известные причины, а технические SQL-сообщения сохраняйте только в закрытом журнале.
Полный контракт, ограничения и следующие варианты. Ниже сохранены параметры, возвращаемые значения, ошибки, версии и дополнительные задачи этой операции. Перед каждым более длинным примером сначала прочитайте его подготовку и ход действий.
Получить машинный код последнего отказа, а не разбирать локализованный текст.
| Вход / параметр | Смысл |
|---|---|
| Аргументов нет | Читается состояние этого соединения сразу после операции. |
Возвращает код результата последней операции SQLite; 0 соответствует успеху. По умолчанию основной код, с enableExtendedResultCodes(true) — расширенный. Значение состояния может измениться следующим обращением, поэтому фиксируйте сразу при отказе.
- операция нарушила ограничение
- catch
- сразу сохранить код/контекст
- показать понятное сообщение
Как читать этот вариант. 19 — общий SQLITE_CONSTRAINT, 2067 — частный UNIQUE. Пример знает контекст конкретной операции; в универсальном коде сначала проверяют код, а неизвестную ошибку передают дальше. Текст SQL-ошибки не отправляется посетителю.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE products (sku TEXT UNIQUE)');
$db->exec("INSERT INTO products VALUES ('A-1')");
try { $db->exec("INSERT INTO products VALUES ('A-1')"); }
catch (Exception $error) {
echo $db->lastErrorCode(), ':', $db->lastExtendedErrorCode(), "\n";
echo "Такой артикул уже существует\n";
}
Ожидаемый результат
19:2067
Такой артикул уже существует
Как читать этот вариант. Ошибка схемы — не «товар не найден». В рабочем сервисе сохраняют код, безопасный контекст и исключение в закрытом логе, а пользователю возвращают общий ответ об отказе.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
try { $db->querySingle('SELECT price FROM missing_products'); }
catch (Exception $error) {
echo $db->lastErrorCode(), "\n";
echo str_contains($db->lastErrorMsg(), 'missing_products') ? "Диагностика содержит имя отсутствующей таблицы\n" : "Другая ошибка\n";
}
Ожидаемый результат
1
Диагностика содержит имя отсутствующей таблицы
Следующий шаг и альтернативы: SQLite3::enableExceptions, SQLite3::enableExtendedResultCodes.
SQLite3::lastExtendedErrorCodeПонять конкретную причину общего отказа ограничения.
SQLite3::lastExtendedErrorCode(): intОбработать отказ записи без потери исходного товара. Расширенный код уточняет причину: например, какой вид ограничения нарушен.
- $db->lastExtendedErrorCode(...)
- Первичный ключ 1 уже занят. Не скрываем отказ и не продолжаем считать импорт успешным; исходная строка остаётся Молотком с 8 штуками.
- После выполнения: Номер уже занят: товар не сохранён · Молоток — 8 шт.
В архиве catalog.php открывает отдельную учебную базу и выполняет schema.sql: 1 — Молоток, 8 шт.; 2 — Перчатки, 15 шт.; 3 — Кисть, 0 шт. База находится в памяти и начинает работу с этих данных при каждом запуске. Распакуйте все файлы вместе и запустите php example.php. Полная подготовка и схема.
| Запись в этом примере | Как читать |
|---|---|
$db->method(...) | Стрелка вызывает действие у конкретного соединения, команды или результата. Скобки содержат входные значения; результат можно сохранить через =. |
try / catch / finally | try содержит действие, catch обрабатывает отказ, finally выполняет освобождение ресурсов и при успехе, и при исключении. |
[...] | Квадратные скобки задают массив либо читают его элемент. Ключ имени колонки и номер позиции — разные способы адресации. |
<?php
require __DIR__ . '/catalog.php';
try {
$db->exec("INSERT INTO products VALUES (1, 'Повтор', 1)");
} catch (Exception $error) {
$diagnostic = $db->lastExtendedErrorCode();
if ($diagnostic === 0 || $diagnostic === '') { throw new RuntimeException('Причина отказа потеряна'); }
echo "Номер уже занят: товар не сохранён\n";
}
$result = $db->query('SELECT name, stock FROM products WHERE id = 1');
$row = $result->fetchArray(SQLITE3_ASSOC);
echo $row['name'], ' — ', $row['stock'], " шт.\n";
Ожидаемый результат
Номер уже занят: товар не сохранён
Молоток — 8 шт.
Первичный ключ 1 уже занят. Не скрываем отказ и не продолжаем считать импорт успешным; исходная строка остаётся Молотком с 8 штуками.
Эти же строки в табличном виде. CLI-пример выше печатает текст; таблица помогает увидеть соответствие названия и остатка. Как получить такую таблицу настоящим HTML-обработчиком.
| Товар / строка | Наличие |
|---|---|
| Молоток | 8 шт. |
Если требование изменится. Читайте диагностику сразу после отказа, прежде чем новый запрос перезапишет состояние. В рабочем интерфейсе обработайте известные причины, а технические SQL-сообщения сохраняйте только в закрытом журнале.
Полный контракт, ограничения и следующие варианты. Ниже сохранены параметры, возвращаемые значения, ошибки, версии и дополнительные задачи этой операции. Перед каждым более длинным примером сначала прочитайте его подготовку и ход действий.
Понять конкретную причину общего отказа ограничения.
| Вход / параметр | Смысл |
|---|---|
| Аргументов нет | Читается состояние этого соединения сразу после операции. |
Всегда возвращает расширенный код независимо от enableExtendedResultCodes. Младшие 8 бит содержат основной код; UNIQUE=2067, NOTNULL=1299 оба имеют основной 19. Не все коды вынесены в PHP-константы SQLite3 — справочник кодов библиотеки SQLite шире.
- операция нарушила ограничение
- catch
- сразу сохранить код/контекст
- показать понятное сообщение
Как читать этот вариант. 19 — общий SQLITE_CONSTRAINT, 2067 — частный UNIQUE. Пример знает контекст конкретной операции; в универсальном коде сначала проверяют код, а неизвестную ошибку передают дальше. Текст SQL-ошибки не отправляется посетителю.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE products (sku TEXT UNIQUE)');
$db->exec("INSERT INTO products VALUES ('A-1')");
try { $db->exec("INSERT INTO products VALUES ('A-1')"); }
catch (Exception $error) {
echo $db->lastErrorCode(), ':', $db->lastExtendedErrorCode(), "\n";
echo "Такой артикул уже существует\n";
}
Ожидаемый результат
19:2067
Такой артикул уже существует
Как читать этот вариант. 1299 — SQLITE_CONSTRAINT_NOTNULL. Общий код 19 тот же, но причина другая; подмена всех constraint-ошибок сообщением «дубликат» была бы неверной. $diagnostic сохраняет текст для закрытого журнала, решение принимает код, не поиск английской фразы.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE parcels (tracking TEXT NOT NULL)');
try { $db->exec('INSERT INTO parcels VALUES (NULL)'); }
catch (Exception $error) {
echo $db->lastErrorCode(), ':', $db->lastExtendedErrorCode(), "\n";
$diagnostic = $db->lastErrorMsg();
echo $diagnostic !== '' ? "Техническое описание получено\n" : "Описание отсутствует\n";
}
Ожидаемый результат
19:1299
Техническое описание получено
Следующий шаг и альтернативы: SQLite3::enableExceptions, SQLite3::enableExtendedResultCodes.
SQLite3::lastErrorMsgПолучить описание технической ошибки для разработчика.
SQLite3::lastErrorMsg(): stringОбработать отказ записи без потери исходного товара. Текст драйвера нужен для закрытой диагностики; он не является готовым сообщением посетителю.
- $db->lastErrorMsg(...)
- Первичный ключ 1 уже занят. Не скрываем отказ и не продолжаем считать импорт успешным; исходная строка остаётся Молотком с 8 штуками.
- После выполнения: Номер уже занят: товар не сохранён · Молоток — 8 шт.
В архиве catalog.php открывает отдельную учебную базу и выполняет schema.sql: 1 — Молоток, 8 шт.; 2 — Перчатки, 15 шт.; 3 — Кисть, 0 шт. База находится в памяти и начинает работу с этих данных при каждом запуске. Распакуйте все файлы вместе и запустите php example.php. Полная подготовка и схема.
| Запись в этом примере | Как читать |
|---|---|
$db->method(...) | Стрелка вызывает действие у конкретного соединения, команды или результата. Скобки содержат входные значения; результат можно сохранить через =. |
try / catch / finally | try содержит действие, catch обрабатывает отказ, finally выполняет освобождение ресурсов и при успехе, и при исключении. |
[...] | Квадратные скобки задают массив либо читают его элемент. Ключ имени колонки и номер позиции — разные способы адресации. |
<?php
require __DIR__ . '/catalog.php';
try {
$db->exec("INSERT INTO products VALUES (1, 'Повтор', 1)");
} catch (Exception $error) {
$diagnostic = $db->lastErrorMsg();
if ($diagnostic === 0 || $diagnostic === '') { throw new RuntimeException('Причина отказа потеряна'); }
echo "Номер уже занят: товар не сохранён\n";
}
$result = $db->query('SELECT name, stock FROM products WHERE id = 1');
$row = $result->fetchArray(SQLITE3_ASSOC);
echo $row['name'], ' — ', $row['stock'], " шт.\n";
Ожидаемый результат
Номер уже занят: товар не сохранён
Молоток — 8 шт.
Первичный ключ 1 уже занят. Не скрываем отказ и не продолжаем считать импорт успешным; исходная строка остаётся Молотком с 8 штуками.
Эти же строки в табличном виде. CLI-пример выше печатает текст; таблица помогает увидеть соответствие названия и остатка. Как получить такую таблицу настоящим HTML-обработчиком.
| Товар / строка | Наличие |
|---|---|
| Молоток | 8 шт. |
Если требование изменится. Читайте диагностику сразу после отказа, прежде чем новый запрос перезапишет состояние. В рабочем интерфейсе обработайте известные причины, а технические SQL-сообщения сохраняйте только в закрытом журнале.
Полный контракт, ограничения и следующие варианты. Ниже сохранены параметры, возвращаемые значения, ошибки, версии и дополнительные задачи этой операции. Перед каждым более длинным примером сначала прочитайте его подготовку и ход действий.
Получить описание технической ошибки для разработчика.
| Вход / параметр | Смысл |
|---|---|
| Аргументов нет | Читается состояние этого соединения сразу после операции. |
Возвращает текст последнего состояния SQLite. Формулировка может меняться между библиотеками; для ветвления используйте коды. Текст может содержать SQL, названия таблиц и пути: не показывайте его посетителю и не записывайте секреты в доступный лог.
- операция нарушила ограничение
- catch
- сразу сохранить код/контекст
- показать понятное сообщение
Как читать этот вариант. Ошибка схемы — не «товар не найден». В рабочем сервисе сохраняют код, безопасный контекст и исключение в закрытом логе, а пользователю возвращают общий ответ об отказе.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
try { $db->querySingle('SELECT price FROM missing_products'); }
catch (Exception $error) {
echo $db->lastErrorCode(), "\n";
echo str_contains($db->lastErrorMsg(), 'missing_products') ? "Диагностика содержит имя отсутствующей таблицы\n" : "Другая ошибка\n";
}
Ожидаемый результат
1
Диагностика содержит имя отсутствующей таблицы
Как читать этот вариант. 1299 — SQLITE_CONSTRAINT_NOTNULL. Общий код 19 тот же, но причина другая; подмена всех constraint-ошибок сообщением «дубликат» была бы неверной. $diagnostic сохраняет текст для закрытого журнала, решение принимает код, не поиск английской фразы.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE parcels (tracking TEXT NOT NULL)');
try { $db->exec('INSERT INTO parcels VALUES (NULL)'); }
catch (Exception $error) {
echo $db->lastErrorCode(), ':', $db->lastExtendedErrorCode(), "\n";
$diagnostic = $db->lastErrorMsg();
echo $diagnostic !== '' ? "Техническое описание получено\n" : "Описание отсутствует\n";
}
Ожидаемый результат
19:1299
Техническое описание получено
Следующий шаг и альтернативы: SQLite3::enableExceptions, SQLite3::enableExtendedResultCodes.
SQLite3::enableExceptionsПрекратить цепочку действий при ошибке SQL и обработать отказ в одном месте.
SQLite3::enableExceptions(bool $enable = false): boolОбработать отказ записи без потери исходного товара. Явно включаем исключения: ошибочная команда прерывает обычный путь и попадает в catch.
- $db->enableExceptions(...)
- Первичный ключ 1 уже занят. Не скрываем отказ и не продолжаем считать импорт успешным; исходная строка остаётся Молотком с 8 штуками.
- После выполнения: Номер уже занят: товар не сохранён · Молоток — 8 шт.
В архиве catalog.php открывает отдельную учебную базу и выполняет schema.sql: 1 — Молоток, 8 шт.; 2 — Перчатки, 15 шт.; 3 — Кисть, 0 шт. База находится в памяти и начинает работу с этих данных при каждом запуске. Распакуйте все файлы вместе и запустите php example.php. Полная подготовка и схема.
| Запись в этом примере | Как читать |
|---|---|
$db->method(...) | Стрелка вызывает действие у конкретного соединения, команды или результата. Скобки содержат входные значения; результат можно сохранить через =. |
try / catch / finally | try содержит действие, catch обрабатывает отказ, finally выполняет освобождение ресурсов и при успехе, и при исключении. |
[...] | Квадратные скобки задают массив либо читают его элемент. Ключ имени колонки и номер позиции — разные способы адресации. |
<?php
require __DIR__ . '/catalog.php';
$db->enableExceptions(true);
try {
$db->exec("INSERT INTO products VALUES (1, 'Повтор', 1)");
} catch (Exception $error) {
echo "Номер уже занят: товар не сохранён\n";
}
$result = $db->query('SELECT name, stock FROM products WHERE id = 1');
$row = $result->fetchArray(SQLITE3_ASSOC);
echo $row['name'], ' — ', $row['stock'], " шт.\n";
Ожидаемый результат
Номер уже занят: товар не сохранён
Молоток — 8 шт.
Первичный ключ 1 уже занят. Не скрываем отказ и не продолжаем считать импорт успешным; исходная строка остаётся Молотком с 8 штуками.
Эти же строки в табличном виде. CLI-пример выше печатает текст; таблица помогает увидеть соответствие названия и остатка. Как получить такую таблицу настоящим HTML-обработчиком.
| Товар / строка | Наличие |
|---|---|
| Молоток | 8 шт. |
Если требование изменится. Читайте диагностику сразу после отказа, прежде чем новый запрос перезапишет состояние. В рабочем интерфейсе обработайте известные причины, а технические SQL-сообщения сохраняйте только в закрытом журнале.
Полный контракт, ограничения и следующие варианты. Ниже сохранены параметры, возвращаемые значения, ошибки, версии и дополнительные задачи этой операции. Перед каждым более длинным примером сначала прочитайте его подготовку и ход действий.
Прекратить цепочку действий при ошибке SQL и обработать отказ в одном месте.
| Вход / параметр | Смысл |
|---|---|
| enable | true — бросать исключения; false — старый режим warning, deprecated с PHP 8.3. |
Возвращает прежнее значение флага. Без аргумента вы запросите false: название метода само по себе не означает включение. Режим действует также на statements/results этого соединения; коды и сообщение доступны в обоих режимах. TypeError/ValueError/Error не превращаются в успешный SQL-ответ от этого флага.
- enableExceptions(true)
- ошибка prepare
- catch Exception
- код после неуспешного запроса не маскирует отказ
Как читать этот вариант. Режим задаётся на соединении и распространяется на созданные SQLite3Stmt/Result. В PHP 8.3+ конкретный класс — SQLite3Exception; catch(Exception) совместим и с более ранними версиями.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
try { $db->exec('INSERT INTO missing_table VALUES (1)'); }
catch (Exception $error) { echo "Запрос не выполнен\n"; }
Ожидаемый результат
Запрос не выполнен
Как читать этот вариант. Возвращаемое true — предыдущее состояние режима, не успешность запроса. Без аргумента enableExceptions использует false; начиная с 8.3 такое отключение deprecated, поэтому пишем true явно.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
var_dump($db->enableExceptions(true));
$stmt = $db->prepare('SELECT :n');
try { $db->prepare('SELECT FROM'); }
catch (Exception $error) { echo "Ошибка подготовки перехвачена\n"; }
Ожидаемый результат
bool(true)
Ошибка подготовки перехвачена
Следующий шаг и альтернативы: SQLite3::lastErrorCode, SQLite3::lastErrorMsg.
SQLite3::enableExtendedResultCodesРазрешить коду приложения получать уточнённые причины в общем канале ошибок.
SQLite3::enableExtendedResultCodes(bool $enable = true): boolОбработать отказ записи без потери исходного товара. Включаем более подробные коды SQLite, чтобы различать причины одного общего класса ошибки.
- $db->enableExtendedResultCodes(...)
- Первичный ключ 1 уже занят. Не скрываем отказ и не продолжаем считать импорт успешным; исходная строка остаётся Молотком с 8 штуками.
- После выполнения: Номер уже занят: товар не сохранён · Молоток — 8 шт.
В архиве catalog.php открывает отдельную учебную базу и выполняет schema.sql: 1 — Молоток, 8 шт.; 2 — Перчатки, 15 шт.; 3 — Кисть, 0 шт. База находится в памяти и начинает работу с этих данных при каждом запуске. Распакуйте все файлы вместе и запустите php example.php. Полная подготовка и схема.
| Запись в этом примере | Как читать |
|---|---|
$db->method(...) | Стрелка вызывает действие у конкретного соединения, команды или результата. Скобки содержат входные значения; результат можно сохранить через =. |
try / catch / finally | try содержит действие, catch обрабатывает отказ, finally выполняет освобождение ресурсов и при успехе, и при исключении. |
[...] | Квадратные скобки задают массив либо читают его элемент. Ключ имени колонки и номер позиции — разные способы адресации. |
<?php
require __DIR__ . '/catalog.php';
$db->enableExtendedResultCodes(true);
try {
$db->exec("INSERT INTO products VALUES (1, 'Повтор', 1)");
} catch (Exception $error) {
echo "Номер уже занят: товар не сохранён\n";
}
$result = $db->query('SELECT name, stock FROM products WHERE id = 1');
$row = $result->fetchArray(SQLITE3_ASSOC);
echo $row['name'], ' — ', $row['stock'], " шт.\n";
Ожидаемый результат
Номер уже занят: товар не сохранён
Молоток — 8 шт.
Первичный ключ 1 уже занят. Не скрываем отказ и не продолжаем считать импорт успешным; исходная строка остаётся Молотком с 8 штуками.
Эти же строки в табличном виде. CLI-пример выше печатает текст; таблица помогает увидеть соответствие названия и остатка. Как получить такую таблицу настоящим HTML-обработчиком.
| Товар / строка | Наличие |
|---|---|
| Молоток | 8 шт. |
Если требование изменится. Читайте диагностику сразу после отказа, прежде чем новый запрос перезапишет состояние. В рабочем интерфейсе обработайте известные причины, а технические SQL-сообщения сохраняйте только в закрытом журнале.
Полный контракт, ограничения и следующие варианты. Ниже сохранены параметры, возвращаемые значения, ошибки, версии и дополнительные задачи этой операции. Перед каждым более длинным примером сначала прочитайте его подготовку и ход действий.
Разрешить коду приложения получать уточнённые причины в общем канале ошибок.
| Вход / параметр | Смысл |
|---|---|
| enable | true включает, false выключает расширенные коды в lastErrorCode. |
Возвращает true при успехе, false при отказе. Это не переключение исключений и не изменение lastExtendedErrorCode: последний всегда подробный. Включение требует согласовать проверки кодов во всей обёртке.
- NOT NULL нарушен
- код1299
- основной код1299 & 255 =19
- сервис выбирает причину
Как читать этот вариант. Не смешивайте ожидание lastErrorCode===19 со включёнными extended codes: после переключения там может быть 2067. Основной код при необходимости получают & 0xff.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE rows (key TEXT UNIQUE)');
$db->exec("INSERT INTO rows VALUES ('same')");
try { $db->exec("INSERT INTO rows VALUES ('same')"); } catch (Exception $error) {}
echo $db->lastErrorCode(), ':', $db->lastExtendedErrorCode(), "\n";
$db->enableExtendedResultCodes(true);
echo $db->lastErrorCode(), "\n";
Ожидаемый результат
19:2067
2067
Как читать этот вариант. У этого метода default true, в отличие от enableExceptions. Побитовое & оставляет основные 8 бит кода; расширенная причина остаётся доступна отдельно.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->enableExtendedResultCodes();
$db->exec('CREATE TABLE records (name TEXT NOT NULL)');
try { $db->exec('INSERT INTO records VALUES (NULL)'); }
catch (Exception $error) {
$code = $db->lastErrorCode();
echo $code, ':', $code & 0xff, "\n";
}
Ожидаемый результат
1299:19
Следующий шаг и альтернативы: SQLite3::lastErrorCode, SQLite3::lastExtendedErrorCode.
SQLite3::busyTimeoutНе проваливать операцию мгновенно, если другой процесс ненадолго пишет тот же файл.
SQLite3::busyTimeout(int $milliseconds): boolДать короткой блокировке ограниченное время ожидания. Значение задаётся в миллисекундах для одного соединения. Таймаут помогает при временной занятости, но не отменяет необходимость обработать отказ.
- $second->busyTimeout(5)
- Первое соединение удерживает транзакцию записи. Второе не может записать за 5 мс; после отката блокирующей транзакции читаем неизменённый остаток 8.
- После выполнения: Запись занята: изменение не выполнено · Молоток — 8 шт.
В этом самостоятельном примере подготовка указана непосредственно в коде и дополнительных файлах архива. Запуск: php example.php; требования и внешние зависимости отмечены перед кодом.
| Запись в этом примере | Как читать |
|---|---|
$db->method(...) | Стрелка вызывает действие у конкретного соединения, команды или результата. Скобки содержат входные значения; результат можно сохранить через =. |
try / catch / finally | try содержит действие, catch обрабатывает отказ, finally выполняет освобождение ресурсов и при успехе, и при исключении. |
[...] | Квадратные скобки задают массив либо читают его элемент. Ключ имени колонки и номер позиции — разные способы адресации. |
<?php
$path = __DIR__ . '/busy.sqlite';
$first = new SQLite3($path);
$first->enableExceptions(true);
$first->exec(file_get_contents(__DIR__ . '/schema.sql'));
$second = new SQLite3($path);
$second->enableExceptions(true);
$second->busyTimeout(5);
$first->exec('BEGIN IMMEDIATE');
try {
$second->exec('UPDATE products SET stock = 6 WHERE id = 1');
} catch (Exception $error) {
echo "Запись занята: изменение не выполнено\n";
} finally { $first->exec('ROLLBACK'); }
$row = $second->querySingle('SELECT name, stock FROM products WHERE id = 1', true);
echo $row['name'], ' — ', $row['stock'], " шт.\n";
Ожидаемый результат
Запись занята: изменение не выполнено
Молоток — 8 шт.
Первое соединение удерживает транзакцию записи. Второе не может записать за 5 мс; после отката блокирующей транзакции читаем неизменённый остаток 8.
Эти же строки в табличном виде. CLI-пример выше печатает текст; таблица помогает увидеть соответствие названия и остатка. Как получить такую таблицу настоящим HTML-обработчиком.
| Товар / строка | Наличие |
|---|---|
| Молоток | 8 шт. |
Если требование изменится. Это локальный воспроизводимый конфликт двух соединений, не нагрузочный тест. Избегайте длинных транзакций; WAL не превращает SQLite в систему с неограниченными одновременными писателями.
Полный контракт, ограничения и следующие варианты. Ниже сохранены параметры, возвращаемые значения, ошибки, версии и дополнительные задачи этой операции. Перед каждым более длинным примером сначала прочитайте его подготовку и ход действий.
Не проваливать операцию мгновенно, если другой процесс ненадолго пишет тот же файл.
| Вход / параметр | Смысл |
|---|---|
| milliseconds | Время ожидания в миллисекундах; 0/отрицательное значение отключает busy handler SQLite. |
Возвращает true/false установки. Предел не гарантирует ожидание каждого вида блокировки: ради предотвращения deadlock SQLite может вернуть BUSY раньше; SQLITE_LOCKED имеет другой контекст. Не устанавливает timeout исполнения любого медленного SELECT. Каждый connection имеет свой busy handler; PRAGMA busy_timeout заменяет его настройку. WAL позволяет сочетать читателя и писателя, но писатель всё ещё один.
- A:BEGIN IMMEDIATE
- B пытается INSERT
- ожидание до границы→BUSY
- A отпустил→новая попытка B успешна
Как читать этот вариант. Единицы — миллисекунды, 1500 = 1,5 секунды. Это не принудительная пауза каждого запроса: ожидание возникает только при подходящей блокировке.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
var_dump($db->busyTimeout(1500));
echo $db->querySingle('PRAGMA busy_timeout'), "\n";
Ожидаемый результат
bool(true)
1500
Как читать этот вариант. Код5 SQLITE_BUSY: другое соединение удерживает право записи. 1 мс нужен только для быстрого воспроизводимого опыта. В приложении выбирают предел по допустимой задержке, короткие транзакции и ограниченные идемпотентные повторы; бесконечный цикл sleep не решает архитектуру блокировок.
<?php
$path = tempnam(sys_get_temp_dir(), 'sql3-lock-');
$a = new SQLite3($path); $a->enableExceptions(true);
$b = new SQLite3($path); $b->enableExceptions(true);
try {
$a->exec('CREATE TABLE queue (id INTEGER)');
$a->exec('BEGIN IMMEDIATE');
$b->busyTimeout(1);
try { $b->exec('INSERT INTO queue VALUES (1)'); }
catch (Exception $error) { echo $b->lastErrorCode(), "\n"; }
$a->exec('ROLLBACK');
$b->exec('INSERT INTO queue VALUES (1)');
echo $b->querySingle('SELECT COUNT(*) FROM queue'), "\n";
} finally { $b->close(); $a->close(); unlink($path); }
Ожидаемый результат
5
1
Следующий шаг и альтернативы: SQLite3::exec, SQLite3::lastErrorCode, SQLite3Stmt::busy.
SQLite3::escapeStringПонять и поддержать старую сборку SQL-литералов, предпочтя параметры в новом коде.
SQLite3::escapeString(string $string): stringСохранить апостроф при чтении старого SQL-кода. Метод удваивает специальные SQL-кавычки внутри текста, но не добавляет внешние кавычки литерала. Для нового кода с вводом проще bindValue.
- SQLite3::escapeString($name)
- Внешние одинарные кавычки добавлены автором SQL один раз, обработанный апостроф остаётся частью названия товара.
- После выполнения: Кисть O'Reilly — 2 шт.
В архиве catalog.php открывает отдельную учебную базу и выполняет schema.sql: 1 — Молоток, 8 шт.; 2 — Перчатки, 15 шт.; 3 — Кисть, 0 шт. База находится в памяти и начинает работу с этих данных при каждом запуске. Распакуйте все файлы вместе и запустите php example.php. Полная подготовка и схема.
| Запись в этом примере | Как читать |
|---|---|
$db->method(...) | Стрелка вызывает действие у конкретного соединения, команды или результата. Скобки содержат входные значения; результат можно сохранить через =. |
Класс::ИМЯ | Двойное двоеточие обращается к константе режима либо статическому методу класса; это не выбор колонки SQL. |
[...] | Квадратные скобки задают массив либо читают его элемент. Ключ имени колонки и номер позиции — разные способы адресации. |
<?php
require __DIR__ . '/catalog.php';
$name = "Кисть O'Reilly";
$escaped = SQLite3::escapeString($name);
$db->exec("INSERT INTO products (name, stock) VALUES ('" . $escaped . "', 2)");
$result = $db->query('SELECT name, stock FROM products WHERE id = 4');
$row = $result->fetchArray(SQLITE3_ASSOC);
echo $row['name'], ' — ', $row['stock'], " шт.\n";
Ожидаемый результат
Кисть O'Reilly — 2 шт.
Внешние одинарные кавычки добавлены автором SQL один раз, обработанный апостроф остаётся частью названия товара.
Эти же строки в табличном виде. CLI-пример выше печатает текст; таблица помогает увидеть соответствие названия и остатка. Как получить такую таблицу настоящим HTML-обработчиком.
| Товар / строка | Наличие |
|---|---|
| Кисть O'Reilly | 2 шт. |
Если требование изменится. Метод не подходит для бинарных данных с NUL и не экранирует имена таблиц или HTML. Увеличение числа параметров — повод перейти на prepare, а не усложнять склейку.
Полный контракт, ограничения и следующие варианты. Ниже сохранены параметры, возвращаемые значения, ошибки, версии и дополнительные задачи этой операции. Перед каждым более длинным примером сначала прочитайте его подготовку и ход действий.
Понять и поддержать старую сборку SQL-литералов, предпочтя параметры в новом коде.
| Вход / параметр | Смысл |
|---|---|
| string | Текст для содержимого SQL-строки, не имя колонки, не весь запрос и не HTML. |
Возвращает экранированную строку без окружающих кавычек. Метод не binary-safe для NUL: BLOB передают bindValue/bindParam с SQLITE3_BLOB. addslashes для SQLite непригоден. Значение, безопасное в SQL-строке, не становится безопасным в HTML; там нужен отдельный htmlspecialchars.
- O'Neil
- escapeString→O''Neil
- внешние кавычки создают SQL-литерал
- лучше prepare с исходным текстом
Как читать этот вариант. Метод удваивает апостроф для содержимого строкового литерала. Внешние SQL-кавычки он не добавляет. Это не HTML-экранирование и не валидация имени.
<?php
$text = "O'Neil";
echo SQLite3::escapeString($text), "\n";
Ожидаемый результат
O''Neil
Как читать этот вариант. Показаны одинаковые результаты. Для нового кода второй путь надёжнее разделяет SQL и данные. Перед bindValue передаётся исходный $name, не $escaped: иначе апостроф будет испорчен двойной обработкой.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$name = "O'Neil";
$escaped = SQLite3::escapeString($name);
echo $db->querySingle("SELECT '$escaped'"), "\n";
$stmt = $db->prepare('SELECT :name');
$stmt->bindValue(':name', $name, SQLITE3_TEXT);
echo $stmt->execute()->fetchArray(SQLITE3_NUM)[0], "\n";
Ожидаемый результат
O'Neil
O'Neil
Следующий шаг и альтернативы: SQLite3Stmt::bindValue, SQLite3::prepare.
SQLite3::backupСохранить согласованную копию или перенести базу между памятью и файлом.
SQLite3::backup(SQLite3 $destination, string $sourceDatabase = "main", string $destinationDatabase = "main"): boolПроверить резервную копию чтением настоящего товара. backup копирует состояние одной базы в соединение-получатель. Здесь получатель — новый учебный файл, а не рабочая база с чужими данными.
- $db->backup($copy)
- Появляется catalog-backup.sqlite рядом с example.php. Проверка SELECT из получателя подтверждает, что копия содержит таблицу и нужные данные.
- После выполнения: Копия: Молоток — 8 шт.
В архиве catalog.php открывает отдельную учебную базу и выполняет schema.sql: 1 — Молоток, 8 шт.; 2 — Перчатки, 15 шт.; 3 — Кисть, 0 шт. База находится в памяти и начинает работу с этих данных при каждом запуске. Распакуйте все файлы вместе и запустите php example.php. Полная подготовка и схема.
| Запись в этом примере | Как читать |
|---|---|
$db->method(...) | Стрелка вызывает действие у конкретного соединения, команды или результата. Скобки содержат входные значения; результат можно сохранить через =. |
[...] | Квадратные скобки задают массив либо читают его элемент. Ключ имени колонки и номер позиции — разные способы адресации. |
<?php
require __DIR__ . '/catalog.php';
$path = __DIR__ . '/catalog-backup.sqlite';
if (file_exists($path)) { throw new RuntimeException('Учебная копия уже существует'); }
$copy = new SQLite3($path);
$copy->enableExceptions(true);
if (!$db->backup($copy)) { throw new RuntimeException('Копия не создана'); }
$row = $copy->querySingle('SELECT name, stock FROM products WHERE id = 1', true);
echo 'Копия: ', $row['name'], ' — ', $row['stock'], " шт.\n";
$copy->close();
Ожидаемый результат
Копия: Молоток — 8 шт.
Появляется catalog-backup.sqlite рядом с example.php. Проверка SELECT из получателя подтверждает, что копия содержит таблицу и нужные данные.
Эти же строки в табличном виде. CLI-пример выше печатает текст; таблица помогает увидеть соответствие названия и остатка. Как получить такую таблицу настоящим HTML-обработчиком.
| Товар / строка | Наличие |
|---|---|
| Копия: Молоток | 8 шт. |
Если требование изменится. Не копируйте наугад один открытый файл базы, особенно при WAL. Отдельно проверяйте восстановление, доступность каталога, срок хранения и права; возвращённый успех не заменяет проверку пригодности копии.
Полный контракт, ограничения и следующие варианты. Ниже сохранены параметры, возвращаемые значения, ошибки, версии и дополнительные задачи этой операции. Перед каждым более длинным примером сначала прочитайте его подготовку и ход действий.
Сохранить согласованную копию или перенести базу между памятью и файлом.
| Вход / параметр | Смысл |
|---|---|
| destination | Открытое отдельное соединение-назначение; его содержимое будет заменено. |
| sourceDatabase | Имя схемы в источнике: main, temp или имя AS при ATTACH. |
| destinationDatabase | Имя схемы в соединении назначения, обычно main. |
true — успех, false/исключение — отказ. Копирование не является добавлением строк, backup может быть дорогим и столкнуться с блокировкой. Не выбирайте рабочую базу как случайное назначение. Альтернатива SQLite 3.27+ — VACUUM INTO для нового файла, с иными ограничениями и стоимостью. Запасная копия полезна, когда проверено восстановление.
- источник products(3 строки)
- backup в отдельное назначение
- независимые 3 строки
- проверка COUNT/SUM
Как читать этот вариант. Направление — источник->backup(назначение). Новая копия независима: удаление из исходной учебной базы не удаляет её три товара. backup перезаписывает содержимое назначения!
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT NOT NULL, price INTEGER NOT NULL)');
$db->exec("INSERT INTO products VALUES (1, 'Pen', 120), (2, 'Notebook', 350), (3, 'Box', 200)");
$copy = new SQLite3(':memory:');
$copy->enableExceptions(true);
var_dump($db->backup($copy));
$db->exec('DELETE FROM products');
echo $copy->querySingle('SELECT COUNT(*) FROM products'), "\n";
$copy->close();
Ожидаемый результат
bool(true)
3
Как читать этот вариант. Созданный нами временный файл получает согласованную SQLite-копию, затем открывается только для чтения. Для производственной копии выбирают новый путь и отдельно проверяют восстановление; обычное copy одного файла живой WAL-базы недостаточно.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT NOT NULL, price INTEGER NOT NULL)');
$db->exec("INSERT INTO products VALUES (1, 'Pen', 120), (2, 'Notebook', 350), (3, 'Box', 200)");
$path = tempnam(sys_get_temp_dir(), 'sql3-copy-');
try {
$file = new SQLite3($path); $file->enableExceptions(true);
$db->backup($file, 'main', 'main');
$file->close();
$reader = new SQLite3($path, SQLITE3_OPEN_READONLY);
echo $reader->querySingle('SELECT SUM(price) FROM products'), "\n";
$reader->close();
} finally { unlink($path); }
Ожидаемый результат
670
Следующий шаг и альтернативы: SQLite3::open, SQLite3::close, SQLite3::busyTimeout.
SQLite3::versionПроверить возможности фактической SQLite-библиотеки в установленном PHP.
SQLite3::version(): arrayПроверить библиотеку перед использованием её возможностей. Метод возвращает версию библиотеки SQLite внутри PHP, а не версию PHP и не версию формата отдельного файла.
- SQLite3::version()
- Проверка возможности предшествует CREATE STRICT. На старой библиотеке вместо непонятной SQL-ошибки приложение объяснит недостающую зависимость.
- После выполнения: Проверенный остаток: 8 шт.
В архиве catalog.php открывает отдельную учебную базу и выполняет schema.sql: 1 — Молоток, 8 шт.; 2 — Перчатки, 15 шт.; 3 — Кисть, 0 шт. База находится в памяти и начинает работу с этих данных при каждом запуске. Распакуйте все файлы вместе и запустите php example.php. Полная подготовка и схема.
| Запись в этом примере | Как читать |
|---|---|
$db->method(...) | Стрелка вызывает действие у конкретного соединения, команды или результата. Скобки содержат входные значения; результат можно сохранить через =. |
Класс::ИМЯ | Двойное двоеточие обращается к константе режима либо статическому методу класса; это не выбор колонки SQL. |
[...] | Квадратные скобки задают массив либо читают его элемент. Ключ имени колонки и номер позиции — разные способы адресации. |
<?php
require __DIR__ . '/catalog.php';
$version = SQLite3::version();
if ($version['versionNumber'] < 3037000) { throw new RuntimeException('Для STRICT нужен SQLite 3.37+'); }
$db->exec('CREATE TABLE measurements (value INTEGER NOT NULL) STRICT');
$db->exec('INSERT INTO measurements VALUES (8)');
echo 'Проверенный остаток: ', $db->querySingle('SELECT value FROM measurements'), " шт.\n";
Ожидаемый результат
Проверенный остаток: 8 шт.
Проверка возможности предшествует CREATE STRICT. На старой библиотеке вместо непонятной SQL-ошибки приложение объяснит недостающую зависимость.
Если требование изменится. Новая версия библиотеки не добавляет автоматически методы нового PHP. Для API PHP 8.5 проверяйте также PHP_VERSION_ID или наличие метода; не сравнивайте строки версий как обычные числа.
Полный контракт, ограничения и следующие варианты. Ниже сохранены параметры, возвращаемые значения, ошибки, версии и дополнительные задачи этой операции. Перед каждым более длинным примером сначала прочитайте его подготовку и ход действий.
Проверить возможности фактической SQLite-библиотеки в установленном PHP.
| Вход / параметр | Смысл |
|---|---|
| Аргументов нет | Статический вызов без открытия базы. |
Массив содержит versionString и versionNumber. Числовая форма major*1000000 + minor*1000 + patch; проверять 3.43.0 удобно по 3043000. Возможность также зависит от compile-time flags: одного номера недостаточно для всех модулей FTS, расширений и callback API.
- SQLite3::version
- versionNumber=3053004 в тестовой среде
- проверка >=3043000
- плюс method_exists для PHP API
Как читать этот вариант. versionString — строка вроде 3.53.4, versionNumber — 3053004. Точный вывод номера зависит от библиотеки сборки; обновление PHP и обновление SQLite — связанные, но разные события.
<?php
$version = SQLite3::version();
echo implode(',', array_keys($version)), "\n";
echo is_int($version['versionNumber']) ? "Числовая версия доступна\n" : "Неожиданный формат\n";
Ожидаемый результат
versionString,versionNumber
Числовая версия доступна
Как читать этот вариант. Ожидаемый результат указан для проверочной PHP 8.5.11 / SQLite 3.53.4. В старой среде ответ другой; проверка нужна именно для осознанного выбора ветки, а не жёсткого сравнения строк версий.
<?php
$available = method_exists(SQLite3Stmt::class, 'setExplain')
&& SQLite3::version()['versionNumber'] >= 3043000;
echo $available ? "EXPLAIN API доступен\n" : "Используйте SQL EXPLAIN QUERY PLAN\n";
Ожидаемый результат
EXPLAIN API доступен
Следующий шаг и альтернативы: SQLite3Stmt::setExplain, SQLite3::loadExtension.
SQLite3::loadExtensionДобавить SQLite нативную возможность, например приблизительный поиск, которой нет в стандартной библиотеке.
SQLite3::loadExtension(string $name): boolОбъяснить отказ, если нужная библиотека отчёта не установлена. Загружается нативная библиотека SQLite, установленная администратором для конкретной сборки. Путь приходит из доверенной конфигурации, никогда из формы.
- $db->loadExtension($library)
- Архив не содержит стороннего бинарника: выполняется только явная ветка отсутствия зависимости. Успешная загрузка не проверялась и не заявляется.
- После выполнения: Расширенный отчёт недоступен: библиотека не установлена
В архиве catalog.php открывает отдельную учебную базу и выполняет schema.sql: 1 — Молоток, 8 шт.; 2 — Перчатки, 15 шт.; 3 — Кисть, 0 шт. База находится в памяти и начинает работу с этих данных при каждом запуске. Распакуйте все файлы вместе и запустите php example.php. Полная подготовка и схема.
| Запись в этом примере | Как читать |
|---|---|
$db->method(...) | Стрелка вызывает действие у конкретного соединения, команды или результата. Скобки содержат входные значения; результат можно сохранить через =. |
<?php
require __DIR__ . '/catalog.php';
$library = __DIR__ . '/native/report-extension';
if (!is_file($library)) {
echo "Расширенный отчёт недоступен: библиотека не установлена\n";
} else {
$db->loadExtension($library);
echo "Библиотека отчёта подключена\n";
}
Ожидаемый результат
Расширенный отчёт недоступен: библиотека не установлена
Архив не содержит стороннего бинарника: выполняется только явная ветка отсутствия зависимости. Успешная загрузка не проверялась и не заявляется.
Если требование изменится. Разрешённый каталог/ini, совместимость ОС, разрядности и запрет загрузки в конкретной сборке проверяют отдельно. PHP callback createFunction часто достаточен для небольшого собственного вычисления и не требует бинарной библиотеки.
Полный контракт, ограничения и следующие варианты. Ниже сохранены параметры, возвращаемые значения, ошибки, версии и дополнительные задачи этой операции. Перед каждым более длинным примером сначала прочитайте его подготовку и ход действий.
Добавить SQLite нативную возможность, например приблизительный поиск, которой нет в стандартной библиотеке.
| Вход / параметр | Смысл |
|---|---|
| name | Имя доверенной библиотеки в sqlite3.extension_dir; формат и зависимые библиотеки должны подходить ОС и сборке. |
true — загрузилась, false/исключение — отказ. Это загрузка машинного кода в PHP-процесс, а не подключение PHP-файла и не расширение php_sqlite3.dll. Настройка extension_dir относится к SQLite-модулям. Возможность может быть исключена при сборке; PHP8.5.11 также запрещает её в многопоточных web-SAPI ZTS, кроме cli и SAPI с именами cgi*/embed*. Включение требует серверного контроля. Не загружайте путь из пользовательского ввода и не включайте SQL load_extension для произвольного SQL. Для простой скалярной логики достаточно createFunction без бинарного модуля.
- доверенный модуль spellfix
- loadExtension регистрирует virtual table
- CREATE VIRTUAL TABLE
- MATCH находит notebook
Как читать этот вариант. Контекстный пример, проверен только синтаксис: нужна собранная для этой ОС/SQLite доверенная библиотека spellfix (реальное имя файла зависит от сборки) в sqlite3.extension_dir. Никакие библиотеки пример не скачивает. spellfix1 отсутствует в обычной сборке по умолчанию.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->loadExtension('spellfix');
$db->exec('CREATE VIRTUAL TABLE vocabulary USING spellfix1');
$db->exec("INSERT INTO vocabulary(word) VALUES ('notebook'), ('pencil')");
$result = $db->query("SELECT word FROM vocabulary WHERE word MATCH 'notebok' AND top = 1");
echo $result->fetchArray(SQLITE3_ASSOC)['word'], "\n";
$result->finalize();
Ожидаемый результат
notebook
Как читать этот вариант. Контекстный пример, только lint. Показан успех при установленном модуле; если модуль отсутствует/запрещён, выводится «Дополнительный поиск временно недоступен». Техническое исключение журналируют отдельно. Имя библиотеки задаёт конфигурация администратора, никогда параметр формы.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
try {
if (!$db->loadExtension('spellfix')) { throw new RuntimeException('Модуль не загружен'); }
$db->exec('CREATE VIRTUAL TABLE vocabulary USING spellfix1');
echo "Поиск похожих слов готов\n";
} catch (Exception $error) {
echo "Дополнительный поиск временно недоступен\n";
}
Ожидаемый результат
Поиск похожих слов готов
Следующий шаг и альтернативы: SQLite3::createFunction, SQLite3::version.
SQLite3::createFunctionИспользовать понятное PHP-правило в SQL, когда встроенного выражения недостаточно.
SQLite3::createFunction(string $name, callable $callback, int $argCount = -1, int $flags = 0): boolРассчитать текст наличия при чтении каталога. Регистрируем правило в соединении до SQL, который его использует. function задаёт тело callback; return возвращает результат базе. Для старого PDO соответствующий метод имеет префикс sqlite.
- $db->createFunction('stock_label', $callback, 1)
- SQL передаёт stock0 в PHP callback; return отдаёт текст обратно SQL под именем availability. Один параметр функции указан числом 1.
- После выполнения: Кисть: Нет в наличии
В архиве catalog.php открывает отдельную учебную базу и выполняет schema.sql: 1 — Молоток, 8 шт.; 2 — Перчатки, 15 шт.; 3 — Кисть, 0 шт. База находится в памяти и начинает работу с этих данных при каждом запуске. Распакуйте все файлы вместе и запустите php example.php. Полная подготовка и схема.
| Запись в этом примере | Как читать |
|---|---|
$db->method(...) | Стрелка вызывает действие у конкретного соединения, команды или результата. Скобки содержат входные значения; результат можно сохранить через =. |
function (...) { ... } | Объявляет функцию; тело выполняется при её вызове, не при объявлении. Кто и с какими данными её вызывает, показано в текущем примере. return передаёт значение обратно в место вызова. |
[...] | Квадратные скобки задают массив либо читают его элемент. Ключ имени колонки и номер позиции — разные способы адресации. |
<?php
require __DIR__ . '/catalog.php';
$db->createFunction('stock_label', function ($stock) {
return $stock > 0 ? $stock . ' шт.' : 'Нет в наличии';
}, 1);
$result = $db->query('SELECT name, stock_label(stock) AS availability FROM products WHERE id = 3');
$row = $result->fetchArray(SQLITE3_ASSOC);
echo $row['name'], ': ', $row['availability'], "\n";
Ожидаемый результат
Кисть: Нет в наличии
SQL передаёт stock0 в PHP callback; return отдаёт текст обратно SQL под именем availability. Один параметр функции указан числом 1.
Если требование изменится. Callback исполняется для строк запроса и может стоить дорого. Он существует только у данного соединения. Не делайте внутри повторный запрос к той же базе или внешнюю сеть; обычные выражения CASE часто проще.
Полный контракт, ограничения и следующие варианты. Ниже сохранены параметры, возвращаемые значения, ошибки, версии и дополнительные задачи этой операции. Перед каждым более длинным примером сначала прочитайте его подготовку и ход действий.
Использовать понятное PHP-правило в SQL, когда встроенного выражения недостаточно.
| Вход / параметр | Смысл |
|---|---|
| name | Имя SQL-функции в этом соединении; существующее имя можно переопределить, что требует осторожности. |
| callback | Вызываемый PHP-код принимает SQL-аргументы и возвращает понятное SQLite scalar/null, не PHP-массив. |
| argCount | Число SQL-аргументов; -1 разрешает произвольное количество. |
| flags | 0 либо SQLITE3_DETERMINISTIC (PHP 7.1.4+): одинаковые входы дают одинаковый результат, без зависимости от текущего времени/случайности/меняющейся внешней среды. |
true — регистрация успешна, false/исключение — отказ. Регистрация относится к соединению, не хранит PHP-код в файле базы. Поздний SQL, использующий имя, требует повторной регистрации после открытия. Callback должен обрабатывать NULL и реальные типы данных; не рассчитывайте на точное число/порядок вызовов оптимизатора. DETERMINISTIC — обещание разработчика, не проверка и не гарантия кеширования.
- SELECT add_delivery(500)
- SQLite вызывает PHP callback($price=500)
- $price+200→return700
- SELECT→querySingle→echo700
Как читать этот вариант. SQL передаёт 500 в параметр $price PHP-функции; тело прибавляет 200; return возвращает 700 в ту же ячейку SELECT. Затем querySingle отдаёт 700 вызывающему PHP-коду. Регистрация ещё не вызывает функцию.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->createFunction('add_delivery', function ($price) { return $price + 200; }, 1);
echo $db->querySingle('SELECT add_delivery(500)'), "\n";
Ожидаемый результат
700
Как читать этот вариант. Для такого сложения хватает встроенного SQL price+20; пример показывает мост к уже существующему PHP-правилу. Callback вызывается для обрабатываемых строк, поэтому дорогой HTTP-запрос внутри него превратит SELECT в множество сетевых вызовов.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT NOT NULL, price INTEGER NOT NULL)');
$db->exec("INSERT INTO products VALUES (1, 'Pen', 120), (2, 'Notebook', 350), (3, 'Box', 200)");
$db->createFunction('packed_price', function ($price, $packing) {
if ($price === null || $packing === null) { return null; }
return $price + $packing;
}, 2, SQLITE3_DETERMINISTIC);
$result = $db->query('SELECT name, packed_price(price, 20) AS total FROM products ORDER BY id');
while (($row = $result->fetchArray(SQLITE3_ASSOC)) !== false) { echo $row['name'], ':', $row['total'], "\n"; }
$result->finalize();
Ожидаемый результат
Pen:140
Notebook:370
Box:220
Следующий шаг и альтернативы: SQLite3::createAggregate, SQLite3::createCollation, SQLite3::setAuthorizer.
SQLite3::createAggregateСвернуть много строк в один показатель, постепенно сохраняя только нужное состояние.
SQLite3::createAggregate(string $name, callable $stepCallback, callable $finalCallback, int $argCount = -1): boolСобрать итог остатков в собственном агрегате. Регистрируем правило в соединении до SQL, который его использует. function задаёт тело callback; return возвращает результат базе. Для старого PDO соответствующий метод имеет префикс sqlite.
- $db->createAggregate('stock_total', $step, $final, 1)
- Шаг получает предыдущую сумму и stock очередного товара:0+8,8+15,23+0. Итоговый callback возвращает 23. В обычном запросе это проще делает SUM(stock); пример объясняет устройство собственного агрегата.
- После выполнения: Всего на складе: 23 шт.
В архиве catalog.php открывает отдельную учебную базу и выполняет schema.sql: 1 — Молоток, 8 шт.; 2 — Перчатки, 15 шт.; 3 — Кисть, 0 шт. База находится в памяти и начинает работу с этих данных при каждом запуске. Распакуйте все файлы вместе и запустите php example.php. Полная подготовка и схема.
| Запись в этом примере | Как читать |
|---|---|
$db->method(...) | Стрелка вызывает действие у конкретного соединения, команды или результата. Скобки содержат входные значения; результат можно сохранить через =. |
function (...) { ... } | Объявляет функцию; тело выполняется при её вызове, не при объявлении. Кто и с какими данными её вызывает, показано в текущем примере. return передаёт значение обратно в место вызова. |
[...] | Квадратные скобки задают массив либо читают его элемент. Ключ имени колонки и номер позиции — разные способы адресации. |
<?php
require __DIR__ . '/catalog.php';
$db->createAggregate('stock_total',
function ($sum, $rowNumber, $stock) { return ($sum ?? 0) + $stock; },
function ($sum, $rowCount) { return $sum ?? 0; }, 1);
$result = $db->query('SELECT stock_total(stock) AS total FROM products');
$row = $result->fetchArray(SQLITE3_ASSOC);
echo 'Всего на складе: ', $row['total'], " шт.\n";
Ожидаемый результат
Всего на складе: 23 шт.
Шаг получает предыдущую сумму и stock очередного товара:0+8,8+15,23+0. Итоговый callback возвращает 23. В обычном запросе это проще делает SUM(stock); пример объясняет устройство собственного агрегата.
Если требование изменится. Состояние передаётся возвращаемым значением callback, а не изменением случайной внешней переменной. Определите исход пустой группы и NULL; не держите все строки в массиве ради накопления одного итога.
Полный контракт, ограничения и следующие варианты. Ниже сохранены параметры, возвращаемые значения, ошибки, версии и дополнительные задачи этой операции. Перед каждым более длинным примером сначала прочитайте его подготовку и ход действий.
Свернуть много строк в один показатель, постепенно сохраняя только нужное состояние.
| Вход / параметр | Смысл |
|---|---|
| name | Имя нового/переопределяемого SQL-агрегата в соединении. |
| stepCallback | function($context, int $rowNumber, ...$values): mixed. Первый context=null, следующий равен return прошлого шага; rowNumber — номер обрабатываемой строки. |
| finalCallback | function($context, int $rowNumber): mixed. Получает итог последнего шага, rowNumber всегда 0; возвращает scalar/null для SQL. |
| argCount | Число аргументов самого SQL-вызова, не context/rowNumber; отрицательное разрешает любое число. |
true — зарегистрировано, false/исключение — отказ. State может быть массивом/объектом, но финальный SQL-результат должен быть понятен SQLite. Не складывайте миллион исходных строк в context ради обработки в конце: это уничтожит преимущество пошагового агрегата и увеличит память. Встроенные SUM/AVG/COUNT обычно проще и не платят за переход PHP на каждой строке.
- (100,2)→step state cost200/qty2
- (160,1)→state360/3
- final→120
- querySingle получает120
Как читать этот вариант. Первый context=null. Каждый step возвращает новый итог: 120→470→670. После всех строк final получает 670 и возвращает SQL-значение. При пустом наборе final получает null; выбранный контракт возвращает 0. Для обычной суммы встроенный SUM предпочтительнее; callback нужен для собственного правила.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT NOT NULL, price INTEGER NOT NULL)');
$db->exec("INSERT INTO products VALUES (1, 'Pen', 120), (2, 'Notebook', 350), (3, 'Box', 200)");
$db->createAggregate('order_sum',
function ($context, $rowNumber, $price) { return ($context ?? 0) + $price; },
function ($context, $rowNumber) { return $context ?? 0; },
1
);
echo $db->querySingle('SELECT order_sum(price) FROM products'), "\n";
echo $db->querySingle('SELECT order_sum(price) FROM products WHERE id = 99'), "\n";
Ожидаемый результат
670
0
Как читать этот вариант. State хранит только стоимость и количество: [200,2]→[360,3]→120. Все партии не копируются в PHP-массив. Для групп GROUP BY SQLite ведёт отдельный context каждой группы; требования к допустимым отрицательным/нулевым quantity задают отдельно.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE batches (price INTEGER, quantity INTEGER)');
$db->exec('INSERT INTO batches VALUES (100, 2), (160, 1)');
$db->createAggregate('weighted_price',
function ($context, $rowNumber, $price, $quantity) {
$context ??= ['cost' => 0, 'quantity' => 0];
$context['cost'] += $price * $quantity;
$context['quantity'] += $quantity;
return $context;
},
function ($context, $rowNumber) {
return ($context['quantity'] ?? 0) === 0 ? null : $context['cost'] / $context['quantity'];
},
2
);
echo $db->querySingle('SELECT weighted_price(price, quantity) FROM batches'), "\n";
Ожидаемый результат
120
Следующий шаг и альтернативы: SQLite3::createFunction, SQLite3::querySingle.
SQLite3::createCollationСделать SQL-сортировку и сравнение похожими на ожидаемые пользователем.
SQLite3::createCollation(string $name, callable $callback): boolУпорядочить номера полок так, как их читает человек. Регистрируем правило в соединении до SQL, который его использует. function задаёт тело callback; return возвращает результат базе. Для старого PDO соответствующий метод имеет префикс sqlite.
- $db->createCollation('shelf_order', $compare)
- SQLite передаёт две строки правилу сравнения. strnatcmp сравнивает числовые части; знак результата задаёт порядок. Без этого правила обычный текстовый порядок поместил быA10 доA2.
- После выполнения: A1 · A2 · A10
В архиве catalog.php открывает отдельную учебную базу и выполняет schema.sql: 1 — Молоток, 8 шт.; 2 — Перчатки, 15 шт.; 3 — Кисть, 0 шт. База находится в памяти и начинает работу с этих данных при каждом запуске. Распакуйте все файлы вместе и запустите php example.php. Полная подготовка и схема.
| Запись в этом примере | Как читать |
|---|---|
$db->method(...) | Стрелка вызывает действие у конкретного соединения, команды или результата. Скобки содержат входные значения; результат можно сохранить через =. |
while (...) | Повторяет блок, пока условие истинно. В чтении результата проверка конца связана с конкретным методом: false или null указаны в его контракте. |
function (...) { ... } | Объявляет функцию; тело выполняется при её вызове, не при объявлении. Кто и с какими данными её вызывает, показано в текущем примере. return передаёт значение обратно в место вызова. |
[...] | Квадратные скобки задают массив либо читают его элемент. Ключ имени колонки и номер позиции — разные способы адресации. |
<?php
require __DIR__ . '/catalog.php';
$db->exec('CREATE TABLE shelves (label TEXT NOT NULL)');
$db->exec("INSERT INTO shelves VALUES ('A10'), ('A2'), ('A1')");
$db->createCollation('shelf_order', function ($left, $right) { return strnatcmp($left, $right); });
$result = $db->query('SELECT label FROM shelves ORDER BY label COLLATE shelf_order');
while (($row = $result->fetchArray(SQLITE3_ASSOC)) !== false) { echo $row['label'], "\n"; }
Ожидаемый результат
A1
A2
A10
SQLite передаёт две строки правилу сравнения. strnatcmp сравнивает числовые части; знак результата задаёт порядок. Без этого правила обычный текстовый порядок поместил быA10 доA2.
Если требование изменится. Это пример технических обозначений, не лингвистическая сортировка русского алфавита. Колляция должна быть последовательной; изменение правила требует пересмотра индексов, построенных по старому сравнению.
Полный контракт, ограничения и следующие варианты. Ниже сохранены параметры, возвращаемые значения, ошибки, версии и дополнительные задачи этой операции. Перед каждым более длинным примером сначала прочитайте его подготовку и ход действий.
Сделать SQL-сортировку и сравнение похожими на ожидаемые пользователем.
| Вход / параметр | Смысл |
|---|---|
| name | Имя правила, используемое после COLLATE. |
| callback | Два текстовых значения → int меньше/равно/больше 0, как strcmp. Возвращать bool вместо трёхстороннего сравнения неверно. |
true/false регистрации. Сравнение обязано быть согласованным, симметричным и транзитивным: A
Как читать этот вариант. Сравниватель принимает две строки. Отрицательное число означает «первая раньше», 0 — равны, положительное — позже. Он не получает сразу весь список и не возвращает отсортированный массив.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE shelves (code TEXT)');
$db->exec("INSERT INTO shelves VALUES ('Rack-12'), ('Rack-3'), ('Rack-1')");
$db->createCollation('shelf_order', 'strnatcmp');
$result = $db->query('SELECT code FROM shelves ORDER BY code COLLATE shelf_order');
while (($row = $result->fetchArray(SQLITE3_ASSOC)) !== false) { echo $row['code'], "\n"; }
$result->finalize();
Ожидаемый результат
Rack-1
Rack-3
Rack-12
Как читать этот вариант. Collation влияет не только на ORDER BY, но и на сравнение текста. Здесь deliberately ASCII-артикулы; strtolower не задаёт полноценную локализованную Unicode-сортировку. Для неё нужна осознанная ICU/Collator-политика.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE labels (sku TEXT)');
$db->exec("INSERT INTO labels VALUES ('a-10'), ('B-2')");
$db->createCollation('sku_fold', function ($left, $right) { return strcmp(strtolower($left), strtolower($right)); });
echo $db->querySingle("SELECT sku FROM labels WHERE sku = 'A-10' COLLATE sku_fold"), "\n";
Ожидаемый результат
a-10
Следующий шаг и альтернативы: SQLite3::createFunction, SQLite3::query.
SQLite3::setAuthorizerПроверить разрешённые действия при подготовке SQL, например для ограниченного просмотрщика отчётов.
SQLite3::setAuthorizer(?callable $callback): boolРазрешить учебному отчёту только чтение products. SQLite вызывает правило при подготовке операций SQL. В этом узком правиле разрешены SELECT и READ таблицы products; всё остальное получает DENY.
- $db->setAuthorizer($callback)
- Чтение успешно, удаление отклонено ещё до изменения строк. Для PDO используются числовые коды действий SQLite21/20 с собственными понятными именами; OK/DENY — результаты проверки.
- После выполнения: Молоток — 8 шт. · Отчёт не имеет права удалять товары
В архиве catalog.php открывает отдельную учебную базу и выполняет schema.sql: 1 — Молоток, 8 шт.; 2 — Перчатки, 15 шт.; 3 — Кисть, 0 шт. База находится в памяти и начинает работу с этих данных при каждом запуске. Распакуйте все файлы вместе и запустите php example.php. Полная подготовка и схема.
| Запись в этом примере | Как читать |
|---|---|
$db->method(...) | Стрелка вызывает действие у конкретного соединения, команды или результата. Скобки содержат входные значения; результат можно сохранить через =. |
Класс::ИМЯ | Двойное двоеточие обращается к константе режима либо статическому методу класса; это не выбор колонки SQL. |
function (...) { ... } | Объявляет функцию; тело выполняется при её вызове, не при объявлении. Кто и с какими данными её вызывает, показано в текущем примере. return передаёт значение обратно в место вызова. |
try / catch / finally | try содержит действие, catch обрабатывает отказ, finally выполняет освобождение ресурсов и при успехе, и при исключении. |
[...] | Квадратные скобки задают массив либо читают его элемент. Ключ имени колонки и номер позиции — разные способы адресации. |
<?php
require __DIR__ . '/catalog.php';
$db->setAuthorizer(function ($action, $table = null) {
if ($action === SQLite3::SELECT) { return SQLite3::OK; }
if ($action === SQLite3::READ && $table === 'products') { return SQLite3::OK; }
return SQLite3::DENY;
});
$result = $db->query('SELECT name, stock FROM products WHERE id = 1');
$row = $result->fetchArray(SQLITE3_ASSOC);
echo $row['name'], ' — ', $row['stock'], " шт.\n";
try { $db->exec('DELETE FROM products'); }
catch (Exception $error) { echo "Отчёт не имеет права удалять товары\n"; }
$db->setAuthorizer(null);
Ожидаемый результат
Молоток — 8 шт.
Отчёт не имеет права удалять товары
Чтение успешно, удаление отклонено ещё до изменения строк. Для PDO используются числовые коды действий SQLite21/20 с собственными понятными именами; OK/DENY — результаты проверки.
Эти же строки в табличном виде. CLI-пример выше печатает текст; таблица помогает увидеть соответствие названия и остатка. Как получить такую таблицу настоящим HTML-обработчиком.
| Товар / строка | Наличие |
|---|---|
| Молоток | 8 шт. |
Если требование изменится. Это механизм ограничения операций, а не готовая песочница произвольного SQL. Отдельно нужны лимиты работы, памяти, доступных функций и ресурсов. Callback не должен менять это же соединение.
Полный контракт, ограничения и следующие варианты. Ниже сохранены параметры, возвращаемые значения, ошибки, версии и дополнительные задачи этой операции. Перед каждым более длинным примером сначала прочитайте его подготовку и ход действий.
Проверить разрешённые действия при подготовке SQL, например для ограниченного просмотрщика отчётов.
| Вход / параметр | Смысл |
|---|---|
| callback | Функция до пяти аргументов: action(int), detail1(?string), detail2(?string), database(?string), origin(?string). null снимает политику. |
| action / return | action — константа SQLite3. Return OK разрешает, DENY отклоняет; IGNORE имеет специальный смысл для READ/DELETE. |
| database / origin | main/temp/имя ATTACH, если применимо; имя самого внутреннего триггера/view либо null для прямого SQL. |
Возвращает true/false установки. Одновременно действует один authorizer; новый заменяет прежний. Обычно callback вызывается на подготовке, не на каждой выдаваемой строке; повторная подготовка SQLite также может вызвать его. Нельзя модифицировать то же соединение внутри callback. Неверный возвращённый код приводит к ошибке подготовки. Для SELECT count(*) READ может прийти с пустым именем колонки. READ+IGNORE подставляет NULL, DELETE+IGNORE не запрещает удаление, а отключает его truncate-оптимизацию. Политика не является ограничением CPU/памяти и сама по себе не изолирует опасный PHP callback.
| Код SQLite3:: | detail1 | detail2 |
|---|---|---|
| CREATE_INDEX / DROP_INDEX | Имя индекса | Имя таблицы |
| CREATE_TABLE / DROP_TABLE | Имя таблицы | null |
| CREATE_TEMP_INDEX / DROP_TEMP_INDEX | Имя индекса | Имя таблицы |
| CREATE_TEMP_TABLE / DROP_TEMP_TABLE | Имя таблицы | null |
| CREATE_TEMP_TRIGGER / DROP_TEMP_TRIGGER | Имя триггера | Имя таблицы |
| CREATE_TEMP_VIEW / DROP_TEMP_VIEW | Имя view | null |
| CREATE_TRIGGER / DROP_TRIGGER | Имя триггера | Имя таблицы |
| CREATE_VIEW / DROP_VIEW | Имя view | null |
| DELETE / INSERT | Имя таблицы | null |
| PRAGMA | Имя pragma | Первый аргумент либо null |
| READ / UPDATE | Имя таблицы | Имя колонки |
| SELECT | null | null |
| TRANSACTION | Операция BEGIN/COMMIT/ROLLBACK | null |
| ATTACH | Имя файла | null |
| DETACH | Имя схемы | null |
| ALTER_TABLE | Имя схемы | Имя таблицы |
| REINDEX | Имя индекса | null |
| ANALYZE | Имя таблицы | null |
| CREATE_VTABLE / DROP_VTABLE | Имя таблицы | Имя модуля |
| FUNCTION | null | Имя функции |
| SAVEPOINT | Операция | Имя savepoint |
| RECURSIVE | null | null |
| COPY | Исторический код, не используется современным SQLite | Не строить новую политику вокруг него |
- SQL SELECT name,private_note
- authorizer при подготовке
- name→OK;private_note→IGNORE
- строка Ann,NULL
Как читать этот вариант. Политика установлена до подготовки запроса. Разрешены только нужные actions/table; остальные запрещены. Это пример механизма, не готовая песочница произвольного SQL: также ограничивают функции, объём работы, память, файлы и время.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT NOT NULL, price INTEGER NOT NULL)');
$db->exec("INSERT INTO products VALUES (1, 'Pen', 120), (2, 'Notebook', 350), (3, 'Box', 200)");
$db->setAuthorizer(function ($action, $first = null, $second = null, $schema = null, $origin = null) {
if ($action === SQLite3::SELECT) { return SQLite3::OK; }
if ($action === SQLite3::READ && $first === 'products') { return SQLite3::OK; }
return SQLite3::DENY;
});
echo $db->querySingle('SELECT name FROM products WHERE id = 1'), "\n";
try { $db->exec('DELETE FROM products'); }
catch (Exception $error) { echo "Изменение запрещено\n"; }
$db->setAuthorizer(null);
Ожидаемый результат
Pen
Изменение запрещено
Как читать этот вариант. IGNORE для READ заменяет значение этой колонки на NULL. Это не то же самое, что DENY всей команды. Не путать с IGNORE для DELETE: там удаление продолжается, только отключается truncate-оптимизация.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE contacts (name TEXT, private_note TEXT)');
$db->exec("INSERT INTO contacts VALUES ('Ann', 'Internal note')");
$db->setAuthorizer(function ($action, $table = null, $column = null) {
if ($action === SQLite3::SELECT) { return SQLite3::OK; }
if ($action === SQLite3::READ && $table === 'contacts') {
return $column === 'name' ? SQLite3::OK : SQLite3::IGNORE;
}
return SQLite3::DENY;
});
$row = $db->querySingle('SELECT name, private_note FROM contacts', true);
echo $row['name'], "\n";
var_dump($row['private_note']);
$db->setAuthorizer(null);
Ожидаемый результат
Ann
NULL
Следующий шаг и альтернативы: SQLite3Stmt::readOnly, SQLite3::prepare, SQLite3::createFunction.
SQLite3::openBlobРаботать с бинарным вложением частями, не загружая весь BLOB в PHP-строку.
SQLite3::openBlob(string $table, string $column, int $rowid, string $database = "main", int $flags = SQLITE3_OPEN_READONLY): resource|falseПрочитать содержимое квитанции потоком. BLOB хранит байты. Имя таблицы, колонки и rowid1 указывают конкретное вложение. Поток читается через stream_get_contents и обязательно закрывается.
- $db->openBlob('receipts', 'body', 1, 'main')
- В схеме X'...' задаёт известные байты UTF-8/ASCII-квитанции. Вывод содержит сам текст вложения, а не true или размер файла.
- После выполнения: RECEIPT #1
В архиве catalog.php открывает отдельную учебную базу и выполняет schema.sql: 1 — Молоток, 8 шт.; 2 — Перчатки, 15 шт.; 3 — Кисть, 0 шт. База находится в памяти и начинает работу с этих данных при каждом запуске. Распакуйте все файлы вместе и запустите php example.php. Полная подготовка и схема.
| Запись в этом примере | Как читать |
|---|---|
$db->method(...) | Стрелка вызывает действие у конкретного соединения, команды или результата. Скобки содержат входные значения; результат можно сохранить через =. |
<?php
require __DIR__ . '/catalog.php';
$db->exec('CREATE TABLE receipts (id INTEGER PRIMARY KEY, body BLOB NOT NULL)');
$db->exec("INSERT INTO receipts VALUES (1, X'524543454950542023310A')");
$stream = $db->openBlob('receipts', 'body', 1, 'main');
if ($stream === false) { throw new RuntimeException('Квитанция недоступна'); }
echo stream_get_contents($stream);
fclose($stream);
Ожидаемый результат
RECEIPT #1
В схеме X'...' задаёт известные байты UTF-8/ASCII-квитанции. Вывод содержит сам текст вложения, а не true или размер файла.
Если требование изменится. Не отдавайте вложение по произвольному rowid без проверки прав. Запись в openBlob имеет фиксированный размер и ограничения схемы; для изменения длины обновляют BLOB через SQL. PDO-подкласс требует PHP 8.4+.
Полный контракт, ограничения и следующие варианты. Ниже сохранены параметры, возвращаемые значения, ошибки, версии и дополнительные задачи этой операции. Перед каждым более длинным примером сначала прочитайте его подготовку и ход действий.
Работать с бинарным вложением частями, не загружая весь BLOB в PHP-строку.
| Вход / параметр | Смысл |
|---|---|
| table / column | Имена таблицы и поля с BLOB/TEXT; выбираются приложением. |
| rowid | Целочисленный идентификатор строки rowid-таблицы. WITHOUT ROWID не подходит. |
| database | Схема main/temp либо имя ATTACH. |
| flags | READONLY по умолчанию; READWRITE разрешает запись (параметр с PHP 7.2). |
Возвращает stream resource или false/исключение. Поток имеет фиксированный размер; его меняют SQL UPDATE, не записью за конец. Для отсутствующей строки/неподходящей колонки/ограничений записи будет отказ. Изменение связанной строки может сделать BLOB handle недействительным; не держите поток открытым через произвольные UPDATE. При частичной записи проверяйте fwrite и суммарный размер, закрывайте в finally. Не выдавайте посетителю произвольные имена таблиц/полей.
- rowid1 содержит zeroblob(6)
- openBlob READWRITE
- ABC→позиции0..2;DEF→3..5
- fclose→SELECT ABCDEF
Как читать этот вариант. BLOB сохраняет нулевой байт. openBlob возвращает PHP stream resource; его читают стандартными stream-функциями. bin2hex делает бинарный результат видимым, не меняя сохранённые байты.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE attachments (content BLOB)');
$insert = $db->prepare('INSERT INTO attachments VALUES (?)');
$insert->bindValue(1, "A\0B", SQLITE3_BLOB);
$insert->execute()->finalize();
$stream = $db->openBlob('attachments', 'content', 1);
echo bin2hex(stream_get_contents($stream)), "\n";
fclose($stream);
$db->close();
Ожидаемый результат
410042
Как читать этот вариант. zeroblob(6) заранее выделяет 6 байт. Поток не умеет увеличивать BLOB: для другого размера сначала UPDATE/новое значение. fclose обязателен до закрытия базы; открытый BLOB удерживает ресурсы и может влиять на завершение транзакции.
<?php
$db = new SQLite3(':memory:');
$db->enableExceptions(true);
$db->exec('CREATE TABLE attachments (content BLOB)');
$db->exec('INSERT INTO attachments VALUES (zeroblob(6))');
$stream = $db->openBlob('attachments', 'content', 1, 'main', SQLITE3_OPEN_READWRITE);
fwrite($stream, 'ABC');
fwrite($stream, 'DEF');
fclose($stream);
echo $db->querySingle('SELECT content FROM attachments'), "\n";
$db->close();
Ожидаемый результат
ABCDEF
Следующий шаг и альтернативы: SQLite3Stmt::bindValue, SQLite3::close, SQLite3::backup.