PHP: полный мануал с примерами, функциями и ООП
Практический мануал PHP: от переменных и читаемого кода до безопасных форм, интеграций, базы данных и ООП на примере магазина. Учебный маршрут, поиск функций, подробные примеры, разбор ошибок и исходники для скачивания.

Дерево руководства
Глава 38 · С нуля → справочник
DBA: полный справочник хранилищ «ключ → значение»
От короткой записи тарифа в файл до всех 15 функций, обработчиков, режимов открытия, блокировок, CDB, LMDB и границ общего API.
Сохранить тариф и прочитать после закрытия
В форме доставки нужны тарифы: самовывоз бесплатный, курьер стоит 350 рублей. Задача — записать выбранные строки в файл и прочитать их после закрытия соединения. Перед первым запуском создайте отдельный учебный каталог: режим n заново создаёт наш delivery.db и обнуляет уже существующий файл с тем же именем.
- Ключ courier
- Строка 35000, копейки
- DBA + flatfile → delivery.db
- close → новое открытие r
- fetch courier → строка 35000
$db = dba_open(__DIR__ . '/delivery.db', 'n', 'flatfile');
dba_insert('courier', '35000', $db);
echo dba_fetch('courier', $db);
dba_close($db);__DIR__ — каталог текущего PHP-файла; точка соединяет строки. Первый аргумент open задаёт путь, n — создать или обнулить, flatfile — конкретный физический формат. Успешный open возвращает соединение, а отказ — false. insert получает ключ, строковое значение и соединение, возвращает bool. fetch по ключу возвращает строку либо false; echo выводит её. close освобождает соединение и не удаляет данные. На PHP 8.4+ соединение — Dba\Connection, раньше resource. Полная программа ниже проверяет открытие и повторно открывает файл только для чтения.
Что получится
| Delivery | Price in kopecks |
|---|---|
| pickup | 0 |
| courier | 35000 |
Эту же таблицу программа записывает в result.html, исходные значения — в result.json. HTML открывается обычным браузером; это реальный продукт запуска, а не условная картинка. Открыть сохранённую таблицу · Получить JSON результата.
<?php
require __DIR__ . '/db105-report.php';
$path = __DIR__ . '/delivery.db';
$db = dba_open($path, 'n', 'flatfile');
if ($db === false) { throw new RuntimeException('Cannot create delivery dictionary'); }
dba_insert('pickup', '0', $db);
dba_insert('courier', '35000', $db);
dba_close($db);
$reader = dba_open($path, 'r', 'flatfile');
if ($reader === false) { throw new RuntimeException('Cannot reopen dictionary'); }
$pickup = dba_fetch('pickup', $reader);
$courier = dba_fetch('courier', $reader);
dba_close($reader);
$rows = [['pickup',$pickup],['courier',$courier]];
db105Report('dba105-first-dictionary',['Delivery','Price in kopecks'],$rows,['zero_preserved'=>$pickup==='0','readback'=>$courier==='35000'],['delivery.db']);
echo "pickup=0 courier=35000; reopened=yes\n";
Ожидаемый результат
pickup=0 courier=35000; reopened=yes
Скачайте пример, распакуйте в новый отдельный каталог и выполните php example.php с DBA и указанным handler. Код с режимом n заново создаёт только учебные файлы этого каталога. Вложенный db105-report.php сохраняет таблицу и измерения; он не нужен для самого вызова DBA. Служебный обход читает пары до false и не изменяет базу во время перебора. Результаты остаются рядом со скриптом; рабочие базы не открываются.
Число 35000 хранится строкой в копейках; это выбранный формат приложения. DBA не знает ни рублей, ни округления. Обычный PHP-массив живёт только в процессе, файл остаётся. Flatfile удобен для этого маленького урока; он не становится подходящим решением для миллионов конкурентных запросов.
Нужны определения: аргументы и результаты функций DBA, массивы и foreach, файлы и пути, JSON.
Сохранить простое значение и найти его по ключу
У вас есть настройки интерфейса: user:42:language → ru или catalog:version → v7. В PHP-массиве такая связь исчезнет после завершения запроса. DBA даёт похожий по смыслу доступ к постоянному файловому хранилищу: передаёте строковый ключ и получаете строковое значение. Например, знаете номер пользователя и сразу читаете его язык. SQL, таблицы, JOIN и фильтрация JSON-полей здесь не предоставляются.
- ключ user:42:language — подписанная ячейка
- строка ru — её содержимое
- dba_insert кладёт строку в файловый формат
- dba_fetch по тому же ключу возвращает ru
Модель ячеек помогает понять интерфейс, но физически драйвер может использовать дерево, хеш, mapped file или текстовый INI. DBA — слой общего доступа к разным dbm-подобным библиотекам, а не один универсальный формат. Расширение файла не меняет формат. Для начального опыта выберем flatfile на новом временном файле; в рабочем проекте формат выбирают по требованиям и доступным библиотекам.
<?php
$path = tempnam(sys_get_temp_dir(), 'dba-');
$db = dba_open($path, 'n', 'flatfile');
if ($db === false) { throw new RuntimeException('База не открыта'); }
dba_insert('user:42:language', 'ru', $db);
echo dba_fetch('user:42:language', $db), "\n";
dba_close($db);
unlink($path);
Ожидаемый результат
ru
tempnam создаёт только наш новый временный файл; n инициализирует его. Ключ user:42:language — наша договорённость об имени, значение ru — строка. dba_open возвращает соединение, insert сохраняет, fetch возвращает значение, close завершает. flatfile выбран для учебного опыта и совместимости; это не рекомендация для большой рабочей базы.
Каждый аргумент здесь отвечает на конкретный вопрос: path — где хранить, n — инициализировать новый файл, flatfile — как кодировать, $db — через какое открытое соединение действовать. = сохраняет результат вызова, === false отличает отказ, echo показывает ru. dba_close заканчивает работу, unlink удаляет именно созданный нами учебный файл. Сохраните код в отдельном example.php и выполните php example.php с расширением DBA. Базовые переменные, аргументы и возврат и файловые операции разобраны отдельно.
<?php
$directory = sys_get_temp_dir() . '/dba-' . bin2hex(random_bytes(8));
mkdir($directory, 0700);
$path = $directory . '/demo.db';
$db = dba_open($path, 'n', 'flatfile');
if ($db === false) { throw new RuntimeException('Не удалось открыть учебную базу'); }
dba_insert('count', '0', $db);
dba_insert('note', '', $db);
var_dump(dba_fetch('count', $db));
var_dump(dba_fetch('note', $db));
var_dump(dba_fetch('missing', $db));
dba_close($db);
foreach (glob($directory . '/*') as $file) { unlink($file); }
rmdir($directory);
Ожидаемый результат
string(1) "0"
string(0) ""
bool(false)
Возврат — строка либо false. if (!$value) смешает все три случая; === false обнаруживает именно отсутствие/отказ, а "0" можно затем отдельно проверить и преобразовать в число.
Более сложный профиль можно кодировать в JSON-строку, но это не превращает DBA в документную базу: обновление одного поля требует прочитать/проверить/сформировать новое полное значение. Для связанных таблиц, индексов и групповой транзакции рассмотрите SQLite3. Для временных значений с TTL, конкурентного захвата и многомашинного доступа нужен подходящий протокол/хранилище, а не обещание, что любой файл станет таким сервисом.
Установка: расширение и хотя бы один handler
При сборке PHP --enable-dba=shared создаёт загружаемое расширение DBA, но нужен хотя бы один handler с соответствующей библиотекой и configure-ключом. В Windows используйте php_dba.dll из совместимой поставки PHP и нужные зависимые библиотеки; в Linux пакет или собственная сборка должны включать требуемый backend. Версия, архитектура и тип сборки расширения должны совпадать с PHP. Наличие подсказки в IDE не доказывает, что runtime загрузил DBA.
После собственной сборки документация требует запустить штатные тесты ext/dba: php run-tests.php ext/dba из дерева PHP с соответствующим test runner. Особенно важны dbm/ndbm: одна библиотека может оказаться wrapper другой и конфликтовать лишь в комбинации. Проверка configure одного драйвера не проверяет все их сочетания. Здесь мы не пересобираем сервер; используем совместимую bundled-DLL только через параметры отдельного тестового процесса.
<?php
$handlers = dba_handlers();
echo in_array('flatfile', $handlers, true) ? "Учебный flatfile доступен\n" : "Нужен другой handler\n";
echo in_array('lmdb', $handlers, true) ? "LMDB доступен\n" : "LMDB не включён\n";
Ожидаемый результат
Учебный flatfile доступен
LMDB доступен
Вывод соответствует проверочной bundled-сборке. Список на Linux-сервере или другом php.ini может отличаться; наличие расширения DBA не означает наличия всех handlers.
<?php
$details = dba_handlers(true);
foreach (['cdb', 'qdbm', 'lmdb'] as $name) {
echo $name, ':', isset($details[$name]) && $details[$name] !== '' ? 'описание есть' : 'не установлен', "\n";
}
Ожидаемый результат
cdb:описание есть
qdbm:описание есть
lmdb:описание есть
true меняет форму ответа: ключи — имена handlers, значения — их версии/описания. Эти строки годятся для диагностики, но не являются стабильным машинным форматом.
Все обработчики форматов и ограничения совместимости
| Handler | Включение в сборку | Назначение и нюансы |
|---|---|---|
| dbm | --with-dbm[=DIR] | Самый старый формат, избегайте для нового проекта. Совместимые функции db2/gdbm на уровне C API не гарантируют чтение исходного dbm-формата. |
| ndbm | --with-ndbm[=DIR] | Более гибкий наследник dbm, сохраняет произвольные ограничения; старый вариант не рекомендуется. Wrapper-библиотеки могут конфликтовать. |
| gdbm | --with-gdbm[=DIR] | GNU Database Manager. Формат и блокировки задаёт библиотека; конфликтует с qdbm при некоторых сборках. |
| db2 | --with-db2[=DIR] | Oracle Berkeley DB2. Не комбинируется с db3/db4 в одной конфигурации. |
| db3 | --with-db3[=DIR] | Oracle Berkeley DB3. Конфликтует с db2/db4. |
| db4 | --with-db4[=DIR] | Oracle Berkeley DB4/5; BDB5 поддерживается сPHP5.3.3. Версии библиотеки4.1..4.1.24 не подходят; конфликты db2/db3. |
| cdb | --with-cdb[=DIR] | Constant database для чтения заранее построенного файла. Встроенная библиотека также умеет построение, но не обновление существующей базы; DIR можно опустить. |
| cdb_make | Встроенная cdb-библиотека | Построить новый cdb целиком в режиме n; после close открывать cdb для чтения. Не изменять опубликованный файл по месту. |
| flatfile | --enable-flatfile | Совместимость с удалённым dbm-расширением, без внешней библиотеки. Для новых больших баз не рекомендуется; учебные примеры используют его для понятности. |
| inifile | --enable-inifile | Читать/изменять Microsoft-style INI. Ключ [group]name либо [group,name]. Это не полный PHP-парсер конфигурации и не повод менять рабочий php.ini. |
| qdbm | --with-qdbm[=DIR] | Quick Database Manager; может конфликтовать с dbm/gdbm при сборке. Собственные optimize/sync. |
| tcadb | --with-tcadb[=DIR] | Tokyo Cabinet, доступность и формат зависят от установленной библиотеки. |
| lmdb | --with-lmdb[=DIR] | Lightning Memory-Mapped Database; сPHP7.2. Отображаемая память, свой lock/transaction backend; общий DBA не открывает наружу все возможности LMDB. |
До PHP5.2.1 flatfile/inifile включались --with-flatfile/--with-inifile; современная форма --enable-. Документация предупреждает о конфликтах db2/db3/db4 между собой, qdbm с dbm/gdbm, а также ограничениях legacy wrappers. В tagged исходнике дополнительно остаётся условная ветка db1 для старого Berkeley DB1; она не является обещанием доступности в актуальной поставке и отсутствует в нашей сборке. Не открывайте один файл случайными драйверами «пока не заработает».
В проверочной PHP8.5.11 доступны cdb, cdb_make, flatfile, inifile, qdbm и lmdb. Для остальных примеры ниже только синтаксические: результат требует отдельной compatible-сборки. Оптимизация и sync имеют разный смысл; flatfile в tagged исходнике делает оба как no-op. Общий API открывает лишь подмножество возможностей библиотеки: даже если библиотека поддерживает транзакции/множество баз, DBA может не дать их наружу.
dba.default_handler и различие сред запуска
| Директива | Default / изменение | Смысл |
|---|---|---|
| dba.default_handler | DBA_DEFAULT сборки; INI_ALL | Handler, используемый при handler=null. Реальное имя зависит от сборки. |
<?php
$old = ini_get('dba.default_handler');
ini_set('dba.default_handler', 'flatfile');
echo ini_get('dba.default_handler'), "\n";
ini_set('dba.default_handler', $old);
Ожидаемый результат
flatfile
Директива INI_ALL допускает такую локальную смену; прежнее значение восстановлено. Это не меняет формат уже существующих файлов и не устанавливает отсутствующую библиотеку.
<?php
$directory = sys_get_temp_dir() . '/dba-' . bin2hex(random_bytes(8));
mkdir($directory, 0700);
$path = $directory . '/demo.db';
$old = ini_get('dba.default_handler');
ini_set('dba.default_handler', 'flatfile');
$db = dba_open($path, 'n', null);
if ($db === false) { throw new RuntimeException('Default недоступен'); }
dba_insert('language', 'ru', $db);
echo dba_fetch('language', $db), "\n";
ini_set('dba.default_handler', $old);
dba_close($db);
foreach (glob($directory . '/*') as $file) { unlink($file); }
rmdir($directory);
Ожидаемый результат
ru
В примере default выбран нами. Для переносимого приложения явный handler при open обычно понятнее: изменение php.ini не заставит открыть старый файл другим форматом.
Если default не задан или отсутствует, open с null выдаёт warning и false. PHP Manual у open указывает nullable-handler с8.2, у popen — с8.1; для общего примера используется PHP8.2+ контракт, а класс результата появился с8.4. Веб-SAPI и CLI могут иметь разные php.ini; проверяйте ini_get/dba_handlers там, где работает приложение, без вывода полного phpinfo в публичную страницу.
Dba\Connection и переход от resource к объекту
До PHP8.4 dba_open/dba_popen возвращали resource, который большинство функций принимало последним/единственным аргументом. С8.4 это final Dba\Connection: непрозрачный объект без публичных методов и свойств. Он не предназначен для наследования, сериализации, создания через new или добавления произвольных динамических свойств. Свой класс приложения может хранить такой handle внутри, но не раскрывать/копировать внутреннее соединение.
<?php
$directory = sys_get_temp_dir() . '/dba-' . bin2hex(random_bytes(8));
mkdir($directory, 0700);
$path = $directory . '/demo.db';
$db = dba_open($path, 'n', 'flatfile');
if ($db === false) { throw new RuntimeException('Не удалось открыть учебную базу'); }
echo get_debug_type($db), "\n";
echo $db instanceof Dba\Connection ? "Это DBA-соединение\n" : "Старый PHP-resource\n";
dba_close($db);
foreach (glob($directory . '/*') as $file) { unlink($file); }
rmdir($directory);
Ожидаемый результат
Dba\Connection
Это DBA-соединение
Начиная сPHP8.4 handle стал непрозрачным final-объектом. Функциональный API остался прежним: dba_fetch(key,$db), а не выдуманный $db->fetch().
<?php
try { $db = new Dba\Connection(); }
catch (Error $error) { echo "Соединение создаёт dba_open\n"; }
Ожидаемый результат
Соединение создаёт dba_open
Класс не предлагает публичный конструктор. Его создаёт расширение, связав с настоящим backend; вручную изготовить пустой handle нельзя.
Проверка старого кода is_resource($db) после успешного open теперь вернёт false. Надёжная общая проверка отказа — $db===false; PHP8.4+ может дополнительно проверять instanceof. Наличие объекта не доказывает, что dba_close ещё не вызван. Возврат void close нельзя принимать за bool. Передача handles через PHP-сессии/JSON между запросами не создаёт persistent database; данные хранит файл, а время жизни handle управляется API.
Режимы открытия и полная логика блокировок
| Запись режима | Значение |
|---|---|
| r | Только чтение уже существующей базы. |
| w | Чтение/запись уже существующей базы. |
| c | Чтение/запись, создание при отсутствии без обнуления существующей. |
| n | Создать/обнулить и читать/писать. Текущие данные будут потеряны. |
| Второй символ l | Lock в соседнем .lck-файле. Доступ к каталогу/lock-файлу нужен даже reader. |
| Второй символ d | Lock самого файла; default, если не указан l/d/- для handlers с общей блокировкой. |
| Второй символ - | Не использовать общую блокировку DBA. Только если всю согласованность обеспечивает другой осознанный механизм; не средство лечения ошибок. |
| Третий символ t | Try lock: не ждать, вернуть false при занятости. Например rlt/wdt. |
| Уже открыт | Новый reader с тем же l/d | Новый writer с тем же l/d |
|---|---|---|
| Никто | Успех | Успех |
| Reader r | Успех (с t также) | Ожидание; с t→false |
| Writer w/c/n | Ожидание; с t→false | Ожидание; с t→false |
Нельзя смешивать l и d для одного файла: все комбинации reader/writer при разных lock-объектах недопустимы. Без t ожидание продолжается до освобождения конфликтующего обычного handle. Один writer исключает остальных readers/writers в этой общей DBA-схеме. Но gdbm/qdbm/lmdb могут использовать внешние механизмы своей библиотеки: детали и допустимость modifiers сверяют с handler, а не насильно переносят таблицу flatfile на LMDB.
<?php
$directory = sys_get_temp_dir() . '/dba-' . bin2hex(random_bytes(8));
mkdir($directory, 0700);
$path = $directory . '/demo.db';
$db = dba_open($path, 'nl', 'flatfile');
if ($db === false) { throw new RuntimeException('Writer не открыт'); }
$warning = null;
set_error_handler(function ($severity, $message) use (&$warning) { $warning = $message; return true; }, E_WARNING);
try { $reader = dba_open($path, 'rlt', 'flatfile'); }
finally { restore_error_handler(); }
echo $reader === false ? "База занята writer\n" : "Reader получил доступ\n";
if ($reader !== false) { dba_close($reader); }
dba_close($db);
foreach (glob($directory . '/*') as $file) { unlink($file); }
rmdir($directory);
Ожидаемый результат
База занята writer
l выбирает соседний .lck, t просит не ждать. Warning локально собран для ожидаемого конфликта, а общий обработчик восстановлен в finally. Сервис должен выбрать понятный ответ/ограниченный повтор, не отключать lock знаком - ради устранения сообщения.
<?php
$directory = sys_get_temp_dir() . '/dba-' . bin2hex(random_bytes(8));
mkdir($directory, 0700);
$path = $directory . '/demo.db';
$db = dba_open($path, 'n', 'flatfile');
if ($db === false) { throw new RuntimeException('Не удалось открыть учебную базу'); }
dba_insert('counter', '4', $db);
$current = dba_fetch('counter', $db);
if ($current === false || !ctype_digit($current)) { throw new RuntimeException('Неверный счётчик'); }
if (!dba_replace('counter', (string) ((int) $current + 1), $db)) { throw new RuntimeException('Счётчик не записан'); }
echo dba_fetch('counter', $db), "\n";
dba_close($db);
foreach (glob($directory . '/*') as $file) { unlink($file); }
rmdir($directory);
Ожидаемый результат
5
Для показанного flatfile весь fetch+replace находится под одним write-lock с открытия до close. Это не атомарный increment для любого handler: драйверы со своей блокировкой могут иначе ограничивать операции. Для нескольких ключей lock всё равно не создаёт crash-safe транзакцию.
На время открытия writer не вызывают внешний HTTP-сервис и не выполняют длинный sleep. read-modify-write надо защищать одной согласованной областью; открыли r, прочитали, закрыли, затем открыли w — это уже окно гонки. Сетевые файловые системы могут иметь другую семантику locks, поэтому такую эксплуатацию проверяют отдельно. Persistent-handle может прожить дольше запроса; преимущество повторного открытия нельзя покупать ценой непредсказуемо удержанного writer.
LMDB, map_size и обе константы размещения файлов
LMDB появился в DBA сPHP7.2. Пятый аргумент map_size добавлен в7.3.14/7.4.2, шестой flags — в8.2. Размер map задают байтами, кратно странице ОС, 0 оставляет default. При исчерпании ёмкости операция может отказать: нужно планировать объём и изменение конфигурации; это не параметр числа записей и не указание «выделить столько RAM прямо сейчас».
| Константа (PHP8.2+) | Значение |
|---|---|
| DBA_LMDB_USE_SUB_DIR | Размещение внутри каталога; числовое0. |
| DBA_LMDB_NO_SUB_DIR | Размещение по файловому пути, библиотека использует MDB_NOSUBDIR; tagged driver выбирает это по умолчанию при flags=null. |
<?php
$directory = sys_get_temp_dir() . '/dba-' . bin2hex(random_bytes(8));
mkdir($directory, 0700);
$path = $directory . '/demo.db';
$db = dba_open($path, 'c', 'lmdb', 0600, 16 * 1024 * 1024, DBA_LMDB_NO_SUB_DIR);
if ($db === false) { throw new RuntimeException('LMDB не открыт'); }
dba_replace('catalog:version', 'v3', $db);
echo dba_fetch('catalog:version', $db), "\n";
dba_close($db);
foreach (glob($directory . '/*') as $file) { unlink($file); }
rmdir($directory);
Ожидаемый результат
v3
16MiB — выбранная ёмкость map учебного опыта, не размер уже заполненных данных и не универсальная настройка production. NO_SUB_DIR задаёт путь к файлу; LMDB создаёт также служебный lock-файл. Cleanup касается только нашей новой папки.
<?php
$directory = sys_get_temp_dir() . '/dba-' . bin2hex(random_bytes(8));
mkdir($directory, 0700);
$path = $directory . '/demo.db';
$store = $directory . '/store';
mkdir($store, 0700);
$db = dba_open($store, 'c', 'lmdb', 0600, 16 * 1024 * 1024, DBA_LMDB_USE_SUB_DIR);
if ($db === false) { throw new RuntimeException('LMDB-каталог не открыт'); }
dba_insert('language', 'ru', $db);
echo dba_fetch('language', $db), "\n";
dba_close($db);
foreach (glob($store . '/*') as $file) { unlink($file); }
rmdir($store);
rmdir($directory);
Ожидаемый результат
ru
USE_SUB_DIR=0 просит directory layout; каталог создаётся нами заранее. Не смешивайте этот путь с прежним file layout. Нулевое числовое значение константы — настоящий режим, не отсутствие настройки.
Общий DBA не экспортирует все native LMDB flags; недопустимый flags даёт ValueError. Наличие LMDB-констант также зависит от сборки handler, поэтому проверяют dba_handlers/defined. LMDB использует собственные транзакции для отдельных действий, но не предоставляет через dba_* begin/commit группы изменений. Нельзя из этого выводить атомарность «два replace составляют один заказ». Для роста и конкурентной работы изучают ограничения именно подключённой версии библиотеки, включая размер ключа и жизненный цикл reader.
INI: секции, ключи и строковые значения
inifile обслуживает формат [секция] плюс имя=значение. Массив ключа содержит ровно два элемента: группа и имя; строковая форма [group]name подходит для того же доступа. Без секции группа пустая. firstkey/nextkey выдают строковую форму, а dba_key_split разбирает её. В обходе могут встретиться отдельные заголовки групп с пустым именем: их обрабатывают явно, не называют пользовательской настройкой.
<?php
$directory = sys_get_temp_dir() . '/dba-' . bin2hex(random_bytes(8));
mkdir($directory, 0700);
$path = $directory . '/demo.db';
$db = dba_open($path, 'n', 'inifile');
if ($db === false) { throw new RuntimeException('INI не открыт'); }
dba_insert(['delivery', 'city'], 'Tula', $db);
dba_replace(['delivery', 'city'], 'Moscow', $db);
echo dba_fetch('[delivery]city', $db), "\n";
dba_close($db);
foreach (glob($directory . '/*') as $file) { unlink($file); }
rmdir($directory);
Ожидаемый результат
Moscow
Массив [delivery,city] и строка [delivery]city — два представления одного ключа. Только наш временный файл: рабочий php.ini сервера не изменяется, его директивы требуют собственной схемы и уровня доступа.
<?php
$directory = sys_get_temp_dir() . '/dba-' . bin2hex(random_bytes(8));
mkdir($directory, 0700);
$path = $directory . '/demo.db';
$db = dba_open($path, 'n', 'inifile');
if ($db === false) { throw new RuntimeException('INI не открыт'); }
dba_insert(['delivery', 'city'], 'Tula', $db);
for ($key = dba_firstkey($db); $key !== false; $key = dba_nextkey($db)) {
[$group, $name] = dba_key_split($key);
if ($name === '') { echo 'Секция:', $group, "\n"; continue; }
echo $group, '/', $name, '=', dba_fetch([$group, $name], $db), "\n";
}
dba_close($db);
foreach (glob($directory . '/*') as $file) { unlink($file); }
rmdir($directory);
Ожидаемый результат
Секция:delivery
delivery/city=Tula
Обход inifile возвращает и отдельную секцию с пустым именем. Её показываем отдельно. Сначала проверяем конец обхода, затем разбираем строку. dba_key_split(false/null) исторически возвращает false, но с PHP8.4 оба варианта deprecated. Учебный INI — наш файл, не настоящий php.ini сервера.
<?php
$directory = sys_get_temp_dir() . '/dba-' . bin2hex(random_bytes(8));
mkdir($directory, 0700);
$path = $directory . '/demo.db';
$db = dba_open($path, 'n', 'inifile');
dba_insert(['servers', 'host'], 'alpha', $db);
dba_insert(['servers', 'host'], 'beta', $db);
echo dba_fetch(['servers', 'host'], $db, 0), "\n";
echo dba_fetch(['servers', 'host'], $db, 1), "\n";
dba_close($db);
foreach (glob($directory . '/*') as $file) { unlink($file); }
rmdir($directory);
Ожидаемый результат
alpha
beta
В tagged inifile insert дописывает запись, а fetch передаёт skip внутреннему reader. Официальная короткая карточка fetch выделяет CDB, поэтому этот нюанс отдельно проверен реальным запуском. Для одиночного значения используйте осознанный replace.
Формат текстовый: не рассчитывайте на хранение произвольных бинарных байтов/NUL и на сохранение точного исходного оформления комментариев как у редактора текста. Значения остаются строками, не булевыми/числовыми типами автоматически. dba_key_split(false/null) больше не следует использовать как условие цикла: сPHP8.4 это deprecated.
CDB и cdb_make: отдельные этапы построения и чтения
Constant database хорошо выражает сценарий «раз в сутки сформировать большой словарь, затем много раз читать». cdb_make из bundled-библиотеки строит новый файл, cdb читает готовый. update/delete уже созданного файла не поддерживаются. Встроенная библиотека также поддерживает PHP streams для доступа к cdb, но не каждый stream умеет seek/lock и не стоит считать произвольный удалённый URL обычной локальной базой.
<?php
$directory = sys_get_temp_dir() . '/dba-' . bin2hex(random_bytes(8));
mkdir($directory, 0700);
$path = $directory . '/demo.db';
$builder = dba_open($path, 'n', 'cdb_make');
if ($builder === false) { throw new RuntimeException('Нет cdb builder'); }
dba_insert('pickup', 'Самовывоз', $builder);
dba_insert('courier', 'Курьер', $builder);
dba_close($builder);
$db = dba_open($path, 'r', 'cdb');
if ($db === false) { throw new RuntimeException('CDB не прочитан'); }
echo dba_fetch('courier', $db), "\n";
dba_close($db);
foreach (glob($directory . '/*') as $file) { unlink($file); }
rmdir($directory);
Ожидаемый результат
Курьер
Строитель сначала пишет набор, close завершает формат, затем отдельный cdb-reader читает. Для обновления выпускают новый файл и после проверки переключают читателей по согласованной схеме; не truncate действующий справочник под пользователями.
<?php
$directory = sys_get_temp_dir() . '/dba-' . bin2hex(random_bytes(8));
mkdir($directory, 0700);
$path = $directory . '/demo.db';
$builder = dba_open($path, 'n', 'cdb_make');
dba_insert('city', 'Moscow', $builder);
dba_insert('city', 'Tula', $builder);
dba_close($builder);
$db = dba_open($path, 'r', 'cdb');
echo dba_fetch('city', $db, 0), "\n";
echo dba_fetch('city', $db, 1), "\n";
var_dump(dba_fetch('city', $db, 2));
dba_close($db);
foreach (glob($directory . '/*') as $file) { unlink($file); }
rmdir($directory);
Ожидаемый результат
Moscow
Tula
bool(false)
CDB умеет повторные ключи; skip0 берёт первое совпадение, skip1 пропускает одно. Это исключение к привычному ожиданию insert «только уникальный ключ». Третий аргумент skip — современный порядок PHP8.2+.
Распределённое обновление файла требует контроля версии и совместимого способа переключения/копирования; на Windows открытые файлы могут мешать rename/удалению. Проверяйте созданный файл до публикации. При нескольких значениях одного ключа заранее определите порядок/семантику данных и не полагайтесь на unique-key контракт других handlers.
Остальные драйверы: тот же API в их реальном окружении
Примеры ниже показывают, где подставляется конкретный формат. Они не утверждают, что эти библиотеки установлены здесь, и не предлагают legacy dbm/ndbm для нового проекта. Практический смысл — обслужить имеющийся формат или проверить выбранный backend в отдельной сборке. Данные из файла другого handler сами собой не преобразуются; для миграции нужен читающий старый формат процесс, проверка ключей/значений и построение нового хранилища.
<?php
$directory = sys_get_temp_dir() . '/dba-' . bin2hex(random_bytes(8));
mkdir($directory, 0700);
$path = $directory . '/demo.db';
$db = dba_open($path, 'n', 'dbm');
if ($db === false) { throw new RuntimeException('Handler не смог создать базу'); }
dba_insert('site:language', 'ru', $db);
echo dba_fetch('site:language', $db), "\n";
dba_close($db);
foreach (glob($directory . '/*') as $file) { unlink($file); }
rmdir($directory);
Ожидаемый результат
ru
Контекстный пример: нужен именно dbm в dba_handlers и совместимая библиотека/сборка. Проверен только PHP-синтаксис; данный handler отсутствует в проверочной Windows-DLL. Используется только новый уникальный каталог; некоторые форматы создают несколько файлов. Не применять n к существующей базе.
<?php
$directory = sys_get_temp_dir() . '/dba-' . bin2hex(random_bytes(8));
mkdir($directory, 0700);
$path = $directory . '/demo.db';
$db = dba_open($path, 'n', 'ndbm');
if ($db === false) { throw new RuntimeException('Handler не смог создать базу'); }
dba_insert('site:language', 'ru', $db);
echo dba_fetch('site:language', $db), "\n";
dba_close($db);
foreach (glob($directory . '/*') as $file) { unlink($file); }
rmdir($directory);
Ожидаемый результат
ru
Контекстный пример: нужен именно ndbm в dba_handlers и совместимая библиотека/сборка. Проверен только PHP-синтаксис; данный handler отсутствует в проверочной Windows-DLL. Используется только новый уникальный каталог; некоторые форматы создают несколько файлов. Не применять n к существующей базе.
<?php
$directory = sys_get_temp_dir() . '/dba-' . bin2hex(random_bytes(8));
mkdir($directory, 0700);
$path = $directory . '/demo.db';
$db = dba_open($path, 'n', 'gdbm');
if ($db === false) { throw new RuntimeException('Handler не смог создать базу'); }
dba_insert('site:language', 'ru', $db);
echo dba_fetch('site:language', $db), "\n";
dba_close($db);
foreach (glob($directory . '/*') as $file) { unlink($file); }
rmdir($directory);
Ожидаемый результат
ru
Контекстный пример: нужен именно gdbm в dba_handlers и совместимая библиотека/сборка. Проверен только PHP-синтаксис; данный handler отсутствует в проверочной Windows-DLL. Используется только новый уникальный каталог; некоторые форматы создают несколько файлов. Не применять n к существующей базе.
<?php
$directory = sys_get_temp_dir() . '/dba-' . bin2hex(random_bytes(8));
mkdir($directory, 0700);
$path = $directory . '/demo.db';
$db = dba_open($path, 'n', 'db2');
if ($db === false) { throw new RuntimeException('Handler не смог создать базу'); }
dba_insert('site:language', 'ru', $db);
echo dba_fetch('site:language', $db), "\n";
dba_close($db);
foreach (glob($directory . '/*') as $file) { unlink($file); }
rmdir($directory);
Ожидаемый результат
ru
Контекстный пример: нужен именно db2 в dba_handlers и совместимая библиотека/сборка. Проверен только PHP-синтаксис; данный handler отсутствует в проверочной Windows-DLL. Используется только новый уникальный каталог; некоторые форматы создают несколько файлов. Не применять n к существующей базе.
<?php
$directory = sys_get_temp_dir() . '/dba-' . bin2hex(random_bytes(8));
mkdir($directory, 0700);
$path = $directory . '/demo.db';
$db = dba_open($path, 'n', 'db3');
if ($db === false) { throw new RuntimeException('Handler не смог создать базу'); }
dba_insert('site:language', 'ru', $db);
echo dba_fetch('site:language', $db), "\n";
dba_close($db);
foreach (glob($directory . '/*') as $file) { unlink($file); }
rmdir($directory);
Ожидаемый результат
ru
Контекстный пример: нужен именно db3 в dba_handlers и совместимая библиотека/сборка. Проверен только PHP-синтаксис; данный handler отсутствует в проверочной Windows-DLL. Используется только новый уникальный каталог; некоторые форматы создают несколько файлов. Не применять n к существующей базе.
<?php
$directory = sys_get_temp_dir() . '/dba-' . bin2hex(random_bytes(8));
mkdir($directory, 0700);
$path = $directory . '/demo.db';
$db = dba_open($path, 'n', 'db4');
if ($db === false) { throw new RuntimeException('Handler не смог создать базу'); }
dba_insert('site:language', 'ru', $db);
echo dba_fetch('site:language', $db), "\n";
dba_close($db);
foreach (glob($directory . '/*') as $file) { unlink($file); }
rmdir($directory);
Ожидаемый результат
ru
Контекстный пример: нужен именно db4 в dba_handlers и совместимая библиотека/сборка. Проверен только PHP-синтаксис; данный handler отсутствует в проверочной Windows-DLL. Используется только новый уникальный каталог; некоторые форматы создают несколько файлов. Не применять n к существующей базе.
<?php
$directory = sys_get_temp_dir() . '/dba-' . bin2hex(random_bytes(8));
mkdir($directory, 0700);
$path = $directory . '/demo.db';
$db = dba_open($path, 'n', 'tcadb');
if ($db === false) { throw new RuntimeException('Handler не смог создать базу'); }
dba_insert('site:language', 'ru', $db);
echo dba_fetch('site:language', $db), "\n";
dba_close($db);
foreach (glob($directory . '/*') as $file) { unlink($file); }
rmdir($directory);
Ожидаемый результат
ru
Контекстный пример: нужен именно tcadb в dba_handlers и совместимая библиотека/сборка. Проверен только PHP-синтаксис; данный handler отсутствует в проверочной Windows-DLL. Используется только новый уникальный каталог; некоторые форматы создают несколько файлов. Не применять n к существующей базе.
Полный указатель функций DBA
Все функции книги собраны по одному API. Настройка драйвера меняет физический формат, но не обещает одинаковую поддержку duplicate keys, optimize, sync или блокировок.
Практический выбор и типичные ошибки
| Ситуация | Действие |
|---|---|
| Файл заканчивается на .db | Определить его настоящий формат до выбора handler. SQLite-файл открывает SQLite3/PDO, не произвольный DBA. |
| Undefined function dba_open | Проверить extension_loaded в нужном SAPI и установку расширения. |
| No such handler | Проверить dba_handlers; chmod не добавляет библиотеку. |
| Пустое значение стало «не найдено» | Проверять dba_fetch через ===false. |
| Нужно много запросов по полям JSON | Рассмотреть SQLite3 с индексами; DBA ищет по ключу, не по JSON-полям. |
| Нужно изменить несколько ключей атомарно | У общего DBA API нет begin/commit; lock не гарантирует восстановление всех ключей при аварии. |
| Писатели ждут веб-запрос | Сократить срок write-handle, не делать HTTP-вызов под lock; отдельно оценить persistent. |
| optimize вернул true, размер прежний | Драйвер мог сделать no-op; измерять фактический эффект и смотреть его контракт. |
Упражнение: сохраняйте статус задания под job:42, затем измените queued на done и удалите после истечения выбранного срока. Срок годности хранится в вашем значении: у DBA нет универсального TTL. Предлагаемое решение — dba_insert для нового ключа, dba_replace для полного нового состояния, dba_fetch со строгой проверкой и dba_delete для очистки. Реальный обработчик очереди также требует атомарного захвата задания, повторов и восстановления; одна пара fetch/replace при конкуренции ещё не готовая очередь.
- Обсуждение практической проблемы: Расширение .db не определяет формат; DBA должен быть установлен и иметь совместимый handler.
- Обсуждение практической проблемы: rl создаёт/открывает .lck: нужны права также на этот файл и каталог.
- Обсуждение практической проблемы: Ошибка No such handler — вопрос сборки, а не повод выдавать каталогу0777.
- Обсуждение практической проблемы: Подсказки IDE и наличие расширения в реальном PHP — две разные проверки.
- Обсуждение практической проблемы: Выбор хранения для большого словаря: стоимость чтения файла, индекс и характер обновлений.
- Обсуждение практической проблемы: Ключ-значение и постоянное хранение: требования к поиску, конкуренции и сохранности важнее старых универсальных советов.
Обсуждения использованы как описание затруднений, не как нормативный контракт. Старые ответы о скорости и выборе продукта нельзя переносить на новую нагрузку. Источники правил — PHP Manual и tagged PHP8.5.11; оригинальные примеры выполняются только на вновь созданных учебных файлах. Неустановленные handlers честно отмечены как контекстные lint-примеры.
Функции и методы этой темы
Откройте карточку, чтобы увидеть договор функции, примеры, ограничения и связанные инструменты.
dba_openОткрыть небольшое файловое хранилище, где нужное значение ищут по известному ключу.
PHP 4+; Dba\Connection с PHP 8.4 · dba и выбранный handler
dba_open(string $path, string $mode, ?string $handler = null, int $permission = 0644, int $map_size = 0, ?int $flags = null): Dba\Connection|falseОткрыть небольшое файловое хранилище, где нужное значение ищут по известному ключу.
| Параметр | Что означает |
|---|---|
| path | Путь к файлу/расположению handler. Одно расширение .db не определяет формат; выбирайте совместимый handler. |
| mode | r читать; w читать/писать существующую; c читать/писать и создать при отсутствии; n создать/обнулить. Дополнительно l/d/- и t для блокировки, подробно в разделе ниже. |
| handler | Имя из dba_handlers; null выбирает dba.default_handler. Для переносимого кода лучше явно задать формат. |
| permission | Права новых файлов как chmod, default0644; применяются поддерживающими драйверами и зависят от umask/ОС. Поддержка db1/db2/db3/db4/dbm/gdbm/ndbm/lmdb. |
| map_size | Только LMDB: размер map в байтах, кратный странице ОС; 0 — default. Поддержка с PHP7.3.14/7.4.2. |
| flags | PHP8.2+: только LMDB, USE_SUB_DIR/NO_SUB_DIR; null — driver default, это не набор произвольных LMDB C-флагов. |
Возвращает Dba\Connection с PHP8.4, раньше resource; false при отказе. Отсутствующий default при handler=null даёт warning+false. В Berkeley DB handler создаётся BTree, а произвольные Hash/Queue-режимы через общий DBA API не выбираются. Это ограничение конкретного BDB-интерфейса, не утверждение, что любой handler имеет BTree-формат. DBA обычно сообщает операционный отказ через false и, в зависимости от причины, E_WARNING; автоматического SQL-подобного режима исключений нет. Неверные аргументы/закрытый объект могут дать ValueError/TypeError/Error. Проверяйте === false, а не общую «ложность»: строки "0" и "" могут быть нормальными значениями.
- новый временный файл
- dba_open→$db
- user:42:language→ru
- dba_fetch→ru
<?php
$path = tempnam(sys_get_temp_dir(), 'dba-');
$db = dba_open($path, 'n', 'flatfile');
if ($db === false) { throw new RuntimeException('База не открыта'); }
dba_insert('user:42:language', 'ru', $db);
echo dba_fetch('user:42:language', $db), "\n";
dba_close($db);
unlink($path);
Ожидаемый результат
ru
tempnam создаёт только наш новый временный файл; n инициализирует его. Ключ user:42:language — наша договорённость об имени, значение ru — строка. dba_open возвращает соединение, insert сохраняет, fetch возвращает значение, close завершает. flatfile выбран для учебного опыта и совместимости; это не рекомендация для большой рабочей базы.
<?php
$directory = sys_get_temp_dir() . '/dba-' . bin2hex(random_bytes(8));
mkdir($directory, 0700);
$path = $directory . '/demo.db';
$db = dba_open($path, 'n', 'flatfile');
if ($db === false) { throw new RuntimeException('Не удалось открыть учебную базу'); }
dba_insert('delivery:pickup', 'Самовывоз', $db);
dba_close($db);
$db = dba_open($path, 'r', 'flatfile');
if ($db === false) { throw new RuntimeException('Не удалось прочитать базу'); }
echo dba_fetch('delivery:pickup', $db), "\n";
dba_close($db);
foreach (glob($directory . '/*') as $file) { unlink($file); }
rmdir($directory);
Ожидаемый результат
Самовывоз
После закрытия данные остались в файле. r не позволяет менять их через это соединение; w откроет существующий файл для записи, c также разрешит создать отсутствующий. n обнулит существующий файл, поэтому его нельзя подставлять как обычное «открыть».
Связанные действия: dba_close, dba_handlers, dba_popen.
dba_insertЗаписать новое значение, сохранив старое при конфликте обычного уникального ключа.
PHP 4+; Dba\Connection с PHP 8.4 · dba и выбранный handler
dba_insert(string|array $key, string $value, Dba\Connection $dba): boolЗаписать новое значение, сохранив старое при конфликте обычного уникального ключа.
| Параметр | Что означает |
|---|---|
| key | Строковый ключ. Для inifile допустим array [группа, имя] или строка [группа]имя. |
| value | Строка: объект/массив сначала осознанно сериализуют, например JSON. |
| dba | Открытый Dba\Connection (PHP8.4+); в более старых версиях resource. |
true — вставлено, false — отказ; у стандартного уникального ключа существующая запись не заменяется. Driver-specific исключение: cdb builder и inifile могут добавлять одинаковые ключи, поэтому контракт уникальности приложения нельзя предполагать для всех форматов. Режим должен разрешать запись. DBA обычно сообщает операционный отказ через false и, в зависимости от причины, E_WARNING; автоматического SQL-подобного режима исключений нет. Неверные аргументы/закрытый объект могут дать ValueError/TypeError/Error. Проверяйте === false, а не общую «ложность»: строки "0" и "" могут быть нормальными значениями.
- currency отсутствует
- insert RUB→true
- повтор insert USD→false в flatfile
- fetch сохранил RUB
<?php
$directory = sys_get_temp_dir() . '/dba-' . bin2hex(random_bytes(8));
mkdir($directory, 0700);
$path = $directory . '/demo.db';
$db = dba_open($path, 'n', 'flatfile');
if ($db === false) { throw new RuntimeException('Не удалось открыть учебную базу'); }
var_dump(dba_insert('currency', 'RUB', $db));
var_dump(dba_insert('currency', 'USD', $db));
echo dba_fetch('currency', $db), "\n";
dba_close($db);
foreach (glob($directory . '/*') as $file) { unlink($file); }
rmdir($directory);
Ожидаемый результат
bool(true)
bool(false)
RUB
Для обычного unique-key handler insert отказал второй раз и сохранил RUB. Если по требованию нужна замена, используйте replace. Поведение duplicate-capable inifile/cdb описано отдельно: не стройте универсальное ограничение уникальности поверх любого handler.
<?php
$directory = sys_get_temp_dir() . '/dba-' . bin2hex(random_bytes(8));
mkdir($directory, 0700);
$path = $directory . '/demo.db';
$db = dba_open($path, 'n', 'flatfile');
if ($db === false) { throw new RuntimeException('Не удалось открыть учебную базу'); }
$profile = ['language' => 'ru', 'theme' => 'dark'];
$value = json_encode($profile, JSON_THROW_ON_ERROR);
if (!dba_insert('user:42', $value, $db)) { throw new RuntimeException('Профиль не записан'); }
$raw = dba_fetch('user:42', $db);
if ($raw === false) { throw new RuntimeException('Профиль не найден'); }
$loaded = json_decode($raw, true, 512, JSON_THROW_ON_ERROR);
echo $loaded['theme'], "\n";
dba_close($db);
foreach (glob($directory . '/*') as $file) { unlink($file); }
rmdir($directory);
Ожидаемый результат
dark
DBA хранит строку JSON как неделимое значение, не отдельные поля. Чтобы искать всех пользователей с theme=dark, придётся обходить и декодировать записи; для сложных выборок удобнее SQLite. JSON проверяется при кодировании/чтении, а структуру из внешнего источника проверяют дополнительно.
Связанные действия: dba_replace, dba_exists, dba_fetch.
dba_replaceСохранить актуальную настройку или последнюю позицию обработки, независимо от наличия ключа.
PHP 4+; Dba\Connection с PHP 8.4 · dba и выбранный handler
dba_replace(string|array $key, string $value, Dba\Connection $dba): boolСохранить актуальную настройку или последнюю позицию обработки, независимо от наличия ключа.
| Параметр | Что означает |
|---|---|
| key | Строковый ключ. Для inifile допустим array [группа, имя] или строка [группа]имя. |
| value | Полное новое строковое значение. |
| dba | Открытый Dba\Connection (PHP8.4+); в более старых версиях resource. |
true — обновлено/создано, false — отказ. Это upsert по ключу, а не частичное изменение JSON-поля, не атомарный increment и не транзакция группы записей. cdb — постоянная база, её не редактируют этим методом после построения. DBA обычно сообщает операционный отказ через false и, в зависимости от причины, E_WARNING; автоматического SQL-подобного режима исключений нет. Неверные аргументы/закрытый объект могут дать ValueError/TypeError/Error. Проверяйте === false, а не общую «ложность»: строки "0" и "" могут быть нормальными значениями.
- cursor отсутствует→replace page-1
- replace page-2
- fetch→page-2
<?php
$directory = sys_get_temp_dir() . '/dba-' . bin2hex(random_bytes(8));
mkdir($directory, 0700);
$path = $directory . '/demo.db';
$db = dba_open($path, 'n', 'flatfile');
if ($db === false) { throw new RuntimeException('Не удалось открыть учебную базу'); }
dba_insert('theme', 'light', $db);
var_dump(dba_replace('theme', 'dark', $db));
echo dba_fetch('theme', $db), "\n";
dba_close($db);
foreach (glob($directory . '/*') as $file) { unlink($file); }
rmdir($directory);
Ожидаемый результат
bool(true)
dark
replace выбирает новую строку целиком. У него нет SQL-условия WHERE old_value=... и универсального compare-and-swap; согласованность нескольких шагов требует правил блокировки.
<?php
$directory = sys_get_temp_dir() . '/dba-' . bin2hex(random_bytes(8));
mkdir($directory, 0700);
$path = $directory . '/demo.db';
$db = dba_open($path, 'n', 'flatfile');
if ($db === false) { throw new RuntimeException('Не удалось открыть учебную базу'); }
dba_replace('sync:catalog:cursor', 'page-1', $db);
dba_replace('sync:catalog:cursor', 'page-2', $db);
echo dba_fetch('sync:catalog:cursor', $db), "\n";
dba_close($db);
foreach (glob($directory . '/*') as $file) { unlink($file); }
rmdir($directory);
Ожидаемый результат
page-2
replace работает и для отсутствующего ключа. Но курсор нельзя продвигать до успешного сохранения данных страницы: DBA не предоставляет общую транзакцию между этим файлом и сторонней SQL-базой.
Связанные действия: dba_insert, dba_delete, dba_sync.
dba_fetchПолучить строку по известному ключу, например язык посетителя или кэш результата.
PHP 4+; Dba\Connection с PHP 8.4 · dba и выбранный handler
dba_fetch(string|array $key, Dba\Connection $dba, int $skip = 0): string|falseПолучить строку по известному ключу, например язык посетителя или кэш результата.
| Параметр | Что означает |
|---|---|
| key | Строковый ключ. Для inifile допустим array [группа, имя] или строка [группа]имя. |
| dba | Открытый Dba\Connection (PHP8.4+); в более старых версиях resource. |
| skip | Сколько совпадающих key/value пропустить в duplicate-capable базе, прежде всего cdb; у остальных поддержка зависит от handler. Современное место — третий аргумент. |
Строка при найденной паре, false при отсутствии/отказе. Значение не преобразуется автоматически в int/array. PHP8.2 перенёс skip в конец; старый overload dba_fetch(key,skip,resource) deprecated с8.3. Tagged inifile тоже передаёт skip своему reader; не считайте поддержку дубликатов универсальной. DBA обычно сообщает операционный отказ через false и, в зависимости от причины, E_WARNING; автоматического SQL-подобного режима исключений нет. Неверные аргументы/закрытый объект могут дать ValueError/TypeError/Error. Проверяйте === false, а не общую «ложность»: строки "0" и "" могут быть нормальными значениями.
- ключ count
- DBA находит строку "0"
- PHP сохраняет string, не false
- приложение проверяет тип/смысл
<?php
$directory = sys_get_temp_dir() . '/dba-' . bin2hex(random_bytes(8));
mkdir($directory, 0700);
$path = $directory . '/demo.db';
$db = dba_open($path, 'n', 'flatfile');
if ($db === false) { throw new RuntimeException('Не удалось открыть учебную базу'); }
dba_insert('count', '0', $db);
dba_insert('note', '', $db);
var_dump(dba_fetch('count', $db));
var_dump(dba_fetch('note', $db));
var_dump(dba_fetch('missing', $db));
dba_close($db);
foreach (glob($directory . '/*') as $file) { unlink($file); }
rmdir($directory);
Ожидаемый результат
string(1) "0"
string(0) ""
bool(false)
Возврат — строка либо false. if (!$value) смешает все три случая; === false обнаруживает именно отсутствие/отказ, а "0" можно затем отдельно проверить и преобразовать в число.
<?php
$directory = sys_get_temp_dir() . '/dba-' . bin2hex(random_bytes(8));
mkdir($directory, 0700);
$path = $directory . '/demo.db';
$db = dba_open($path, 'n', 'flatfile');
if ($db === false) { throw new RuntimeException('Не удалось открыть учебную базу'); }
$language = dba_fetch('user:77:language', $db);
if ($language === false) { $language = 'ru'; }
echo $language, "\n";
dba_insert('user:77:language', 'en', $db);
echo dba_fetch('user:77:language', $db), "\n";
dba_close($db);
foreach (glob($directory . '/*') as $file) { unlink($file); }
rmdir($directory);
Ожидаемый результат
ru
en
Default выбран приложением, не DBA. Проверка существования перед каждым fetch обычно делает лишнюю операцию; часто проще один fetch и строгая проверка.
Связанные действия: dba_exists, dba_key_split, dba_firstkey.
dba_existsПонять, задана ли настройка вообще, даже если её значение пустое.
PHP 4+; Dba\Connection с PHP 8.4 · dba и выбранный handler
dba_exists(string|array $key, Dba\Connection $dba): boolПонять, задана ли настройка вообще, даже если её значение пустое.
| Параметр | Что означает |
|---|---|
| key | Строковый ключ. Для inifile допустим array [группа, имя] или строка [группа]имя. |
| dba | Открытый Dba\Connection (PHP8.4+); в более старых версиях resource. |
true при наличии, false при отсутствии/отказе. Не является проверкой доступа пользователя, срока годности кэша и валидности содержимого. Отдельное exists+insert не создаёт универсальную транзакцию; удерживайте согласованный lock и проверяйте сам результат изменения.
- banner→пустая строка
- exists banner=true
- missing не записан→false
<?php
$directory = sys_get_temp_dir() . '/dba-' . bin2hex(random_bytes(8));
mkdir($directory, 0700);
$path = $directory . '/demo.db';
$db = dba_open($path, 'n', 'flatfile');
if ($db === false) { throw new RuntimeException('Не удалось открыть учебную базу'); }
dba_insert('banner', '', $db);
var_dump(dba_exists('banner', $db));
var_dump(dba_exists('missing', $db));
dba_close($db);
foreach (glob($directory . '/*') as $file) { unlink($file); }
rmdir($directory);
Ожидаемый результат
bool(true)
bool(false)
Ключ banner существует, хотя его значение пустое. exists отвечает о наличии, а не истинности сохранённой строки.
<?php
$directory = sys_get_temp_dir() . '/dba-' . bin2hex(random_bytes(8));
mkdir($directory, 0700);
$path = $directory . '/demo.db';
$db = dba_open($path, 'n', 'flatfile');
if ($db === false) { throw new RuntimeException('Не удалось открыть учебную базу'); }
dba_insert('language', 'en', $db);
if (!dba_exists('language', $db)) { dba_insert('language', 'ru', $db); }
echo dba_fetch('language', $db), "\n";
dba_close($db);
foreach (glob($directory . '/*') as $file) { unlink($file); }
rmdir($directory);
Ожидаемый результат
en
Здесь эксклюзивное write-открытие удерживается на всю пару exists+insert. Если отключить блокировки или разнести шаги между открытиями, другой writer может вмешаться; для уникального handler результат insert сам по себе важнее предварительной проверки.
Связанные действия: dba_fetch, dba_insert.
dba_deleteУбрать ненужное значение по конкретному ключу.
PHP 4+; Dba\Connection с PHP 8.4 · dba и выбранный handler
dba_delete(string|array $key, Dba\Connection $dba): boolУбрать ненужное значение по конкретному ключу.
| Параметр | Что означает |
|---|---|
| key | Строковый ключ. Для inifile допустим array [группа, имя] или строка [группа]имя. |
| dba | Открытый Dba\Connection (PHP8.4+); в более старых версиях resource. |
true — удалено, false — не получилось (в том числе отсутствующий ключ в обычном handler). Нужно соединение с записью; cdb после построения не поддерживает удаление. При форматах с дубликатами проверяйте семантику handler. Для целого префикса нет встроенного deletePrefix: отберите ключи и отдельно удалите, не меняя базу во время активного обхода. DBA обычно сообщает операционный отказ через false и, в зависимости от причины, E_WARNING; автоматического SQL-подобного режима исключений нет. Неверные аргументы/закрытый объект могут дать ValueError/TypeError/Error. Проверяйте === false, а не общую «ложность»: строки "0" и "" могут быть нормальными значениями.
- cache:weather существует
- delete→true
- exists→false
- файл может сохранить свободные страницы
<?php
$directory = sys_get_temp_dir() . '/dba-' . bin2hex(random_bytes(8));
mkdir($directory, 0700);
$path = $directory . '/demo.db';
$db = dba_open($path, 'n', 'flatfile');
if ($db === false) { throw new RuntimeException('Не удалось открыть учебную базу'); }
dba_insert('cache:weather', 'old response', $db);
var_dump(dba_delete('cache:weather', $db));
var_dump(dba_exists('cache:weather', $db));
dba_close($db);
foreach (glob($directory . '/*') as $file) { unlink($file); }
rmdir($directory);
Ожидаемый результат
bool(true)
bool(false)
Удалён ключ вместе со значением. Освобождение места внутри файла зависит от формата; размер файла не обязан немедленно уменьшиться.
<?php
$directory = sys_get_temp_dir() . '/dba-' . bin2hex(random_bytes(8));
mkdir($directory, 0700);
$path = $directory . '/demo.db';
$db = dba_open($path, 'n', 'flatfile');
if ($db === false) { throw new RuntimeException('Не удалось открыть учебную базу'); }
$key = 'draft:old';
if (dba_exists($key, $db)) {
if (!dba_delete($key, $db)) { throw new RuntimeException('Удаление не выполнено'); }
}
echo "Старого черновика нет\n";
dba_close($db);
foreach (glob($directory . '/*') as $file) { unlink($file); }
rmdir($directory);
Ожидаемый результат
Старого черновика нет
Приложение считает отсутствие нормальным конечным состоянием. Не следует превращать любое false удаления в успешную очистку без понимания причины; путь проверки находится под write-lock.
Связанные действия: dba_exists, dba_optimize, dba_replace.
dba_firstkeyНачать последовательный обход всех ключей и сбросить прежнюю позицию.
PHP 4+; Dba\Connection с PHP 8.4 · dba и выбранный handler
dba_firstkey(Dba\Connection $dba): string|falseНачать последовательный обход всех ключей и сбросить прежнюю позицию.
| Параметр | Что означает |
|---|---|
| dba | Открытый Dba\Connection (PHP8.4+); в более старых версиях resource. |
Возвращает строковый ключ либо false при конце/ошибке. firstkey начинает заново, nextkey продолжает один внутренний курсор соединения: вложенный обход того же handle изменит позицию внешнего. Ключи inifile приходят строками [group]name и разбираются dba_key_split. Во время обхода не insert/delete/replace ту же базу: предварительно соберите нужные ключи либо выполните отдельный проход.
- firstkey→первый ключ
- fetch(key)→значение
- nextkey→следующий
- false→конец; "0" остаётся ключом
<?php
$directory = sys_get_temp_dir() . '/dba-' . bin2hex(random_bytes(8));
mkdir($directory, 0700);
$path = $directory . '/demo.db';
$db = dba_open($path, 'n', 'flatfile');
if ($db === false) { throw new RuntimeException('Не удалось открыть учебную базу'); }
dba_insert('theme', 'dark', $db);
dba_insert('language', 'ru', $db);
$settings = [];
for ($key = dba_firstkey($db); $key !== false; $key = dba_nextkey($db)) { $settings[$key] = dba_fetch($key, $db); }
ksort($settings);
foreach ($settings as $key => $value) { echo $key, '=', $value, "\n"; }
dba_close($db);
foreach (glob($directory . '/*') as $file) { unlink($file); }
rmdir($directory);
Ожидаемый результат
language=ru
theme=dark
firstkey сбрасывает внутреннюю позицию, nextkey двигает её. Порядок обхода зависит от handler: для стабильного вывода явно сортируем маленький собранный массив. Большую базу целиком так собирать дорого.
<?php
$directory = sys_get_temp_dir() . '/dba-' . bin2hex(random_bytes(8));
mkdir($directory, 0700);
$path = $directory . '/demo.db';
$db = dba_open($path, 'n', 'flatfile');
if ($db === false) { throw new RuntimeException('Не удалось открыть учебную базу'); }
dba_insert('0', 'zero', $db);
$count = 0;
for ($key = dba_firstkey($db); $key !== false; $key = dba_nextkey($db)) { ++$count; }
echo $count, "\n";
dba_close($db);
foreach (glob($directory . '/*') as $file) { unlink($file); }
rmdir($directory);
Ожидаемый результат
1
Условие while($key) потеряло бы строковый ключ "0". Точный признак окончания — false. Для пустой базы firstkey сразу вернёт false.
Связанные действия: dba_nextkey, dba_fetch, dba_key_split.
dba_nextkeyПолучить следующий ключ начатого обхода, не загружая список ключей сразу.
PHP 4+; Dba\Connection с PHP 8.4 · dba и выбранный handler
dba_nextkey(Dba\Connection $dba): string|falseПолучить следующий ключ начатого обхода, не загружая список ключей сразу.
| Параметр | Что означает |
|---|---|
| dba | Открытый Dba\Connection (PHP8.4+); в более старых версиях resource. |
Возвращает строковый ключ либо false при конце/ошибке. firstkey начинает заново, nextkey продолжает один внутренний курсор соединения: вложенный обход того же handle изменит позицию внешнего. Ключи inifile приходят строками [group]name и разбираются dba_key_split. Во время обхода не insert/delete/replace ту же базу: предварительно соберите нужные ключи либо выполните отдельный проход.
- firstkey→первый ключ
- fetch(key)→значение
- nextkey→следующий
- false→конец; "0" остаётся ключом
<?php
$directory = sys_get_temp_dir() . '/dba-' . bin2hex(random_bytes(8));
mkdir($directory, 0700);
$path = $directory . '/demo.db';
$db = dba_open($path, 'n', 'flatfile');
if ($db === false) { throw new RuntimeException('Не удалось открыть учебную базу'); }
dba_insert('theme', 'dark', $db);
dba_insert('language', 'ru', $db);
$settings = [];
for ($key = dba_firstkey($db); $key !== false; $key = dba_nextkey($db)) { $settings[$key] = dba_fetch($key, $db); }
ksort($settings);
foreach ($settings as $key => $value) { echo $key, '=', $value, "\n"; }
dba_close($db);
foreach (glob($directory . '/*') as $file) { unlink($file); }
rmdir($directory);
Ожидаемый результат
language=ru
theme=dark
firstkey сбрасывает внутреннюю позицию, nextkey двигает её. Порядок обхода зависит от handler: для стабильного вывода явно сортируем маленький собранный массив. Большую базу целиком так собирать дорого.
<?php
$directory = sys_get_temp_dir() . '/dba-' . bin2hex(random_bytes(8));
mkdir($directory, 0700);
$path = $directory . '/demo.db';
$db = dba_open($path, 'n', 'flatfile');
if ($db === false) { throw new RuntimeException('Не удалось открыть учебную базу'); }
dba_insert('0', 'zero', $db);
$count = 0;
for ($key = dba_firstkey($db); $key !== false; $key = dba_nextkey($db)) { ++$count; }
echo $count, "\n";
dba_close($db);
foreach (glob($directory . '/*') as $file) { unlink($file); }
rmdir($directory);
Ожидаемый результат
1
Условие while($key) потеряло бы строковый ключ "0". Точный признак окончания — false. Для пустой базы firstkey сразу вернёт false.
Связанные действия: dba_firstkey, dba_fetch, dba_key_split.
dba_key_splitПреобразовать ключ INI из представления [section]name в два понятных элемента.
PHP 5+; null/false deprecated с8.4 · dba и выбранный handler
dba_key_split(string|false|null $key): array|falseПреобразовать ключ INI из представления [section]name в два понятных элемента.
| Параметр | Что означает |
|---|---|
| key | Строковый ключ из firstkey/nextkey или явно заданная строка. null/false — устаревшие входы с PHP8.4. |
Возвращает array [0=>группа,1=>имя]; без группы первый элемент пустой. null/false дают false с deprecation в8.4+, поэтому сперва проверяют конец обхода. Функция не открывает базу и не интерпретирует значение INI.
<?php
$parts = dba_key_split('[mail]sender');
echo $parts[0], ':', $parts[1], "\n";
$plain = dba_key_split('language');
echo $plain[0] === '' ? "Без секции:" : $plain[0];
echo $plain[1], "\n";
Ожидаемый результат
mail:sender
Без секции:language
Это специальный синтаксис ключей inifile, не разбиение любого ключа по двоеточию. Строка user:42 остаётся целым именем без секции.
<?php
$directory = sys_get_temp_dir() . '/dba-' . bin2hex(random_bytes(8));
mkdir($directory, 0700);
$path = $directory . '/demo.db';
$db = dba_open($path, 'n', 'inifile');
if ($db === false) { throw new RuntimeException('INI не открыт'); }
dba_insert(['delivery', 'city'], 'Tula', $db);
for ($key = dba_firstkey($db); $key !== false; $key = dba_nextkey($db)) {
[$group, $name] = dba_key_split($key);
if ($name === '') { echo 'Секция:', $group, "\n"; continue; }
echo $group, '/', $name, '=', dba_fetch([$group, $name], $db), "\n";
}
dba_close($db);
foreach (glob($directory . '/*') as $file) { unlink($file); }
rmdir($directory);
Ожидаемый результат
Секция:delivery
delivery/city=Tula
Обход inifile возвращает и отдельную секцию с пустым именем. Её показываем отдельно. Сначала проверяем конец обхода, затем разбираем строку. dba_key_split(false/null) исторически возвращает false, но с PHP8.4 оба варианта deprecated. Учебный INI — наш файл, не настоящий php.ini сервера.
Связанные действия: dba_firstkey, dba_nextkey, dba_fetch.
dba_closeЗакончить работу с базой и не удерживать обычный файловый lock лишнее время.
PHP 4+; Dba\Connection с PHP 8.4 · dba и выбранный handler
dba_close(Dba\Connection $dba): voidЗакончить работу с базой и не удерживать обычный файловый lock лишнее время.
| Параметр | Что означает |
|---|---|
| dba | Открытое соединение; после закрытия его нельзя использовать. |
Ничего не возвращает: не проверяют if(dba_close(...)). Для обычного dba_open освобождает ресурсы и блокировку. Persistent-handles имеют отдельный жизненный цикл пула процесса; не проектируйте освобождение рабочего write-lock на предположении, что запрос HTTP закончился. В долгом worker закрывайте как можно раньше и проверяйте поведение persistent выбранной сборки. Закрытый объект может вызвать Error при дальнейшем обращении.
- writer сохранил ready
- dba_close
- новый reader получает lock
- fetch→ready
<?php
$directory = sys_get_temp_dir() . '/dba-' . bin2hex(random_bytes(8));
mkdir($directory, 0700);
$path = $directory . '/demo.db';
$db = dba_open($path, 'n', 'flatfile');
if ($db === false) { throw new RuntimeException('Не удалось открыть учебную базу'); }
dba_insert('status', 'ready', $db);
dba_close($db);
$db = dba_open($path, 'r', 'flatfile');
echo dba_fetch('status', $db), "\n";
dba_close($db);
foreach (glob($directory . '/*') as $file) { unlink($file); }
rmdir($directory);
Ожидаемый результат
ready
Обычный write-handle удерживает lock до закрытия. Отдельный reader открывается после close, поэтому не ждёт нашу собственную блокировку.
<?php
$directory = sys_get_temp_dir() . '/dba-' . bin2hex(random_bytes(8));
mkdir($directory, 0700);
$path = $directory . '/demo.db';
$db = dba_open($path, 'n', 'flatfile');
if ($db === false) { throw new RuntimeException('Не удалось открыть учебную базу'); }
dba_close($db);
try { dba_fetch('status', $db); }
catch (Error $error) { echo "Соединение уже закрыто\n"; }
unlink($path);
rmdir($directory);
Ожидаемый результат
Соединение уже закрыто
Переменная всё ещё содержит PHP-объект, но внутренний handle уже недействителен. is_object не проверяет открытость; управление сроком жизни должно быть явным.
dba_handlersПроверить, какие форматы баз действительно поддерживает текущий PHP.
PHP 4.3+ · dba и выбранный handler
dba_handlers(bool $full_info = false): arrayПроверить, какие форматы баз действительно поддерживает текущий PHP.
| Параметр | Что означает |
|---|---|
| full_info | false — список имён; true — ассоциативный массив имя→описание/версия. |
Возвращает array, возможно пустой при отсутствии handlers. Информация относится к этому PHP-процессу/SAPI. Сравнивать наличие удобно in_array(name,handlers(),true) либо array_key_exists(name,handlers(true)); не путайте ключи двух форм ответа. Дополнительная библиотека устанавливается при сборке/настройке, её имя не загрузит код автоматически через dba_open.
- расширение dba
- handlers(false)→имена
- handlers(true)→описания
- выбор установленного формата
<?php
$handlers = dba_handlers();
echo in_array('flatfile', $handlers, true) ? "Учебный flatfile доступен\n" : "Нужен другой handler\n";
echo in_array('lmdb', $handlers, true) ? "LMDB доступен\n" : "LMDB не включён\n";
Ожидаемый результат
Учебный flatfile доступен
LMDB доступен
Вывод соответствует проверочной bundled-сборке. Список на Linux-сервере или другом php.ini может отличаться; наличие расширения DBA не означает наличия всех handlers.
<?php
$details = dba_handlers(true);
foreach (['cdb', 'qdbm', 'lmdb'] as $name) {
echo $name, ':', isset($details[$name]) && $details[$name] !== '' ? 'описание есть' : 'не установлен', "\n";
}
Ожидаемый результат
cdb:описание есть
qdbm:описание есть
lmdb:описание есть
true меняет форму ответа: ключи — имена handlers, значения — их версии/описания. Эти строки годятся для диагностики, но не являются стабильным машинным форматом.
Связанные действия: dba_open.
dba_listДиагностировать оставшиеся открытые DBA-handles в долгом процессе.
PHP 4.3+ · dba и выбранный handler
dba_list(): arrayДиагностировать оставшиеся открытые DBA-handles в долгом процессе.
| Параметр | Что означает |
|---|---|
| Аргументов нет | Рассматривается текущий процесс/контекст расширения. |
Ассоциативный array технический_id→путь. Историческое описание называет ключ resourceid; в PHP8.4+ соединение — объект, но считать числовой ключ его публичным стабильным идентификатором всё равно нельзя. Пустой array означает отсутствие видимых открытых handles, не отсутствие файлов. Не является глобальным монитором locks других процессов.
- открыли demo.db
- dba_list содержит путь
- close
- обычный handle исчез
<?php
$directory = sys_get_temp_dir() . '/dba-' . bin2hex(random_bytes(8));
mkdir($directory, 0700);
$path = $directory . '/demo.db';
$db = dba_open($path, 'n', 'flatfile');
if ($db === false) { throw new RuntimeException('Не удалось открыть учебную базу'); }
$opened = dba_list();
echo in_array(realpath($path), array_map('realpath', $opened), true) ? "Учебный файл открыт\n" : "Файл не найден\n";
dba_close($db);
foreach (glob($directory . '/*') as $file) { unlink($file); }
rmdir($directory);
Ожидаемый результат
Учебный файл открыт
realpath нормализует путь и разделители Windows перед сравнением. dba_list показывает открытые handles текущего контекста, а не все .db-файлы диска и не подключения других workers. Не выводите полные серверные пути посетителю.
<?php
$directory = sys_get_temp_dir() . '/dba-' . bin2hex(random_bytes(8));
mkdir($directory, 0700);
$path = $directory . '/demo.db';
$db = dba_open($path, 'n', 'flatfile');
if ($db === false) { throw new RuntimeException('Не удалось открыть учебную базу'); }
$before = count(dba_list());
dba_close($db);
echo $before - count(dba_list()), "\n";
unlink($path);
rmdir($directory);
Ожидаемый результат
1
После close один обычный handle исчез. Числовые ключи массива — технические идентификаторы, не стабильные номера файлов и не интерфейс для восстановления соединения.
Связанные действия: dba_close, dba_handlers.
dba_optimizeПопросить выбранный драйвер выполнить поддерживаемое обслуживание базы после множества изменений.
PHP 4+; Dba\Connection с PHP 8.4 · dba и выбранный handler
dba_optimize(Dba\Connection $dba): boolПопросить выбранный драйвер выполнить поддерживаемое обслуживание базы после множества изменений.
| Параметр | Что означает |
|---|---|
| dba | Открытое для записи соединение нужного handler. |
true при успехе, false при отказе. Реальная работа полностью зависит от handler; некоторые реализации ничего не делают и возвращают true. Это не общая SQL-оптимизация запросов, не гарантия shrink и не резервная копия. Перед обслуживанием нужен продуманный режим доступа и проверяемая копия.
- удалили старый ключ
- optimize зависит от handler
- нужный keep остаётся value
- время/размер измеряются отдельно
<?php
$directory = sys_get_temp_dir() . '/dba-' . bin2hex(random_bytes(8));
mkdir($directory, 0700);
$path = $directory . '/demo.db';
$db = dba_open($path, 'n', 'flatfile');
if ($db === false) { throw new RuntimeException('Не удалось открыть учебную базу'); }
dba_insert('cache:1', 'old', $db);
dba_delete('cache:1', $db);
var_dump(dba_optimize($db));
var_dump(dba_exists('cache:1', $db));
dba_close($db);
foreach (glob($directory . '/*') as $file) { unlink($file); }
rmdir($directory);
Ожидаемый результат
bool(true)
bool(false)
В tagged flatfile optimize — no-op с true. Поэтому true нельзя переводить как «файл уменьшился» или «доступ ускорился». Это успешное действие в контракте handler, которое здесь ничего не перестраивает.
<?php
$directory = sys_get_temp_dir() . '/dba-' . bin2hex(random_bytes(8));
mkdir($directory, 0700);
$path = $directory . '/demo.db';
$db = dba_open($path, 'n', 'qdbm');
if ($db === false) { throw new RuntimeException('QDBM недоступен'); }
dba_insert('keep', 'value', $db);
dba_insert('remove', 'old', $db);
dba_delete('remove', $db);
var_dump(dba_optimize($db));
echo dba_fetch('keep', $db), "\n";
dba_close($db);
foreach (glob($directory . '/*') as $file) { unlink($file); }
rmdir($directory);
Ожидаемый результат
bool(true)
value
QDBM выполняет собственную оптимизацию. Проверяем сохранность нужного значения, не обещаем конкретный процент ускорения/уменьшения. Обслуживание большого файла может держать lock и потребовать свободное место, поэтому не запускается после каждой покупки.
Связанные действия: dba_sync, dba_delete.
dba_syncПопросить драйвер сбросить поддерживаемые буферы хранения.
PHP 4+; Dba\Connection с PHP 8.4 · dba и выбранный handler
dba_sync(Dba\Connection $dba): boolПопросить драйвер сбросить поддерживаемые буферы хранения.
| Параметр | Что означает |
|---|---|
| dba | Открытое соединение выбранного handler. |
true — запрос синхронизации успешен, false — отказ. Может привести к физической записи, если это поддерживает handler; у других no-op. Не является begin/commit и не связывает несколько изменений в транзакцию. Для гарантий сохранности нужны контракт конкретной библиотеки, файловая система и тест восстановления; общий DBA bool этого не доказывает.
- replace snapshot=v2
- dba_sync→driver-specific action
- close
- новый reader получаетv2
<?php
$directory = sys_get_temp_dir() . '/dba-' . bin2hex(random_bytes(8));
mkdir($directory, 0700);
$path = $directory . '/demo.db';
$db = dba_open($path, 'n', 'flatfile');
if ($db === false) { throw new RuntimeException('Не удалось открыть учебную базу'); }
dba_replace('build', 'ready', $db);
var_dump(dba_sync($db));
echo dba_fetch('build', $db), "\n";
dba_close($db);
foreach (glob($directory . '/*') as $file) { unlink($file); }
rmdir($directory);
Ожидаемый результат
bool(true)
ready
В flatfile sync также no-op; true не доказывает fsync и устойчивость к отключению питания. Совпадение fetch доказывает только видимость значения в этой проверке.
<?php
$directory = sys_get_temp_dir() . '/dba-' . bin2hex(random_bytes(8));
mkdir($directory, 0700);
$path = $directory . '/demo.db';
$db = dba_open($path, 'n', 'qdbm');
if ($db === false) { throw new RuntimeException('QDBM недоступен'); }
dba_insert('snapshot', 'v2', $db);
if (!dba_sync($db)) { throw new RuntimeException('Синхронизация не выполнена'); }
dba_close($db);
$db = dba_open($path, 'r', 'qdbm');
echo dba_fetch('snapshot', $db), "\n";
dba_close($db);
foreach (glob($directory . '/*') as $file) { unlink($file); }
rmdir($directory);
Ожидаемый результат
v2
Драйвер QDBM использует собственную процедуру sync; после закрытия читаем новым handle. Это полезный функциональный опыт, не испытание сбоя питания и не обещание атомарности нескольких ключей.
Связанные действия: dba_close, dba_optimize.
dba_popenСократить повторные открытия в подходящем долгоживущем PHP-worker, понимая срок жизни блокировки.
PHP 4+; Dba\Connection с PHP 8.4 · dba и выбранный handler
dba_popen(string $path, string $mode, ?string $handler = null, int $permission = 0644, int $map_size = 0, ?int $flags = null): Dba\Connection|falseСократить повторные открытия в подходящем долгоживущем PHP-worker, понимая срок жизни блокировки.
| Параметр | Что означает |
|---|---|
| path / mode / handler | Те же значения, что dba_open; режим и формат являются частью выбора persistent backend. |
| permission / map_size / flags | Права новых файлов; map_size только LMDB; flags LMDB сPHP8.2. Default0644/0/null. |
Dba\Connection либо false (до8.4 resource). Backend может жить до окончания процесса, дольше одного HTTP-запроса, с памятью и locks; это не универсальное ускорение и особенно опасно для долго удерживаемого writer. В tagged8.5.11 ключ поиска пула строится из path/mode/handler, поэтому смену permission/map_size/flags нельзя считать надёжной перенастройкой уже закешированного backend. Для таких изменений закрывают/перезапускают соответствующий worker штатно. Default handler и ошибки как у dba_open. Не смешивайте разные способы locks. Для обычной короткой операции предпочтительнее понятный dba_open с close.
- worker открывает backend
- получает handle
- повторный запрос с теми же параметрами
- backend может переиспользоваться до конца процесса
<?php
$path = tempnam(__DIR__, 'dba-cache-');
$db = dba_popen($path, 'n', 'flatfile');
if ($db === false) { throw new RuntimeException('Persistent база не открыта'); }
dba_insert('catalog:version', 'v1', $db);
echo dba_fetch('catalog:version', $db), "\n";
dba_close($db);
Ожидаемый результат
v1
Запускайте сохранённый пример только в отдельном учебном каталоге. Созданный уникальный dba-cache-* удаляют после завершения PHP-процесса, когда закрыт persistent backend; проверочная среда делает это автоматически. В коротком CLI нет следующего HTTP-запроса для выигрыша от persistent-пула.
<?php
$path = tempnam(__DIR__, 'dba-cache-');
$first = dba_popen($path, 'c', 'flatfile');
if ($first === false) { throw new RuntimeException('База не открыта'); }
dba_replace('language', 'ru', $first);
dba_close($first);
$second = dba_popen($path, 'c', 'flatfile');
if ($second === false) { throw new RuntimeException('Повторное открытие не удалось'); }
echo dba_fetch('language', $second), "\n";
dba_close($second);
Ожидаемый результат
ru
Совпадение path/mode/handler позволяет этой реализации переиспользовать backend процесса. Это не общий пул всех workers, не общая PHP-переменная и не обещание, что close освободил persistent lock. Учебный файл удаляется внешней средой после процесса; рабочий файл хранится осознанно.
Источники и обсуждения
Код и руководство с собой
Скачайте самостоятельные примеры с исходными данными, инструкциями и ожидаемыми результатами. Примеры HTTP и фрагменты для проекта помечены отдельно: им нужно указанное окружение.
Код не исполняется на сайте. Скачайте нужный пример и запускайте его в своём локальном окружении.
Материалы сверены 2026-10-12. Примеры и объяснения написаны для этого руководства; источники и обсуждения доступны в каждой главе.


