Перейти к содержимому
Поиск
Php Разработка

PHP: полный мануал с примерами, функциями и ООП

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

Александр НекрасовИзменено:
Практический мануал PHP: от переменных и функций к объектной модели интернет-магазина
PHP 8.5.11468 глав6256 карточек справочника
Дерево руководства

Глава 38 · С нуля → справочник

DBA: полный справочник хранилищ «ключ → значение»

От короткой записи тарифа в файл до всех 15 функций, обработчиков, режимов открытия, блокировок, CDB, LMDB и границ общего API.

Сохранить тариф и прочитать после закрытия

В форме доставки нужны тарифы: самовывоз бесплатный, курьер стоит 350 рублей. Задача — записать выбранные строки в файл и прочитать их после закрытия соединения. Перед первым запуском создайте отдельный учебный каталог: режим n заново создаёт наш delivery.db и обнуляет уже существующий файл с тем же именем.

Как данные проходят через пример
  1. Ключ courier
  2. Строка 35000, копейки
  3. DBA + flatfile → delivery.db
  4. close → новое открытие r
  5. 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. Полная программа ниже проверяет открытие и повторно открывает файл только для чтения.

Что получится

DeliveryPrice in kopecks
pickup0
courier35000

Эту же таблицу программа записывает в 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-полей здесь не предоставляются.

Модель справочника
  1. ключ user:42:language — подписанная ячейка
  2. строка ru — её содержимое
  3. dba_insert кладёт строку в файловый формат
  4. 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_handlerDBA_DEFAULT сборки; INI_ALLHandler, используемый при handler=null. Реальное имя зависит от сборки.
Явно выбрать default только в своём процессеСамостоятельный пример
<?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 допускает такую локальную смену; прежнее значение восстановлено. Это не меняет формат уже существующих файлов и не устанавливает отсутствующую библиотеку.

Открыть с null после осознанной настройки defaultСамостоятельный пример
<?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().

Понять, почему соединение нельзя сделать через newСамостоятельный пример
<?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Создать/обнулить и читать/писать. Текущие данные будут потеряны.
Второй символ lLock в соседнем .lck-файле. Доступ к каталогу/lock-файлу нужен даже reader.
Второй символ dLock самого файла; default, если не указан l/d/- для handlers с общей блокировкой.
Второй символ -Не использовать общую блокировку DBA. Только если всю согласованность обеспечивает другой осознанный механизм; не средство лечения ошибок.
Третий символ tTry 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 знаком - ради устранения сообщения.

Выполнить read-modify-write под одним обычным write-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('Не удалось открыть учебную базу'); }
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.
Открыть LMDB в режиме отдельного файлаСамостоятельный пример
<?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 касается только нашей новой папки.

Разместить файлы LMDB внутри отдельного каталогаСамостоятельный пример
<?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 разбирает её. В обходе могут встретиться отдельные заголовки групп с пустым именем: их обрабатывают явно, не называют пользовательской настройкой.

Записать собственный 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);
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 сервера не изменяется, его директивы требуют собственной схемы и уровня доступа.

Обойти INI-секции без передачи false в разборщикСамостоятельный пример
<?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 сервера.

Не принять INI insert за универсальное ограничение уникальностиСамостоятельный пример
<?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 действующий справочник под пользователями.

Прочитать несколько вариантов одного ключа через skipСамостоятельный пример
<?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 сами собой не преобразуются; для миграции нужен читающий старый формат процесс, проверка ключей/значений и построение нового хранилища.

Тот же словарь через установленный dbmПример для указанного окружения
<?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 к существующей базе.

Тот же словарь через установленный ndbmПример для указанного окружения
<?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 к существующей базе.

Тот же словарь через установленный gdbmПример для указанного окружения
<?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 к существующей базе.

Тот же словарь через установленный db2Пример для указанного окружения
<?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 к существующей базе.

Тот же словарь через установленный db3Пример для указанного окружения
<?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 к существующей базе.

Тот же словарь через установленный db4Пример для указанного окружения
<?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 к существующей базе.

Тот же словарь через установленный tcadbПример для указанного окружения
<?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 при конкуренции ещё не готовая очередь.

Обсуждения использованы как описание затруднений, не как нормативный контракт. Старые ответы о скорости и выборе продукта нельзя переносить на новую нагрузку. Источники правил — 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.
moder читать; 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.
flagsPHP8.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" и "" могут быть нормальными значениями.

Путь данных и действий
  1. новый временный файл
  2. dba_open→$db
  3. user:42:language→ru
  4. 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" и "" могут быть нормальными значениями.

Путь данных и действий
  1. currency отсутствует
  2. insert RUB→true
  3. повтор insert USD→false в flatfile
  4. 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" и "" могут быть нормальными значениями.

Путь данных и действий
  1. cursor отсутствует→replace page-1
  2. replace page-2
  3. 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" и "" могут быть нормальными значениями.

Путь данных и действий
  1. ключ count
  2. DBA находит строку "0"
  3. PHP сохраняет string, не false
  4. приложение проверяет тип/смысл
Отличить отсутствие записи от нуля и пустой строкиСамостоятельный пример
<?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 и проверяйте сам результат изменения.

Путь данных и действий
  1. banner→пустая строка
  2. exists banner=true
  3. 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" и "" могут быть нормальными значениями.

Путь данных и действий
  1. cache:weather существует
  2. delete→true
  3. exists→false
  4. файл может сохранить свободные страницы
Удалить устаревший результатСамостоятельный пример
<?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 ту же базу: предварительно соберите нужные ключи либо выполните отдельный проход.

Путь данных и действий
  1. firstkey→первый ключ
  2. fetch(key)→значение
  3. nextkey→следующий
  4. 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: для стабильного вывода явно сортируем маленький собранный массив. Большую базу целиком так собирать дорого.

Ключ "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('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 ту же базу: предварительно соберите нужные ключи либо выполните отдельный проход.

Путь данных и действий
  1. firstkey→первый ключ
  2. fetch(key)→значение
  3. nextkey→следующий
  4. 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: для стабильного вывода явно сортируем маленький собранный массив. Большую базу целиком так собирать дорого.

Ключ "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('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.

Путь данных и действий
  1. [mail]sender
  2. key_split
  3. ["mail","sender"]
  4. массив годится для dba_fetch
Разобрать секцию и имя 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 остаётся целым именем без секции.

Обойти INI-секции без передачи false в разборщикСамостоятельный пример
<?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 при дальнейшем обращении.

Путь данных и действий
  1. writer сохранил ready
  2. dba_close
  3. новый reader получает lock
  4. fetch→ready
Закрыть writer перед отдельным чтениемСамостоятельный пример
<?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_open, dba_popen, dba_list.

dba_handlersПроверить, какие форматы баз действительно поддерживает текущий PHP.

PHP 4.3+ · dba и выбранный handler

dba_handlers(bool $full_info = false): array

Проверить, какие форматы баз действительно поддерживает текущий PHP.

ПараметрЧто означает
full_infofalse — список имён; true — ассоциативный массив имя→описание/версия.

Возвращает array, возможно пустой при отсутствии handlers. Информация относится к этому PHP-процессу/SAPI. Сравнивать наличие удобно in_array(name,handlers(),true) либо array_key_exists(name,handlers(true)); не путайте ключи двух форм ответа. Дополнительная библиотека устанавливается при сборке/настройке, её имя не загрузит код автоматически через dba_open.

Путь данных и действий
  1. расширение dba
  2. handlers(false)→имена
  3. handlers(true)→описания
  4. выбор установленного формата
Выбрать только установленный форматСамостоятельный пример
<?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 других процессов.

Путь данных и действий
  1. открыли demo.db
  2. dba_list содержит путь
  3. close
  4. обычный 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 и не резервная копия. Перед обслуживанием нужен продуманный режим доступа и проверяемая копия.

Путь данных и действий
  1. удалили старый ключ
  2. optimize зависит от handler
  3. нужный keep остаётся value
  4. время/размер измеряются отдельно
Увидеть, что успешный optimize не обещает сжатияСамостоятельный пример
<?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, которое здесь ничего не перестраивает.

Обслужить изменяемый QDBM-каталог отдельно от запроса посетителяСамостоятельный пример
<?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 этого не доказывает.

Путь данных и действий
  1. replace snapshot=v2
  2. dba_sync→driver-specific action
  3. close
  4. новый reader получаетv2
Отличить общий API sync от гарантии дисковой записиСамостоятельный пример
<?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.

Путь данных и действий
  1. worker открывает backend
  2. получает handle
  3. повторный запрос с теми же параметрами
  4. backend может переиспользоваться до конца процесса
Открыть persistent-handle в отдельном учебном CLI-процессеСамостоятельный пример
<?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-пула.

Повторно запросить 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. Учебный файл удаляется внешней средой после процесса; рабочий файл хранится осознанно.

Связанные действия: dba_open, dba_close, dba_list.

Источники и обсуждения

Код и руководство с собой

Скачайте самостоятельные примеры с исходными данными, инструкциями и ожидаемыми результатами. Примеры HTTP и фрагменты для проекта помечены отдельно: им нужно указанное окружение.

Код не исполняется на сайте. Скачайте нужный пример и запускайте его в своём локальном окружении.

Материалы сверены 2026-10-12. Примеры и объяснения написаны для этого руководства; источники и обсуждения доступны в каждой главе.