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

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

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

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

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

Firebird Services API: паспорт экземпляра, сведения о базе и диагностика пути

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

Сначала понять экземпляр, затем проверять файл базы

Перед копированием или разбором сбоя базы нужно знать, с каким экземпляром Firebird вы работаете и какой файл он обслуживает. В этой главе вы получите настоящий паспорт экземпляра, узнаете формат и размер страницы учебной базы, сопоставите физическую статистику с выборкой товара и разберёте намеренно неверный путь. Каждый результат выводится в понятный HTML-отчёт и JSON; скрытые данные подключения остаются в локальной конфигурации.

Два разных подключения
  1. SQL-соединение $db → конкретный файл базы → SELECT товаров
  2. Сервисное соединение $service → менеджер экземпляра Firebird
  3. server_info($service, VERSION) → версия экземпляра
  4. db_info($service, путь, HDR_PAGES) → свойства выбранной базы
  5. Завершить действие → service_detach → убрать ссылку

Представьте справочную службу здания и карточку конкретного помещения: сначала вы находите нужное здание, затем указываете помещение для отдельной проверки. Успешно дозвониться в справочную ещё не означает, что указанное помещение существует. Это аналогия адресации: она не описывает устройство страниц базы, права файловой системы или внутренние потоки сервера.

Что требуетсяКакой ресурс и действие
Получить название и остаток товараОбычный $db: SELECT с нужными колонками и условием.
Узнать версию FirebirdСервисный $service: IBASE_SVC_SERVER_VERSION.
Узнать ODS и размер страницы конкретной базы$service и отдельный путь базы: IBASE_STS_HDR_PAGES.
Понять число страниц данных и структуру индекса$service и путь базы: статистика; отдельно SELECT и анализ плана.
Сохранить документ целикомОбычный $db/транзакция: INSERT/UPDATE и commit, не service_detach.

Предыдущие главы объясняют обычное соединение и SELECT, транзакции и немедленную диагностику. Здесь используются PHP 8.2.35, отдельный драйвер FirebirdSQL interbase 3.0.1 и Firebird 5.0.4. Старый встроенный interbase был удалён из поставки PHP 7.4; наличие исторических страниц PHP Manual не означает автоматическую доступность этих функций в современном PHP.

Самостоятельные файлы и границы учебной базы

Установка учебного окружения находится в README каждого ZIP. Примеры работают с частным локальным движком, а два сценария создают отдельный собственный файл базы из приложенного синтетического шаблона. В шаблоне только LAB_ITEMS: Cable с остатком 3 и Clamp с остатком 0; исходная SQL-схема приложена. Это не копия чужой или рабочей базы. В конце удаляются только файлы, зарегистрированные текущим примером как собственные.

f157Service открывает сервис, f157Capture сохраняет результат и диагностику немедленно, f157CreateDatabase готовит собственную учебную базу, f157Read читает ограниченный SQL-результат. f157Detach освобождает принадлежащий программе сервис. Полный текст служебного отчёта может содержать пути и служебные идентификаторы: для публичного результата выбираем необходимые поля либо обрабатываем его через f157PublicText. Короткие фрагменты ниже показывают один шаг; полный файл содержит проверки и завершение ресурсов.

Паспорт экземпляра: узнать версию и платформу перед проверкой базы

На компьютере могут стоять разные версии Firebird, а приложение может обращаться к удалённому экземпляру. До разбора базы получим паспорт именно того сервиса, с которым установлено соединение. В результате будут отдельные строки версии движка, платформы, PHP и расширения — четыре разных уровня окружения.

Путь проверки
  1. f157Service → локальный service handle
  2. server_info(VERSION) → WI-V5.0.4.1812 Firebird 5.0
  3. server_info(IMPLEMENTATION) → Firebird/Windows/AMD/Intel/x64
  4. PHP_VERSION и phpversion(interbase) → сведения PHP
  5. service_detach → собственная ссылка null

Сначала один шаг

$service = ibase_service_attach($host, $user, $password);
if ($service === false) { throw new RuntimeException("Сервис не подключён"); }
$version = ibase_server_info($service, IBASE_SVC_SERVER_VERSION);
if (!is_string($version)) { throw new RuntimeException("Версия не получена"); }
echo $version;

host указывает экземпляр, а не файл .fdb. SERVER_VERSION — константа выбора сведений. В самостоятельной программе START из lab.php выполняет эти реальные вызовы и сохраняет их в $runtime; код урока выводит результат и явно закрывает сервис. Пустой host используется только в проверенном локальном embedded-профиле README. Для сети указывают имя/IP с необязательным /портом.

Что получится после запуска

НаблюдениеРезультат
Версия экземпляраWI-V5.0.4.1812 Firebird 5.0
РеализацияFirebird/Windows/AMD/Intel/x64
Версия PHP8.2.35
Версия interbase3.0.1
Сервис освобождёнда
Создано учебных баз0
Паспорт экземпляра: узнать версию и платформу перед проверкой базыСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__.'/lab.php';
$owned=[];$removed=[];$db=$service=null;
try {
    $config=f157Config();$service=f157Service();$runtime=f157Runtime($service);

    function statistics157($service,string $database,int $action,int $argument=0): string {
        $call=f157Capture(static fn()=>ibase_db_info($service,$database,$action,$argument));
        if (!is_string($call['value']) || $call['warning'] || $call['code']!==0) {
            f157Failure('Текст статистического отчёта не получен',$call);
        }
        return $call['value'];
    }
    function headerField157(string $report,string $pattern): string {
        if (preg_match($pattern,$report,$match)!==1) {
            throw new RuntimeException('Ожидаемое поле отсутствует в формате отчёта.');
        }
        return $match[1];
    }

    // START obtained both strings through actual server_info calls with immediate diagnostics.
    $version=$runtime['server_version'];$implementation=$runtime['implementation'];
    $closed=f157Detach($service);
    $rows=[['Версия экземпляра',$version],['Реализация',$implementation],['Версия PHP',$runtime['php']],
        ['Версия interbase',$runtime['extension']],['Сервис освобождён',$closed && $service===null],['Создано учебных баз',count($owned)]];
    $checks=['version_observed'=>is_string($version) && str_contains($version,'V5.0.4.'),
        'implementation_observed'=>is_string($implementation) && str_contains($implementation,'Firebird/Windows/'),
        'pinned_extension'=>$runtime['extension']==='3.0.1','service_closed'=>$closed===true && $service===null,
        'no_database_required'=>$owned===[]];
    $observations=['server_version'=>$version,'implementation'=>$implementation,
        'connection_kind'=>'локальный Services API отдельного учебного экземпляра',
        'database_path_not_required'=>true,'php_extension_not_server_version'=>true];

} finally {
    if(is_resource($db)){f157Close($db);}
    if(is_resource($service)){f157Detach($service);}
    $removed=f157Cleanup($owned);
}

f157Report('fb157-service-passport','Паспорт экземпляра: узнать версию и платформу перед проверкой базы',$rows,$checks,$observations,$runtime,$owned,$removed);
Ожидаемый результат
Версия экземпляра — WI-V5.0.4.1812 Firebird 5.0
Реализация — Firebird/Windows/AMD/Intel/x64
Версия PHP — 8.2.35
Версия interbase — 3.0.1
Сервис освобождён — да
Создано учебных баз — 0

Самостоятельный CLI-пример с lab.php, README и синтетическим шаблоном. Выполняются настоящие вызовы Services API; собственные файлы базы освобождаются и удаляются после проверки. Создаются result.html и result.json.

Фактический HTML-результат · Данные результата JSON.

Ошибки, ограничения и применение

Наличие строки Firebird 5.0 в ответе не доказывает, что приложение подключается к нужному файлу базы или имеет права на все операции. Нельзя принять phpversion("interbase") за версию сервера. На другой установленной сборке текст паспорта будет другим; ожидаемые строки относятся к указанному учебному профилю. Для паспорта не нужно создавать таблицу, выполнять SELECT * или открывать рабочую базу.

В реальном вопросе разработчик поместил путь файла базы внутрь адреса service_mgr и получил отказ подключения к сервису. Исходная ситуация на Stack Overflow. Разделяем адрес экземпляра и путь выбранной базы. Сервисное подключение сначала открывается к менеджеру; путь базы передаётся отдельному действию. В PHP-драйвере суффикс :service_mgr формируется самой функцией, поэтому синтаксис CLI нельзя копировать в host целиком.

Измените условие задачи

В служебном отчёте показаны PHP 8.2.35 и interbase 3.0.1. Можно ли по ним заключить, что сервер Firebird тоже версии 3.0.1?

Ожидаемое решение

Нет. Это версия PHP-расширения. Версия движка получается отдельным server_info с IBASE_SVC_SERVER_VERSION; адрес сервиса нужно сопоставить с конфигурацией приложения.

Заголовок базы: отличить формат файла от версии сервера и строк каталога

Перед переносом базы нужно понимать формат её файла. Создадим отдельную учебную базу из приложенного шаблона и получим заголовок: страница 8192 байта, ODS 13.1, диалект 3. Затем обычным SELECT прочитаем товар Cable и его остаток 3. Так видно, какие сведения относятся к хранению, а какие — к предметным данным.

Путь проверки
  1. Создать собственную учебную базу из seed
  2. db_info(HDR_PAGES) → текст заголовка
  3. Извлечь Page size, ODS version, Database dialect
  4. Обычный SELECT → Cable, остаток 3
  5. Закрыть соединения → удалить только свою базу

Сначала один шаг

$header = ibase_db_info($service, $serverDatabasePath, IBASE_STS_HDR_PAGES);
if (!is_string($header)) { throw new RuntimeException("Заголовок не получен"); }
echo $header; // Локальная служебная консоль.

serverDatabasePath — путь к базе с точки зрения выбранного сервиса. ODS описывает формат хранения; номер сборки сервера взят из предыдущего сценария. Page size — размер страницы хранения, а не размер товара или число строк. Полный файл извлекает три именованных поля регулярными выражениями и явно отказывает, если ожидаемая подпись отсутствует; это не универсальный парсер всех выпусков gstat.

Что получится после запуска

НаблюдениеРезультат
Размер страницы, байт8192
Формат файла ODS13.1
SQL-диалект3
Товарных строк в учебной таблице2
Пример товараCable
Остаток примера3
Заголовок базы: отличить формат файла от версии сервера и строк каталогаСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__.'/lab.php';
$owned=[];$removed=[];$db=$service=null;
try {
    $config=f157Config();$service=f157Service();$runtime=f157Runtime($service);

    function statistics157($service,string $database,int $action,int $argument=0): string {
        $call=f157Capture(static fn()=>ibase_db_info($service,$database,$action,$argument));
        if (!is_string($call['value']) || $call['warning'] || $call['code']!==0) {
            f157Failure('Текст статистического отчёта не получен',$call);
        }
        return $call['value'];
    }
    function headerField157(string $report,string $pattern): string {
        if (preg_match($pattern,$report,$match)!==1) {
            throw new RuntimeException('Ожидаемое поле отсутствует в формате отчёта.');
        }
        return $match[1];
    }

    $database=f157CreateDatabase($service,$owned,'header');
    $report=statistics157($service,$database,IBASE_STS_HDR_PAGES);
    $pageSize=(int)headerField157($report,'/\bPage size\s+(\d+)\b/u');
    $ods=headerField157($report,'/\bODS version\s+(\d+\.\d+)\b/u');
    $dialect=(int)headerField157($report,'/\bDatabase dialect\s+(\d+)\b/u');
    $db=f157Connect($database);
    $count=(int)f157Read($db,'SELECT COUNT(*) AS "n" FROM LAB_ITEMS')[0]['n'];
    $sample=f157Read($db,'SELECT ID AS "id",TITLE AS "title",QTY AS "qty" FROM LAB_ITEMS WHERE ID=?',[1])[0];
    f157Close($db);
    $rows=[['Размер страницы, байт',$pageSize],['Формат файла ODS',$ods],['SQL-диалект',$dialect],
        ['Товарных строк в учебной таблице',$count],['Пример товара',$sample['title']],['Остаток примера',$sample['qty']]];
    $checks=['header_text_received'=>str_contains($report,'Database header page information'),
        'page_size_observed'=>$pageSize===8192,'ods_observed'=>$ods==='13.1','dialect_observed'=>$dialect===3,
        'business_rows_read'=>$count===2 && $sample===['id'=>1,'title'=>'Cable','qty'=>3],
        'sql_link_closed'=>$db===null];
    $observations=['header_fields'=>['page_size'=>$pageSize,'ods'=>$ods,'dialect'=>$dialect],
        'selected'=>$sample,'business_row_count'=>$count,'report_excerpt'=>f157PublicText($report),
        'page_size_is_not_row_count'=>true,'source'=>'собственная база из приложенного синтетического шаблона'];

} finally {
    if(is_resource($db)){f157Close($db);}
    if(is_resource($service)){f157Detach($service);}
    $removed=f157Cleanup($owned);
}

f157Report('fb157-database-header','Заголовок базы: отличить формат файла от версии сервера и строк каталога',$rows,$checks,$observations,$runtime,$owned,$removed);
Ожидаемый результат
Размер страницы, байт — 8192
Формат файла ODS — 13.1
SQL-диалект — 3
Товарных строк в учебной таблице — 2
Пример товара — Cable
Остаток примера — 3

Самостоятельный CLI-пример с lab.php, README и синтетическим шаблоном. Выполняются настоящие вызовы Services API; собственные файлы базы освобождаются и удаляются после проверки. Создаются result.html и result.json.

Фактический HTML-результат · Данные результата JSON.

Ошибки, ограничения и применение

Текст заголовка содержит служебные пути и время создания: в JSON/HTML сохраняется обработанный вариант f157PublicText, а основные показатели показаны отдельно. COUNT относится к собственной таблице из двух строк. Успешное чтение заголовка не является полной проверкой целостности, наличия нужных таблиц или возможности восстановить резервную копию. Номер ODS не следует менять вручную в файле.

Вопрос на PHP: service manager сообщил ноль подключений, хотя запрос к MON$ATTACHMENTS показывал подключения к базе. Исходная ситуация на Stack Overflow. Объясняем область наблюдения: сервисный счётчик относится к экземпляру/процессу, а MON$ATTACHMENTS — к выбранной базе. Режим Classic создаёт отдельные процессы, поэтому нельзя считать сервисную цифру универсальным числом всех подключений на машине. По этому симптому не меняем архитектуру сервера автоматически.

Измените условие задачи

В заголовке page size=8192, а SELECT COUNT(*) вернул 2. Что означает каждое число и какое следует показать в карточке каталога?

Ожидаемое решение

8192 — байты одной страницы хранения. 2 — число товарных строк в этой небольшой таблице. Пользовательская карточка получает только нужные предметные поля; параметры хранения остаются в служебном отчёте.

Страницы и индекс: сопоставить физический отчёт с нужными товарами

В каталоге есть Cable с остатком 3 и Clamp с остатком 0. На экран нужен только товар в наличии. Создадим индекс по QTY, получим статистику страниц и отдельно выполним SELECT с фильтром в базе. В индексе будут два узла, а на экране — один товар: физическая структура и результат условия отвечают на разные вопросы.

Путь проверки
  1. Два синтетических товара → собственный индекс IX_FB157_QTY
  2. WHERE QTY>0 ORDER BY ID ROWS 10 → один товар Cable
  3. DATA_PAGES + IDX_PAGES → статистический текст
  4. LAB_ITEMS: 1 страница; IX_FB157_QTY: 2 узла
  5. Сопоставить показатели, не объявляя план запроса измеренным

Сначала один шаг

$stats = ibase_db_info(
    $service, $serverDatabasePath, IBASE_STS_DATA_PAGES, IBASE_STS_IDX_PAGES
);
if (!is_string($stats)) { throw new RuntimeException("Отчёт не получен"); }

Третий аргумент выбирает статистику данных, четвёртый добавляет индексные страницы. В драйвере 3.0.1 составная маска DATA_PAGES | IDX_PAGES в третьей позиции не равнозначна этой форме: action сначала проверяется как отдельное значение. Индекс — дополнительная структура для поиска, а не вторая копия результата конкретного SELECT. Команда CREATE INDEX и данные шаблона находятся в полном комплекте.

Что получится после запуска

НаблюдениеРезультат
ТаблицаLAB_ITEMS
Страниц данных1
ИндексIX_FB157_QTY
Глубина индекса1
Листовых страниц индекса1
Узлов в индексе2
Товаров с остатком больше нуля1
Показанный товарCable
Его остаток3
Страницы и индекс: сопоставить физический отчёт с нужными товарамиСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__.'/lab.php';
$owned=[];$removed=[];$db=$service=null;
try {
    $config=f157Config();$service=f157Service();$runtime=f157Runtime($service);

    function statistics157($service,string $database,int $action,int $argument=0): string {
        $call=f157Capture(static fn()=>ibase_db_info($service,$database,$action,$argument));
        if (!is_string($call['value']) || $call['warning'] || $call['code']!==0) {
            f157Failure('Текст статистического отчёта не получен',$call);
        }
        return $call['value'];
    }
    function headerField157(string $report,string $pattern): string {
        if (preg_match($pattern,$report,$match)!==1) {
            throw new RuntimeException('Ожидаемое поле отсутствует в формате отчёта.');
        }
        return $match[1];
    }

    $database=f157CreateDatabase($service,$owned,'statistics');
    $db=f157Connect($database);
    f157Command($db,'CREATE INDEX IX_FB157_QTY ON LAB_ITEMS(QTY)');
    f157Commit($db);
    $selected=f157Read($db,'SELECT ID AS "id",TITLE AS "title",QTY AS "qty" FROM LAB_ITEMS WHERE QTY>0 ORDER BY ID ROWS 10');
    f157Close($db);
    // In this driver DATA is the action; additional IDX bits belong to the fourth argument.
    $report=statistics157($service,$database,IBASE_STS_DATA_PAGES,IBASE_STS_IDX_PAGES);
    if (preg_match('/\bLAB_ITEMS\s+\(\d+\).*?\bData pages:\s*(\d+)/s',$report,$tableMatch)!==1) {
        throw new RuntimeException('Блок LAB_ITEMS отсутствует в отчёте.');
    }
    if (preg_match('/\bIndex IX_FB157_QTY\s+\(\d+\).*?\bdepth:\s*(\d+),\s*leaf buckets:\s*(\d+),\s*nodes:\s*(\d+)/s',$report,$indexMatch)!==1) {
        throw new RuntimeException('Блок собственного индекса отсутствует в отчёте.');
    }
    $pages=(int)$tableMatch[1];$depth=(int)$indexMatch[1];$leafPages=(int)$indexMatch[2];$nodes=(int)$indexMatch[3];
    $rows=[['Таблица','LAB_ITEMS'],['Страниц данных',$pages],['Индекс','IX_FB157_QTY'],['Глубина индекса',$depth],
        ['Листовых страниц индекса',$leafPages],['Узлов в индексе',$nodes],['Товаров с остатком больше нуля',count($selected)],
        ['Показанный товар',$selected[0]['title']],['Его остаток',$selected[0]['qty']]];
    $checks=['data_pages_observed'=>$pages===1,'own_index_observed'=>str_contains($report,'Index IX_FB157_QTY'),
        'index_structure_observed'=>$depth===1 && $leafPages===1 && $nodes===2,
        'bounded_business_selection'=>$selected===[['id'=>1,'title'=>'Cable','qty'=>3]],
        'physical_nodes_not_selected_rows'=>$nodes!==count($selected),'sql_link_closed'=>$db===null];
    $observations=['statistics'=>['table'=>'LAB_ITEMS','data_pages'=>$pages,'index'=>'IX_FB157_QTY','depth'=>$depth,'leaf_pages'=>$leafPages,'nodes'=>$nodes],
        'selected'=>$selected,'query'=>'SELECT ID,TITLE,QTY FROM LAB_ITEMS WHERE QTY>0 ORDER BY ID ROWS 10',
        'service_action'=>'IBASE_STS_DATA_PAGES','service_argument'=>'IBASE_STS_IDX_PAGES','report_excerpt'=>f157PublicText($report),
        'index_existence_does_not_prove_query_plan'=>true];

} finally {
    if(is_resource($db)){f157Close($db);}
    if(is_resource($service)){f157Detach($service);}
    $removed=f157Cleanup($owned);
}

f157Report('fb157-table-statistics','Страницы и индекс: сопоставить физический отчёт с нужными товарами',$rows,$checks,$observations,$runtime,$owned,$removed);
Ожидаемый результат
Таблица — LAB_ITEMS
Страниц данных — 1
Индекс — IX_FB157_QTY
Глубина индекса — 1
Листовых страниц индекса — 1
Узлов в индексе — 2
Товаров с остатком больше нуля — 1
Показанный товар — Cable
Его остаток — 3

Самостоятельный CLI-пример с lab.php, README и синтетическим шаблоном. Выполняются настоящие вызовы Services API; собственные файлы базы освобождаются и удаляются после проверки. Создаются result.html и result.json.

Фактический HTML-результат · Данные результата JSON.

Ошибки, ограничения и применение

Числа страниц, глубины и узлов относятся только к показанному маленькому набору. Из существования индекса нельзя заключить, что оптимизатор выбрал его для SELECT: это проверяют планом и измерением конкретного запроса. Физическая статистика может содержать старые версии строк; она не заменяет транзакционно видимую выборку. API получает статистику всей выбранной базы, а не только LAB_ITEMS; фильтр по имени в этом уроке выделяет собственный блок текста. На большой базе такое действие не выполняют при каждом открытии каталога.

Измените условие задачи

В таблице стало 100000 товаров, а пользователю нужна первая десятка имеющихся в наличии. Нужно ли загружать весь каталог и сортировать его в PHP?

Ожидаемое решение

Нет. Сохраняйте нужные колонки, WHERE, ORDER BY и ограничение строк в SQL; отдельно проверьте план и подходящий индекс. Статистический отчёт используется для служебного анализа, а не вместо запроса экрана.

Неверный путь: сохранить причину отказа и отдельно проверить доступность сервиса

Сервис доступен, но в конфигурации указан неверный файл. Вызов статистики должен дать понятный отказ, а его сообщение не должно затеряться после следующего запроса. Используем заранее отсутствующее учебное имя, сохраняем диагностику немедленно, затем новым сервисным соединением получаем версию. Результат показывает: отказ конкретного пути и доступность экземпляра — разные проверки.

Путь проверки
  1. Проверить, что собственный тестовый путь отсутствует
  2. db_info(HDR_PAGES) → false, -901, предупреждение
  3. Сохранить безопасные признаки ошибки до очистки
  4. Драйвер уже инвалидировал ресурс → убрать ссылку без повторного detach
  5. Новый service_attach → версия доступна; файла по-прежнему нет

Сначала один шаг

$report = ibase_db_info($service, $serverDatabasePath, IBASE_STS_HDR_PAGES);
if ($report === false) {
    $errorCode = ibase_errcode();
    $errorMessage = ibase_errmsg();
    // Сохранить диагностику сейчас, до следующего вызова ibase.
}

Полный пример использует f157Capture, чтобы сохранить и ожидаемое предупреждение PHP. В его результате value — значение функции, code — код, warning и message_present — логические признаки. Настоящий текст ошибки может раскрывать путь к файлу; в опубликованном результате остаётся заранее известная причина учебного отказа. Эта причина установлена проверкой отсутствия файла, а не угадывается по одному числу -901.

Что получится после запуска

НаблюдениеРезультат
Результат db_info — falseда
Код отказа-901
Предупреждение сохраненода
Сообщение полученода
Понятная причинаУказанный учебный файл базы отсутствует
Новый сервис ответил версиейда
Отсутствующий файл не созданда
Создано учебных баз0
Неверный путь: сохранить причину отказа и отдельно проверить доступность сервисаСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__.'/lab.php';
$owned=[];$removed=[];$db=$service=null;
try {
    $config=f157Config();$service=f157Service();$runtime=f157Runtime($service);

    function statistics157($service,string $database,int $action,int $argument=0): string {
        $call=f157Capture(static fn()=>ibase_db_info($service,$database,$action,$argument));
        if (!is_string($call['value']) || $call['warning'] || $call['code']!==0) {
            f157Failure('Текст статистического отчёта не получен',$call);
        }
        return $call['value'];
    }
    function headerField157(string $report,string $pattern): string {
        if (preg_match($pattern,$report,$match)!==1) {
            throw new RuntimeException('Ожидаемое поле отсутствует в формате отчёта.');
        }
        return $match[1];
    }

    $missing='fb157_missing_0000000000.fdb';$missingPath=f157Path($missing);
    if (file_exists($missingPath)) {throw new RuntimeException('Ожидался заведомо отсутствующий учебный путь.');}
    $failure=f157Capture(static fn()=>ibase_db_info($service,$missing,IBASE_STS_HDR_PAGES));
    // Capture is complete before releasing the invalidated reference or requesting a new service.
    $safe=['returned_false'=>$failure['value']===false,'code'=>$failure['code'],
        'warning'=>$failure['warning'],'message_present'=>$failure['message_present']];
    if (!$safe['returned_false']) {throw new RuntimeException('Отсутствующий файл неожиданно дал отчёт.');}
    // In this native error branch interbase 3.0.1 already calls zend_list_delete.
    // Do not consume that resource again through ibase_service_detach.
    $service=null;
    $service=f157Service();
    $versionCall=f157Capture(static fn()=>ibase_server_info($service,IBASE_SVC_SERVER_VERSION));
    if (!is_string($versionCall['value']) || $versionCall['warning']) {f157Failure('Независимая проверка сервиса не прошла',$versionCall);}
    $serviceWorks=str_contains($versionCall['value'],'V5.0.4.');
    $closed=f157Detach($service);clearstatcache(true,$missingPath);
    $absent=!file_exists($missingPath);
    $rows=[['Результат db_info — false',$safe['returned_false']],['Код отказа',$safe['code']],
        ['Предупреждение сохранено',$safe['warning']],['Сообщение получено',$safe['message_present']],
        ['Понятная причина','Указанный учебный файл базы отсутствует'],['Новый сервис ответил версией',$serviceWorks],
        ['Отсутствующий файл не создан',$absent],['Создано учебных баз',count($owned)]];
    $checks=['missing_path_refused'=>$safe['returned_false'],'immediate_diagnostics_saved'=>$safe['code']===-901 && $safe['warning'] && $safe['message_present'],
        'fresh_service_works'=>$serviceWorks,'missing_file_not_created'=>$absent,'no_database_created'=>$owned===[],
        'fresh_service_closed'=>$closed===true && $service===null];
    $observations=['failure'=>$safe,'cause_in_this_owned_fixture'=>'Файл заранее проверен как отсутствующий.',
        'fresh_service_version'=>$versionCall['value'],'private_error_message_not_published'=>true,
        'same_code_can_require_other_investigation'=>true,'diagnostics_saved_before_cleanup'=>true];

} finally {
    if(is_resource($db)){f157Close($db);}
    if(is_resource($service)){f157Detach($service);}
    $removed=f157Cleanup($owned);
}

f157Report('fb157-missing-path','Неверный путь: сохранить причину отказа и отдельно проверить доступность сервиса',$rows,$checks,$observations,$runtime,$owned,$removed);
Ожидаемый результат
Результат db_info — false — да
Код отказа — -901
Предупреждение сохранено — да
Сообщение получено — да
Понятная причина — Указанный учебный файл базы отсутствует
Новый сервис ответил версией — да
Отсутствующий файл не создан — да
Создано учебных баз — 0

Самостоятельный CLI-пример с lab.php, README и синтетическим шаблоном. Выполняются настоящие вызовы Services API; собственные файлы базы освобождаются и удаляются после проверки. Создаются result.html и result.json.

Фактический HTML-результат · Данные результата JSON.

Ошибки, ограничения и применение

Код -901 не является универсальным синонимом «файла нет»: в другой ситуации нужны конкретный текст, серверный путь и права процесса. Для этой нативной ошибки исходник interbase 3.0.1 вызывает IBASE_SVC_ERROR и уже уменьшает счётчик ресурса. Поэтому пример убирает свою ссылку через $service = null без повторного ibase_service_detach, затем открывает новое соединение. Это правило разобранной ветки драйвера, а не обещание, что всякое значение false автоматически закрывает любой ресурс. Успешный ответ версии доказывает доступность сервиса в этой попытке, но не исправляет путь и не подтверждает доступ ко всем базам. Никакой пустой файл вместо отсутствующей базы не создаётся.

Клиент достиг сервера Firebird, но получил I/O-ошибку открытия файла после переноса базы на другую Windows-машину. Исходная ситуация на Stack Overflow. Показываем разные проверки: доступен сервис, существует ли нужная база и может ли именно процесс Firebird открыть путь. Сообщение о файле не объявляем ошибкой сети. Для учебного отказа намеренно выбирается отсутствующий файл внутри собственной области, затем выполняется независимый запрос по правильному пути.

Измените условие задачи

Версия сервера получается, а рабочая база не открывается. Какие проверки следуют дальше?

Ожидаемое решение

Проверьте, какой путь видит именно сервер, существует ли файл, доступны ли каталог и файл его учётной записи, подходит ли формат базы. Сохраните точную локальную диагностику, не выводя секреты посетителям. Проверка сетевого порта не заменяет эти проверки.

Четыре карточки Services API

ЗадачаФункция
Создать сервисное соединение с экземпляром Firebird.ibase_service_attach
Узнать версию, платформу и доступные сведения экземпляра.ibase_server_info
Получить статистический отчёт по конкретной базе.ibase_db_info
Завершить владение сервисным соединением.ibase_service_detach

От диагностики к проверяемому восстановлению

Теперь у служебной проверки есть конкретный итог: известен экземпляр, прочитаны свойства выбранного файла, статистика отделена от строк SELECT, а отказ неверного пути сохранён до следующего вызова. Следующий шаг — резервная копия, восстановление в новый файл и сверка данных.

Не объявляйте базу исправной только потому, что удалось получить заголовок. Не объявляйте индекс эффективным только потому, что он присутствует в статистике. Каждая проверка отвечает на свой вопрос: приложение проверяет нужные строки, план — способ выполнения запроса, восстановление — пригодность конкретной копии. Остальные операции администрирования и полный справочник Firebird продолжаются в следующих разделах.

Функции и методы этой темы

Откройте карточку, чтобы увидеть договор функции, примеры, ограничения и связанные инструменты.

ibase_service_attachСоздать сервисное соединение с экземпляром Firebird.

Исторический PHP 5 / PHP 7 < 7.4; отдельный FirebirdSQL 3.0.1 · interbase

ibase_service_attach(string $host, string $dba_username, string $dba_password): resource|false

Чтобы узнать версию экземпляра, запросить статистику базы или создать резервную копию, открывают соединение с менеджером сервисов. Оно даёт доступ к служебным операциям Firebird. Обычный $db из ibase_connect нужен для SQL; новый $service — для Services API. Один ресурс не подставляется вместо другого.

Аргумент / результатКонтракт
host: stringИмя или IP хоста. Порт задаётся через /, например db-host/3050; без указания порта используется стандартный 3050. Здесь нет имени файла базы. Поддерживаемый драйвер сам добавляет :service_mgr.
dba_username: stringИмя допустимого пользователя сервера. Название аргумента не означает, что всегда требуется SYSDBA: доступность отдельной операции определяется полномочиями.
dba_password: stringПароль выбранного пользователя. Получайте его из конфигурации окружения; не выводите в публичный отчёт и не храните прямо в опубликованном примере.
resource | falseСервисный handle при успехе, false при отказе. Проверяйте результат до server_info/db_info, сразу сохраняйте диагностику. Неправильные PHP-типы также могут вызвать ошибку PHP.
Локальный режим примеровПодключение к частному embedded-движку имеет отдельную проверенную настройку в README; оно не доказывает наличие сетевого сервера на localhost:3050. Удалённый host/port и локальное подключение — разные способы адресации.

Узнать, какой экземпляр обслуживает приложение

$service = ibase_service_attach($host, $user, $password);
if ($service === false) {
    $code = ibase_errcode();
    $message = ibase_errmsg();
    throw new RuntimeException("Сервисное подключение не создано");
}
try {
    $version = ibase_server_info($service, IBASE_SVC_SERVER_VERSION);
    if ($version === false) {
        $code = ibase_errcode();
        $message = ibase_errmsg();
        $service = null; // Ошибочное состояние не передаём в повторный detach.
        throw new RuntimeException("Версия не получена");
    }
    echo $version;
} finally {
    if ($service !== null) { ibase_service_detach($service); $service = null; }
}

В полном паспорте экземпляра версия получается реальным вызовом, а не записывается заранее строкой. На удалённом сервере host/port определяет, к какому экземпляру вы обратились. Доступный SQL-сайт и другой установленный локальный Firebird могут оказаться разными экземплярами — сравнивайте адрес и полученные сведения.

Подготовить служебное соединение для копии базы

$service = ibase_service_attach($host, $user, $password);
if ($service === false) { throw new RuntimeException("Сервис недоступен"); }
// Вызвать backup для конкретного серверного пути базы.
// Дождаться результата операции и проверить восстановление.
// Только после завершения работы — service_detach и $service = null.

Это начало проверяемого резервного копирования. Успешный attach не означает, что файл базы существует, что хватает прав на него или что будущая копия уже готова. Доступ к сервису проверяется отдельно от доступа к конкретному пути. На одном service handle не запускайте конкурирующие операции; для независимых заданий разделяйте владение соединениями.

Историческая документация относится к PHP 5 / PHP 7 ниже 7.4. В этой главе эти функции предоставляет отдельное поддерживаемое расширение FirebirdSQL 3.0.1. Оно не реализует все новые возможности Services API современных версий Firebird; список принимаемых аргументов нельзя расширять по аналогии с другим клиентом.

ibase_server_infoУзнать версию, платформу и доступные сведения экземпляра.

Исторический PHP 5 / PHP 7 < 7.4; отдельный FirebirdSQL 3.0.1 · interbase

ibase_server_info(resource $service_handle, int $action): string; фактический тип зависит от action

server_info читает сведения о сервисном экземпляре: версию и реализацию, расположение служебных каталогов либо структурированные сведения о пользователях и подключениях. Выбирайте только данные, нужные для диагностики; внутренние пути и список пользователей не являются содержимым публичной страницы.

ЭлементКонтракт
service_handleДействующий ресурс из ibase_service_attach. Это не SQL-соединение к файлу базы.
action: intОдна константа запроса информации. Это выбор операции, а не маска из нескольких IBASE_SVC_* через |. Для нескольких сведений нужны отдельные вызовы.
РезультатИсторическая сигнатура показывает string, но раздел Return Values говорит о разных типах. В драйвере 3.0.1 версию/пути возвращают строки, списки — массивы; при отказе возможен false. Проверяйте тип, подходящий конкретной action.
ОтказСнимите ibase_errcode/ibase_errmsg и предупреждение сразу. В нативной ветке ошибки драйвер 3.0.1 уже выполняет zend_list_delete через IBASE_SVC_ERROR. Для такого ресурса уберите свою ссылку без повторного detach и откройте новое соединение; другие причины false нужно различать по диагностике. Не продолжайте команды на повреждённом состоянии вслепую.
КонстантаЧто возвращает / как понимать
IBASE_SVC_SERVER_VERSIONСтрока версии экземпляра, а не версии PHP или расширения interbase.
IBASE_SVC_IMPLEMENTATIONСтрока реализации и платформы Firebird.
IBASE_SVC_GET_ENVКаталог установки/окружения экземпляра. Это серверный путь.
IBASE_SVC_GET_ENV_LOCKПуть служебного каталога блокировок. Не путь выбранной базы.
IBASE_SVC_GET_ENV_MSGПуть к служебным сообщениям Firebird.
IBASE_SVC_USER_DBPATHПуть базы безопасности пользователей; не прикладной .fdb.
IBASE_SVC_SVR_DB_INFOМассив: attachments, databases и элементы с именами подключённых баз. Область видимости зависит от процесса и архитектуры сервера; это не полный поиск всех файлов .fdb на диске.
IBASE_SVC_GET_USERSМассив описаний пользователей, насколько это разрешает выбранная версия и полномочия. Драйвер разбирает user_name, first_name, middle_name, last_name, user_id, group_id; наличие отдельных полей зависит от ответа.

Версия и платформа в служебном отчёте

$version = ibase_server_info($service, IBASE_SVC_SERVER_VERSION);
if (!is_string($version)) { throw new RuntimeException("Версия не получена"); }
$implementation = ibase_server_info($service, IBASE_SVC_IMPLEMENTATION);
if (!is_string($implementation)) { throw new RuntimeException("Платформа не получена"); }
echo $version, "\n", $implementation;

Контекст — паспорт перед диагностикой базы. Даже в коротком фрагменте отказ первого вызова прерывает цепочку; самостоятельный файл дополнительно сохраняет его диагностику до следующего. Не выдавайте строку серверной версии за версию драйвера PHP: phpversion("interbase") отвечает на другой вопрос.

Проверить форму ответа о подключениях

$info = ibase_server_info($service, IBASE_SVC_SVR_DB_INFO);
if (!is_array($info)) { throw new RuntimeException("Сведения не получены"); }
echo "Подключений в области этого сервиса: ", $info["attachments"], "\n";
echo "Баз в области этого сервиса: ", $info["databases"];

Такой отчёт помогает разбирать соединения одного экземпляра, но не заменяет мониторинг каждой базы. В Classic сервис работает в отдельном процессе и может не видеть SQL-подключения других процессов. MON$ATTACHMENTS относится к одной выбранной базе; эти показатели нельзя сравнивать как одинаковые. Сначала выясните режим и адрес экземпляра, а не меняйте конфигурацию, чтобы добиться ожидаемой цифры. Физические свойства конкретного .fdb смотрят через ibase_db_info.

ibase_db_infoПолучить статистический отчёт по конкретной базе.

Исторический PHP 5 / PHP 7 < 7.4; отдельный FirebirdSQL 3.0.1 · interbase

ibase_db_info(resource $service_handle, string $db, int $action, int $argument = 0): string; выбранный драйвер также имеет ветви true/false

db_info запускает получение статистического отчёта по конкретной базе через Services API. Он полезен, когда нужно узнать формат и размер страницы базы или увидеть страницы данных и индексов. Это служебная статистика, а не SELECT строк заказов и не команда ремонта.

Аргумент / результатКонтракт
service_handleОткрытое сервисное соединение. Право подключиться к сервису само по себе не гарантирует доступ к указанному файлу.
db: stringПуть/поддерживаемое имя базы с точки зрения выбранного сервера. Путь на машине браузера или PHP-клиента не становится серверным автоматически. В локальном уроке это собственный файл внутри проверенного каталога.
action: intОдин из пяти распознаваемых IBASE_STS_* ниже. В выбранной реализации первая action проверяется как отдельное значение.
argument: int = 0Дополнительные биты параметров статистики. Драйвер соединяет action | argument после проверки action. Для данных вместе с индексами передавайте DATA_PAGES третьим аргументом и IDX_PAGES четвёртым, не составную action наугад.
РезультатPHP Manual содержит только сигнатуру string и отмечает отсутствие остального описания. Драйвер 3.0.1 запускает статистику и собирает строки отчёта; возвращает текст, при отсутствии строк может вернуть true, при отказе — false. Значения проверяются вместе с диагностикой и ожидаемым содержимым отчёта.
ОшибкиНераспознанная action, недоступный путь, права или отказ сервиса. Сообщение может содержать частные пути; сохраните его для локальной диагностики, не печатайте без обработки на публичной странице. В разобранном отказе отсутствующего файла interbase 3.0.1 уже инвалидирует ресурс: после сохранения диагностики присвойте $service = null без повторного service_detach. Подробный порядок показан в сценарии неверного пути.
КонстантаНазначение и предел
IBASE_STS_HDR_PAGESЗаголовок базы: размер страницы, ODS, диалект и служебные сведения. Отдельный лёгкий запрос перед подробной статистикой.
IBASE_STS_DATA_PAGESСтатистика страниц данных таблиц. Она не равна числу строк текущего SELECT.
IBASE_STS_IDX_PAGESСтатистика страниц индексов: структура и заполнение. Это не доказательство того, что конкретный SELECT использовал индекс.
IBASE_STS_SYS_RELATIONSВключение системных отношений в статистику; при разборе прикладной таблицы обычно не требуется.
IBASE_STS_DB_LOGИсторический флаг страниц журнала из API InterBase. Не путайте его с чтением обычного firebird.log. Константа присутствует в драйвере; применимость зависит от версии движка, а эти уроки её работу не утверждают.

Узнать формат файла перед восстановлением

$header = ibase_db_info($service, $serverDatabasePath, IBASE_STS_HDR_PAGES);
if (!is_string($header)) { throw new RuntimeException("Текст заголовка не получен"); }
echo $header; // Только в закрытом служебном интерфейсе или локальной консоли.

В полном примере заголовка из реального отчёта выделяются понятные показатели. ODS — формат хранения файла, версия Firebird — версия обслуживающего движка; эти строки решают разные задачи. Размер страницы описывает блок размещения данных, а не размер одной строки и не число товаров.

Данные и индекс одной учебной таблицы

$stats = ibase_db_info(
    $service, $serverDatabasePath, IBASE_STS_DATA_PAGES, IBASE_STS_IDX_PAGES
);
if (!is_string($stats)) { throw new RuntimeException("Статистика не получена"); }
// Найти блок собственной таблицы и её индекса, сохранить закрытый отчёт.

В сценарии каталога создаются ограниченные данные и собственный индекс, а результат сопоставляется с реальным запросом нужных товаров. У этого PHP API нет параметра имени таблицы: действие относится к базе. Не запускайте его на большой рабочей базе ради каждого HTTP-запроса. Статистика физических страниц не учитывает видимость транзакций так же, как SELECT; в ней могут отражаться старые версии. План запроса и время исполнения изучаются отдельно.

Поля текстового отчёта и их размещение могут меняться между версиями. Не превращайте полный вывод в постоянный машинный протокол. В уроке извлекаются только ожидаемые подписи и сохраняется исходный безопасный фрагмент; неподходящий формат — явный отказ анализа. Разъяснение показателей приведено в официальном руководстве gstat. Здесь статистика читается через сервис, без запуска внешнего процесса gstat.

ibase_service_detachЗавершить владение сервисным соединением.

Исторический PHP 5 / PHP 7 < 7.4; отдельный FirebirdSQL 3.0.1 · interbase

ibase_service_detach(resource $service_handle): bool

После завершения служебной задачи соединение с менеджером сервисов нужно освободить. Оно отличается от SQL-соединения: для обычного $db используется ibase_close, для $service — service_detach. Освобождение ресурса не подтверждает бизнес-транзакцию и не доказывает готовность запущенной резервной копии.

ЭлементКонтракт
service_handleРесурс, возвращённый ibase_service_attach. Не передавайте сюда query/result/db вместо сервиса.
boolПо официальному описанию true означает успех, false — отказ. Неверный PHP-тип может дать ошибку PHP. В выбранном драйвере вызов также связан с удалением ссылки на ресурс; собственную переменную после завершения следует обнулить.
ПорядокПосле успешной задачи прочитать результат, один раз освободить действующий сервис и убрать свою ссылку. Если нативная ошибка 3.0.1 уже прошла через IBASE_SVC_ERROR, повторный detach не выполняется: сохраните диагностику, уберите ссылку и создайте новый сервис.

Паспорт получен — соединение больше не нужно

try {
    $version = ibase_server_info($service, IBASE_SVC_SERVER_VERSION);
    if ($version === false) {
        $code = ibase_errcode();
        $message = ibase_errmsg();
        $service = null; // Не закрываем повторно после нативной ошибки.
        throw new RuntimeException("Версия не получена");
    }
} finally {
    if ($service !== null) {
        $closed = ibase_service_detach($service);
        $service = null;
        if ($closed !== true) { throw new RuntimeException("Сервис не освобождён"); }
    }
}

Это успешная ветка паспорта экземпляра. При получении ошибки сначала снимите её код/сообщение, а затем запускайте очистку: последующие вызовы могут заменить диагностическое состояние. Полная программа учитывает принадлежность сервиса и не использует его после закрытия.

Отдельная проверка после неверного пути

// При отказе db_info: немедленно сохранить диагностику.
// В нативной ветке отказа 3.0.1 ресурс уже инвалидирован.
$service = null; // Без повторного ibase_service_detach.
$nextService = ibase_service_attach($host, $user, $password);
if ($nextService === false) { throw new RuntimeException("Новый сервис не создан"); }
try {
    // Независимый запрос по правильному пути.
} finally {
    ibase_service_detach($nextService);
    $nextService = null;
}

В сценарии отсутствующей базы отказ не маскируется успешной следующей командой: сведения о нём сохранены, а новая попытка имеет отдельный ресурс. Для backup/restore действует ещё одно правило: сначала дождаться фактического завершения задания и проверить данные, затем отсоединяться. Сам true от detach не означает, что архив можно восстановить.

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

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

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

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

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