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

Дерево руководства
Глава 39 · После первого словаря DBA и основных операций с массивами
DBA на практике: импорт, обновление, снимок и восстановление
Проверяем CSV, различаем ноль и отсутствие, сохраняем версию CDB, переносим пары JSON и объясняем отказы открытия.
Различить бесплатную доставку, пустую заметку и отсутствующий ключ
Одно поле действительно пустое, другая цена равна нулю, а третьего ключа вообще нет. Если писать if (!$value), эти три результата смешаются. Здесь прежде всего проверяется именно false; вывод показывает три разные ситуации.
- pickup → строка "0"
- note → пустая строка
- unknown → false
- Строгое сравнение → разные статусы
$value = dba_fetch('pickup', $db);
echo $value === false ? 'Не найдено' : $value;Три знака === сравнивают значение и тип. Строки "0" и "" являются допустимыми данными. Ветвь справа от ? выбирается только при false. $db в коротком фрагменте уже открыт; полный пример создаёт все записи сам. В таблице пустая строка подписана словами только для наглядности: в базе она остаётся пустой.
Что получится
| Key | Status | Value |
|---|---|---|
| pickup | found | 0 |
| note | found | empty string |
| unknown | missing | not stored |
Эту же таблицу программа записывает в result.html, исходные значения — в result.json. HTML открывается обычным браузером; это реальный продукт запуска, а не условная картинка. Открыть сохранённую таблицу · Получить JSON результата.
<?php
require __DIR__ . '/db105-report.php';
$db = dba_open(__DIR__.'/values.db', 'n', 'flatfile');
if ($db === false) { throw new RuntimeException('Cannot create values'); }
dba_insert('pickup', '0', $db); dba_insert('note', '', $db);
$rows=[];
foreach (['pickup','note','unknown'] as $key) {
$value=dba_fetch($key,$db);
$rows[]=[$key,$value===false?'missing':'found',$value===false?'not stored':($value===''?'empty string':$value)];
}
dba_close($db);
db105Report('dba105-falsy-values',['Key','Status','Value'],$rows,['zero_is_found'=>$rows[0]===['pickup','found','0'],'empty_is_found'=>$rows[1]===['note','found','empty string'],'missing_is_false'=>$rows[2]===['unknown','missing','not stored']],['values.db']);
echo "found=2 missing=1; zero and empty kept\n";
Ожидаемый результат
found=2 missing=1; zero and empty kept
Скачайте пример, распакуйте в новый отдельный каталог и выполните php example.php с DBA и указанным handler. Код с режимом n заново создаёт только учебные файлы этого каталога. Вложенный db105-report.php сохраняет таблицу и измерения; он не нужен для самого вызова DBA. Служебный обход читает пары до false и не изменяет базу во время перебора. Результаты остаются рядом со скриптом; рабочие базы не открываются.
Когда false ожидаем как «нет ключа», заранее убедитесь, что база успешно открыта и соединение не закрыто. Нельзя выдавать любую ошибку процесса за отсутствие настройки.
Нужны определения: аргументы и результаты функций DBA, массивы и foreach, файлы и пути, JSON.
Загрузить остатки из CSV с отчётом по каждой строке
Склад прислал CSV: один артикул повторяется, одна строка содержит отрицательное количество, другая не содержит артикула. Сначала проверим строки, покажем причину отказа каждой, затем создадим новый словарь принятых остатков. Старый рабочий файл в этом уроке не открывается.
- stock.csv → массив одной строки
- Проверка двух полей и количества
- Принятые значения → dba_insert
- stock.db + таблица причин по всем строкам
foreach ($accepted as $sku => $quantity) {
dba_insert('stock:' . $sku, $quantity, $db);
}CSV — текстовые строки с полями, здесь разделитель запятая и две колонки sku,quantity. fgetcsv читает одну строку в массив; явный пустой escape задаёт поведение CSV без устаревшего неявного значения PHP. trim убирает пробелы по краям, ctype_digit разрешает непустую строку цифр. Вход stock.csv вложен в архив. Политика этого импорта: первый корректный артикул принимается, повторный отклоняется; это наше правило, а не универсальное свойство всех DBA handlers.
Что получится
| CSV line | SKU | Quantity | Decision |
|---|---|---|---|
| 2 | A100 | 12 | accepted |
| 3 | B200 | 0 | accepted |
| 4 | A100 | 7 | duplicate |
| 5 | C300 | -2 | invalid |
| 6 | 5 | invalid |
Эту же таблицу программа записывает в result.html, исходные значения — в result.json. HTML открывается обычным браузером; это реальный продукт запуска, а не условная картинка. Открыть сохранённую таблицу · Получить JSON результата.
<?php
require __DIR__ . '/db105-report.php';
$input=fopen(__DIR__.'/stock.csv','rb');
if ($input===false || fgetcsv($input,0,',','"','')!==['sku','quantity']) { throw new RuntimeException('Unexpected CSV columns'); }
$accepted=[];$seen=[];$rows=[];$line=1;
while(($row=fgetcsv($input,0,',','"',''))!==false){
$line++;$sku=trim($row[0]??'');$quantity=trim($row[1]??'');
if(count($row)!==2||$sku===''||!ctype_digit($quantity)){$status='invalid';}
elseif(isset($seen[$sku])){$status='duplicate';}
else{$status='accepted';$seen[$sku]=true;$accepted[$sku]=$quantity;}
$rows[]=[(string)$line,$sku,$quantity,$status];
}
fclose($input);
$db=dba_open(__DIR__.'/stock.db','n','flatfile');
if($db===false){throw new RuntimeException('Cannot create stock');}
foreach($accepted as $sku=>$quantity){if(!dba_insert('stock:'.$sku,$quantity,$db)){throw new RuntimeException('Cannot store accepted row');}}
$readback=[dba_fetch('stock:A100',$db),dba_fetch('stock:B200',$db)];dba_close($db);
db105Report('dba105-csv-import',['CSV line','SKU','Quantity','Decision'],$rows,['two_accepted'=>count($accepted)===2,'readback'=>$readback===['12','0'],'three_rejected'=>count(array_filter($rows,fn($r)=>$r[3]!=='accepted'))===3],['stock.db'],['readback'=>$readback]);
echo "accepted=2 rejected=3; A100=12 B200=0\n";
Ожидаемый результат
accepted=2 rejected=3; A100=12 B200=0
Скачайте пример, распакуйте в новый отдельный каталог и выполните php example.php с DBA и указанным handler. Код с режимом n заново создаёт только учебные файлы этого каталога. Вложенный db105-report.php сохраняет таблицу и измерения; он не нужен для самого вызова DBA. Служебный обход читает пары до false и не изменяет базу во время перебора. Результаты остаются рядом со скриптом; рабочие базы не открываются.
Проверка количества здесь синтаксическая: для своего склада задайте верхнюю границу, правила артикула и размер входа. Это маленький импорт в новую базу; частичное применение обновлений к рабочей базе потребовало бы отдельного протокола восстановления.
Нужны определения: аргументы и результаты функций DBA, массивы и foreach, файлы и пути, JSON.
Показать остатки до и после изменения
Нужно зарезервировать три единицы товара A100. Сохраняем снимок значений, читаем остаток, проверяем его и записываем новый. Вторая позиция остаётся неизменной; таблица показывает до и после.
- Writer открыт → один согласованный lock flatfile
- Прочитали 12 → проверили ≥3
- Записали 9
- Закрыли writer → показываем 12→9 и 0→0
$current = dba_fetch('stock:A100', $db);
dba_replace('stock:A100', (string) ((int) $current - 3), $db);Короткий фрагмент предполагает уже проверенную числовую строку и открытый writer; полная программа проверяет false, цифры и достаточность остатка. (int) переводит строку в целое для вычитания, (string) возвращает строковый формат хранения. replace заменяет значение целиком либо вставляет отсутствующий ключ; возвращает bool. Поэтому проверка наличия до replace нужна для нашей задачи обновления существующего товара.
Что получится
| Key | Before | After |
|---|---|---|
| stock:A100 | 12 | 9 |
| stock:B200 | 0 | 0 |
Эту же таблицу программа записывает в result.html, исходные значения — в result.json. HTML открывается обычным браузером; это реальный продукт запуска, а не условная картинка. Открыть сохранённую таблицу · Получить JSON результата.
<?php
require __DIR__ . '/db105-report.php';
$db=dba_open(__DIR__.'/stock.db','n','flatfile');
if($db===false){throw new RuntimeException('Cannot create stock');}
dba_insert('stock:A100','12',$db);dba_insert('stock:B200','0',$db);
$before=db105ReadAll($db);
$old=dba_fetch('stock:A100',$db);
if($old===false||!ctype_digit($old)||(int)$old<3){throw new RuntimeException('Cannot reserve three units');}
if(!dba_replace('stock:A100',(string)((int)$old-3),$db)){throw new RuntimeException('Update failed');}
$after=db105ReadAll($db);dba_close($db);
$rows=[];foreach($before as $index=>$pair){$rows[]=[$pair['key'],$pair['value'],$after[$index]['value']];}
db105Report('dba105-stock-update',['Key','Before','After'],$rows,['three_reserved'=>$rows[0]===['stock:A100','12','9'],'other_key_unchanged'=>$rows[1]===['stock:B200','0','0']],['stock.db']);
echo "stock:A100 12 -> 9; stock:B200 remains 0\n";
Ожидаемый результат
stock:A100 12 -> 9; stock:B200 remains 0
Скачайте пример, распакуйте в новый отдельный каталог и выполните php example.php с DBA и указанным handler. Код с режимом n заново создаёт только учебные файлы этого каталога. Вложенный db105-report.php сохраняет таблицу и измерения; он не нужен для самого вызова DBA. Служебный обход читает пары до false и не изменяет базу во время перебора. Результаты остаются рядом со скриптом; рабочие базы не открываются.
Все действия выполняются под одним обычным write-handle flatfile. Это не SQL-транзакция: несколько replace не становятся общей атомарной операцией и не получают откат после аварии. Полный снимок db105ReadAll обходится firstkey/nextkey без изменений во время обхода, сортируется для стабильной таблицы; обновление начинается после завершения чтения.
Нужны определения: аргументы и результаты функций DBA, массивы и foreach, файлы и пути, JSON.
Выпустить новый справочник, сохранив предыдущую версию
Справочник тарифов меняется редко, а читается часто. CDB удобно готовить целиком: построить v2 рядом с v1, закрыть builder, прочитать и проверить результат, затем выбрать новую версию. Старую можно оставить для отката.
- v1 остаётся с 35000
- Построить отдельный v2 с 39000
- close + readback v2
- Выбрать v2 в локальной настройке
- v1 сохранён
$builder = dba_open(__DIR__ . '/delivery-v2.cdb', 'n', 'cdb_make');
dba_insert('courier', '39000', $builder);
dba_close($builder);
$reader = dba_open(__DIR__ . '/delivery-v2.cdb', 'r', 'cdb');cdb_make строит новый файл, cdb читает законченный. close завершает этап построения до открытия reader. Обычные update/delete готового CDB не подходят: собирается новая версия. selected.txt в полном примере — простая настройка нашего одного процесса, которая указывает проверенное имя; она не является самостоятельной системой атомарной публикации для сервера.
Что получится
| Version | Courier in kopecks |
|---|---|
| delivery-v1.cdb | 35000 |
| delivery-v2.cdb | 39000 |
Эту же таблицу программа записывает в result.html, исходные значения — в result.json. HTML открывается обычным браузером; это реальный продукт запуска, а не условная картинка. Открыть сохранённую таблицу · Получить JSON результата.
<?php
require __DIR__ . '/db105-report.php';
$versions=['delivery-v1.cdb'=>['pickup'=>'0','courier'=>'35000'],'delivery-v2.cdb'=>['pickup'=>'0','courier'=>'39000']];
foreach($versions as $filename=>$pairs){
$builder=dba_open(__DIR__.'/'.$filename,'n','cdb_make');
if($builder===false){throw new RuntimeException('CDB builder unavailable');}
foreach($pairs as $key=>$value){if(!dba_insert($key,$value,$builder)){throw new RuntimeException('Cannot insert tariff');}}
dba_close($builder);
}
$rows=[];
foreach(array_keys($versions) as $filename){
$reader=dba_open(__DIR__.'/'.$filename,'r','cdb');
if($reader===false){throw new RuntimeException('CDB readback failed');}
$rows[]=[$filename,dba_fetch('courier',$reader)];dba_close($reader);
}
// In this isolated single-process example a setting chooses the checked new file.
$selected='delivery-v2.cdb';file_put_contents(__DIR__.'/selected.txt',$selected."\n");
$reader=dba_open(__DIR__.'/'.trim(file_get_contents(__DIR__.'/selected.txt')),'r','cdb');
if($reader===false){throw new RuntimeException('Selected version unavailable');}
$actual=dba_fetch('courier',$reader);dba_close($reader);
db105Report('dba105-cdb-snapshot',['Version','Courier in kopecks'],$rows,['old_preserved'=>$rows[0][1]==='35000','new_valid'=>$rows[1][1]==='39000','selected_readback'=>$actual==='39000'],['delivery-v1.cdb','delivery-v2.cdb','selected.txt']);
echo "v1=35000 v2=39000 selected=v2; old file kept\n";
Ожидаемый результат
v1=35000 v2=39000 selected=v2; old file kept
Скачайте пример, распакуйте в новый отдельный каталог и выполните php example.php с DBA и указанным handler. Код с режимом n заново создаёт только учебные файлы этого каталога. Вложенный db105-report.php сохраняет таблицу и измерения; он не нужен для самого вызова DBA. Служебный обход читает пары до false и не изменяет базу во время перебора. Результаты остаются рядом со скриптом; рабочие базы не открываются.
На работающем сервере нужны договорённость об обновлении указателя, правах и времени жизни читателей; запись selected.txt обычным file_put_contents не доказывает атомарность для других процессов. Имена файлов выбирает приложение из известных версий, а не пользовательский путь. В уроке нет параллельной публикации и удаления открытых файлов.
Нужны определения: аргументы и результаты функций DBA, массивы и foreach, файлы и пути, JSON.
Выгрузить пары в JSON и восстановить отдельный файл
Нужно перенести небольшой словарь в отдельный файл, сохранив строки "0", пустую строку и даже ключ "0". Экспортируем список пар в JSON, затем создадим новую базу и проверим точное совпадение всех пар.
- firstkey/nextkey → список строковых пар
- JSON → backup.json
- Проверка структуры
- insert → новый restored.db
- Повторный обход → точное сравнение
$pairs[] = ['key' => $key, 'value' => dba_fetch($key, $db)];
$json = json_encode(['format' => 'dba-pairs-v1', 'pairs' => $pairs]);Здесь JSON вводится после обычного ключа и значения: это переносимая текстовая оболочка для экспорта. Пары представлены списком объектов key/value, а не ключами PHP-массива: числовая строка ключа не превращается в целочисленный индекс. Полный код использует JSON_THROW_ON_ERROR, проверяет оболочку и строковые поля, затем вставляет их в новый restored.db. Равенство сравнивается после сортировки по ключу; порядок физического обхода DBA не обещан.
Что получится
| Restored key | Restored value |
|---|---|
| 0 | zero key |
| empty | empty string |
| price | 0 |
Эту же таблицу программа записывает в result.html, исходные значения — в result.json. HTML открывается обычным браузером; это реальный продукт запуска, а не условная картинка. Открыть сохранённую таблицу · Получить JSON результата.
<?php
require __DIR__ . '/db105-report.php';
$db=dba_open(__DIR__.'/original.db','n','flatfile');
if($db===false){throw new RuntimeException('Cannot create source');}
dba_insert('0','zero key',$db);dba_insert('empty','',$db);dba_insert('price','0',$db);
$pairs=db105ReadAll($db);dba_close($db);
$backup=['format'=>'dba-pairs-v1','pairs'=>$pairs];
file_put_contents(__DIR__.'/backup.json',json_encode($backup,JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR)."\n");
$decoded=json_decode(file_get_contents(__DIR__.'/backup.json'),true,512,JSON_THROW_ON_ERROR);
if(($decoded['format']??null)!=='dba-pairs-v1'||!is_array($decoded['pairs']??null)){throw new RuntimeException('Unexpected backup envelope');}
foreach($decoded['pairs'] as $pair){if(!is_array($pair)||!is_string($pair['key']??null)||!is_string($pair['value']??null)){throw new RuntimeException('Expected string key and value');}}
$restored=dba_open(__DIR__.'/restored.db','n','flatfile');
if($restored===false){throw new RuntimeException('Cannot create destination');}
foreach($decoded['pairs'] as $pair){if(!dba_insert($pair['key'],$pair['value'],$restored)){throw new RuntimeException('Duplicate or failed restored pair');}}
$again=db105ReadAll($restored);dba_close($restored);
$rows=[];foreach($again as $pair){$rows[]=[$pair['key'],$pair['value']===''?'empty string':$pair['value']];}
db105Report('dba105-portable-backup',['Restored key','Restored value'],$rows,['same_pairs'=>$again===$pairs,'three_pairs'=>count($again)===3],['original.db','restored.db','backup.json']);
echo "backup pairs=3 restored=3; exact string pairs matched\n";
Ожидаемый результат
backup pairs=3 restored=3; exact string pairs matched
Скачайте пример, распакуйте в новый отдельный каталог и выполните php example.php с DBA и указанным handler. Код с режимом n заново создаёт только учебные файлы этого каталога. Вложенный db105-report.php сохраняет таблицу и измерения; он не нужен для самого вызова DBA. Служебный обход читает пары до false и не изменяет базу во время перебора. Результаты остаются рядом со скриптом; рабочие базы не открываются.
Такой формат предназначен для строк UTF-8. Общий DBA может хранить бинарные данные, но произвольные байты не всегда представимы в JSON: для них нужна явно согласованная base64-оболочка. Список пар также подходит для объяснения дубликатов, но этот импорт использует уникальные ключи flatfile и отвергает повтор при insert. Экспорт большого хранилища целиком в память — отдельное ограничение этого учебного решения.
Нужны определения: аргументы и результаты функций DBA, массивы и foreach, файлы и пути, JSON.
Разделить отсутствующий файл, handler и каталог
Сообщение «база не открылась» само по себе не подсказывает, что исправить. В отдельном каталоге создадим три контролируемых отказа: чтение отсутствующего файла, неизвестный handler и запись в отсутствующий подкаталог. Таблица содержит статус, metrics.json — исходные warning и список доступных handlers.
- Проверить расширение PHP в нужном SAPI
- Проверить имя handler
- Проверить полный путь и режим
- Сохранить конкретный warning
- Выбрать исправление причины
$handlers = dba_handlers(false);
$db = dba_open(__DIR__ . '/never-created.db', 'r', 'flatfile');
if ($db === false) { echo 'Файл не открыт'; }dba_handlers(false) возвращает имена встроенных в текущий PHP обработчиков; true даёт более подробную информацию. Режим r не создаёт файл, c может создать сам файл, но не всю отсутствующую цепочку каталогов. Неизвестный handler не появляется от изменения прав. Служебная db105Attempt перехватывает E_WARNING только вокруг ожидаемой неудачи и всегда восстанавливает обработчик в finally; обычные непредвиденные ошибки не маскируются.
Что получится
| Situation | Mode | Handler | Result |
|---|---|---|---|
| missing file | r | flatfile | failed |
| unknown handler | c | not_a_handler_105 | failed |
| missing directory | c | flatfile | failed |
Эту же таблицу программа записывает в result.html, исходные значения — в result.json. HTML открывается обычным браузером; это реальный продукт запуска, а не условная картинка. Открыть сохранённую таблицу · Получить JSON результата.
<?php
require __DIR__ . '/db105-report.php';
$handlers=dba_handlers(false);
if(!in_array('flatfile',$handlers,true)){throw new RuntimeException('This lesson needs flatfile');}
$missing=db105Attempt(fn()=>dba_open(__DIR__.'/never-created.db','r','flatfile'));
$handler=db105Attempt(fn()=>dba_open(__DIR__.'/unused.db','c','not_a_handler_105'));
$directory=db105Attempt(fn()=>dba_open(__DIR__.'/absent-directory-105/data.db','c','flatfile'));
$rows=[['missing file','r','flatfile',$missing['failed']?'failed':'opened'],['unknown handler','c','not_a_handler_105',$handler['failed']?'failed':'opened'],['missing directory','c','flatfile',$directory['failed']?'failed':'opened']];
db105Report('dba105-open-errors',['Situation','Mode','Handler','Result'],$rows,['missing_failed'=>$missing['failed'],'handler_failed'=>$handler['failed'],'directory_failed'=>$directory['failed'],'warnings_captured'=>count($missing['warnings'])>0&&count($handler['warnings'])>0&&count($directory['warnings'])>0],[],['warnings'=>['missing'=>$missing['warnings'],'handler'=>$handler['warnings'],'directory'=>$directory['warnings']],'available_handlers'=>$handlers]);
echo "missing-file=failed unknown-handler=failed missing-directory=failed\n";
Ожидаемый результат
missing-file=failed unknown-handler=failed missing-directory=failed
Скачайте пример, распакуйте в новый отдельный каталог и выполните php example.php с DBA и указанным handler. Код с режимом n заново создаёт только учебные файлы этого каталога. Вложенный db105-report.php сохраняет таблицу и измерения; он не нужен для самого вызова DBA. Служебный обход читает пары до false и не изменяет базу во время перебора. Результаты остаются рядом со скриптом; рабочие базы не открываются.
Наши проверки не воспроизводят права Apache/FPM или сетевого диска: здесь намеренно отсутствует каталог. Для отказа доступа проверяйте пользователя процесса, сам файл, каталог и lock-файл; не назначайте 0777 ради скрытия неизвестной причины. SQLite, DBF и разные dbm форматы нельзя различить лишь по расширению .db.
Нужны определения: аргументы и результаты функций DBA, массивы и foreach, файлы и пути, JSON.
Задача из обсуждения: How to open .db file and read tables in it using PHP. Файл с расширением .db пытаются читать DBA, не определив формат; расширение PHP тоже отсутствует. Сначала различить установку расширения, наличие handler и известный формат файла.
Задача из обсуждения: PHP permissions. Открытие gdbm ошибочно лечат chmod0777, хотя warning сообщает No such handler. Проверить dba_handlers и отдельно показать неизвестный handler и отсутствие каталога.
Не зависнуть на занятом справочнике и прочитать после освобождения
Пока один процесс обновляет словарь, читателю лучше вернуть понятное «занято», чем бесконечно ждать. Покажем это двумя handles в одном учебном процессе: reader rlt получает false при открытом writer, затем успешно читает после close. Это проверяет режим блокировки, а не производительность нескольких серверных процессов.
- Writer nl удерживает lock
- Reader rlt → false без ожидания
- close writer освобождает lock
- Reader rlt → catalog:version=v2
$reader = dba_open($path, 'rlt', 'flatfile');
if ($reader === false) { echo 'Занято: повторите позже'; }r означает чтение, l — общий для участников отдельный .lck, t — не ждать освобождения. Writer открыт с nl; обе стороны используют один механизм l. Точка остановки в сценарии — close writer. Полная программа сохраняет warning ожидаемого конфликта, не запускает бесконечный цикл повторов и не отключает блокировку.
Что получится
| Moment | Attempt | Result |
|---|---|---|
| writer open | reader rlt | busy |
| writer closed | reader rlt | v2 |
Эту же таблицу программа записывает в result.html, исходные значения — в result.json. HTML открывается обычным браузером; это реальный продукт запуска, а не условная картинка. Открыть сохранённую таблицу · Получить JSON результата.
<?php
require __DIR__ . '/db105-report.php';
$path=__DIR__.'/locked.db';
$writer=dba_open($path,'nl','flatfile');
if($writer===false){throw new RuntimeException('Cannot open writer');}
dba_insert('catalog:version','v2',$writer);
$busy=db105Attempt(fn()=>dba_open($path,'rlt','flatfile'));
if(!$busy['failed']){dba_close($busy['value']);}
dba_close($writer);
$reader=dba_open($path,'rlt','flatfile');
if($reader===false){throw new RuntimeException('Still cannot read after close');}
$version=dba_fetch('catalog:version',$reader);dba_close($reader);
$rows=[['writer open','reader rlt',$busy['failed']?'busy':'opened'],['writer closed','reader rlt',$version]];
db105Report('dba105-lock-recovery',['Moment','Attempt','Result'],$rows,['busy_returned_false'=>$busy['failed'],'warning_captured'=>count($busy['warnings'])>0,'read_after_close'=>$version==='v2'],['locked.db'],['busy_warnings'=>$busy['warnings']]);
echo "reader while writer=busy; after close=v2\n";
Ожидаемый результат
reader while writer=busy; after close=v2
Скачайте пример, распакуйте в новый отдельный каталог и выполните php example.php с DBA и указанным handler. Код с режимом n заново создаёт только учебные файлы этого каталога. Вложенный db105-report.php сохраняет таблицу и измерения; он не нужен для самого вызова DBA. Служебный обход читает пары до false и не изменяет базу во время перебора. Результаты остаются рядом со скриптом; рабочие базы не открываются.
Приложение само выбирает ограниченное число повторов, задержку и ответ пользователю. Сеть/HTTP под долгим writer увеличивает очередь ожидающих. Persistent writer может удерживать ресурс дольше одного запроса; общий DBA API не превращает данный пример в распределённую блокировку. Для сторонних handlers правила их библиотек могут отличаться от flatfile.
Нужны определения: аргументы и результаты функций DBA, массивы и foreach, файлы и пути, JSON.
Задача из обсуждения: Why php is throwing error when using dba_open for db4?. Чтение с режимом rl требует доступа к отдельному .lck, и открытие отказало с Permission denied. Разобрать отдельный lock-файл, единый режим блокировки и ограниченную проверку rlt без обещания исправить права чужого сервера.
Источники и обсуждения
Код и руководство с собой
Скачайте самостоятельные примеры с исходными данными, инструкциями и ожидаемыми результатами. Примеры HTTP и фрагменты для проекта помечены отдельно: им нужно указанное окружение.
Код не исполняется на сайте. Скачайте нужный пример и запускайте его в своём локальном окружении.
Материалы сверены 2026-10-12. Примеры и объяснения написаны для этого руководства; источники и обсуждения доступны в каждой главе.


