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

Дерево руководства
Глава 67 · После соединений, запросов, транзакций и знакомства с BLOB Firebird
Firebird: резервная копия, восстановление в новую базу и проверка данных
Полный каталог, чистая схема, проверка связанной накладной с BLOB и удаление только своей восстановленной базы.
Получить проверяемую копию, а не только сообщение об успехе
Перед обновлением приложения или восстановлением после ошибки нужно уметь вернуть нужные данные. Здесь пройдём весь небольшой путь: получить логический архив FBK, восстановить его под новым именем, открыть восстановленную базу и показать товары либо накладную. Отдельная задача — чистая схема без строк. В конце удалим только собственную проверочную копию, заранее отказавшись от чужого назначения.
- Исходная FDB: таблицы, строки, связи и документы
- Service Manager получает задание backup → создаёт FBK
- FBK содержит логическую копию; напрямую как базу его не открывают
- Service Manager получает restore CREATE → создаёт новую FDB
- Обычное соединение читает новую FDB → конкретные данные совпадают
- Отчёт остаётся; временные базы и FBK этого запуска удаляются
Сервисное соединение управляет заданием; обычное соединение выполняет SQL внутри конкретной базы. Для первых шагов прочитайте подключение к Service Manager и диагностику, таблицу и SELECT и зачем подтверждать изменения. Служба backup видит сохранённое состояние через своё подключение; незавершённый INSERT приложения не следует считать частью готовой копии.
В этой лаборатории Firebird и PHP работают на одном компьютере. В общем случае пути source и destination принадлежат серверу Firebird: файл на сервере не становится файлом браузера или другого компьютера с PHP. Программы используют только новые собственные базы в отдельном каталоге, а не таблицы рабочего сайта. Все четыре архива включают README с подготовкой, открытый lab.php, синтетический seed.sql и проверенный шаблон FBK в текстовом представлении Base64.
Профиль примеров: PHP 8.2.35 NTS x64, внешнее расширение interbase 3.0.1 и Firebird 5.0.4 embedded SuperClassic. Историческая отметка PHP Manual «PHP 5, PHP 7 < 7.4» относится к прежнему встроенному расширению. У реализации 3.0.1 обнаружен отдельный дефект порядка аргументов restore; ниже он показан явно вместе с адаптером точной версии. Заменять пути после произвольной ошибки автоматически нельзя.
Сделать полную копию каталога и получить те же товары в новой базе
Перед изменением схемы каталога нужно сохранить возможность восстановления. Сначала создадим отдельную учебную базу с двумя товарами, затем полный FBK и новую восстановленную базу. Результатом будут сами названия и остатки в новой базе; файл копии не открывается через ibase_connect напрямую.
- Собственная исходная база: Кабель — 3; Клемма — 0
- COMMIT → данные доступны службе резервного копирования
- backup → полный FBK структуры и данных
- restore CREATE → другое, ещё не существующее имя базы
- SELECT в новой базе → Кабель — 3; Клемма — 0
- Сравнить строки; сохранить отчёт; убрать только свои временные базы
Самый короткий шаг
$result = ibase_backup($service, $sourceDatabase, $newBackupFile, 0, true);
if ($result === false) { throw new RuntimeException('Копия не получена'); }
// В этом профиле true включает чтение вывода службы. Следующий шаг — восстановление.Соединение $service относится к Service Manager. Первый путь — база-источник, второй — новый FBK на стороне сервера. Ноль означает обычные опции полной копии. Пятый аргумент true в проверенном драйвере возвращает собранный вывод службы; он не превращает FBK в FDB. В полной программе исходная запись подтверждена до backup, потому что служба использует собственное подключение.
Что получится после запуска
| Наблюдение | Результат |
|---|---|
| Первый восстановленный товар | Кабель |
| Остаток первого товара | 3 |
| Второй восстановленный товар | Клемма |
| Остаток второго товара | 0 |
| Количество восстановленных товаров | 2 |
| Строки источника и копии совпали | да |
| Назначение отличается от исходной базы | да |
| Полный FBK создан | да |
| Подробный вывод backup получен | да |
Полная программа, подготовка данных и архив для запуска
<?php
declare(strict_types=1);
require __DIR__.'/lab.php';
$owned=[];$removed=[];$db=$service=null;
try {
$config=f157Config();$service=f157Service();$runtime=f157Runtime($service);
$source=f157CreateDatabase($service,$owned,'catalog');
$db=f157Connect($source);
f157Command($db,'UPDATE LAB_ITEMS SET TITLE=? WHERE ID=?',['Кабель',1]);
f157Command($db,'UPDATE LAB_ITEMS SET TITLE=? WHERE ID=?',['Клемма',2]);f157Commit($db);
$before=f157Read($db,'SELECT ID,TITLE,QTY FROM LAB_ITEMS ORDER BY ID');f157Commit($db);f157Close($db);
$backup=f157NewBackup('catalog');
$backupLog=f157Backup($service,$source,$backup);
$restored=f157RestoreDatabase($service,$owned,'catalog_copy',$backup);
$db=f157Connect($restored);
$after=f157Read($db,'SELECT ID,TITLE,QTY FROM LAB_ITEMS ORDER BY ID');f157Commit($db);f157Close($db);
$expected=[['ID'=>1,'TITLE'=>'Кабель','QTY'=>3],['ID'=>2,'TITLE'=>'Клемма','QTY'=>0]];
$rows=[['Первый восстановленный товар',$after[0]['TITLE']],['Остаток первого товара',$after[0]['QTY']],
['Второй восстановленный товар',$after[1]['TITLE']],['Остаток второго товара',$after[1]['QTY']],
['Количество восстановленных товаров',count($after)],['Строки источника и копии совпали',$before===$after],
['Назначение отличается от исходной базы',$source!==$restored],['Полный FBK создан',is_file($backup)&&filesize($backup)>0],
['Подробный вывод backup получен',is_string($backupLog)&&$backupLog!=='']];
$checks=['source_fixture'=>$before===$expected,'restored_rows'=>$after===$expected,'rows_equal'=>$before===$after,
'new_database'=>$source!==$restored,'backup_nonempty'=>is_file($backup)&&filesize($backup)>0,
'backup_output_read'=>is_string($backupLog)&&$backupLog!=='','zero_stock_retained'=>$after[1]['QTY']===0];
$observations=['source_rows'=>$before,'restored_rows'=>$after,'backup_basename'=>basename($backup),
'backup_bytes'=>filesize($backup),'backup_sha256'=>hash_file('sha256',$backup),
'backup_output_received'=>true,'raw_service_log_published'=>false,'existing_database_replaced'=>false];
} finally {
if(is_resource($db)){f157Close($db);}
if(is_resource($service)){f157Detach($service);}
$removed=f157Cleanup($owned);
}
f157Report('fb157-full-backup','Сделать полную копию каталога и получить те же товары в новой базе',$rows,$checks,$observations,$runtime,$owned,$removed);
Ожидаемый результат
Первый восстановленный товар — Кабель
Остаток первого товара — 3
Второй восстановленный товар — Клемма
Остаток второго товара — 0
Количество восстановленных товаров — 2
Строки источника и копии совпали — да
Назначение отличается от исходной базы — да
Полный FBK создан — да
Подробный вывод backup получен — да
Запустите example.php по README.txt. Создаются две собственные базы: источник и восстановленная копия. Программа не требует существующей рабочей базы: она использует приложенный синтетический шаблон. После наблюдений удаляются только базы и промежуточные FBK своего запуска; result.html, result.json и metrics.json остаются. Защита в lab.php проверяет собственный реестр, фактический путь соединения и маркер перед удалением. Сырой журнал службы с путями в публичный отчёт не попадает.
Открыть наглядный результат · Посмотреть строки и наблюдения JSON.
Границы, ошибки и выбор другого способа
Вместе с данными копируется структура, но не каждая байтовая страница исходного файла. FBK может быть существенно меньше FDB. Размер и SHA полезны для учёта конкретного артефакта, но не проверяют возможность восстановления. В этом небольшом сценарии совпадают все строки своего каталога; для рабочей базы заранее задайте достаточный набор содержательных проверок. Ни один шаг не заменяет существующую базу. Пути службы относятся к серверу Firebird; локальная проверка существования файла в PHP осмысленна здесь только потому, что лаборатория находится на том же компьютере.
Резервная копия оказалась намного меньше исходного файла; попытка открыть её как готовую базу не удалась. Исходная задача на Stack Overflow. FBK — логический архив структуры и данных, а не побайтовый FDB. Размер не подтверждает и не опровергает пригодность. В нашем примере доказательством служит восстановленная новая база с конкретными товарами.
Попробуйте изменить условие
Почему перед backup нельзя просто выполнить INSERT без commit и ожидать, что новая строка обязательно попадёт в копию?
Разбор решения
У службы своё подключение и транзакция. Незавершённые изменения приложения не являются сохранённым состоянием базы для копирования. Сначала решите, какие изменения действительно надо подтвердить, проверьте commit и только затем запускайте задание.
Получить пустую базу с той же схемой для нового теста
Для проверки новой версии приложения часто нужна структура каталога без прежних заказов и товаров. METADATA_ONLY намеренно исключает строки. После восстановления покажем оба независимых факта: таблица и её поля существуют, но исходных товаров нет. Так не получится перепутать пустой шаблон с потерявшей данные полной копией.
- Исходная учебная таблица LAB_ITEMS содержит 2 товара
- backup с METADATA_ONLY → архив схемы без строк
- restore CREATE → новая пустая база
- Запрос метаданных → ID, TITLE, QTY существуют
- SELECT COUNT(*) FROM LAB_ITEMS → 0
- Исходная база по-прежнему содержит 2 товара
Самый короткий шаг
$result = ibase_backup($service, $sourceDatabase, $newSchemaBackup,
IBASE_BKP_METADATA_ONLY, true);
if ($result === false) { throw new RuntimeException('Схема не сохранена'); }IBASE_BKP_METADATA_ONLY — именованная числовая опция, обозначающая копирование метаданных. Метаданные описывают устройство базы: таблицы, столбцы, ограничения и другие объекты. Они отличаются от строк с товарами. Восстановление такого FBK создаёт таблицы, в которые затем можно вставлять новые данные.
Что получится после запуска
| Наблюдение | Результат |
|---|---|
| Товаров в исходной базе | 2 |
| Таблица в восстановленной схеме | LAB_ITEMS |
| Восстановленные столбцы | ID, TITLE, QTY |
| Товаров в восстановленной базе | 0 |
| Исходная база сохранила товары | 2 |
| Пустота ожидаема из-за METADATA_ONLY | да |
| Схема восстановлена под новым именем | да |
Полная программа, подготовка данных и архив для запуска
<?php
declare(strict_types=1);
require __DIR__.'/lab.php';
$owned=[];$removed=[];$db=$service=null;
try {
$config=f157Config();$service=f157Service();$runtime=f157Runtime($service);
$source=f157CreateDatabase($service,$owned,'schema_src');
$db=f157Connect($source);
$sourceCount=(int)f157Read($db,'SELECT COUNT(*) AS N FROM LAB_ITEMS')[0]['N'];f157Commit($db);f157Close($db);
$backup=f157NewBackup('schema');
$backupLog=f157Backup($service,$source,$backup,IBASE_BKP_METADATA_ONLY);
$restored=f157RestoreDatabase($service,$owned,'schema_copy',$backup);
$db=f157Connect($restored);
$fields=f157Read($db,'SELECT TRIM(RDB$FIELD_NAME) AS FIELD_NAME FROM RDB$RELATION_FIELDS WHERE RDB$RELATION_NAME=? ORDER BY RDB$FIELD_POSITION',['LAB_ITEMS']);
$columns=array_column($fields,'FIELD_NAME');
$emptyCount=(int)f157Read($db,'SELECT COUNT(*) AS N FROM LAB_ITEMS')[0]['N'];
f157Commit($db);f157Close($db);
$db=f157Connect($source);
$stillSource=(int)f157Read($db,'SELECT COUNT(*) AS N FROM LAB_ITEMS')[0]['N'];f157Commit($db);f157Close($db);
$rows=[['Товаров в исходной базе',$sourceCount],['Таблица в восстановленной схеме','LAB_ITEMS'],
['Восстановленные столбцы',implode(', ',$columns)],['Товаров в восстановленной базе',$emptyCount],
['Исходная база сохранила товары',$stillSource],['Пустота ожидаема из-за METADATA_ONLY',$emptyCount===0&&$sourceCount===2],
['Схема восстановлена под новым именем',$source!==$restored]];
$checks=['source_has_two'=>$sourceCount===2,'schema_columns'=>$columns===['ID','TITLE','QTY'],
'schema_without_rows'=>$emptyCount===0,'source_unchanged'=>$stillSource===2,'separate_destination'=>$source!==$restored,
'metadata_backup_nonempty'=>is_file($backup)&&filesize($backup)>0];
$observations=['backup_mode'=>'IBASE_BKP_METADATA_ONLY','source_row_count'=>$sourceCount,'restored_columns'=>$columns,
'restored_row_count'=>$emptyCount,'source_after'=>$stillSource,'backup_bytes'=>filesize($backup),
'backup_output_received'=>is_string($backupLog)&&$backupLog!=='','raw_service_log_published'=>false];
} finally {
if(is_resource($db)){f157Close($db);}
if(is_resource($service)){f157Detach($service);}
$removed=f157Cleanup($owned);
}
f157Report('fb157-metadata-copy','Получить пустую базу с той же схемой для нового теста',$rows,$checks,$observations,$runtime,$owned,$removed);
Ожидаемый результат
Товаров в исходной базе — 2
Таблица в восстановленной схеме — LAB_ITEMS
Восстановленные столбцы — ID, TITLE, QTY
Товаров в восстановленной базе — 0
Исходная база сохранила товары — 2
Пустота ожидаема из-за METADATA_ONLY — да
Схема восстановлена под новым именем — да
Запустите example.php по README.txt. Создаются две собственные базы: источник и восстановленная копия. Программа не требует существующей рабочей базы: она использует приложенный синтетический шаблон. После наблюдений удаляются только базы и промежуточные FBK своего запуска; result.html, result.json и metrics.json остаются. Защита в lab.php проверяет собственный реестр, фактический путь соединения и маркер перед удалением. Сырой журнал службы с путями в публичный отчёт не попадает.
Открыть наглядный результат · Посмотреть строки и наблюдения JSON.
Границы, ошибки и выбор другого способа
Пустой шаблон не является анонимизированной копией: в определениях, процедурах, описаниях и настройках могут оставаться чувствительные сведения. Этот пример использует только вымышленные данные. Успешный SELECT COUNT с нулём показывает пустую таблицу; сообщение «table unknown» означало бы другую проблему — отсутствие структуры. Здесь опция применяется на этапе backup. Полный gbak умеет и другие режимы, но наличие функции ibase_restore не означает, что все параметры CLI доступны через её options.
Попробуйте изменить условие
Как отличить корректную пустую таблицу от отсутствующей таблицы, если экран приложения показывает пустой список в обоих случаях?
Разбор решения
Проверьте успех самого запроса и диагностику. COUNT из существующей пустой таблицы возвращает строку с 0. Запрос к отсутствующей таблице откажет; нельзя превращать любой false в пустой массив. Дополнительно сравните выбранные метаданные схемы.
Проверить восстановление накладной: позиции, связи, итог и BLOB-документ
Каталог открылся, но приложению нужна накладная с позициями, действующими связями и содержимым документа. Поэтому успешное подключение и число таблиц — только начало. Сохраним учебную накладную № 7, восстановим её в новое имя и покажем названия двух товаров, рассчитанный SQL итог 41500 копеек и тот же текст BLOB.
- Накладная № 7 → Кабель 3 × 12500 + Клемма 2 × 2000 копеек
- Документ хранится в BLOB; позиции связаны с документом и товарами
- Полный backup → восстановление в новую базу
- JOIN → исходные названия и количества; SUM → 41500
- BLOB → исходный текст; метаданные → обе внешние связи
- Совпадение содержательных проверок записывается в отчёт
Самый короткий шаг
// После подтверждённого восстановления выполняем запрос в НОВОМ соединении.
$result = ibase_query($restored,
'SELECT CAST(SUM(QUANTITY * UNIT_CENTS) AS BIGINT) AS TOTAL_CENTS FROM LAB_DOCUMENT_LINES WHERE DOC_ID = ?', 7);
if ($result === false) { throw new RuntimeException('Итог накладной не прочитан'); }SUM вычисляет итог на стороне базы по отобранной накладной. Произведение количества и целой цены в копейках даёт сумму позиции без преобразования в float. CAST(... AS BIGINT) задаёт тип результата для старого драйвера: Firebird 5 расширяет SUM от BIGINT до INT128, который interbase 3.0.1 не поддерживает. Для двух небольших позиций итог 41500 точно помещается в знаковый 64-битный BIGINT. Такое приведение допустимо только при известной границе суммы; оно не исправляет переполнение больших данных. Этот SELECT не просит все таблицы или все документы. Полный пример отдельно читает позиции через JOIN, текст BLOB и сведения о внешних ключах.
Что получится после запуска
| Наблюдение | Результат |
|---|---|
| Документ | Накладная № 7 |
| Первая позиция | Кабель — 3 × 12500 коп. |
| Вторая позиция | Клемма — 2 × 2000 коп. |
| Итог SQL в копейках | 41500 |
| Внешних связей у таблицы позиций | 2 |
| Текст восстановленного документа | Накладная № 7. Кабель — 3 шт.; Клемма — 2 шт. |
| Байты BLOB совпали | да |
| Строки позиций совпали | да |
Полная программа, подготовка данных и архив для запуска
<?php
declare(strict_types=1);
require __DIR__.'/lab.php';
$owned=[];$removed=[];$db=$service=null;
try {
$config=f157Config();$service=f157Service();$runtime=f157Runtime($service);
$source=f157CreateDatabase($service,$owned,'invoice');$db=f157Connect($source);
f157Command($db,'UPDATE LAB_ITEMS SET TITLE=? WHERE ID=?',['Кабель',1]);
f157Command($db,'UPDATE LAB_ITEMS SET TITLE=? WHERE ID=?',['Клемма',2]);f157Commit($db);
f157Command($db,'CREATE TABLE LAB_DOCUMENTS (ID INTEGER NOT NULL PRIMARY KEY, TITLE VARCHAR(60) CHARACTER SET UTF8 NOT NULL, DOC_TEXT BLOB SUB_TYPE TEXT CHARACTER SET UTF8)');f157Commit($db);
f157Command($db,'CREATE TABLE LAB_DOCUMENT_LINES (ID INTEGER NOT NULL PRIMARY KEY, DOC_ID INTEGER NOT NULL, ITEM_ID INTEGER NOT NULL, QUANTITY INTEGER NOT NULL CHECK (QUANTITY>0), UNIT_CENTS INTEGER NOT NULL CHECK (UNIT_CENTS>=0), CONSTRAINT FK157_DOC FOREIGN KEY (DOC_ID) REFERENCES LAB_DOCUMENTS(ID), CONSTRAINT FK157_ITEM FOREIGN KEY (ITEM_ID) REFERENCES LAB_ITEMS(ID))');f157Commit($db);
$document='Накладная № 7. Кабель — 3 шт.; Клемма — 2 шт.';$blob=null;
try {
$blob=ibase_blob_create($db);
if ($blob===false) {throw new RuntimeException('Документ-BLOB не открыт');}
if (ibase_blob_add($blob,$document)===false) {throw new RuntimeException('Текст документа не записан');}
$blobId=ibase_blob_close($blob);
if ($blobId===false) {throw new RuntimeException('Документ-BLOB не завершён');}$blob=null;
f157Command($db,'INSERT INTO LAB_DOCUMENTS (ID,TITLE,DOC_TEXT) VALUES (?,?,?)',[7,'Накладная № 7',$blobId]);
} finally {if (is_resource($blob)) {ibase_blob_cancel($blob);$blob=null;}}
f157Command($db,'INSERT INTO LAB_DOCUMENT_LINES (ID,DOC_ID,ITEM_ID,QUANTITY,UNIT_CENTS) VALUES (?,?,?,?,?)',[1,7,1,3,12500]);
f157Command($db,'INSERT INTO LAB_DOCUMENT_LINES (ID,DOC_ID,ITEM_ID,QUANTITY,UNIT_CENTS) VALUES (?,?,?,?,?)',[2,7,2,2,2000]);
f157Commit($db);f157Close($db);
$backup=f157NewBackup('invoice');f157Backup($service,$source,$backup);
$restored=f157RestoreDatabase($service,$owned,'invoice_copy',$backup);$db=f157Connect($restored);
$lines=f157Read($db,'SELECT L.ID,P.TITLE,L.QUANTITY,L.UNIT_CENTS FROM LAB_DOCUMENT_LINES L JOIN LAB_ITEMS P ON P.ID=L.ITEM_ID WHERE L.DOC_ID=? ORDER BY L.ID',[7]);
$sum=(int)f157Read($db,'SELECT CAST(SUM(QUANTITY*UNIT_CENTS) AS BIGINT) AS TOTAL_CENTS FROM LAB_DOCUMENT_LINES WHERE DOC_ID=?',[7])[0]['TOTAL_CENTS'];
$foreignKeys=(int)f157Read($db,'SELECT COUNT(*) AS N FROM RDB$RELATION_CONSTRAINTS WHERE RDB$RELATION_NAME=? AND RDB$CONSTRAINT_TYPE=?',['LAB_DOCUMENT_LINES','FOREIGN KEY'])[0]['N'];
$saved=f157Read($db,'SELECT TITLE,DOC_TEXT FROM LAB_DOCUMENTS WHERE ID=?',[7])[0];
$actualDocument='';$blob=null;
try {
$blob=ibase_blob_open($db,$saved['DOC_TEXT']);
if ($blob===false) {throw new RuntimeException('Восстановленный BLOB не открыт');}
while (true) {
$part=ibase_blob_get($blob,8192);
if ($part===false) {throw new RuntimeException('Текст восстановленного BLOB не прочитан');}
if ($part==='') {break;}$actualDocument.=$part;
}
if (ibase_blob_close($blob)===false) {throw new RuntimeException('Восстановленный BLOB не закрыт');}$blob=null;
} finally {if (is_resource($blob)) {ibase_blob_close($blob);$blob=null;}}
f157Commit($db);f157Close($db);
$expectedLines=[['ID'=>1,'TITLE'=>'Кабель','QUANTITY'=>3,'UNIT_CENTS'=>12500],['ID'=>2,'TITLE'=>'Клемма','QUANTITY'=>2,'UNIT_CENTS'=>2000]];
$rows=[['Документ',$saved['TITLE']],['Первая позиция',$lines[0]['TITLE'].' — '.$lines[0]['QUANTITY'].' × '.$lines[0]['UNIT_CENTS'].' коп.'],
['Вторая позиция',$lines[1]['TITLE'].' — '.$lines[1]['QUANTITY'].' × '.$lines[1]['UNIT_CENTS'].' коп.'],
['Итог SQL в копейках',$sum],['Внешних связей у таблицы позиций',$foreignKeys],['Текст восстановленного документа',$actualDocument],
['Байты BLOB совпали',$actualDocument===$document],['Строки позиций совпали',$lines===$expectedLines]];
$checks=['document_title'=>$saved['TITLE']==='Накладная № 7','exact_line_rows'=>$lines===$expectedLines,
'sql_total'=>$sum===41500,'foreign_key_definitions'=>$foreignKeys===2,'exact_blob'=>$actualDocument===$document,
'blob_hash'=>hash('sha256',$actualDocument)===hash('sha256',$document),'new_database'=>$source!==$restored];
$observations=['document_title'=>$saved['TITLE'],'lines'=>$lines,'total_cents'=>$sum,'foreign_key_count'=>$foreignKeys,
'document_text'=>$actualDocument,'document_sha256'=>hash('sha256',$actualDocument),'blob_id_not_compared'=>true,
'backup_bytes'=>filesize($backup),'raw_service_log_published'=>false,'late_restore_failure_simulated'=>false];
} finally {
if(is_resource($db)){f157Close($db);}
if(is_resource($service)){f157Detach($service);}
$removed=f157Cleanup($owned);
}
f157Report('fb157-restore-checks','Проверить восстановление накладной: позиции, связи, итог и BLOB-документ',$rows,$checks,$observations,$runtime,$owned,$removed);
Ожидаемый результат
Документ — Накладная № 7
Первая позиция — Кабель — 3 × 12500 коп.
Вторая позиция — Клемма — 2 × 2000 коп.
Итог SQL в копейках — 41500
Внешних связей у таблицы позиций — 2
Текст восстановленного документа — Накладная № 7. Кабель — 3 шт.; Клемма — 2 шт.
Байты BLOB совпали — да
Строки позиций совпали — да
Запустите example.php по README.txt. Создаются две собственные базы: источник и восстановленная копия. Программа не требует существующей рабочей базы: она использует приложенный синтетический шаблон. После наблюдений удаляются только базы и промежуточные FBK своего запуска; result.html, result.json и metrics.json остаются. Защита в lab.php проверяет собственный реестр, фактический путь соединения и маркер перед удалением. Сырой журнал службы с путями в публичный отчёт не попадает.
Открыть наглядный результат · Посмотреть строки и наблюдения JSON.
Границы, ошибки и выбор другого способа
Контрольный итог не заменяет проверку каждой важной сущности: одинаковая сумма возможна у разных позиций. Поэтому здесь сравниваются и строки, и BLOB, и схема связей. Это выбранный контракт маленькой учебной базы, не полное доказательство бизнес-целостности любой системы. Отказ на поздней стадии restore может оставить файл и часть структуры; не объявляйте его рабочей базой по одному успешному SELECT. Отключение индексов или проверок ради завершения восстановления требует отдельного разбора, а не скрытого признания данных корректными.
После незавершённого восстановления вычисляемые поля выглядели как обычные; сам факт появления файла ещё не означал окончания восстановления структуры. Исходная задача на Stack Overflow. Проверяем не только наличие файла и строк: сверяем связанные данные, BLOB и контрольный итог после завершения задания. Мы не повреждаем базу ради демонстрации позднего отказа и не заявляем воспроизведение чужой аварии.
Попробуйте изменить условие
После восстановления итог 41500 совпал, но в двух строках поменялись товары. Пройдена ли проверка накладной?
Разбор решения
Нет. Итог — только один инвариант. Нужно сравнить выбранные строки с ID товаров, названиями, количествами и ценами, а также связи и документ. Проверяйте то, что действительно использует приложение, а не только удобную единственную метрику.
Удалить свою проверочную базу и заранее отклонить чужой путь
Пробные восстановления не должны накапливаться на диске. При этом очистка опаснее простого закрытия соединения: удаляется целая база. Создадим собственную восстановленную копию, проверим её данные, покажем отказ для незарегистрированного пути без вызова drop, затем удалим только подтверждённую свою базу.
- Восстановленная копия записана в реестр текущего запуска
- Незарегистрированное назначение → guard отказал до drop
- Своя копия → путь/маркер/фактическая база совпали
- Освобождены результаты и завершена транзакция чтения
- ibase_drop_db(своё соединение) → true; переменная = null
- Файл своей базы отсутствует; исходный шаблон не менялся
Самый короткий шаг
// Только после проверки владельца, пути и завершения своих ресурсов:
if (ibase_drop_db($verifiedOwnConnection) !== true) {
throw new RuntimeException('Своя временная база не удалена');
}
$verifiedOwnConnection = null;Функция принимает уже открытое соединение и удаляет связанную с ним базу на сервере. Строкового пути у её параметра нет, поэтому правильный путь нужно проверить до вызова и сопоставить с реальным подключением. После успеха ресурс больше не используют, даже если PHP ещё показывает его как resource.
Что получится после запуска
| Наблюдение | Результат |
|---|---|
| Первый товар до удаления | Cable — 3 |
| Второй товар до удаления | Clamp — 0 |
| Чужое имя отклонено до удаления | да |
| Отказ не изменил реестр удалённых баз | да |
| Своя копия сохранилась после чужого запроса | да |
| Удаление своей копии вернуло true | да |
| Файл своей копии отсутствует | да |
| Шаблон FBK остался неизменным до общей очистки | да |
Полная программа, подготовка данных и архив для запуска
<?php
declare(strict_types=1);
require __DIR__.'/lab.php';
$owned=[];$removed=[];$db=$service=null;
try {
$config=f157Config();$service=f157Service();$runtime=f157Runtime($service);
$template=f157Template();$templateHash=hash_file('sha256',f157Path($template));
$restored=f157RestoreDatabase($service,$owned,'disposable',$template);
$db=f157Connect($restored);$items=f157Read($db,'SELECT ID,TITLE,QTY FROM LAB_ITEMS ORDER BY ID');
f157Commit($db);f157Close($db);
// This name is syntactically inside the laboratory, but was never registered as an owned database.
$foreign=f157NewName('foreign','fdb');$guardRefused=false;
$removedBefore=count(f157State()['removed']);
try {f157DropDatabase($owned,$foreign);}
catch (RuntimeException $error) {
if ($error->getMessage()!=='База не принадлежит этому запуску.') {throw $error;}
$guardRefused=true;
}
$removedAfterRefusal=count(f157State()['removed']);
$ownStillPresent=is_file(f157Path($restored));
$dropResult=f157DropDatabase($owned,$restored);
clearstatcache(true,f157Path($restored));$ownAbsent=!file_exists(f157Path($restored));
$templateUnchanged=is_file(f157Path($template))&&hash_file('sha256',f157Path($template))===$templateHash;
$rows=[['Первый товар до удаления',$items[0]['TITLE'].' — '.$items[0]['QTY']],
['Второй товар до удаления',$items[1]['TITLE'].' — '.$items[1]['QTY']],
['Чужое имя отклонено до удаления',$guardRefused],['Отказ не изменил реестр удалённых баз',$removedBefore===$removedAfterRefusal],
['Своя копия сохранилась после чужого запроса',$ownStillPresent],['Удаление своей копии вернуло true',$dropResult],
['Файл своей копии отсутствует',$ownAbsent],['Шаблон FBK остался неизменным до общей очистки',$templateUnchanged]];
$checks=['restored_fixture'=>$items===[['ID'=>1,'TITLE'=>'Cable','QTY'=>3],['ID'=>2,'TITLE'=>'Clamp','QTY'=>0]],
'unowned_refused'=>$guardRefused,'no_removal_on_refusal'=>$removedBefore===$removedAfterRefusal,
'owned_survived_refusal'=>$ownStillPresent,'owned_drop_success'=>$dropResult===true,'owned_file_absent'=>$ownAbsent,
'template_unchanged'=>$templateUnchanged,'only_one_owned'=>count($owned)===1];
$observations=['rows_before_drop'=>$items,'unowned_name_registered'=>false,'unowned_request_rejected'=>$guardRefused,
'removed_count_before'=>$removedBefore,'removed_count_after_refusal'=>$removedAfterRefusal,
'own_drop_return'=>$dropResult,'own_file_absent'=>$ownAbsent,'template_sha256'=>$templateHash,
'template_unchanged_before_cleanup'=>$templateUnchanged,'unowned_connection_opened'=>false];
} finally {
if(is_resource($db)){f157Close($db);}
if(is_resource($service)){f157Detach($service);}
$removed=f157Cleanup($owned);
}
f157Report('fb157-owned-drop','Удалить свою проверочную базу и заранее отклонить чужой путь',$rows,$checks,$observations,$runtime,$owned,$removed);
Ожидаемый результат
Первый товар до удаления — Cable — 3
Второй товар до удаления — Clamp — 0
Чужое имя отклонено до удаления — да
Отказ не изменил реестр удалённых баз — да
Своя копия сохранилась после чужого запроса — да
Удаление своей копии вернуло true — да
Файл своей копии отсутствует — да
Шаблон FBK остался неизменным до общей очистки — да
Запустите example.php по README.txt. Создаются одна собственная восстановленная база. Программа не требует существующей рабочей базы: она использует приложенный синтетический шаблон. После наблюдений удаляются только базы и промежуточные FBK своего запуска; result.html, result.json и metrics.json остаются. Защита в lab.php проверяет собственный реестр, фактический путь соединения и маркер перед удалением. Сырой журнал службы с путями в публичный отчёт не попадает.
Открыть наглядный результат · Посмотреть строки и наблюдения JSON.
Границы, ошибки и выбор другого способа
Проверка одного расширения .fdb или наличия слова test в имени недостаточна. Полный helper ограничивает каталог, имя и запись владения текущим запуском, затем подтверждает реальный путь базы. Чужой путь проверяется только функцией разрешения: к нему не открывается соединение для удаления. Файловые проверки лаборатории рассчитаны на локальную службу; для удалённого сервера нужен серверный реестр владения и проверка его путей. Не используйте drop_db как замену close и не рассчитывайте вернуть удалённую базу через rollback.
Вместо резервной копии выбрали посторонний XML, а восстановление с заменой уже успело уничтожить прежнюю базу. Исходная задача на Stack Overflow. Восстанавливаем только в новое свободное имя. Проверяем результат отдельно, исходную базу не заменяем. Пример отказа guard не вызывает drop для чужого пути; это отдельная защита владельца учебных файлов.
Попробуйте изменить условие
Почему после успешного ibase_drop_db не нужно ещё вызывать ibase_close на той же переменной?
Разбор решения
drop уже закрывает и удаляет базу. Сохранённая переменная больше не обозначает пригодное подключение, даже если оболочка ресурса ещё существует. Сбросьте её в null и проверяйте ожидаемое отсутствие собственного файла; не выполняйте второй вызов по устаревшему ресурсу.
Справочник копирования, восстановления и удаления базы
| Функция | Когда нужна |
|---|---|
| ibase_backup | Запросить логическую копию базы или только её схемы через Service Manager. |
| ibase_restore | Восстановить FBK в новую базу с явным учётом версии драйвера. |
| ibase_drop_db | Закрыть и удалить именно базу переданного соединения. |
Определить, какие данные доказывают пригодность копии
Обычный каталог проверяется конкретными товарами и остатками. Шаблон схемы — существующими столбцами и ожидаемым отсутствием строк. Накладная — позициями, связями, итогом и содержимым документа. Эти разные контракты заранее определяют, что считать полезным результатом восстановления. Одно сообщение true, малый размер FBK или успешное подключение не заменяют такую проверку.
Следующий шаг — связать этот подход с диагностикой сервисных заданий и правилами своего приложения. Для связей и записи полезны транзакции, для документа — чтение BLOB, для выбранного состава данных — параметры и результаты SQL. Расписание, хранение поколений и рабочая процедура аварийного восстановления требуют отдельного решения; учебный cleanup намеренно сохраняет отчёт, а не долгосрочный архив.
Функции и методы этой темы
Откройте карточку, чтобы увидеть договор функции, примеры, ограничения и связанные инструменты.
ibase_backupЗапросить логическую копию базы или только её схемы через Service Manager.
Встроено в PHP 5 и PHP 7 до 7.4; затем внешнее расширение. Профиль примеров: interbase 3.0.1 / PHP 8.2 x64. · interbase
ibase_backup(resource $service_handle, string $source_db, string $dest_file, int $options = 0, bool $verbose = false): mixedПеред обновлением приложения нужна копия, из которой действительно можно вернуть каталог. ibase_backup передаёт Service Manager задание: прочитать базу и записать логический архив FBK на стороне сервера. Сначала нужно понять, куда записывается файл и какой результат подтверждает функция, затем проверить копию восстановлением.
ibase_backup(resource $service_handle, string $source_db, string $dest_file,
int $options = 0, bool $verbose = false): mixed| Аргумент | Что передать |
|---|---|
| $service_handle | Ресурс ibase_service_attach, а не соединение ibase_connect и не путь к service_mgr. |
| $source_db | Путь к исходной базе на сервере Firebird либо разрешённый серверный alias. |
| $dest_file | Имя файла копии на том же сервере, где выполняется задание. Это не обязательно диск компьютера с PHP или браузером. |
| $options = 0 | Допустимые backup-флаги; совместимые флаги объединяются побитовым OR. Полный список приведён ниже. |
| $verbose = false | Без подробного вывода возвращается факт запуска. Историческая страница PHP.net называет аргумент бесполезным, однако драйвер 3.0.1 при true запрашивает строковый вывод службы; этот режим не следует проверять как строгое === true. |
- PHP → сервисное соединение Firebird
- backup → задание принято или отклонено
- На сервере создаётся FBK → следим за завершением и диагностикой
- restore в новое свободное имя
- SQL в восстановленной базе → товары, связи и документ совпали
Первый шаг: запросить обычную полную копию
$started = ibase_backup($service, $sourceDatabase, $newBackupFile, 0, false);
if ($started === false) {
throw new RuntimeException('Задание резервного копирования не запущено');
}
// true здесь не означает, что файл полностью записан и проверен восстановлением.Все три переменные получены из доверенной конфигурации администратора. Имена назначения заранее новые; исходная база содержит только собственные учебные данные. Полная программа показывает дальнейший путь: окончание задания, восстановление под новым именем и чтение реальных товаров. Автоматическое игнорирование ошибок и замена оригинала в этот сценарий не входят.
Вторая задача: сохранить только устройство таблиц
$started = ibase_backup($service, $sourceDatabase, $newSchemaBackup,
IBASE_BKP_METADATA_ONLY, false);
if ($started === false) {
throw new RuntimeException('Задание копирования схемы не запущено');
}
// После завершения и восстановления ожидается таблица с нулём строк.Так можно передать чистую схему для тестов. Самостоятельный пример сравнивает исходные товары с пустой восстановленной таблицей и отдельно доказывает, что нужные столбцы существуют. Не называйте такую копию резервной копией данных: строки намеренно отсутствуют.
| Опция ibase_backup | Смысл и когда нужна |
|---|---|
| 0 | Обычная логическая копия структуры и данных без перечисленных специальных режимов. Отправная точка для полного учебного архива. |
| IBASE_BKP_METADATA_ONLY | Сохранить схему без строк таблиц. Подходит для чистого шаблона, но не заменяет копию данных. |
| IBASE_BKP_NO_GARBAGE_COLLECT | Не запускать сборку старых версий записей в ходе резервного копирования. Это решение об обслуживании и нагрузке, не способ исключить текущие данные. |
| IBASE_BKP_IGNORE_CHECKSUMS | Игнорировать ошибочные контрольные суммы при попытке аварийного извлечения. Успех такого задания не гарантирует полноту и пригодность данных; это не настройка обычного backup. |
| IBASE_BKP_IGNORE_LIMBO | Пропустить данные незавершённых распределённых транзакций в состоянии limbo. Специальный режим восстановления после проблем двухфазного завершения, не универсальное исправление ошибок. |
| IBASE_BKP_OLD_DESCRIPTIONS | Сохранять описания метаданных в старом формате для совместимости. В обычном современном сценарии не требуется. |
| IBASE_BKP_NON_TRANSPORTABLE | Использовать непереносимый формат копии. Ограничивает перенос между платформами; по умолчанию такой режим не включаем. |
| IBASE_BKP_CONVERT | Сохранить внешние таблицы как обычные: после восстановления их данные будут в обычных таблицах базы. Это изменение модели хранения, а не преобразование кодировки. |
В режиме verbose = false успешный старт обычно даёт true, отказ — false; подробный режим реализации 3.0.1 может вернуть строку вывода службы либо false. Проверка результата запуска и чтение дальнейшего состояния — разные шаги. Ошибка пути, места на диске или чтения данных может обнаружиться после старта. Сохраните текущую диагностику до следующего API-вызова, контролируйте время ожидания и подтвердите копию восстановлением. Сам по себе размер FBK, наличие файла или строка «started» этого не доказывают.
Service Manager работает с правами серверного процесса и учётной записью службы, а не с правами браузера. Источнику и месту архива нужны соответствующие разрешения; пароль не размещают в общедоступном коде. Учебный профиль локальный, поэтому сервер и PHP видят одну папку; это нельзя переносить как предположение на удалённый сервер. Обычная копия файлов работающей FDB не заменяет логический backup. Другие механизмы, например физический nbackup, имеют собственные правила. См. ibase_restore и подключение к Service Manager.
ibase_restoreВосстановить FBK в новую базу с явным учётом версии драйвера.
Встроено в PHP 5 и PHP 7 до 7.4; затем внешнее расширение. Профиль примеров: interbase 3.0.1 / PHP 8.2 x64. · interbase
ibase_restore(resource $service_handle, string $source_file, string $dest_db, int $options = 0, bool $verbose = false): mixedРезервная копия нужна для возвращения данных. ibase_restore просит Service Manager построить базу из FBK-архива. Проверочный сценарий создаёт новое имя и сравнивает полезные данные; исходную базу он не заменяет. Отдельно разберём обнаруженное расхождение между официальным порядком аргументов и реализацией драйвера 3.0.1, чтобы читатель мог запустить именно этот пример.
// Официальный контракт PHP Manual:
ibase_restore(resource $service_handle, string $source_file, string $dest_db,
int $options = 0, bool $verbose = false): mixed| Аргумент | Смысл по официальному контракту |
|---|---|
| $service_handle | Ресурс подключения к Service Manager. |
| $source_file | Путь к FBK на сервере Firebird, который будет выполнять восстановление. |
| $dest_db | Путь новой базы на сервере либо настроенный серверный alias назначения. |
| $options = 0 | Флаги восстановления; для новых учебных файлов используется CREATE. |
| $verbose = false | Без подробного чтения вывода функция сообщает о запуске. В реализации 3.0.1 true включает чтение строк службы, поэтому успешный возврат может быть строкой, а не bool. |
Важное различие именно драйвера interbase 3.0.1
В проверенном расширении 3.0.1 первый строковый аргумент кодируется как имя базы, второй — как файл копии. Это противоречит порядку source_file, dest_db на странице PHP.net. Ограниченное наблюдение показало: документированный порядок пытается открыть имя новой базы как файл копии и отказывает; для этого конкретного драйвера действует порядок dest_db, source_file. Это дефект совместимости драйвера, а не новое общее правило API Firebird.
- Понятный API примера: f157Restore(service, source.fbk, new.fdb)
- Проверить точную версию interbase = 3.0.1
- Адаптер этого профиля → ibase_restore(service, new.fdb, source.fbk, CREATE, true)
- Служба читает source.fbk и создаёт new.fdb
- Проверить SQL/данные в new.fdb; source и исходная база остаются
Применение 1: восстановить каталог в свободное имя
// Только для подтверждённой реализации interbase 3.0.1.
if (phpversion('interbase') !== '3.0.1') {
throw new RuntimeException('Для этой версии нужен проверенный адаптер восстановления');
}
// guard ранее подтвердил: newDatabase отсутствует, путь принадлежит этому запуску.
$result = ibase_restore($service, $newDatabase, $sourceBackup, IBASE_RES_CREATE, true);
if ($result === false) {
throw new RuntimeException('Восстановление не выполнено');
}
// Далее подключаемся именно к newDatabase и проверяем товары.Два пути в этом фрагменте намеренно подписаны. В архиве полная функция f157Restore сохраняет удобный порядок source, target и проверяет версию до вызова. Она не делает опасную попытку «если не получилось, поменяем местами»: после отказа мог остаться частично созданный файл, поэтому последствия сначала нужно выяснить. Полный каталог показывает конкретные восстановленные названия и остатки.
Применение 2: восстановить и проверить документ вместе со связями
// Адаптер лаборатории: исходный архив, затем новое назначение.
f157Restore($service, $documentBackup, $newDocumentDatabase);
// Успешный запуск не заменяет проверку данных в новом подключении:
// 1. Выбрать документ по ID и его позиции через JOIN.
// 2. Сравнить итог SUM(quantity * unit_price) с контрольным итогом.
// 3. Прочитать документ-BLOB и сравнить его точные байты.
// 4. Подтвердить сохранение ограничения связи, а не только наличие двух таблиц.Самостоятельный сценарий проверки восстановления выполняет эти действия на собственных учебных данных. Комментарии выше — карта проверки, а весь запускаемый SQL и PHP находится в полном примере. Наличие файла, возможность открыть базу или одно число COUNT недостаточны, если приложению нужны связи, содержимое документа и правильный итог.
| Опция ibase_restore | Смысл и ограничение |
|---|---|
| IBASE_RES_CREATE | Создать новую базу; существующее назначение должно приводить к отказу. В учебных программах дополнительно проверяется свободное имя. |
| IBASE_RES_REPLACE | Заменить существующую базу. Может удалить прежнюю базу до успешного окончания восстановления; в наших программах запрещён. Не объединяйте с CREATE как будто это дополнительная страховка. |
| IBASE_RES_DEACTIVATE_IDX | Не активировать индексы после восстановления. Затрагивает и ограничения, опирающиеся на индексы; требуется отдельное исправление и активация перед использованием базы. |
| IBASE_RES_NO_SHADOW | Не воссоздавать shadow-файлы. Shadow — не то же самое, что FBK-архив. |
| IBASE_RES_NO_VALIDITY | Не включать проверки CHECK при восстановлении. Это специальный режим разбора проблем данных, а не обычное «ускорение». |
| IBASE_RES_ONE_AT_A_TIME | Восстанавливать данные с отдельным подтверждением каждой таблицы. При отказе возможна частично заполненная база; наличие строк не доказывает завершение всего задания. |
| IBASE_RES_USE_ALL_SPACE | Использовать всё место страниц под данные вместо обычного резерва для дальнейших версий. Это характеристика восстановленной базы, не показатель надёжности копии. |
| 0 | Историческое значение options по умолчанию. В полном сценарии намерение создать новый файл задаётся явно через CREATE и проверяется до запуска. |
Исправление неточности списка options в PHP Manual
На исторической странице ibase_restore также перечислены IBASE_PRP_PAGE_BUFFERS и IBASE_PRP_SWEEP_INTERVAL, а CREATE повторён дважды. PRP-константы относятся к операциям изменения свойств с отдельным числовым аргументом; они не являются битовыми флагами restore, их нельзя добавлять через | к IBASE_RES_CREATE. Наличие константы в расширении не делает её подходящей для любого параметра options. Драйвер 3.0.1 передаёт restore options одним битовым полем; отдельные современные параметры Service API, например размер страницы, этим аргументом автоматически не задаются.
По базовому описанию true означает принятие задания, false — отказ; в подробном режиме 3.0.1 возвращается собранный вывод службы либо false, а при отсутствии строк возможен bool. Полный пример проверяет отказ, сохраняет вывод в переменной, не публикуя его и подтверждает конкретные данные после восстановления. Нельзя проверять подробный успех только через === true, публиковать сырой журнал с серверными путями или считать первое появление файла завершением. Поздний отказ может оставить частичную базу; она не становится допустимым назначением приложения только потому, что несколько SELECT уже работают.
Восстановление в новое имя сохраняет возможность сравнения с исходником. Не включайте REPLACE для «проверки архива»: сначала может быть удалено прежнее назначение, и лишь затем обнаружится плохой файл. Копия, изготовленная более новой версией, и перенос между поколениями серверов требуют поддерживаемого маршрута; логический формат не означает совместимость всех версий в обе стороны. После проверки собственную временную базу удаляет ibase_drop_db. Источник заданий и диагностики — глава Service Manager.
ibase_drop_dbЗакрыть и удалить именно базу переданного соединения.
Встроено в PHP 5 и PHP 7 до 7.4; затем внешнее расширение. Профиль примеров: interbase 3.0.1 / PHP 8.2 x64. · interbase
ibase_drop_db(resource $connection = ...): boolПроверочная восстановленная база больше не нужна. ibase_drop_db удаляет целую базу, связанную с переданным соединением. Поэтому сначала доказывают, что это именно собственный временный результат проверки, закрывают его результаты и транзакции, и только затем вызывают удаление. Для обычного завершения чтения нужен ibase_close, а не drop_db.
ibase_drop_db(): bool
ibase_drop_db(resource $connection): bool| Часть контракта | Объяснение |
|---|---|
| $connection | Соединение базы от ibase_connect либо ibase_pconnect по официальному контракту. Не service handle, не строковый путь и не ресурс транзакции. В наших примерах используется обычное явное соединение. |
| Аргумент опущен | Используется последнее открытое соединение. Для удаления это слишком неявный выбор: полный пример передаёт конкретный проверенный ресурс. |
| true | База закрыта и удалена с сервера. Старый ресурс больше не используют; переменную сбрасывают в null. |
| false | Удаление не выполнено. Сохраните диагностику сразу; отказ прав, активные подключения и неверные ресурсы требуют отдельного разбора. |
Историческая сигнатура PHP.net показывает null по умолчанию. Проверенный драйвер 3.0.1 разбирает явно переданный параметр как resource: отсутствие аргумента и явное null не являются одной гарантированной формой вызова. У функции нет режима «только попробовать», нет фильтра таблиц и нет автоматической проверки, что вы выбрали тестовую базу. Удаление не отменяется обычным rollback пользовательской транзакции.
- Путь из реестра текущего запуска → находится в своей папке
- Имя назначения создано этим примером и отмечено маркером
- Подключение сообщает тот же путь базы → чужое назначение отклоняется
- Закрыть результаты и завершить свои транзакции
- drop_db(проверенное соединение) → true → resource = null
- Проверить отсутствие собственного файла; оригинал остаётся
Применение 1: убрать проверенную восстановленную копию
// Условие: guard уже подтвердил владение и фактический путь базы.
// Все результаты освобождены, собственные транзакции завершены.
if (ibase_drop_db($ownedRestoredConnection) !== true) {
throw new RuntimeException('Временная база не удалена');
}
$ownedRestoredConnection = null;Это последняя операция полной программы с проверкой владельца. Она не просто сравнивает суффикс .fdb: проверяется собственная папка, запись текущего запуска и реальное подключение. Сама функция drop_db получает ресурс только после guard. Исходная учебная база и резервный архив — отдельные объекты, их судьба не определяется этим вызовом.
Применение 2: отказаться от чужого назначения до подключения и drop
// Пример логики приложения; $registeredRestoredPath получен из собственного реестра.
if ($requestedPath !== $registeredRestoredPath) {
throw new RuntimeException('Назначение не принадлежит этой проверке');
}
// Дальше необходимы проверки разрешённого каталога, marker и фактического пути подключения.
// До их успеха ibase_drop_db вообще не вызывается.Простое сравнение строк здесь показывает момент отказа, а не является готовой универсальной нормализацией путей. В самостоятельном примере guard выполняет полный контракт своей локальной папки, а отказ для чужого пути показан как полезный результат: функция удаления не получила этот ресурс. Не принимайте путь удаления из HTTP-формы без такого владения и не используйте последнее соединение по умолчанию.
Удаление восстановленной копии выполняется после проверки данных и сохранения наблюдений в памяти; готовый отчёт записывается после контролируемой очистки. При непонятном исходе восстановления частичный файл не объявляют корректным и не подменяют им оригинал. Чтобы просто закрыть успешное соединение, используйте ibase_close; чтобы восстановить архив под новым именем — ibase_restore. Учебное автоматическое удаление ограничено объектами своего запуска и не является инструкцией удаления произвольной базы администратора.
Источники и обсуждения
- PHP Manual — ibase_backup
- PHP Manual — ibase_restore
- PHP Manual — ibase_drop_db
- FirebirdSQL — Service API драйвера 3.0.1
- FirebirdSQL — удаление базы в драйвере 3.0.1
- Firebird — логическое резервное копирование и восстановление gbak
- Firebird 5 — тип результата SUM
- FirebirdSQL — чтение SQL-типов в драйвере 3.0.1
Код и руководство с собой
Скачайте самостоятельные примеры с исходными данными, инструкциями и ожидаемыми результатами. Примеры HTTP и фрагменты для проекта помечены отдельно: им нужно указанное окружение.
Код не исполняется на сайте. Скачайте нужный пример и запускайте его в своём локальном окружении.
Материалы сверены 2026-10-12. Примеры и объяснения написаны для этого руководства; источники и обсуждения доступны в каждой главе.


