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

Дерево руководства
Глава 81 · После параметризованных запросов и транзакций Firebird
Firebird fbird_*: живое подключение, свежий остаток и события после commit
Реальный HTTP с повторным подключением, свежие остатки, commit/rollback, синхронное ожидание и прекращение callback.
Разделить готовый канал, свежие данные и уведомление
В существующем PHP-проекте встречаются fbird_pconnect и функции событий. Чтобы сопровождать такой код, надо видеть три разных вещи: сколько живёт канал к базе, когда изменение действительно подтверждено и какой запрос покажет новое состояние. В этой главе первый HTTP-ответ содержит Cable 3, второй — Cable 7 при прежнем подключении. Затем отдельные процессы показывают отмену 99, сохранение 7, уведомление и поздний остаток 11 после снятия подписки.
- PHP worker может сохранить подключение между HTTP-запросами
- Каждый запрос выбирает нужные данные в своей завершённой транзакции
- Другой процесс: UPDATE Cable 7 + POST_EVENT → commit
- Событие сообщает имя, а не переносит товар
- Получатель делает новый ограниченный SELECT → Cable 7
- Освобождение handler прекращает подписку, но не отменяет commit
Представьте рабочее место с готовой телефонной линией: линию можно использовать снова, но ответы о товаре надо получать заново. Событие похоже на звонок «остаток изменился». Эти сравнения объясняют назначение, а не устройство памяти: PHP-переменные запроса, серверное подключение и ресурс callback — разные объекты владения. Наличие канала само не подписывает на события, а событие само не обновляет страницу браузера.
| Что называется так | Где живёт и зачем нужно |
|---|---|
| PHP worker | Процесс, исполняющий запросы; persistent-подключение повторно используется в его области. |
| Connection / attachment | Канал к одной базе, с которым связано серверное состояние. CURRENT_CONNECTION помогает сравнить его между запросами. |
| Transaction | Граница чтения/изменения. commit подтверждает, rollback отменяет неподтверждённые изменения. |
| Event handler | Регистрация PHP-callback на точное имя события; отдельный ресурс от connection. |
| POST_EVENT | Команда PSQL в серверном блоке/триггере. Доставка интересующимся клиентам связана с commit. |
Имена fbird_* здесь являются aliases interbase, а не новой библиотекой и не миграцией PDO. Для первого знакомства сначала нужны соединение, параметры и строки запроса, явные commit и rollback и аргументы функции и return. В минимальных фрагментах ниже уже есть открытый $db; его создание и завершение полностью включены в самостоятельные архивы.
Где исполняется PHP и откуда берётся результат
Запуск производится из отдельного TCP-профиля firebird164: PHP 8.2.35 NTS x64, interbase 3.0.1, Firebird 5.0.4. Файлы данных и security принадлежат только этой лаборатории. SQL-порт 127.0.0.1:9764 и вспомогательный порт событий 9765 не требуют открытия входа из интернета. В README есть установка и команда запуска. Основной файл example.php создаёт одну свою базу с Cable 3/Clamp 0 и маркером владельца; после наблюдений дочерние процессы прекращаются, а собственная база и временный FBK удаляются.
| Файлы проекта | Задача |
|---|---|
| example.php, lab.php, README.txt, seed.* | Самостоятельная подготовка своего каталога, ограничения владельца, видимые HTML/JSON и очистка. |
| http-lab.php, http-router.php | Только в HTTP-проекте:1 worker,2 реальных GET, один UPDATE между ними, private receipt и тела ответов. |
| events-lab.php, events-child.php | В трёх проектах событий: listener + sender, готовность/ACK через ограниченные служебные файлы, реальные POST_EVENT и aliases. |
| result.html, result.json | Полезные наблюдения: названия товаров, остатки, смысл сравнения подключения и подписки. |
| metrics.json и process receipts | Локальные проверки/доказательства остановки; секреты и произвольные URL не используются как вход проекта. |
Большой код запуска не надо изучать раньше небольшого вызова. Пройдите нужный сценарий сверху вниз: задача, схема с конкретными значениями, короткая запись, результат, затем полный проект. HTTP-ответы показывают реальное межзапросное поведение, а не два вызова в одном CLI. События принимает другой PHP-процесс, а не напечатанная заранее строка. Внешние сроки ограничивают именно лабораторию, не добавляют несуществующий timeout в API.
Два HTTP-запроса: одно подключение и два разных остатка
Страница склада открывается повторно. Нам хочется использовать готовое соединение, но показать новый остаток. Первый запрос прочитает Cable = 3. Между ответами другой участник сохранит 7. Второй запрос попадёт в тот же PHP worker и должен получить 7 новым SELECT. В отчёте одновременно видны полезные данные и совпадение серверного номера подключения.
- GET /stock №1 → worker P → fbird_pconnect → attachment A
- Новая транзакция чтения → Cable: 3 → commit → HTTP 200
- Отдельное подключение parent: UPDATE ID = 1, QTY = 7 → commit
- GET /stock №2 → тот же P → pconnect с теми же параметрами → A
- Новая транзакция чтения → Cable: 7 → HTTP 200
- Worker остановлен; только потом удаляется собственная учебная база
Первый понятный вызов
$db = fbird_pconnect($database, $user, $password, 'UTF8', 0, 3);
if ($db === false) {
throw new RuntimeException('Подключение не открыто');
}
// Получить текущий остаток нужно отдельным SQL-запросом.$database, $user и $password содержат настройки, а не сам товар. Строка UTF8 задаёт кодировку обмена; 0 оставляет выбор буферов серверу, 3 выбирает SQL-диалект. Результат $db — ресурс канала к базе. Он не является массивом строк. В полном http-router.php каждый запрос открывает собственную короткую read-only транзакцию, выбирает ID, TITLE, QTY только для ID = 1 и явно завершает её. Переменные первого запроса не продолжают жить во втором.
Что получится после запуска
| Наблюдение | Результат |
|---|---|
| Первый HTTP-ответ | Cable / остаток 3 |
| Подтверждённая запись между запросами | Cable / остаток 7 |
| Второй HTTP-ответ | Cable / остаток 7 |
| Один и тот же PHP worker | да |
| Один CURRENT_CONNECTION | да |
| Реальных HTTP-запросов | 2 |
| HTTP worker остановлен | да |
Полный самостоятельный проект и архив
<?php
declare(strict_types=1);
require __DIR__.'/lab.php';
$owned=[];$removed=[];$db=$service=null;
try {
$config=f164Config();$service=f164Service();$runtime=f164Runtime($service);
require __DIR__.'/http-lab.php';
$database=f164CreateDatabase($service,$owned,'_stock_reuse');
$session=f164HttpSession($database);$first=$session['responses'][0];$second=$session['responses'][1];
$before=$first['observed']['item'];$after=$second['observed']['item'];
$sameWorker=$first['pid']===$second['pid'];$sameConnection=$first['attachment_id']===$second['attachment_id'];
$rows=[['Первый HTTP-ответ',$before['title'].' / остаток '.$before['qty']],
['Подтверждённая запись между запросами','Cable / остаток 7'],['Второй HTTP-ответ',$after['title'].' / остаток '.$after['qty']],
['Один и тот же PHP worker',$sameWorker],['Один CURRENT_CONNECTION',$sameConnection],
['Реальных HTTP-запросов',$session['http_requests']],['HTTP worker остановлен',$session['stopped']]];
$checks=['first_item'=>$before===['id'=>1,'title'=>'Cable','qty'=>3],
'fresh_second_item'=>$after===['id'=>1,'title'=>'Cable','qty'=>7],
'same_worker'=>$sameWorker,'same_attachment'=>$sameConnection,
'positive_attachment'=>$first['attachment_id']>0,'one_business_update'=>$session['business_updates']===1,
'two_real_http_requests'=>$session['http_requests']===2&&$session['proof']['statuses']===[200,200],
'requests_finished_transactions'=>$first['transaction_finished']===true&&$second['transaction_finished']===true,
'alias_used'=>$first['persistent_api']==='fbird_pconnect'&&$second['persistent_api']==='fbird_pconnect',
'http_worker_stopped'=>$session['stopped']===true];
$observations=['http_session'=>$session,'same_worker'=>$sameWorker,'same_attachment'=>$sameConnection,
'connection_cache_is_not_result_cache'=>true,'parent_updates_exactly_one_owned_row'=>true];
} finally {
if(is_resource($db)){f164Close($db);}
if(is_resource($service)){f164Detach($service);}
$removed=f164Cleanup($owned);
}
f164Report('fb164-http-stock-reuse','Два HTTP-запроса: одно подключение и два разных остатка',$rows,$checks,$observations,$runtime,$owned,$removed);
Ожидаемый результат
Первый HTTP-ответ — Cable / остаток 3
Подтверждённая запись между запросами — Cable / остаток 7
Второй HTTP-ответ — Cable / остаток 7
Один и тот же PHP worker — да
Один CURRENT_CONNECTION — да
Реальных HTTP-запросов — 2
HTTP worker остановлен — да
Архив содержит все PHP-файлы и README для отдельной TCP-лаборатории. Выполните example.php из папки проекта: result.html и result.json покажут фактические товары и наблюдения. Создаётся и удаляется одна собственная синтетическая база; чужие БД и учётные записи не затрагиваются. Самостоятельный запуск требует разрешённого PHP proc_open и подготовленного приватного сервера.
Открыть результат программы · Открыть фактические наблюдения JSON.
Границы, ошибки и применение
Два вызова pconnect внутри одного CLI-запуска не доказывают переиспользование между HTTP-запросами. Здесь запросов действительно два, worker один, а CURRENT_CONNECTION читается самой базой. В PHP-FPM запросы могут попасть в разные workers — тогда такое совпадение не обещано. Повторное подключение не сохраняет согласованный снимок данных: устаревшая незавершённая snapshot-транзакция способна помешать увидеть обновление. Поэтому перед следующим запросом заканчивают транзакции и убирают свой USER_SESSION-контекст. Этот пример не измеряет ускорение и не подбирает размер пула.
Измените требование
Сделайте второе сохранённое значение равным 12. Какие наблюдения должны измениться, а какие сравнения останутся прежними?
Вариант решения
В http-lab.php изменяется единственный UPDATE между запросами, в example.php — ожидаемый второй товар и текст таблицы. Два HTTP 200, одинаковые PID/CURRENT_CONNECTION и факт остановки worker остаются проверками того же механизма. SQL не следует заменять выдачей сохранённого первого массива.
Дождаться изменения и прочитать сохранённый остаток
Обработчик должен обновить известную карточку товара после работы другого процесса. Он ждёт FB164_CHANGED, затем получает Cable = 11 обычным запросом. Имя сигнала сообщает, что данные могли поменяться; само число 11 появляется только в результате SELECT. Второй товар Clamp остаётся с нулевым остатком и позволяет увидеть, что запись была адресной.
- Listener закончил прежнюю транзакцию и вызывает fbird_wait_event
- Sender один раз сохраняет Cable: 3 → 11
- POST_EVENT FB164_CHANGED + commit → имя доходит listener
- Новый SELECT ID IN (1,2) ORDER BY ID → Cable 11, Clamp 0
- ACK подтверждает пробуждение; два процесса завершаются
Первый понятный вызов
$event = fbird_wait_event($db, 'FB164_CHANGED');
if ($event === false) {
throw new RuntimeException('Ожидание не завершилось успешно');
}
$result = fbird_query($db, 'SELECT TITLE, QTY FROM LAB_ITEMS WHERE ID = ?', 1);Вызов wait останавливает дальнейшее исполнение этого PHP-процесса до события. $db — его открытое соединение, FB164_CHANGED — точное имя подписки. Следующая строка SQL запускается после возврата функции. Параметр 1 выбран самим приложением: событие не принесло ID. Полная программа проверяет SELECT, извлекает строки, освобождает ресурс и завершает транзакцию.
Что получится после запуска
| Наблюдение | Результат |
|---|---|
| До операции | 1 / Cable / остаток 3 |
| Полученное имя | FB164_CHANGED |
| Новый SELECT | 1 / Cable / остаток 11 |
| Предметных UPDATE | 1 |
| Пробуждение подтверждено | да |
| Оба процесса остановлены | да |
Полный самостоятельный проект и архив
<?php
declare(strict_types=1);
require __DIR__.'/lab.php';
$owned=[];$removed=[];$db=$service=null;
try {
$config=f164Config();$service=f164Service();$runtime=f164Runtime($service);
require __DIR__.'/events-lab.php';
$database=f164CreateDatabase($service,$owned,'event_wait');
$events=f164EventScenario($database,'wait');
$listener=$events['listener'];$sender=$events['sender'];
$items=$listener['items'];
$rows=[['До операции','1 / Cable / остаток 3'],['Полученное имя',$listener['event']],
['Новый SELECT',$items[0]['ID'].' / '.$items[0]['TITLE'].' / остаток '.$items[0]['QTY']],
['Предметных UPDATE',$sender['business_updates']],['Пробуждение подтверждено',$sender['ack_received']],
['Оба процесса остановлены',$events['all_children_stopped']]];
$checks=['event_name'=>$listener['event']==='FB164_CHANGED',
'fresh_selected_rows'=>$items===[['ID'=>1,'TITLE'=>'Cable','QTY'=>11],['ID'=>2,'TITLE'=>'Clamp','QTY'=>0]],
'single_business_update'=>$sender['business_updates']===1,'wakeup_ack'=>$sender['ack_received']===true,
'no_native_error'=>$listener['error_code']===false&&$listener['error_message_present']===false,
'children_stopped'=>$events['all_children_stopped']===true,'two_children'=>$events['process_count']===2];
$observations=['events'=>$events,'initial_items'=>[['ID'=>1,'TITLE'=>'Cable','QTY'=>3],['ID'=>2,'TITLE'=>'Clamp','QTY'=>0]],
'event_name_is_not_payload'=>true,'repeated_wakeup_is_not_repeated_business_write'=>true];
} finally {
if(is_resource($db)){f164Close($db);}
if(is_resource($service)){f164Detach($service);}
$removed=f164Cleanup($owned);
}
f164Report('fb164-event-wait','Дождаться изменения и прочитать сохранённый остаток',$rows,$checks,$observations,$runtime,$owned,$removed);
Ожидаемый результат
До операции — 1 / Cable / остаток 3
Полученное имя — FB164_CHANGED
Новый SELECT — 1 / Cable / остаток 11
Предметных UPDATE — 1
Пробуждение подтверждено — да
Оба процесса остановлены — да
Архив содержит все PHP-файлы и README для отдельной TCP-лаборатории. Выполните example.php из папки проекта: result.html и result.json покажут фактические товары и наблюдения. Создаётся и удаляется одна собственная синтетическая база; чужие БД и учётные записи не затрагиваются. Самостоятельный запуск требует разрешённого PHP proc_open и подготовленного приватного сервера.
Открыть результат программы · Открыть фактические наблюдения JSON.
Границы, ошибки и применение
У wait_event нет параметра timeout. Наш parent ограничивает весь дочерний сценарий 20 секундами, при проблеме завершает только свои процессы, а удаление БД допускает после их остановки. Готовность listener перед блокирующим вызовом не равна подтверждению native-регистрации. Поэтому sender сохраняет остаток ровно один раз, затем ограниченно повторяет лишь уведомление о уже сохранённом состоянии до ACK; это не повтор операции списания. У повторных сигналов есть предел 100 и срок 12 секунд в child. В обычном веб-запросе бесконечно ждать нельзя: для длительного listener нужен отдельный worker с политикой остановки и повторного чтения после соединения.
Клиентам нужен актуальный экран без постоянной выгрузки таблиц на миллионы строк. Обсуждение задачи на Stack Overflow. Сигнал определяет момент перечитывания, а запрос ограничивает поля и товары. Для надёжной фоновой работы отдельно нужны сохранённые задания и обработка перезапуска.
Измените требование
Если нужно показывать только Cable, обязательно ли выгружать весь каталог после каждого сигнала?
Вариант решения
Нет. Получатель уже знает свою задачу. Он выбирает нужные поля WHERE ID = 1; если отображается страница товаров, применяет её фильтр, сортировку и ограничение на сервере. Событие определяет момент обновления, SQL определяет объём полезных данных.
Показывать подтверждённый остаток, а не отменённую попытку
Оператор ошибся: ввёл 99, затем отменил действие. Соседний экран должен оставить подтверждённые 3. После корректного сохранения 7 экран получает уведомление и перечитывает 7. Сравним именно две границы транзакций, чтобы не принимать вызов POST_EVENT за доказательство успешного заказа.
- Callback зарегистрирован, Cable = 3
- UPDATE 99 и POST_EVENT находятся в одной транзакции → rollback
- SELECT по-прежнему показывает 3; в согласованном окне callback нет
- UPDATE 7 + POST_EVENT → commit
- Callback отмечает имя; основной код перечитывает Cable 7
- Handler освобождён, оба процесса завершены
Первый понятный вызов
function markCatalogChanged($eventName, $connection) {
$GLOBALS['catalogNeedsRefresh'] = true;
return true;
}
$handler = fbird_set_event_handler($db, 'markCatalogChanged', 'FB164_CHANGED');
if ($handler === false) { throw new RuntimeException('Подписка не создана'); }Callback — обычная PHP-функция, которую вызывает драйвер. Первый параметр получает имя события, второй — ресурс соединения. Внутри мы лишь ставим флаг, чтобы основной код позже сделал запрос. return true здесь оставляет подписку активной; это не commit. $handler — отдельный ресурс регистрации, его сохраняют для последующего free. Код отправителя в архиве делает UPDATE и POST_EVENT в одной транзакции.
Что получится после запуска
| Наблюдение | Результат |
|---|---|
| Отменённая попытка оператора | Cable / остаток 99 |
| SELECT после rollback | Cable / остаток 3 |
| Callback в окне после rollback | 0 |
| SELECT после commit | Cable / остаток 7 |
| Подтверждённое событие получено | да |
| Подписка освобождена | да |
Полный самостоятельный проект и архив
<?php
declare(strict_types=1);
require __DIR__.'/lab.php';
$owned=[];$removed=[];$db=$service=null;
try {
$config=f164Config();$service=f164Service();$runtime=f164Runtime($service);
require __DIR__.'/events-lab.php';
$database=f164CreateDatabase($service,$owned,'event_commit');
$events=f164EventScenario($database,'commit');
$listener=$events['listener'];$sender=$events['sender'];
$rolled=$listener['rollback_items'];$items=$listener['items'];$seen=$listener['seen'];
$rows=[['Отменённая попытка оператора','Cable / остаток 99'],['SELECT после rollback',$rolled[0]['TITLE'].' / остаток '.$rolled[0]['QTY']],
['Callback в окне после rollback',$listener['rollback_callbacks']],['SELECT после commit',$items[0]['TITLE'].' / остаток '.$items[0]['QTY']],
['Подтверждённое событие получено',$listener['callback_count']>=1],['Подписка освобождена',$listener['freed']]];
$checks=['rolled_back_stock'=>$rolled===[['ID'=>1,'TITLE'=>'Cable','QTY'=>3],['ID'=>2,'TITLE'=>'Clamp','QTY'=>0]],
'rollback_window_silent'=>$listener['rollback_callbacks']===0,
'committed_stock'=>$items===[['ID'=>1,'TITLE'=>'Cable','QTY'=>7],['ID'=>2,'TITLE'=>'Clamp','QTY'=>0]],
'callback_received'=>$listener['callback_count']>=1,
'callback_arguments'=>array_unique(array_column($seen,'name'))===['FB164_CHANGED']&&!in_array(false,array_column($seen,'connection_resource'),true),
'no_payload'=>$listener['payload_exposed']===false&&$listener['counter_argument_exposed']===false,
'handler_freed'=>$listener['freed']===true,'children_stopped'=>$events['all_children_stopped']===true,'two_children'=>$events['process_count']===2];
$observations=['events'=>$events,'attempted_stock'=>99,'rollback_observation_window_ms'=>100,
'event_does_not_store_a_job'=>true,'callback_count_is_not_changed_row_count'=>true];
} finally {
if(is_resource($db)){f164Close($db);}
if(is_resource($service)){f164Detach($service);}
$removed=f164Cleanup($owned);
}
f164Report('fb164-event-commit','Показывать подтверждённый остаток, а не отменённую попытку',$rows,$checks,$observations,$runtime,$owned,$removed);
Ожидаемый результат
Отменённая попытка оператора — Cable / остаток 99
SELECT после rollback — Cable / остаток 3
Callback в окне после rollback — 0
SELECT после commit — Cable / остаток 7
Подтверждённое событие получено — да
Подписка освобождена — да
Архив содержит все PHP-файлы и README для отдельной TCP-лаборатории. Выполните example.php из папки проекта: result.html и result.json покажут фактические товары и наблюдения. Создаётся и удаляется одна собственная синтетическая база; чужие БД и учётные записи не затрагиваются. Самостоятельный запуск требует разрешённого PHP proc_open и подготовленного приватного сервера.
Открыть результат программы · Открыть фактические наблюдения JSON.
Границы, ошибки и применение
POST_EVENT становится доступен подписавшимся клиентам после commit. Проверка отсутствия callback проводится в ограниченном согласованном окне после rollback; документированный смысл отката не выводится из бесконечного ожидания. Callback этого расширения не передаёт строки или счётчик изменений. Firebird может группировать доставку, поэтому число callback не равно числу UPDATE и не является журналом заказов. Для задания, которое нельзя потерять при выключенном listener, сохраните запись задания/outbox вместе с бизнес-изменением и читайте её при запуске. Событие только ускоряет пробуждение; без durable-данных оно не становится очередью с подтверждением обработки.
Автор длительной процедуры хотел получать сигнал ещё до подтверждения транзакции. Обсуждение задачи на Stack Overflow. Наш сценарий отделяет промежуточную попытку 99 от сохранённого 7. Правило commit проверяется по первичным источникам, а не выводится из вопроса без принятого ответа.
Разработчик связал число callback с числом строк, изменённых триггером. Обсуждение задачи на Stack Overflow. Поясняем разные сущности: выполнение SQL по строкам, внутренние счётчики событий и вызовы PHP callback. В отчёте хранится фактический callback_count, но он не используется как количество заказов.
Измените требование
Рабочий процесс был выключен во время commit заказа. Достаточно ли после запуска ждать следующее событие?
Вариант решения
Нет. Если требуется обработать каждый заказ, сначала прочитайте сохранённые необработанные задания по индексу и ограничению партии. После этого можно использовать события для более быстрого пробуждения. Для экрана текущих остатков достаточно перечитать его текущее состояние; это другая задача, ей не нужен подсчёт всех исторических сигналов.
Прекратить подписку и убедиться, что данные продолжают сохраняться
Временная подписка больше не нужна. Сначала она доставит изменение до 7. Затем получатель освободит handler, а отправитель отдельно сохранит 11. Покажем, что отключается конкретный callback, а база и подтверждённые изменения продолжают существовать. Это помогает правильно закончить worker, не перепутав освобождение ресурса с rollback.
- Commit Cable 7 → callback → SELECT 7
- fbird_free_event_handler(handler) → true; handler = null
- Sender после ACK освобождения сохраняет Cable 11 и POST_EVENT → commit
- Число вызовов этой подписки не растёт
- Обычный SELECT видит 11; связь с базой завершается после handler
Первый понятный вызов
if (fbird_free_event_handler($handler) !== true) {
throw new RuntimeException('Подписка не освобождена');
}
$handler = null;Передаётся $handler из set_event_handler, а не $db. После успешного вызова ссылка больше не представляет активную регистрацию; присваивание null делает это явным в приложении. Результат true говорит об освобождении ресурса, а не о количестве доставленных сообщений. Полный проект синхронизирует отправителя: поздний commit происходит только после отметки free.
Что получится после запуска
| Наблюдение | Результат |
|---|---|
| До освобождения handler | Cable / остаток 7 |
| fbird_free_event_handler вернул true | да |
| Поздняя подтверждённая запись | Cable / остаток 11 |
| Callback после free не прибавился | да |
| Освобождение отменило запись | нет |
| Оба процесса остановлены | да |
Полный самостоятельный проект и архив
<?php
declare(strict_types=1);
require __DIR__.'/lab.php';
$owned=[];$removed=[];$db=$service=null;
try {
$config=f164Config();$service=f164Service();$runtime=f164Runtime($service);
require __DIR__.'/events-lab.php';
$database=f164CreateDatabase($service,$owned,'vent_release');
$events=f164EventScenario($database,'release');
$listener=$events['listener'];$sender=$events['sender'];
$before=$listener['committed_items'];$after=$listener['items'];
$unchangedCount=$listener['callbacks_after_free']===$listener['callback_count'];
$rows=[['До освобождения handler',$before[0]['TITLE'].' / остаток '.$before[0]['QTY']],
['fbird_free_event_handler вернул true',$listener['freed']],['Поздняя подтверждённая запись',$after[0]['TITLE'].' / остаток '.$after[0]['QTY']],
['Callback после free не прибавился',$unchangedCount],['Освобождение отменило запись',False],
['Оба процесса остановлены',$events['all_children_stopped']]];
$checks=['first_committed_stock'=>$before===[['ID'=>1,'TITLE'=>'Cable','QTY'=>7],['ID'=>2,'TITLE'=>'Clamp','QTY'=>0]],
'handler_freed'=>$listener['freed']===true,'notification_before_free'=>$listener['callback_count']>=1,
'no_callback_after_free'=>$unchangedCount,
'write_persisted_after_free'=>$after===[['ID'=>1,'TITLE'=>'Cable','QTY'=>11],['ID'=>2,'TITLE'=>'Clamp','QTY'=>0]],
'second_commit_after_release'=>$sender['committed_business_updates']===2&&$sender['post_after_release']===true,
'children_stopped'=>$events['all_children_stopped']===true,'two_children'=>$events['process_count']===2];
$observations=['events'=>$events,'after_release_observation_window_ms'=>100,'free_is_not_rollback'=>true,
'handler_released_before_connection'=>true];
} finally {
if(is_resource($db)){f164Close($db);}
if(is_resource($service)){f164Detach($service);}
$removed=f164Cleanup($owned);
}
f164Report('fb164-event-release','Прекратить подписку и убедиться, что данные продолжают сохраняться',$rows,$checks,$observations,$runtime,$owned,$removed);
Ожидаемый результат
До освобождения handler — Cable / остаток 7
fbird_free_event_handler вернул true — да
Поздняя подтверждённая запись — Cable / остаток 11
Callback после free не прибавился — да
Освобождение отменило запись — нет
Оба процесса остановлены — да
Архив содержит все PHP-файлы и README для отдельной TCP-лаборатории. Выполните example.php из папки проекта: result.html и result.json покажут фактические товары и наблюдения. Создаётся и удаляется одна собственная синтетическая база; чужие БД и учётные записи не затрагиваются. Самостоятельный запуск требует разрешённого PHP proc_open и подготовленного приватного сервера.
Открыть результат программы · Открыть фактические наблюдения JSON.
Границы, ошибки и применение
Освобождайте подписку до закрытия её соединения. Не проверяйте её состояние повторным free: старый native-handle не становится безопасным из-за is_resource. При нескольких PHP-переменных, ссылающихся на один handler, нельзя считать каждую независимым владельцем. Callback с return false прекращает повторную регистрацию, однако сохранённый ресурс всё равно завершают контролируемо. После free не отменяются уже завершённые SQL-транзакции и не исчезают сообщения других подписчиков. Ограниченное окно наблюдения после согласованного позднего commit дополняет документированный контракт отмены.
Измените требование
Если соединение ещё нужно для финального отчёта, обязательно ли закрывать его вместе с подпиской?
Вариант решения
Нет. Сначала освободите handler и обнулите переменную. Затем выполните нужный ограниченный SELECT, освободите его результат, завершите транзакцию и только после этого закройте обычное соединение. Сроки жизни регистрации и канала связаны, но это разные ресурсы.
Справочник постоянного подключения и трёх операций событий
| Функция | Задача |
|---|---|
| fbird_pconnect | Повторно использовать подключение в PHP worker, читая свежие данные отдельным запросом. |
| fbird_wait_event | Дождаться имени события и перейти к новому ограниченному SELECT. |
| fbird_set_event_handler | Зарегистрировать PHP callback и сохранить ресурс подписки. |
| fbird_free_event_handler | Освободить свою подписку, сохранив различие между отменой событий и отменой данных. |
Выбирать срок жизни ресурса под задачу
Проверьте себя по результатам: тот же attachment может вернуть новый остаток 7; отменённые 99 не становятся сохранёнными данными; событие не содержит строку товара; после free другой процесс по-прежнему может сохранить 11. Если это понятно, решение о следующем SQL и сроке жизни каждого ресурса становится явным.
Продолжить можно через пользователей и смену пароля, ограничение прав приложения и параметры запросов. Для экрана выберите узкую выборку. Для обработки каждого задания сначала спроектируйте сохраняемые данные, статусы и восстановление после остановки; только после этого добавляйте событие как уведомление.
Функции и методы этой темы
Откройте карточку, чтобы увидеть договор функции, примеры, ограничения и связанные инструменты.
fbird_pconnectПовторно использовать подключение в PHP worker, читая свежие данные отдельным запросом.
Исторически PHP 5 / PHP 7 до 7.4; затем внешнее interbase. Профиль:3.0.1 / PHP 8.2.35 / Firebird 5.0.4. · interbase
fbird_pconnect(string $database = "", string $username = "", string $password = "", string $charset = "", int $buffers = 0, int $dialect = 0, string $role = "", int $sync = 0): resource|falseКороткие запросы каталога могут обращаться к базе многократно. fbird_pconnect позволяет процессу PHP повторно использовать своё подключение с теми же настройками. При этом остаток товара всё равно надо читать SQL: программа выше показывает переход Cable 3→7 без смены attachment. Польза — возможность сократить повторную установку канала; ускорение конкретного сайта не обещается без измерения.
fbird_pconnect(
string $database = "", string $username = "", string $password = "",
string $charset = "", int $buffers = 0, int $dialect = 0,
string $role = "", int $sync = 0
): resource|falseСигнатура показывает порядок аргументов реализации 3.0.1; это справочная запись, её не вставляют как готовый вызов. Все параметры допускают пропуск. Пустые database, username, password, charset дополняются соответствующими ini-настройками, если они заданы. Скобки вызова не создают пользовательскую функцию; возвращённый resource присваивается переменной слева.
| Аргумент | Значение, единицы и условия |
|---|---|
| database | Путь/алиас базы со стороны сервера. Для TCP, например, host/port:path. Пропуск учитывает ibase.default_db. Это не URL страницы PHP. Старые NetBEUI/IPX варианты из исторического руководства не являются обещанием современной поддержки. |
| username | Учётная запись Firebird; при пропуске ibase.default_user. Она отличается от пользователя сайта. |
| password | Пароль этой учётной записи; при пропуске ibase.default_password. Берите из приватной конфигурации, не из URL и не печатайте в диагностике. |
| charset | Кодировка обмена, например UTF8; при пропуске ibase.default_charset. Не меняет объявление кодировки существующих колонок или базы. |
| buffers | Запрашиваемое число страниц буфера при подключении;0 оставляет выбор серверу. Это не мегабайты и не гарантированная настройка общего пула. Влияние зависит от сервера и его архитектуры. |
| dialect | SQL-диалект запросов соединения.0 означает выбранный драйвером поддерживаемый вариант; урок задаёт 3 явно. Это не преобразует базу из диалекта 1 в 3. Исторически возможность зависит от клиента InterBase6+. |
| role | Роль SQL при подключении; пустая строка не выбирает пользовательскую роль. Историческая поддержка InterBase5+. Членство в роли и её выбор обсуждаются отдельно. |
| sync | Исторический аргумент режима записи; PHP Manual оставляет его без пояснения. В 3.0.1 ненулевое значение формирует isc_dpb_force_write; сравнение с IBASE_PRP_WM_SYNC выбирает синхронную запись, другое ненулевое значение — противоположный режим.0 не отправляет эту настройку. В повседневном подключении оставляйте 0: изменение режима записи требует отдельного осознанного решения о сохранности данных. |
| Возвращаемое значение | Ресурс соединения либо false при отказе. Это не bool «пароль верен» и не массив результатов. Возможны PHP warning и диагностический код/текст; их сохраняют сразу до следующей операции расширения. |
Применение 1: новый HTTP-запрос получает новый остаток
$db = fbird_pconnect($database, $user, $password, 'UTF8', 0, 3);
if ($db === false) { throw new RuntimeException('База недоступна'); }
$tx = fbird_trans(IBASE_READ | IBASE_COMMITTED, $db);
if ($tx === false) { throw new RuntimeException('Чтение не началось'); }
// SELECT выбранных полей; затем освобождение результата и fbird_commit($tx).В полном HTTP-проекте есть два настоящих ответа, SQL-чтение attachment ID и изменение между ними. Транзакция чтения заканчивается до ответа. Повторное использование подключения не сохраняет старый PHP-массив и не освобождает от контроля согласованности данных.
Применение 2: разделить роль читателя и редактирование
$reader = fbird_pconnect($database, $user, $password, 'UTF8', 0, 3, 'LAB_READER');
if ($reader === false) { throw new RuntimeException('Читатель не подключён'); }
$result = fbird_query($reader, 'SELECT TITLE, QTY FROM LAB_ITEMS WHERE ID = ?', $id);
// Проверить результат, прочитать одну карточку и закончить транзакцию.Это контекстный вариант для уже созданной роли, не ещё один исполненный проект. Параметры подключения участвуют в выборе повторно используемого соединения; роль нельзя считать изменяемой меткой посетителя поверх случайно взятого attachment. Разбор выдачи прав и явного выбора роли есть в практике ролей, а создание отдельных учётных записей — в администрировании fbird_*.
| Время жизни / настройка | Что учитывать в приложении |
|---|---|
| Область повторного использования | Процесс PHP worker, не браузер и не весь кластер. Другой worker хранит собственные подключения. Resource нельзя переносить через $_SESSION или сериализацию. |
| Конец запроса | В закреплённом драйвере незаконченная default-транзакция подтверждается, остальные незавершённые транзакции откатываются, события освобождаются. Не поручайте этому бизнес-решение: commit/rollback должны быть явными раньше. |
| Состояние соединения | USER_SESSION-контекст может пережить запрос. Убирайте свои метки клиента, завершайте результаты/транзакции; не предполагайте, что следующий запрос принадлежит тому же человеку. |
| ibase.allow_persistent | Системная настройка разрешения persistent; в новом процессе при 0 новые подключения работают без сохранения в persistent-пуле. |
| ibase.max_persistent | По документации лимит постоянных соединений процесса, -1 означает отсутствие ограничения. В C-коде 3.0.1 обнаружено сравнение, которое для конечного значения использует порог 1 из-за скобок. Это source-наблюдение, не результат отдельного runtime-теста лимитов. Пример запускает только один persistent-канал с -1. |
| ibase.max_links | Общий лимит подключений процесса; -1 без ограничения. Исчерпание может дать false. Лимит одного worker не равен ограничению всех соединений сайта. |
| fbird_close / завершение процесса | Обычный close для persistent-ресурса не обещает отключить физический attachment. При завершении worker его пул прекращает существовать. Не обнуляйте чужие ресурсы и не закрывайте один handle дважды. |
Системные INI-настройки задают до запуска worker. Ускорение зависит от стоимости подключения, объёма запросов и числа процессов; один только переход на alias не меняет производительность. Если база недоступна, права недостаточны или кодировка не подходит, сохранённый канал не исправляет причину. Для простой короткой задачи подходит fbird_connect. Диагностика разобрана в fbird_errcode и fbird_errmsg.
fbird_pconnect — alias ibase_pconnect: тот же обработчик расширения, а не второй механизм или более быстрый протокол. Историческая метка PHP 5 / PHP 7 ниже 7.4 описывает прежнюю поставку interbase. Здесь используется внешнее interbase 3.0.1, PHP 8.2.35 NTS x64 и Firebird 5.0.4 через отдельный TCP-сервер. Версии в README записаны явно. Эти функции не появляются автоматически при установке PDO_FIREBIRD и не мигрируют приложение на PDO.
fbird_wait_eventДождаться имени события и перейти к новому ограниченному SELECT.
Исторически PHP 5 / PHP 7 до 7.4; затем внешнее interbase. Профиль:3.0.1 / PHP 8.2.35 / Firebird 5.0.4. · interbase
fbird_wait_event([resource $connection,] string $event_name, string ...$event_names): string|falseФоновый обработчик знает, какой участок каталога его интересует, и ждёт изменения. fbird_wait_event возвращает имя сигнала; полезные данные получают следующим запросом. Функция проста для одного шага ожидания, но блокирует текущий процесс и сама не задаёт срок остановки.
fbird_wait_event(string $event_name, string ...$event_names): string|false
fbird_wait_event(resource $connection, string $event_name, string ...$event_names): string|false| Параметр / результат | Контракт |
|---|---|
| connection | Необязательное действующее SQL-соединение. Без него используется последнее открытое соединение. Передавайте явно при нескольких базах. Это не ресурс транзакции или Service Manager. |
| event_name, event_names | Одно или несколько точных имён отдельными аргументами, максимум 15 по PHP Manual. Многоточие означает дополнительные строки, а не обязательный массив. POST_EVENT в Firebird 5 допускает имя до 127 байт; регистр и фактическая строка должны совпасть. |
| Возврат | Имя одного сработавшего события. В 3.0.1 native-ошибка или невозможность определить имя дают false. Это не число сообщений, не строка таблицы и не все события сразу. |
| Ожидание | Блокирующее; параметра timeout нет. Срок процесса/внешнего supervisor не является параметром этого API. |
| Ошибки аргументов | Неверное число/тип или неактивный resource могут дать предупреждение/ошибку разбора PHP. Не подменяйте это успешным ожиданием. Native-диагностику сохраняют немедленно. |
Применение 1: перечитать известную карточку товара
$name = fbird_wait_event($db, 'FB164_CHANGED');
if ($name === false) { throw new RuntimeException('Нет корректного события'); }
$result = fbird_query($db, 'SELECT TITLE, QTY FROM LAB_ITEMS WHERE ID = ?', 1);Полный ограниченный запуск listener/sender покажет Cable 11, а не только строку FB164_CHANGED. SELECT не извлекает ID из сигнала: нужный товар уже выбран экраном. Результат SELECT освобождается, транзакция заканчивается; затем можно решить, нужен ли ещё один цикл ожидания.
Применение 2: два повода обновить одну выбранную карточку
$name = fbird_wait_event($db, 'STOCK_CHANGED', 'PRICE_CHANGED');
if ($name === false) { throw new RuntimeException('Ожидание не удалось'); }
$result = fbird_query($db, 'SELECT TITLE, QTY, PRICE_CENTS FROM GOODS WHERE ID = ?', $id);Это вариант для приложения, у которого уже есть GOODS и нужный ID. Любое из двух имён означает, что представление стоит перечитать. Если вернулось STOCK_CHANGED, нельзя заключать, что PRICE_CHANGED за это время не происходило: функция возвращает одно имя. Драйвер 3.0.1 выбирает первое подходящее имя из своего буфера. Для полного журнала изменений нужна отдельная сохраняемая модель.
Перед долгим ожиданием завершите старую snapshot-транзакцию. После сигнала прочитайте данные в новой подходящей транзакции: уведомление не обновляет прежний снимок автоматически. Сигналы поступают после commit, при rollback эта транзакция не публикует подтверждённое изменение. События отключённому клиенту не превращаются в долговечный почтовый ящик. Для браузера нужен ещё прикладной транспорт HTTP/SSE/WebSocket; wait_event сам не меняет HTML.
В нашем TCP-стенде основной порт и вспомогательное соединение событий ограничены loopback. Если SQL работает, а события не приходят, отдельно проверяют сетевой путь events и настройки сервера; успешный SELECT не проверяет доставку callback. Для нескольких одновременно ожидаемых задач обычно нужен отдельный worker либо подходящий событийный клиент. Альтернатива с сохранённым handle — fbird_set_event_handler, отмена его регистрации — fbird_free_event_handler.
fbird_wait_event — alias ibase_wait_event: тот же обработчик расширения, а не второй механизм или более быстрый протокол. Историческая метка PHP 5 / PHP 7 ниже 7.4 описывает прежнюю поставку interbase. Здесь используется внешнее interbase 3.0.1, PHP 8.2.35 NTS x64 и Firebird 5.0.4 через отдельный TCP-сервер. Версии в README записаны явно. Эти функции не появляются автоматически при установке PDO_FIREBIRD и не мигрируют приложение на PDO.
fbird_set_event_handlerЗарегистрировать PHP callback и сохранить ресурс подписки.
Исторически PHP 5 / PHP 7 до 7.4; затем внешнее interbase. Профиль:3.0.1 / PHP 8.2.35 / Firebird 5.0.4. · interbase
fbird_set_event_handler([resource $connection,] callable $callback, string $event, string ...$events): resource|falseЕсли основной PHP-код должен знать, что каталог изменился, можно зарегистрировать небольшую функцию callback. Драйвер вызовет её после доставки события. Она отмечает необходимость перечитывания; проверка предметных данных и решение приложения остаются в основном коде.
fbird_set_event_handler(callable $callback, string $event, string ...$events): resource|false
fbird_set_event_handler(resource $connection, callable $callback, string $event, string ...$events): resource|false| Аргумент / результат | Что означает |
|---|---|
| connection | Открытое SQL-соединение, связанное с подпиской. Пропуск выбирает последнее соединение. Явное соединение уменьшает неоднозначность нескольких баз. |
| callback | Вызываемая PHP-функция с двумя аргументами: string eventName и resource connection. Имя функции передают строкой; допустимость callable проверяет расширение. В 3.0.1 форма без connection распознаётся по первому строковому аргументу, поэтому для closure/массива callable используйте явный connection. |
| event и следующие имена | Точные строки, по официальному контракту от 1 до 15. Не шаблоны и не SQL-выражения. Передают отдельными аргументами. |
| Return callback | Строго false прекращает последующую регистрацию; другое значение не несёт полезного ответа отправителю. В наших примерах true явно оставляет подписку. Это не SQLcommit/rollback. |
| Возврат set_event_handler | Отдельный ресурс регистрации для free_event_handler, либо false при неудаче. Сохраняйте его, а не только bool is_resource. |
| Ошибки | Недействующее соединение, неподходящий callable, неверные аргументы или native-ошибка регистрации. Проверять ===false сразу; диагностика следующих вызовов не является исходной ошибкой. |
Граница документации и реализации важна: PHP Manual обещает до 15 имён. В C-коде 3.0.1 цикл заполнения использует абсолютный индекс аргумента i<15 вместо количества событий: при явном connection рассматривается не более 13 имён, без него 14. Это обнаружено чтением исходника, а не отдельным runtime-прогоном больших списков. Проекты используют одно имя; для другого числа не полагайтесь на непроверенную границу. Документированный максимум сохранён здесь явно вместе с ограничением конкретной версии.
Применение 1: обновление после commit
function requestRefresh($eventName, $connection) {
$GLOBALS['refreshNeeded'] = true;
return true;
}
$handler = fbird_set_event_handler($db, 'requestRefresh', 'FB164_CHANGED');
if ($handler === false) { throw new RuntimeException('Регистрация не удалась'); }Полный проект с отменой 99 и сохранением 7 выполняет реальную запись во втором процессе. Callback не получает значение 7 и не выполняет тяжёлую работу; основной код позже делает SELECT. Это упрощает владение ресурсами и отделяет механизм доставки от бизнес-логики.
Применение 2: одноразовый сигнал о готовности отчёта
function reportReady($name, $connection) {
$GLOBALS['reportNeedsRead'] = true;
return false; // Следующее уведомление этой регистрации уже не нужно.
}
$handler = fbird_set_event_handler($db, 'reportReady', 'REPORT_READY');
if ($handler === false) { throw new RuntimeException('Не удалось ждать отчёт'); }
// Основной код по флагу читает сохранённый отчёт; затем освобождает handler.Контекстный вариант предполагает, что генератор уже сохраняет отчёт в базе и публикует имя после commit. return false — политика подписки, а не подтверждение успешного чтения отчёта. Сохранённый handle освобождают отдельно; пример такого порядка — отмена регистрации с последующим чтением 11.
В callback передаются только имя и соединение. Внутренний Firebird counter не раскрыт этим PHP API; полезные строки, ID заказа и количество изменённых записей не передаются. Реализация 3.0.1 берёт первое подходящее имя из буфера, а группировка доставки не гарантирует один callback на строку/commit. Поэтому обработчик не должен считать вызовы как проданные товары. Сохраняемый outbox или журнал имеют собственные строки и статусы; уведомление может только разбудить их читателя.
Событие принадлежит соединению и процессу, в котором зарегистрировано. Не передавайте resource через HTTP или сериализацию. Не закрывайте connection раньше handler; не используйте callback как способ бесконечно удержать короткий web request. Для блокирующего одного шага есть fbird_wait_event, для контролируемого завершения — fbird_free_event_handler.
fbird_set_event_handler — alias ibase_set_event_handler: тот же обработчик расширения, а не второй механизм или более быстрый протокол. Историческая метка PHP 5 / PHP 7 ниже 7.4 описывает прежнюю поставку interbase. Здесь используется внешнее interbase 3.0.1, PHP 8.2.35 NTS x64 и Firebird 5.0.4 через отдельный TCP-сервер. Версии в README записаны явно. Эти функции не появляются автоматически при установке PDO_FIREBIRD и не мигрируют приложение на PDO.
fbird_free_event_handlerОсвободить свою подписку, сохранив различие между отменой событий и отменой данных.
Исторически PHP 5 / PHP 7 до 7.4; затем внешнее interbase. Профиль:3.0.1 / PHP 8.2.35 / Firebird 5.0.4. · interbase
fbird_free_event_handler(resource $event): boolКогда экран или worker больше не следит за изменениями, принадлежащую ему подписку надо закончить. fbird_free_event_handler освобождает регистрацию. Данные Firebird, открытое соединение и уже выполненный commit имеют другие сроки жизни: в проекте после освобождения подписки товар всё равно получает остаток 11.
fbird_free_event_handler(resource $event): bool| Часть | Правило |
|---|---|
| event | Единственный обязательный аргумент: ещё принадлежащий программе ресурс, ранее возвращённый fbird_set_event_handler или его canonical alias. Не ресурс соединения и не результат SELECT. |
| true | Освобождение принято. Это не отмена данных и не число обработанных событий. |
| false / ошибка | Неудача или неверный ресурс; возможны предупреждения/ошибки PHP. Публично показывают безопасную прикладную ошибку, а native-диагностику сохраняют сразу в приватном журнале. |
| После вызова | Переменную присваивают null, другие копии не используют повторно. Одно владение означает одно освобождение. |
| Порядок | Освободить подписку, закончить свои результаты/транзакции, затем закрыть обычное соединение. |
Применение 1: завершить временное наблюдение
if (fbird_free_event_handler($handler) !== true) {
throw new RuntimeException('Подписка не завершена');
}
$handler = null;
$result = fbird_query($db, 'SELECT TITLE, QTY FROM LAB_ITEMS WHERE ID = ?', 1);В полном двухпроцессном проекте поздний SELECT показывает 11, а счётчик callback не увеличивается после free. Это видимое различие отмены регистрации и отката транзакции. Прочитанный SQL-результат затем освобождают своей функцией, не free_event_handler.
Применение 2: гарантировать один путь освобождения при ошибке приложения
$handler = fbird_set_event_handler($db, 'requestRefresh', 'STOCK_CHANGED');
if ($handler === false) { throw new RuntimeException('Нет подписки'); }
try {
// Ограниченная работа с флагом requestRefresh и свежими SQL-данными.
} finally {
if (fbird_free_event_handler($handler) !== true) {
throw new RuntimeException('Ресурс подписки не освобождён');
}
$handler = null;
}Это каркас владения: requestRefresh объявляется заранее, работа имеет внешний срок, а единственный finally знает, кто отвечает за handler. В более крупном приложении ошибку очистки логируют так, чтобы сохранить и первоначальную причину. Не добавляйте второй free в callback или соседнем участке кода. Если callback вернул false, сохранённая PHP-ссылка всё ещё требует контролируемого завершения.
В закреплённом native-коде нельзя считать is_resource доказательством, что ранее освобождённый handle снова пригоден. Повторный free или закрытие связи в неправильном порядке опаснее обычной логической ошибки. Присваивание null и явный единственный владелец предотвращают такой путь. Для отмены SQL нужен fbird_rollback, для соединения — fbird_close, для регистрации новой подписки — fbird_set_event_handler.
fbird_free_event_handler — alias ibase_free_event_handler: тот же обработчик расширения, а не второй механизм или более быстрый протокол. Историческая метка PHP 5 / PHP 7 ниже 7.4 описывает прежнюю поставку interbase. Здесь используется внешнее interbase 3.0.1, PHP 8.2.35 NTS x64 и Firebird 5.0.4 через отдельный TCP-сервер. Версии в README записаны явно. Эти функции не появляются автоматически при установке PDO_FIREBIRD и не мигрируют приложение на PDO.
Источники и обсуждения
- PHP Manual: fbird_pconnect
- PHP Manual: ibase_pconnect
- PHP Manual: fbird_wait_event
- PHP Manual: ibase_wait_event
- PHP Manual: fbird_set_event_handler
- PHP Manual: ibase_set_event_handler
- PHP Manual: fbird_free_event_handler
- PHP Manual: ibase_free_event_handler
- PHP: область постоянных подключений
- PHP: настройки расширения
- FirebirdSQL 3.0.1: подключение и очистка
- FirebirdSQL 3.0.1: регистрация и доставка событий
- Firebird 5: POST_EVENT
- Firebird: свойства доставки событий
Код и руководство с собой
Скачайте самостоятельные примеры с исходными данными, инструкциями и ожидаемыми результатами. Примеры HTTP и фрагменты для проекта помечены отдельно: им нужно указанное окружение.
Код не исполняется на сайте. Скачайте нужный пример и запускайте его в своём локальном окружении.
Материалы сверены 2026-10-12. Примеры и объяснения написаны для этого руководства; источники и обсуждения доступны в каждой главе.


