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

Дерево руководства
Глава 66 · После запросов, транзакций и диагностики Firebird
Firebird Services API: паспорт экземпляра, сведения о базе и диагностика пути
Паспорт экземпляра, формат файла базы, статистика собственных данных и индекса, независимая диагностика неверного пути: четыре служебных сценария.
Сначала понять экземпляр, затем проверять файл базы
Перед копированием или разбором сбоя базы нужно знать, с каким экземпляром Firebird вы работаете и какой файл он обслуживает. В этой главе вы получите настоящий паспорт экземпляра, узнаете формат и размер страницы учебной базы, сопоставите физическую статистику с выборкой товара и разберёте намеренно неверный путь. Каждый результат выводится в понятный HTML-отчёт и JSON; скрытые данные подключения остаются в локальной конфигурации.
- SQL-соединение $db → конкретный файл базы → SELECT товаров
- Сервисное соединение $service → менеджер экземпляра Firebird
- server_info($service, VERSION) → версия экземпляра
- db_info($service, путь, HDR_PAGES) → свойства выбранной базы
- Завершить действие → 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 и расширения — четыре разных уровня окружения.
- f157Service → локальный service handle
- server_info(VERSION) → WI-V5.0.4.1812 Firebird 5.0
- server_info(IMPLEMENTATION) → Firebird/Windows/AMD/Intel/x64
- PHP_VERSION и phpversion(interbase) → сведения PHP
- 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 |
| Версия PHP | 8.2.35 |
| Версия interbase | 3.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. Так видно, какие сведения относятся к хранению, а какие — к предметным данным.
- Создать собственную учебную базу из seed
- db_info(HDR_PAGES) → текст заголовка
- Извлечь Page size, ODS version, Database dialect
- Обычный SELECT → Cable, остаток 3
- Закрыть соединения → удалить только свою базу
Сначала один шаг
$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 |
| Формат файла ODS | 13.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 с фильтром в базе. В индексе будут два узла, а на экране — один товар: физическая структура и результат условия отвечают на разные вопросы.
- Два синтетических товара → собственный индекс IX_FB157_QTY
- WHERE QTY>0 ORDER BY ID ROWS 10 → один товар Cable
- DATA_PAGES + IDX_PAGES → статистический текст
- LAB_ITEMS: 1 страница; IX_FB157_QTY: 2 узла
- Сопоставить показатели, не объявляя план запроса измеренным
Сначала один шаг
$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; отдельно проверьте план и подходящий индекс. Статистический отчёт используется для служебного анализа, а не вместо запроса экрана.
Неверный путь: сохранить причину отказа и отдельно проверить доступность сервиса
Сервис доступен, но в конфигурации указан неверный файл. Вызов статистики должен дать понятный отказ, а его сообщение не должно затеряться после следующего запроса. Используем заранее отсутствующее учебное имя, сохраняем диагностику немедленно, затем новым сервисным соединением получаем версию. Результат показывает: отказ конкретного пути и доступность экземпляра — разные проверки.
- Проверить, что собственный тестовый путь отсутствует
- db_info(HDR_PAGES) → false, -901, предупреждение
- Сохранить безопасные признаки ошибки до очистки
- Драйвер уже инвалидировал ресурс → убрать ссылку без повторного detach
- Новый 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; фактический тип зависит от actionserver_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/falsedb_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. Примеры и объяснения написаны для этого руководства; источники и обсуждения доступны в каждой главе.


