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

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

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

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

Глава 67 · После соединений, запросов, транзакций и знакомства с BLOB Firebird

Firebird: резервная копия, восстановление в новую базу и проверка данных

Полный каталог, чистая схема, проверка связанной накладной с BLOB и удаление только своей восстановленной базы.

Получить проверяемую копию, а не только сообщение об успехе

Перед обновлением приложения или восстановлением после ошибки нужно уметь вернуть нужные данные. Здесь пройдём весь небольшой путь: получить логический архив FBK, восстановить его под новым именем, открыть восстановленную базу и показать товары либо накладную. Отдельная задача — чистая схема без строк. В конце удалим только собственную проверочную копию, заранее отказавшись от чужого назначения.

Три разных объекта и один понятный результат
  1. Исходная FDB: таблицы, строки, связи и документы
  2. Service Manager получает задание backup → создаёт FBK
  3. FBK содержит логическую копию; напрямую как базу его не открывают
  4. Service Manager получает restore CREATE → создаёт новую FDB
  5. Обычное соединение читает новую FDB → конкретные данные совпадают
  6. Отчёт остаётся; временные базы и 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 напрямую.

Путь конкретных данных
  1. Собственная исходная база: Кабель — 3; Клемма — 0
  2. COMMIT → данные доступны службе резервного копирования
  3. backup → полный FBK структуры и данных
  4. restore CREATE → другое, ещё не существующее имя базы
  5. SELECT в новой базе → Кабель — 3; Клемма — 0
  6. Сравнить строки; сохранить отчёт; убрать только свои временные базы

Самый короткий шаг

$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 намеренно исключает строки. После восстановления покажем оба независимых факта: таблица и её поля существуют, но исходных товаров нет. Так не получится перепутать пустой шаблон с потерявшей данные полной копией.

Путь конкретных данных
  1. Исходная учебная таблица LAB_ITEMS содержит 2 товара
  2. backup с METADATA_ONLY → архив схемы без строк
  3. restore CREATE → новая пустая база
  4. Запрос метаданных → ID, TITLE, QTY существуют
  5. SELECT COUNT(*) FROM LAB_ITEMS → 0
  6. Исходная база по-прежнему содержит 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.

Путь конкретных данных
  1. Накладная № 7 → Кабель 3 × 12500 + Клемма 2 × 2000 копеек
  2. Документ хранится в BLOB; позиции связаны с документом и товарами
  3. Полный backup → восстановление в новую базу
  4. JOIN → исходные названия и количества; SUM → 41500
  5. BLOB → исходный текст; метаданные → обе внешние связи
  6. Совпадение содержательных проверок записывается в отчёт

Самый короткий шаг

// После подтверждённого восстановления выполняем запрос в НОВОМ соединении.
$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 совпалида
Строки позиций совпалида
Полная программа, подготовка данных и архив для запуска
Проверить восстановление накладной: позиции, связи, итог и 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, затем удалим только подтверждённую свою базу.

Путь конкретных данных
  1. Восстановленная копия записана в реестр текущего запуска
  2. Незарегистрированное назначение → guard отказал до drop
  3. Своя копия → путь/маркер/фактическая база совпали
  4. Освобождены результаты и завершена транзакция чтения
  5. ibase_drop_db(своё соединение) → true; переменная = null
  6. Файл своей базы отсутствует; исходный шаблон не менялся

Самый короткий шаг

// Только после проверки владельца, пути и завершения своих ресурсов:
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.
Файл ещё не равен проверенной копии
  1. PHP → сервисное соединение Firebird
  2. backup → задание принято или отклонено
  3. На сервере создаётся FBK → следим за завершением и диагностикой
  4. restore в новое свободное имя
  5. 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.

Два контракта без скрытой перестановки
  1. Понятный API примера: f157Restore(service, source.fbk, new.fdb)
  2. Проверить точную версию interbase = 3.0.1
  3. Адаптер этого профиля → ibase_restore(service, new.fdb, source.fbk, CREATE, true)
  4. Служба читает source.fbk и создаёт new.fdb
  5. Проверить 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 пользовательской транзакции.

Положительное доказательство владения
  1. Путь из реестра текущего запуска → находится в своей папке
  2. Имя назначения создано этим примером и отмечено маркером
  3. Подключение сообщает тот же путь базы → чужое назначение отклоняется
  4. Закрыть результаты и завершить свои транзакции
  5. drop_db(проверенное соединение) → true → resource = null
  6. Проверить отсутствие собственного файла; оригинал остаётся

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

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

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

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

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

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