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

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

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

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

Глава 74 · После основных запросов Firebird

Firebird: fbird_* в существующем коде — каталог, параметры и формы строк

Поддержка существующего native-кода: экран остатков, повторный фильтр, массив/строка/объект и правильный порядок полей импорта.

Как читать существующий код с fbird_*

В работающем проекте встретились fbird_connect, fbird_query и fbird_fetch_assoc. Переписывать всё приложение для чтения двух товаров не требуется: сначала разберитесь, какие ресурсы проходят между этими вызовами. Здесь вы получите экран наличия, две вкладки остатков, три понятные формы строки и импорт с объяснимым отказом неполной записи.

Один API с двумя наборами имён
  1. fbird_connect = ibase_connect → соединение
  2. query или prepare → execute → результат SELECT
  3. fetch_assoc / fetch_row / fetch_object → значения PHP
  4. free_result → результат больше не нужен
  5. free_query → подготовленная команда больше не нужна

Alias — второе имя той же функции. Представьте две подписи у одной кнопки: они запускают один обработчик, а не два разных хранилища. В исходнике выбранного драйвера это PHP_FALIAS. Такое имя само не ускоряет SQL, не меняет транзакции, не превращает native interbase в PDO и не устраняет несовместимость старой базы с другим SQL-диалектом.

Перед главой достаточно понять соединение и SELECT, подготовленную команду и массивы и объекты результата. У каждого раздела сначала короткая запись, затем полный самостоятельный файл с собственными данными. Проверяемое окружение — PHP 8.2.35, расширение interbase 3.0.1, Firebird 5.0.4; исторические отметки PHP 5 / PHP 7 < 7.4 относятся к прежней встроенной поставке, не к отсутствию этих имён в отдельном драйвере.

Практическая задачаРаздел
Экран наличия: получить только два подходящих товараОткрыть сценарий
Две вкладки остатков одним подготовленным SELECTОткрыть сценарий
Одна карточка в массиве, строке CSV и объектеОткрыть сценарий
Импорт: связать имена полей с позициями INSERTОткрыть сценарий

Подготовка примеров и служебные функции

ZIP содержит example.php, lab.php и README.txt. README объясняет отдельную локальную учебную базу и запуск CLI; файл создаёт только свою случайно названную таблицу, а в конце удаляет её. Соединение в example.php действительно открывает fbird_connect. Вспомогательные f161* готовят данные, сохраняют диагностику и результат; aliasRows161 вызывает выбранный fbird_fetch_* и отличает EOF от отказа. Все 13 разбираемых имён присутствуют в основных сценариях, а не только в списке функций.

Маленькие фрагменты ниже предполагают готовое соединение и показанную таблицу; это отдельные шаги полного файла. Ресурс — ссылка PHP на состояние драйвера, а не строка данных. Не печатайте его как результат бизнес-задачи. Данные можно прочитать в массив, закрыть ресурс и затем сформировать HTML. В HTML-результате текст экранируется; в JSON сохраняются строки, числа, null и условия проверки.

Экран наличия: получить только два подходящих товара

Вы поддерживаете складской экран старого сайта. Для него нужны две активные позиции с остатком, название и количество. Получим ровно эти поля и строки из Firebird: браузеру не потребуется отсеивать архив, сортировать большой PHP-массив или угадывать номера колонок.

Путь данных
  1. fbird_connect → соединение с учебной базой
  2. WHERE active=1 AND qty>=1 → только подходящие позиции
  3. ORDER BY id ROWS 2 → стабильные первые две строки
  4. num_fields/field_info → описание трёх колонок
  5. fetch_assoc → Кабель: 3; Клемма: 7
  6. free_result → копии данных остаются в PHP

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

$result = fbird_query($db, 'SELECT title AS "title", qty AS "qty" FROM items WHERE qty>=? ORDER BY id ROWS 2', 1);
if ($result === false) { throw new RuntimeException("Каталог недоступен"); }
$row = fbird_fetch_assoc($result);
if ($row !== false) { echo $row["title"], ": ", $row["qty"]; }
fbird_free_result($result);

Здесь $db — уже открытое соединение, items — существующая таблица. Знак ? обозначает значение фильтра, которое следующим аргументом передаётся отдельно от SQL. AS "title" задаёт точный ключ массива; двойные кавычки сохраняют регистр. Один fetch читает одну следующую строку, поэтому для всех карточек нужен цикл. Минимальный фрагмент показывает только первую; полный файл сохраняет обе строки и отдельно различает конец чтения и ошибку.

Что получится

НаблюдениеРезультат
Колонки экранаid, title, qty
Товаров на экране2
Первая карточкаКабель: 3
Вторая карточкаКлемма: 7
Архивные и нулевые позиции исключеныда
Результат освобождёнда
Экран наличия: получить только два подходящих товараСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__.'/lab.php';
$db=null;$owned=$removed=$rows=$checks=$observations=[];
try {
    $config=f161Config();$connect=f161Capture(static fn()=>fbird_connect(
        $config['database'],$config['user'],$config['password'],'UTF8',0,3));
    if (!is_resource($connect['value']) || $connect['warning']) {f161Failure('Соединение не открыто',$connect);}
    $db=$connect['value'];unset($connect);$runtime=f161Runtime($db,$config);

    function aliasResult161($value,string $operation) {
        if (!is_resource($value)) {throw new RuntimeException($operation.': ожидался результат SELECT.');}
        return $value;
    }
    function aliasFreeResult161(&$result): bool {
        $call=f161Capture(static fn()=>fbird_free_result($result));$result=null;
        if ($call['value']!==true || $call['warning']) {f161Failure('Результат не освобождён',$call);}
        return true;
    }
    function aliasFreeQuery161(&$query): bool {
        $call=f161Capture(static fn()=>fbird_free_query($query));$query=null;
        if ($call['value']!==true || $call['warning']) {f161Failure('Команда не освобождена',$call);}
        return true;
    }
    function aliasRows161($result,string $shape='assoc'): array {
        $rows=[];
        while (true) {
            $call=f161Capture(static fn()=>match($shape) {
                'assoc'=>fbird_fetch_assoc($result),
                'row'=>fbird_fetch_row($result),
                'object'=>fbird_fetch_object($result),
                default=>throw new InvalidArgumentException('Неизвестная форма строки.')
            });
            if ($call['value']===false) {
                if ($call['warning'] || $call['code']!==0) {f161Failure('Чтение прервано',$call);}
                break;
            }
            $rows[]=$call['value'];
        }
        return $rows;
    }
    function aliasPrepare161($db,string $sql) {
        $call=f161Capture(static fn()=>fbird_prepare($db,$sql));
        if (!is_resource($call['value']) || $call['warning']) {f161Failure('Команда не подготовлена',$call);}
        return $call['value'];
    }
    function aliasExecute161($query,array $values) {
        $call=f161Capture(static fn()=>fbird_execute($query,...$values));
        if ($call['value']===false || $call['warning']) {f161Failure('Команда не выполнена',$call);}
        return $call['value'];
    }

    $name=f161OwnTable($db,$owned,'screen','(id INTEGER NOT NULL PRIMARY KEY,title VARCHAR(40) CHARACTER SET UTF8 NOT NULL,qty INTEGER NOT NULL,active SMALLINT NOT NULL)');
    $q=f161Qualified($name);
    foreach ([[1,'Кабель',3,1],[2,'Клемма',7,1],[3,'Архивная позиция',99,0],[4,'Лента',0,1]] as $item) {
        f161Command($db,"INSERT INTO $q (id,title,qty,active) VALUES (?,?,?,?)",$item);
    }
    f161Commit($db);
    $call=f161Capture(static fn()=>fbird_query($db,"SELECT id AS \"id\",title AS \"title\",qty AS \"qty\" FROM $q WHERE active=? AND qty>=? ORDER BY id ROWS 2",1,1));
    if ($call['value']===false || $call['warning']) {f161Failure('Каталог не прочитан',$call);}
    $result=aliasResult161($call['value'],'Каталог');unset($call);
    try {
        $columns=fbird_num_fields($result);$aliases=[];$metadata=[];
        for ($i=0;$i<$columns;$i++) {
            $info=fbird_field_info($result,$i);
            if ($info===false) {throw new RuntimeException('Описание колонки отсутствует.');}
            $aliases[]=$info['alias'];
            $metadata[]=['alias'=>$info['alias'],'type'=>$info['type'],'length'=>$info['length']];
        }
        $items=aliasRows161($result);
    } finally {$released=aliasFreeResult161($result);}
    $rows=[['Колонки экрана',implode(', ',$aliases)],['Товаров на экране',count($items)],
        ['Первая карточка',$items[0]['title'].': '.$items[0]['qty']],['Вторая карточка',$items[1]['title'].': '.$items[1]['qty']],
        ['Архивные и нулевые позиции исключены',array_column($items,'id')===[1,2]],['Результат освобождён',$released]];
    $checks=['screen_columns'=>$columns===3 && $aliases===['id','title','qty'],
        'screen_rows'=>$items===[['id'=>1,'title'=>'Кабель','qty'=>3],['id'=>2,'title'=>'Клемма','qty'=>7]],
        'first_row_preserved'=>$items[0]['id']===1,'result_released'=>$released && $result===null];
    $observations=['selected'=>$items,'columns'=>$metadata,'filter'=>['active'=>1,'minimum_qty'=>1,'row_limit'=>2],
        'sql_does_filter_order_and_limit'=>true,'connection_function'=>'fbird_connect'];

} finally {
    if (is_resource($db)) {try {$removed=f161Cleanup($db,$owned);} finally {ibase_close($db);}}
}

f161Report('fb161-alias-catalog-screen','Экран наличия: получить только два подходящих товара',$rows,$checks,$observations,$runtime,$owned,$removed);
Ожидаемый результат
Колонки экрана — id, title, qty
Товаров на экране — 2
Первая карточка — Кабель: 3
Вторая карточка — Клемма: 7
Архивные и нулевые позиции исключены — да
Результат освобождён — да

Самостоятельный CLI-файл: одна своя таблица, реальные fbird_* вызовы, ограниченные выборки, result.html/result.json, освобождение ресурсов и удаление своей таблицы. lab.php и README.txt приложены.

Фактический HTML-результат · JSON со значениями и проверками.

Ошибки и границы

Метаданные не двигают курсор, а пробный fetch двигает. Не читайте первую строку «для проверки» и затем не начинайте второй цикл без неё. ROWS 2 ограничивает передачу, но само по себе не гарантирует быстрый план без подходящих индексов. Поля, фильтрация и сортировка нужны в SQL. Имя fbird_query не меняет диалект, кодировку и правила доступа; подмена его на ibase_query не исправит неправильный запрос.

В реальной задаче старой базы Firebird 2.5 запрос через PDO упирался в несоответствие SQL-диалекта соединения и базы. Исходная задача на Stack Overflow. Наш вывод: сначала установить драйвер, диалект и кодировку. Переименование fbird_* в ibase_* не переводит native API в PDO и не меняет диалект существующей базы. Учебная база создаётся с диалектом 3; перенос старой требует отдельного плана.

Попробуйте изменить задачу

Сделайте экран только для остатков от 5. Какая строка появится и сколько колонок вернёт num_fields?

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

Передайте minimum_qty=5. Вернётся Клемма: 7; архивная позиция останется исключённой. num_fields по-прежнему равен 3, потому что это число колонок, а не товаров.

Две вкладки остатков одним подготовленным SELECT

Дежурный открывает вкладку «в наличии», затем «малый остаток». Столбцы и условия запроса одинаковые, меняются только две границы количества. Подготовим шаблон один раз, выполним с двумя наборами значений и сохраним нулевой остаток как число 0.

Путь данных
  1. prepare SQL с BETWEEN ? AND ? → команда
  2. num_params=2; param_info=INTEGER/INTEGER
  3. execute(2,8) → Кабель, Клемма
  4. free_result первой вкладки
  5. execute(0,1) → Лента: 0
  6. free_result → free_query

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

$query = fbird_prepare($db, "SELECT title,qty FROM items WHERE qty BETWEEN ? AND ? ORDER BY id");
if ($query === false) { throw new RuntimeException("Фильтр не подготовлен"); }
$result = fbird_execute($query, 2, 8);
// Прочитать и освободить result.
// Для другой вкладки выполнить тот же query с 0, 1.
// После последней вкладки освободить query.

prepare возвращает команду, но ещё не строки. execute связывает значения в порядке ?: сначала минимум, потом максимум. Число входных мест не связано с числом колонок. Оба параметра INTEGER, поэтому метаданные не смогут узнать, что минимум и максимум перепутаны. Для числовой строки fetch_row позиция 0 — title, позиция 1 — qty, потому что именно так записан SELECT.

Что получится

НаблюдениеРезультат
Входных параметров2
Типы границINTEGER / INTEGER
В наличииКабель, Клемма
Малый остатокЛента: 0
Подготовок / выполнений1 / 2
Результатов освобождено2
Команда освобожденада
Две вкладки остатков одним подготовленным SELECTСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__.'/lab.php';
$db=null;$owned=$removed=$rows=$checks=$observations=[];
try {
    $config=f161Config();$connect=f161Capture(static fn()=>fbird_connect(
        $config['database'],$config['user'],$config['password'],'UTF8',0,3));
    if (!is_resource($connect['value']) || $connect['warning']) {f161Failure('Соединение не открыто',$connect);}
    $db=$connect['value'];unset($connect);$runtime=f161Runtime($db,$config);

    function aliasResult161($value,string $operation) {
        if (!is_resource($value)) {throw new RuntimeException($operation.': ожидался результат SELECT.');}
        return $value;
    }
    function aliasFreeResult161(&$result): bool {
        $call=f161Capture(static fn()=>fbird_free_result($result));$result=null;
        if ($call['value']!==true || $call['warning']) {f161Failure('Результат не освобождён',$call);}
        return true;
    }
    function aliasFreeQuery161(&$query): bool {
        $call=f161Capture(static fn()=>fbird_free_query($query));$query=null;
        if ($call['value']!==true || $call['warning']) {f161Failure('Команда не освобождена',$call);}
        return true;
    }
    function aliasRows161($result,string $shape='assoc'): array {
        $rows=[];
        while (true) {
            $call=f161Capture(static fn()=>match($shape) {
                'assoc'=>fbird_fetch_assoc($result),
                'row'=>fbird_fetch_row($result),
                'object'=>fbird_fetch_object($result),
                default=>throw new InvalidArgumentException('Неизвестная форма строки.')
            });
            if ($call['value']===false) {
                if ($call['warning'] || $call['code']!==0) {f161Failure('Чтение прервано',$call);}
                break;
            }
            $rows[]=$call['value'];
        }
        return $rows;
    }
    function aliasPrepare161($db,string $sql) {
        $call=f161Capture(static fn()=>fbird_prepare($db,$sql));
        if (!is_resource($call['value']) || $call['warning']) {f161Failure('Команда не подготовлена',$call);}
        return $call['value'];
    }
    function aliasExecute161($query,array $values) {
        $call=f161Capture(static fn()=>fbird_execute($query,...$values));
        if ($call['value']===false || $call['warning']) {f161Failure('Команда не выполнена',$call);}
        return $call['value'];
    }

    $name=f161OwnTable($db,$owned,'filter','(id INTEGER NOT NULL PRIMARY KEY,title VARCHAR(40) CHARACTER SET UTF8 NOT NULL,qty INTEGER NOT NULL)');
    $q=f161Qualified($name);
    foreach ([[1,'Кабель',3],[2,'Клемма',7],[3,'Лента',0]] as $item) {f161Command($db,"INSERT INTO $q (id,title,qty) VALUES (?,?,?)",$item);}
    f161Commit($db);
    $query=aliasPrepare161($db,"SELECT title AS \"title\",qty AS \"qty\" FROM $q WHERE qty BETWEEN ? AND ? ORDER BY id ROWS 10");
    try {
        $count=fbird_num_params($query);$types=[];
        for ($i=0;$i<$count;$i++) {
            $info=fbird_param_info($query,$i);
            if ($info===false) {throw new RuntimeException('Параметр не описан.');}
            $types[]=$info['type'];
        }
        $tabs=[];$resultReleases=0;
        foreach (['available'=>[2,8],'empty'=>[0,1]] as $tab=>$values) {
            if (count($values)!==$count || $values[0]>$values[1]) {throw new DomainException('Неверные границы фильтра.');}
            $result=aliasResult161(aliasExecute161($query,$values),'Вкладка');
            try {$tabs[$tab]=aliasRows161($result,'row');}
            finally {aliasFreeResult161($result);$resultReleases++;}
        }
    } finally {$released=aliasFreeQuery161($query);}
    $rows=[['Входных параметров',$count],['Типы границ',implode(' / ',$types)],
        ['В наличии',implode(', ',array_column($tabs['available'],0))],['Малый остаток',$tabs['empty'][0][0].': '.$tabs['empty'][0][1]],
        ['Подготовок / выполнений','1 / 2'],['Результатов освобождено',$resultReleases],['Команда освобождена',$released]];
    $checks=['parameter_contract'=>$count===2 && $types===['INTEGER','INTEGER'],
        'available_rows'=>$tabs['available']===[['Кабель',3],['Клемма',7]],'zero_stock_preserved'=>$tabs['empty']===[['Лента',0]],
        'results_released'=>$resultReleases===2,'query_released'=>$released && $query===null];
    $observations=['tabs'=>$tabs,'parameter_types'=>$types,'template_prepares'=>1,'template_executions'=>2,'result_releases'=>$resultReleases];

} finally {
    if (is_resource($db)) {try {$removed=f161Cleanup($db,$owned);} finally {ibase_close($db);}}
}

f161Report('fb161-alias-prepared-filter','Две вкладки остатков одним подготовленным SELECT',$rows,$checks,$observations,$runtime,$owned,$removed);
Ожидаемый результат
Входных параметров — 2
Типы границ — INTEGER / INTEGER
В наличии — Кабель, Клемма
Малый остаток — Лента: 0
Подготовок / выполнений — 1 / 2
Результатов освобождено — 2
Команда освобождена — да

Самостоятельный CLI-файл: одна своя таблица, реальные fbird_* вызовы, ограниченные выборки, result.html/result.json, освобождение ресурсов и удаление своей таблицы. lab.php и README.txt приложены.

Фактический HTML-результат · JSON со значениями и проверками.

Ошибки и границы

Предварительная подготовка позволяет повторно использовать один SQL; без измерения не обещаем ускорения конкретной нагрузки. Native fbird не понимает SQL-параметры :minimum по правилам PDO. Передавайте список значений явно; не подставляйте текст фильтра строковой склейкой. Ноль количества не равен концу чтения: конец — строго false. Каждый выполненный SELECT имеет свой результат, а query живёт до завершения обеих вкладок.

Попробуйте изменить задачу

Что произойдёт при границах 8 и 2 и почему проверки типов недостаточно?

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

Оба значения целые и их два, но деловой порядок неправильный. Наш код отклонит минимум больше максимума до execute. Это полезнее необъяснимой пустой вкладки; param_info не заменяет такую проверку.

Одна карточка в массиве, строке CSV и объекте

Шаблон страницы ждёт $row["title"], CSV-экспорт — упорядоченный список, а существующий код виджета — $row->title. Получим одинаковые два товара тремя способами и убедимся, что отсутствующая заметка, намеренно пустая заметка и нулевой остаток не потерялись.

Путь данных
  1. Один SELECT: title, qty, note
  2. Первое execute + fetch_assoc → ключи title/qty/note
  3. Второе execute + fetch_row → позиции 0/1/2
  4. Третье execute + fetch_object → свойства stdClass
  5. null, пустой текст и 0 остаются разными значениями

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

$row = fbird_fetch_assoc($result);
// При успешном чтении: $row["title"].
// Альтернатива на отдельном результате того же SELECT:
$row = fbird_fetch_row($anotherResult);
// $row[0] — первая колонка SELECT.
$item = fbird_fetch_object($thirdResult);
// $item->title — свойство; это не элемент массива.

Квадратные скобки обращаются к массиву, стрелка -> — к свойству объекта. Форму выбирают до чтения, исходя из следующего потребителя данных. Три fetch подряд на одном результате прочитали бы три разные строки: здесь для честного сравнения SELECT выполняется заново. В полном файле проверяется false перед доступом к данным. Объект — обычный stdClass без вашего конструктора и методов.

Что получится

НаблюдениеРезультат
Ассоциативный ключ titleКабель
Числовая колонка 0Кабель
Свойство объекта titleКабель
Класс объектаstdClass
Нулевой остаток сохранёнда
SQL NULL отличается от пустой строкида
В каждой форме обе строкида
Одна карточка в массиве, строке CSV и объектеСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__.'/lab.php';
$db=null;$owned=$removed=$rows=$checks=$observations=[];
try {
    $config=f161Config();$connect=f161Capture(static fn()=>fbird_connect(
        $config['database'],$config['user'],$config['password'],'UTF8',0,3));
    if (!is_resource($connect['value']) || $connect['warning']) {f161Failure('Соединение не открыто',$connect);}
    $db=$connect['value'];unset($connect);$runtime=f161Runtime($db,$config);

    function aliasResult161($value,string $operation) {
        if (!is_resource($value)) {throw new RuntimeException($operation.': ожидался результат SELECT.');}
        return $value;
    }
    function aliasFreeResult161(&$result): bool {
        $call=f161Capture(static fn()=>fbird_free_result($result));$result=null;
        if ($call['value']!==true || $call['warning']) {f161Failure('Результат не освобождён',$call);}
        return true;
    }
    function aliasFreeQuery161(&$query): bool {
        $call=f161Capture(static fn()=>fbird_free_query($query));$query=null;
        if ($call['value']!==true || $call['warning']) {f161Failure('Команда не освобождена',$call);}
        return true;
    }
    function aliasRows161($result,string $shape='assoc'): array {
        $rows=[];
        while (true) {
            $call=f161Capture(static fn()=>match($shape) {
                'assoc'=>fbird_fetch_assoc($result),
                'row'=>fbird_fetch_row($result),
                'object'=>fbird_fetch_object($result),
                default=>throw new InvalidArgumentException('Неизвестная форма строки.')
            });
            if ($call['value']===false) {
                if ($call['warning'] || $call['code']!==0) {f161Failure('Чтение прервано',$call);}
                break;
            }
            $rows[]=$call['value'];
        }
        return $rows;
    }
    function aliasPrepare161($db,string $sql) {
        $call=f161Capture(static fn()=>fbird_prepare($db,$sql));
        if (!is_resource($call['value']) || $call['warning']) {f161Failure('Команда не подготовлена',$call);}
        return $call['value'];
    }
    function aliasExecute161($query,array $values) {
        $call=f161Capture(static fn()=>fbird_execute($query,...$values));
        if ($call['value']===false || $call['warning']) {f161Failure('Команда не выполнена',$call);}
        return $call['value'];
    }

    $name=f161OwnTable($db,$owned,'shapes','(id INTEGER NOT NULL PRIMARY KEY,title VARCHAR(40) CHARACTER SET UTF8 NOT NULL,qty INTEGER NOT NULL,note VARCHAR(40) CHARACTER SET UTF8)');
    $q=f161Qualified($name);
    f161Command($db,"INSERT INTO $q (id,title,qty,note) VALUES (?,?,?,?)",[1,'Кабель',0,null]);
    f161Command($db,"INSERT INTO $q (id,title,qty,note) VALUES (?,?,?,?)",[2,'Клемма',4,'']);
    f161Commit($db);
    $query=aliasPrepare161($db,"SELECT title AS \"title\",qty AS \"qty\",note AS \"note\" FROM $q WHERE id>=? ORDER BY id ROWS 2");
    try {
        $views=[];$objectClass='';
        foreach (['assoc','row','object'] as $shape) {
            $result=aliasResult161(aliasExecute161($query,[1]),'Карточки');
            try {
                $values=aliasRows161($result,$shape);
                if ($shape==='object') {
                    $objectClass=get_class($values[0]);
                    $views[$shape]=array_map(static fn($value)=>['title'=>$value->title,'qty'=>$value->qty,'note'=>$value->note],$values);
                } else {$views[$shape]=$values;}
            } finally {aliasFreeResult161($result);}
        }
    } finally {$released=aliasFreeQuery161($query);}
    $rows=[['Ассоциативный ключ title',$views['assoc'][0]['title']],['Числовая колонка 0',$views['row'][0][0]],
        ['Свойство объекта title',$views['object'][0]['title']],['Класс объекта',$objectClass],
        ['Нулевой остаток сохранён',$views['assoc'][0]['qty']===0],['SQL NULL отличается от пустой строки',$views['assoc'][0]['note']===null && $views['assoc'][1]['note']===''],
        ['В каждой форме обе строки',count($views['assoc'])===2 && count($views['row'])===2 && count($views['object'])===2]];
    $expected=[['title'=>'Кабель','qty'=>0,'note'=>null],['title'=>'Клемма','qty'=>4,'note'=>'']];
    $checks=['assoc_shape'=>$views['assoc']===$expected,'numeric_shape'=>$views['row']===[['Кабель',0,null],['Клемма',4,'']],
        'object_shape'=>$objectClass==='stdClass' && $views['object']===$expected,
        'nullable_key_present'=>array_key_exists('note',$views['assoc'][0]),'query_released'=>$released && $query===null];
    $observations=['forms'=>$views,'object_class'=>$objectClass,'select_executions'=>3,'each_execution_starts_at_first_row'=>true];

} finally {
    if (is_resource($db)) {try {$removed=f161Cleanup($db,$owned);} finally {ibase_close($db);}}
}

f161Report('fb161-alias-result-shapes','Одна карточка в массиве, строке CSV и объекте',$rows,$checks,$observations,$runtime,$owned,$removed);
Ожидаемый результат
Ассоциативный ключ title — Кабель
Числовая колонка 0 — Кабель
Свойство объекта title — Кабель
Класс объекта — stdClass
Нулевой остаток сохранён — да
SQL NULL отличается от пустой строки — да
В каждой форме обе строки — да

Самостоятельный CLI-файл: одна своя таблица, реальные fbird_* вызовы, ограниченные выборки, result.html/result.json, освобождение ресурсов и удаление своей таблицы. lab.php и README.txt приложены.

Фактический HTML-результат · JSON со значениями и проверками.

Ошибки и границы

Регистр имён имеет значение; quoted alias устраняет зависимость шаблона от верхнего регистра исходной колонки. Два одинаковых имени в assoc/object столкнутся: задайте разные AS, например item_id и warehouse_id. Числовая форма сохраняет обе колонки, но требует известного порядка SELECT. isset($row["note"]) для null вернёт false; array_key_exists отличает присутствующее null от отсутствующего ключа. Не применяйте empty к количеству, если 0 — полезное значение.

Разработчик получил из Firebird числовые позиции и ожидал имена колонок. Исходная задача на Stack Overflow. Показываем разницу fetch_row и fetch_assoc на одних данных, а также отдельно описание колонок field_info. Ключи результата определяются выбранной формой чтения и SQL-псевдонимами, а не догадкой по JSON-выводу.

Попробуйте изменить задачу

Как подписать заметки на экране: null как «не заполнено», пустую строку как «очищено пользователем»?

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

Сначала сравните $row["note"] === null, затем === "", иначе покажите текст с htmlspecialchars. Эти случаи нельзя склеить одной проверкой empty. Способ чтения строки не заменяет экранирование HTML.

Импорт: связать имена полей с позициями INSERT

Из другого сервиса пришли именованные поля товара в разном порядке, а у одной позиции отсутствует qty. Подготовленный запрос ждёт значения строго id, title, qty. Покажем человеку причину отказа неполной строки, затем сохраним две корректные позиции без перестановки полей и без потери нулевого остатка.

Путь данных
  1. Вход: title/id/qty могут идти в любом порядке
  2. INSERT(id,title,qty) → три позиции ?
  3. Проверка наличия qty → товар 100 отклонён до SQL
  4. Явный список [id,title,qty] → execute для 101 и 102
  5. free_query → commit → ограниченный SELECT сохранённых строк

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

$values = [$item["id"], $item["title"], $item["qty"]];
if (count($values) !== fbird_num_params($query)) {
    throw new DomainException("Изменился контракт INSERT");
}
$affected = fbird_execute($query, ...$values);
// Перед этой записью проверить наличие ключей и допустимость значений.

Многоточие ... передаёт элементы списка отдельными аргументами. Входной ассоциативный массив нельзя бездумно превратить в array_values: его порядок может отличаться от INSERT. num_params проверяет количество, а param_info помогает увидеть тип представления конкретной позиции. Имена id/title/qty задаёт наше приложение: сам дескриптор параметра может не содержать названия поля.

Что получится

НаблюдениеРезультат
Входных мест INSERT3
Отклонён товар без поля100: qty
Сохранены товары101, 102
Товар 101Муфта: 0
Товар 102Кабель: 5
Представление названияVARCHAR / 80 байт
Порядок ключей входа не повредил поляда
Команда INSERT освобожденада
Импорт: связать имена полей с позициями INSERTСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__.'/lab.php';
$db=null;$owned=$removed=$rows=$checks=$observations=[];
try {
    $config=f161Config();$connect=f161Capture(static fn()=>fbird_connect(
        $config['database'],$config['user'],$config['password'],'UTF8',0,3));
    if (!is_resource($connect['value']) || $connect['warning']) {f161Failure('Соединение не открыто',$connect);}
    $db=$connect['value'];unset($connect);$runtime=f161Runtime($db,$config);

    function aliasResult161($value,string $operation) {
        if (!is_resource($value)) {throw new RuntimeException($operation.': ожидался результат SELECT.');}
        return $value;
    }
    function aliasFreeResult161(&$result): bool {
        $call=f161Capture(static fn()=>fbird_free_result($result));$result=null;
        if ($call['value']!==true || $call['warning']) {f161Failure('Результат не освобождён',$call);}
        return true;
    }
    function aliasFreeQuery161(&$query): bool {
        $call=f161Capture(static fn()=>fbird_free_query($query));$query=null;
        if ($call['value']!==true || $call['warning']) {f161Failure('Команда не освобождена',$call);}
        return true;
    }
    function aliasRows161($result,string $shape='assoc'): array {
        $rows=[];
        while (true) {
            $call=f161Capture(static fn()=>match($shape) {
                'assoc'=>fbird_fetch_assoc($result),
                'row'=>fbird_fetch_row($result),
                'object'=>fbird_fetch_object($result),
                default=>throw new InvalidArgumentException('Неизвестная форма строки.')
            });
            if ($call['value']===false) {
                if ($call['warning'] || $call['code']!==0) {f161Failure('Чтение прервано',$call);}
                break;
            }
            $rows[]=$call['value'];
        }
        return $rows;
    }
    function aliasPrepare161($db,string $sql) {
        $call=f161Capture(static fn()=>fbird_prepare($db,$sql));
        if (!is_resource($call['value']) || $call['warning']) {f161Failure('Команда не подготовлена',$call);}
        return $call['value'];
    }
    function aliasExecute161($query,array $values) {
        $call=f161Capture(static fn()=>fbird_execute($query,...$values));
        if ($call['value']===false || $call['warning']) {f161Failure('Команда не выполнена',$call);}
        return $call['value'];
    }

    $name=f161OwnTable($db,$owned,'import','(id INTEGER NOT NULL PRIMARY KEY,title VARCHAR(20) CHARACTER SET UTF8 NOT NULL,qty INTEGER NOT NULL CHECK(qty>=0))');
    $q=f161Qualified($name);
    $query=aliasPrepare161($db,"INSERT INTO $q (id,title,qty) VALUES (?,?,?)");
    try {
        $required=['id','title','qty'];$needed=fbird_num_params($query);$parameterDescriptions=[];
        for ($i=0;$i<$needed;$i++) {
            $info=fbird_param_info($query,$i);
            if ($info===false) {throw new RuntimeException('Описание параметра отсутствует.');}
            $parameterDescriptions[]=['position'=>$i,'field'=>$required[$i],'type'=>$info['type'],'bytes'=>$info['length']];
        }
        $input=[['id'=>100,'title'=>'Пропущен остаток'],['qty'=>0,'title'=>'Муфта','id'=>101],['title'=>'Кабель','id'=>102,'qty'=>5]];
        $rejected=[];$inserted=[];
        foreach ($input as $item) {
            $missing=array_values(array_diff($required,array_keys($item)));
            if ($missing!==[]) {$rejected[]=['id'=>$item['id'],'missing'=>$missing];continue;}
            $values=[$item['id'],$item['title'],$item['qty']];
            if (count($values)!==$needed || !is_int($values[0]) || !is_string($values[1]) || !is_int($values[2]) || $values[2]<0) {
                throw new DomainException('Неверный контракт строки импорта.');
            }
            $inserted[]=aliasExecute161($query,$values);
        }
    } finally {$released=aliasFreeQuery161($query);}
    f161Commit($db);
    $call=f161Capture(static fn()=>fbird_query($db,"SELECT id AS \"id\",title AS \"title\",qty AS \"qty\" FROM $q WHERE id BETWEEN ? AND ? ORDER BY id ROWS 3",100,102));
    if ($call['value']===false || $call['warning']) {f161Failure('Проверка импорта не выполнена',$call);}
    $result=aliasResult161($call['value'],'Импорт');unset($call);
    try {$saved=aliasRows161($result);} finally {aliasFreeResult161($result);}
    $rows=[['Входных мест INSERT',$needed],['Отклонён товар без поля',$rejected[0]['id'].': '.implode(', ',$rejected[0]['missing'])],
        ['Сохранены товары',implode(', ',array_column($saved,'id'))],['Товар 101',$saved[0]['title'].': '.$saved[0]['qty']],
        ['Товар 102',$saved[1]['title'].': '.$saved[1]['qty']],['Представление названия',$parameterDescriptions[1]['type'].' / '.$parameterDescriptions[1]['bytes'].' байт'],
        ['Порядок ключей входа не повредил поля',array_column($saved,'qty')===[0,5]],['Команда INSERT освобождена',$released]];
    $checks=['input_count'=>$needed===3,'missing_field_rejected'=>$rejected===[['id'=>100,'missing'=>['qty']]],
        'ordered_values_saved'=>$saved===[['id'=>101,'title'=>'Муфта','qty'=>0],['id'=>102,'title'=>'Кабель','qty'=>5]],
        'title_descriptor'=>$parameterDescriptions[1]['type']==='VARCHAR' && $parameterDescriptions[1]['bytes']==='80',
        'dml_return_counts'=>$inserted===[1,1],'query_released'=>$released && $query===null];
    $observations=['saved'=>$saved,'rejected'=>$rejected,'input_contract'=>$parameterDescriptions,'execute_returns'=>$inserted,
        'business_field_names_are_application_mapping'=>true];

} finally {
    if (is_resource($db)) {try {$removed=f161Cleanup($db,$owned);} finally {ibase_close($db);}}
}

f161Report('fb161-alias-import-contract','Импорт: связать имена полей с позициями INSERT',$rows,$checks,$observations,$runtime,$owned,$removed);
Ожидаемый результат
Входных мест INSERT — 3
Отклонён товар без поля — 100: qty
Сохранены товары — 101, 102
Товар 101 — Муфта: 0
Товар 102 — Кабель: 5
Представление названия — VARCHAR / 80 байт
Порядок ключей входа не повредил поля — да
Команда INSERT освобождена — да

Самостоятельный CLI-файл: одна своя таблица, реальные fbird_* вызовы, ограниченные выборки, result.html/result.json, освобождение ресурсов и удаление своей таблицы. lab.php и README.txt приложены.

Фактический HTML-результат · JSON со значениями и проверками.

Ошибки и границы

VARCHAR(20) в UTF8 описывается длиной 80 байт. Это не разрешение принимать 80 любых символов: проверка длины названия и ограничения предметной области остаются отдельными. Здесь сознательно показана политика частичного импорта: неполная строка пропускается, корректные сохраняются. Если пакет должен приниматься целиком, объедините проверки с транзакцией и отменой всех изменений. Значение 1 от успешного INSERT — число затронутых строк, не идентификатор нового товара. Ошибка SQL не равна пропущенному полю: её нужно диагностировать и не подтверждать ошибочную операцию.

В обсуждении отладки prepared statement хотят увидеть готовую строку SQL; среди предложений есть и пример для native ibase с текстовой подстановкой знаков вопроса. Исходная задача на Stack Overflow. Мы оставляем SQL-шаблон и упорядоченные параметры отдельно. Метаданные позволяют объяснить контракт, но не создают безопасную SQL-строку для повторного исполнения. Значения и секреты не должны попадать в публичный диагностический вывод.

Попробуйте изменить задачу

Сервис добавил ключ category перед id. Нужно ли менять SQL или порядок значений?

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

Если этот экран не хранит category, SQL остаётся прежним. Явный список id/title/qty также остаётся прежним. Если новое поле требуется хранить, сначала измените схему и INSERT, затем список и его проверку; одного совпадения количества мало.

Справочник 13 имён для запросов и строк

Нужно сделатьКарточка
Подключить существующее приложение к Firebird.fbird_connect
Выполнить SQL и отделить набор строк от результата изменения.fbird_query
Создать повторно используемый SQL-шаблон.fbird_prepare
Выполнить prepared-команду с очередными значениями.fbird_execute
Прочитать следующую строку по именам колонок.fbird_fetch_assoc
Прочитать следующую строку по порядку SELECT.fbird_fetch_row
Получить следующую строку как stdClass.fbird_fetch_object
Узнать число выходных колонок, не расходуя строку.fbird_num_fields
Прочитать имя, тип и длину выходной колонки.fbird_field_info
Узнать число входных мест prepared-команды.fbird_num_params
Прочитать описание входного параметра по позиции.fbird_param_info
Закончить срок жизни подготовленной команды.fbird_free_query
Освободить результат SELECT, сохранив уже прочитанные данные.fbird_free_result

От понятного SELECT к целой операции

Теперь при чтении старого кода можно проследить, где появляется соединение, где подготовленная команда, где результат и где обычные данные PHP. Следующий шаг — транзакции, ошибки и идентификаторы через fbird_*. Для изменения прав служебного пользователя есть отдельная глава доступа, для параметров и ограничений формы — подробный входной контракт.

Новые имена не требуют переписывать рабочие запросы механически. Если проводится рефакторинг, сохраняйте смысл SQL, порядок значений, кодировку, диалект, завершение транзакций и освобождение ресурсов. Эта глава раскрывает перечисленные 13 alias; она не выдаёт их за другой драйвер или за завершение всей ветки Firebird.

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

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

fbird_connectПодключить существующее приложение к Firebird.

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

fbird_connect(string $database = "", string $username = "", string $password = "", string $charset = "", int $buffers = 0, int $dialect = 0, string $role = "", int $sync = 0): resource|false

Нужен доступ к существующей базе старого приложения, в исходнике которого стоит fbird_connect. Соединение — это ресурс для следующих запросов, а не сами товары. ibase_connect — каноническое имя той же операции. В драйвере FirebirdSQL 3.0.1 fbird_connect зарегистрировано как alias того же C-обработчика; собственного формата ресурсов или отдельного SQL-движка у него нет.

Параметр / результатПравило
database = ""Путь к базе на машине сервера или его согласованное имя/alias. Исторические формы: hostname:path для TCP, hostname/port:path для другого порта; //hostname/path относится к старому NetBEUI и не обещает наличие этого транспорта сегодня. Пустое значение в 3.0.1 может дополняться ibase.default_db.
username / password = ""Имя пользователя и его пароль. Пустые значения могут дополняться ibase.default_user/default_password. Секрет приходит из конфигурации, не выводится на страницу и не хранится в публичном примере. Вход и права SELECT — отдельные проверки.
charset = ""Кодировка текста соединения. Для наших UTF-8-строк явно UTF8. Драйвер учитывает ibase.default_charset, если аргумент пуст. Этот параметр не перекодирует произвольный исходный файл и не меняет DDL всех текстовых колонок.
buffers = 0Запрашиваемое число серверных буферов страниц. 0 или отсутствие оставляет выбор серверу; это не размер PHP-массива.
dialect = 0SQL-диалект соединения; 0 выбирает текущий поддерживаемый клиентом. Здесь явно 3. Старой базе может требоваться другой диалект: префикс функции этого не исправляет.
role = ""Роль соединения; её имя не выдаёт привилегии само по себе. Историческая страница относит поддержку к InterBase 5 и новее. Firebird требует существующей выданной роли.
sync = 0Восьмой параметр оставлен без пояснения в исторической странице, но принимается C-кодом 3.0.1: ненулевое значение задаёт force-write, сравнение с синхронным режимом выбирает способ записи. Здесь аргумент опущен, чтобы не изменять настройку надёжности базы.
resource / falseРесурс успешного соединения либо false при отказе. Повтор тех же аргументов может вернуть уже открытое соединение. Обычный connect не означает persistent между запросами; явное закрытие и конец скрипта завершают его обычный срок жизни.

Версии: историческая страница PHP Manual указывает PHP 5 и PHP 7 до 7.4. Старое встроенное расширение удалено из поставки PHP 7.4; в наших примерах эти имена предоставляет отдельный interbase 3.0.1 для PHP 8.2.35. Наличие функции проверяют в том PHP, который исполняет приложение. Это не метод PDO и не рекомендация менять работающий драйвер только ради другого префикса.

Подключить каталог в UTF8

$db = fbird_connect($database, $user, $password, "UTF8", 0, 3);
if ($db === false) { throw new RuntimeException("База недоступна"); }

Так начинается экран наличия. Его результат — две карточки, а не печать ресурса соединения. Неверный путь, недоступный сервер, отказ входа и невозможная кодировка требуют диагностики; сырые сообщения могут раскрывать путь базы. Ошибка преобразования текста может появиться позднее при query/fetch.

Тот же вход для подготовленного импорта

$db = fbird_connect($database, $user, $password, "UTF8", 0, 3);
if ($db === false) { throw new RuntimeException("Импорт не подключён"); }
$query = fbird_prepare($db, $insertSql);

Импорт с именованными полями использует то же соединение и затем отдельную команду. Не проверяйте смену пароля повторным connect в том же процессе: возможен повторно используемый ресурс. Проверка настоящего нового входа и права на таблицы разобраны отдельно. Параметры показаны позиционно, потому что историческая arginfo и фактический C-разбор не во всём совпадают.

fbird_queryВыполнить SQL и отделить набор строк от результата изменения.

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

fbird_query([resource $dbOrTransaction, [resource $transaction,]] string $sql, mixed ...$values): resource|int|bool

Разовый SELECT нужен, когда SQL выполняется один раз и требуется сразу прочитать результат. Значения можно передать отдельно даже без явного prepare. ibase_query — каноническое имя той же операции. В драйвере FirebirdSQL 3.0.1 fbird_query зарегистрировано как alias того же C-обработчика; собственного формата ресурсов или отдельного SQL-движка у него нет.

Параметр / результатПравило
Формы вызоваfbird_query($sql, ...$values); fbird_query($dbOrTransaction, $sql, ...$values); fbird_query($db, $transaction, $sql, ...$values). При отсутствии ресурса используется последнее соединение; без явной транзакции — его транзакция по умолчанию.
sql / valuesСтрока SQL и последовательные значения для ?. Это mixed-значения, а не только int из сокращённой исторической сигнатуры. Имена таблиц/полей и ORDER BY не являются параметрами значений: их выбирают из разрешённого приложением набора.
ВозвратSELECT, в том числе пустой, даёт ресурс результата. INSERT/UPDATE/DELETE без возвращаемого набора дают положительное число затронутых строк, а успешная операция без затронутых строк — true. Успешная команда без набора также может вернуть true. false — отказ; сравнивайте строго, а не через if (!$value). Если SQL возвращает набор, сначала обработайте ресурс.
ОшибкиПроверяйте false и сохраняйте диагностику до другого вызова. Кодировка, синтаксис, ограничения и права проверяются настоящим сервером; alias не обходит их. Неверный ресурс отдельно может вызвать ошибку PHP.

Версии: историческая страница PHP Manual указывает PHP 5 и PHP 7 до 7.4. Старое встроенное расширение удалено из поставки PHP 7.4; в наших примерах эти имена предоставляет отдельный interbase 3.0.1 для PHP 8.2.35. Наличие функции проверяют в том PHP, который исполняет приложение. Это не метод PDO и не рекомендация менять работающий драйвер только ради другого префикса.

Данные для экрана

$result = fbird_query($db, 'SELECT title AS "title",qty AS "qty" FROM items WHERE active=? AND qty>=? ORDER BY id ROWS 2', 1, 1);

В экране наличия условия и лимит применяет Firebird. Перед fetch проверьте resource/false. Пустой набор — действующий результат, у которого первый fetch даст false без ошибки.

Убедиться, что импорт сохранился

$result = fbird_query($db, "SELECT id,title,qty FROM items WHERE id BETWEEN ? AND ? ORDER BY id ROWS 3", 100, 102);

В проверке импорта этот SELECT выполняется после commit и выдаёт только 101 и 102. Его ресурс освобождается отдельно. Сама query не является обещанием подтверждения всей бизнес-операции: управляйте транзакцией явно. Исторический специальный режим IBASE_CREATE для CREATE DATABASE также существует в реализации, но не нужен для чтения и не должен принимать произвольную строку пользователя.

fbird_prepareСоздать повторно используемый SQL-шаблон.

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

fbird_prepare(string $sql): resource|false; fbird_prepare(resource $dbOrTransaction, string $sql): resource|false; fbird_prepare(resource $db, resource $transaction, string $sql): resource|false

При одинаковом SQL с меняющимися значениями полезно один раз подготовить команду и затем выполнять её многократно. Подготовка описывает входные места и выходные колонки до чтения строк. ibase_prepare — каноническое имя той же операции. В драйвере FirebirdSQL 3.0.1 fbird_prepare зарегистрировано как alias того же C-обработчика; собственного формата ресурсов или отдельного SQL-движка у него нет.

Параметр / результатПравило
Формы вызоваfbird_prepare($sql); fbird_prepare($dbOrTransaction,$sql); fbird_prepare($db,$transaction,$sql). Вторая форма принимает соединение или транзакцию; третья — оба ресурса. При пропуске используются последнее соединение и его транзакция по умолчанию.
sqlSQL-шаблон с позиционными ?. Параметризуются значения, а не имя таблицы/поля. Значения ещё не передаются; синтаксис и возможность подготовки проверяются сервером.
transactionРесурс транзакции, не строка: историческое отображение сигнатуры содержит неточный string. Подготовленная команда связана с этим контекстом.
resource / falseРесурс prepared-команды либо false. Это не result для fetch. При ошибке сохраняйте сообщение/код сразу; закрытый или чужой тип ресурса не подходит. После последнего execute нужно free_query.

Версии: историческая страница PHP Manual указывает PHP 5 и PHP 7 до 7.4. Старое встроенное расширение удалено из поставки PHP 7.4; в наших примерах эти имена предоставляет отдельный interbase 3.0.1 для PHP 8.2.35. Наличие функции проверяют в том PHP, который исполняет приложение. Это не метод PDO и не рекомендация менять работающий драйвер только ради другого префикса.

Подготовить диапазон остатков

$query = fbird_prepare($db, "SELECT title,qty FROM items WHERE qty BETWEEN ? AND ? ORDER BY id");
if ($query === false) { throw new RuntimeException("Нельзя подготовить диапазон"); }

Две вкладки используют один query для диапазонов 2…8 и 0…1. Положения 0 и 1 остаются минимумом и максимумом при каждом execute; одинаковый тип INTEGER не выражает этот смысл.

Подготовить строку приёмки товаров

$query = fbird_prepare($db, "INSERT INTO items (id,title,qty) VALUES (?,?,?)");
if ($query === false) { throw new RuntimeException("Нельзя подготовить импорт"); }
$needed = fbird_num_params($query); // 3

В импорте подготовка делается до цикла входных строк. Освободить команду нужно и при отказе последующей проверки: finally охватывает весь её срок жизни. Подготовка сама не сохраняет товар и не возвращает его ID.

fbird_executeВыполнить prepared-команду с очередными значениями.

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

fbird_execute(resource $query, mixed ...$values): resource|int|bool

Когда шаблон уже подготовлен, execute связывает очередные значения с его местами и выполняет команду. ibase_execute — каноническое имя той же операции. В драйвере FirebirdSQL 3.0.1 fbird_execute зарегистрировано как alias того же C-обработчика; собственного формата ресурсов или отдельного SQL-движка у него нет.

Параметр / результатПравило
queryДействующий ресурс из fbird_prepare/ibase_prepare. Ресурсы совместимы, потому что имена ведут в один обработчик.
...valuesЗначения отдельными позиционными аргументами; для готового числового списка используйте ...$values. Передавать ассоциативные поля как именованные аргументы PHP — не то же самое, что связывать SQL :name.
ВозвратSELECT, в том числе пустой, даёт ресурс результата. INSERT/UPDATE/DELETE без возвращаемого набора дают положительное число затронутых строк, а успешная операция без затронутых строк — true. Успешная команда без набора также может вернуть true. false — отказ; сравнивайте строго, а не через if (!$value). Если SQL возвращает набор, сначала обработайте ресурс.
Число значений и ошибкиВ 3.0.1 недостаток значений вызывает предупреждение и false; избыток сообщает notice, поэтому не рассчитывайте на молчаливое игнорирование. Проверяйте точное количество сами. Ограничения данных и SQL-ошибки сохраняйте сразу; положительное число не является новым ID.

Версии: историческая страница PHP Manual указывает PHP 5 и PHP 7 до 7.4. Старое встроенное расширение удалено из поставки PHP 7.4; в наших примерах эти имена предоставляет отдельный interbase 3.0.1 для PHP 8.2.35. Наличие функции проверяют в том PHP, который исполняет приложение. Это не метод PDO и не рекомендация менять работающий драйвер только ради другого префикса.

Переключить вкладку

$result = fbird_execute($query, 2, 8);
// Прочитать/освободить результат.
$result = fbird_execute($query, 0, 1);

Подготовленный фильтр сначала возвращает Кабель и Клемму, затем Ленту с нулевым остатком. Освобождается каждый result; общий query остаётся до окончания второй вкладки.

Сохранить именованные данные в порядке SQL

$values = [$item["id"], $item["title"], $item["qty"]];
$affected = fbird_execute($query, ...$values);
if ($affected === false) { throw new RuntimeException("Товар не сохранён"); }

Это сохранение товаров 101 и 102. На каждом INSERT получено 1. После всех успешных записей отдельно выполнен commit. Если требование — принять пакет целиком, не подтверждайте частичный набор после исключения.

fbird_fetch_assocПрочитать следующую строку по именам колонок.

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

fbird_fetch_assoc(resource $result, int $fetch_flag = 0): array|false

Именованные ключи удобны для карточки страницы: название читается как $row["title"], без зависимости от номера колонки. ibase_fetch_assoc — каноническое имя той же операции. В драйвере FirebirdSQL 3.0.1 fbird_fetch_assoc зарегистрировано как alias того же C-обработчика; собственного формата ресурсов или отдельного SQL-движка у него нет.

Параметр / результатПравило
resultДействующий ресурс результата SELECT из query/execute. Каждый вызов читает следующую строку и продвигает курсор; метаданные num_fields/field_info его не продвигают.
fetch_flag = 0Без флагов BLOB представлен ID, даты/время — строками настроенного формата. IBASE_TEXT (современное имя IBASE_FETCH_BLOBS) просит содержимое BLOB; IBASE_UNIXTIME — числовое представление даты/времени. Флаги объединяют |. Выбранный драйвер также поддерживает IBASE_FETCH_ARRAYS для SQL ARRAY. Большой BLOB/ARRAY при этом целиком попадает в память.
ВозвратВозвращается ассоциативный массив одной строки. SQL NULL остаётся null; пустой текст и 0 имеют собственные значения. Строго false обозначает конец набора либо ошибку чтения.
Ошибки и границыПосле false сразу снимите код/предупреждение: нулевой код без предупреждения в наших программах означает EOF. Закрытый результат или неверный тип аргумента — ошибка обращения к API, а не пустая выборка. Не продолжайте fetch после free_result.

Версии: историческая страница PHP Manual указывает PHP 5 и PHP 7 до 7.4. Старое встроенное расширение удалено из поставки PHP 7.4; в наших примерах эти имена предоставляет отдельный interbase 3.0.1 для PHP 8.2.35. Наличие функции проверяют в том PHP, который исполняет приложение. Это не метод PDO и не рекомендация менять работающий драйвер только ради другого префикса.

Вывести первую карточку

$row = fbird_fetch_assoc($result);
if ($row !== false) { echo $row["title"], ": ", $row["qty"]; }

В каталоге первая строка — Кабель: 3. Для всех строк повторяют fetch в цикле, а false разбирают с немедленной диагностикой. Не вставляйте отдельный пробный fetch перед основным циклом.

Различить отсутствующую заметку и пустую

$row = fbird_fetch_assoc($result);
if ($row !== false && array_key_exists("note", $row)) {
    $label = $row["note"] === null ? "не заполнено" : ($row["note"] === "" ? "очищено" : $row["note"]);
}

Одинаковые товары в трёх формах сохраняют note=null у первого и note="" у второго. Повторяющиеся названия колонок перезаписываются последним: используйте разные AS или fetch_row. Alias в двойных кавычках фиксирует регистр ключа.

Флаг IBASE_UNIXTIME нельзя считать обещанием UTC: в закреплённом Windows-драйвере преобразование использует нативное время процесса; отдельный TIME в проверке дал -1. Разбор времён с явной зоной и список флагов разобраны в настройках Firebird. Для больших вложений используйте порционное чтение BLOB, а не автоматическую загрузку всего содержимого. Форму строки выбирают независимо от её экранирования для HTML или правил экспорта CSV.

fbird_fetch_rowПрочитать следующую строку по порядку SELECT.

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

fbird_fetch_row(resource $result, int $fetch_flag = 0): array|false

Числовая строка удобна для фиксированного порядка колонок файла выгрузки. Её индексы идут от нуля в порядке SELECT. ibase_fetch_row — каноническое имя той же операции. В драйвере FirebirdSQL 3.0.1 fbird_fetch_row зарегистрировано как alias того же C-обработчика; собственного формата ресурсов или отдельного SQL-движка у него нет.

Параметр / результатПравило
resultДействующий ресурс результата SELECT из query/execute. Каждый вызов читает следующую строку и продвигает курсор; метаданные num_fields/field_info его не продвигают.
fetch_flag = 0Без флагов BLOB представлен ID, даты/время — строками настроенного формата. IBASE_TEXT (современное имя IBASE_FETCH_BLOBS) просит содержимое BLOB; IBASE_UNIXTIME — числовое представление даты/времени. Флаги объединяют |. Выбранный драйвер также поддерживает IBASE_FETCH_ARRAYS для SQL ARRAY. Большой BLOB/ARRAY при этом целиком попадает в память.
ВозвратВозвращается числовой массив одной строки: [0], [1], … . SQL NULL остаётся null; пустой текст и 0 имеют собственные значения. Строго false обозначает конец набора либо ошибку чтения.
Ошибки и границыПосле false сразу снимите код/предупреждение: нулевой код без предупреждения в наших программах означает EOF. Закрытый результат или неверный тип аргумента — ошибка обращения к API, а не пустая выборка. Не продолжайте fetch после free_result.

Версии: историческая страница PHP Manual указывает PHP 5 и PHP 7 до 7.4. Старое встроенное расширение удалено из поставки PHP 7.4; в наших примерах эти имена предоставляет отдельный interbase 3.0.1 для PHP 8.2.35. Наличие функции проверяют в том PHP, который исполняет приложение. Это не метод PDO и не рекомендация менять работающий драйвер только ради другого префикса.

Прочитать две колонки вкладки

$row = fbird_fetch_row($result);
if ($row !== false) { echo $row[0], ": ", $row[1]; }

В диапазоне остатков SELECT содержит title,qty, поэтому $row[0] — название. Это массив, обращение $row->title к нему неверно. Изменив список SELECT, обновите потребителя.

Получить порядок полей для CSV

$row = fbird_fetch_row($result);
// SELECT title,qty,note: ["Кабель", 0, null].
// Перед fputcsv явно выбрать представление null: например "не заполнено".

Сравнение форм показывает исходные значения для такой выгрузки. Числовые позиции позволяют сохранить два одноимённых поля JOIN; это не даёт им бизнес-названий. Заголовки берут из собственного контракта или field_info, не из попытки прочитать дополнительную строку.

Флаг IBASE_UNIXTIME нельзя считать обещанием UTC: в закреплённом Windows-драйвере преобразование использует нативное время процесса; отдельный TIME в проверке дал -1. Разбор времён с явной зоной и список флагов разобраны в настройках Firebird. Для больших вложений используйте порционное чтение BLOB, а не автоматическую загрузку всего содержимого. Форму строки выбирают независимо от её экранирования для HTML или правил экспорта CSV.

fbird_fetch_objectПолучить следующую строку как stdClass.

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

fbird_fetch_object(resource $result, int $fetch_flag = 0): object|false

Если существующий шаблон читает $item->title, следующую строку можно получить объектом с именованными свойствами. ibase_fetch_object — каноническое имя той же операции. В драйвере FirebirdSQL 3.0.1 fbird_fetch_object зарегистрировано как alias того же C-обработчика; собственного формата ресурсов или отдельного SQL-движка у него нет.

Параметр / результатПравило
resultДействующий ресурс результата SELECT из query/execute. Каждый вызов читает следующую строку и продвигает курсор; метаданные num_fields/field_info его не продвигают.
fetch_flag = 0Без флагов BLOB представлен ID, даты/время — строками настроенного формата. IBASE_TEXT (современное имя IBASE_FETCH_BLOBS) просит содержимое BLOB; IBASE_UNIXTIME — числовое представление даты/времени. Флаги объединяют |. Выбранный драйвер также поддерживает IBASE_FETCH_ARRAYS для SQL ARRAY. Большой BLOB/ARRAY при этом целиком попадает в память.
ВозвратВозвращается stdClass с полями результата в свойствах. Класса приложения, методов и пользовательского конструктора эта функция не принимает. SQL NULL остаётся null; пустой текст и 0 имеют собственные значения. Строго false обозначает конец набора либо ошибку чтения.
Ошибки и границыПосле false сразу снимите код/предупреждение: нулевой код без предупреждения в наших программах означает EOF. Закрытый результат или неверный тип аргумента — ошибка обращения к API, а не пустая выборка. Не продолжайте fetch после free_result.

Версии: историческая страница PHP Manual указывает PHP 5 и PHP 7 до 7.4. Старое встроенное расширение удалено из поставки PHP 7.4; в наших примерах эти имена предоставляет отдельный interbase 3.0.1 для PHP 8.2.35. Наличие функции проверяют в том PHP, который исполняет приложение. Это не метод PDO и не рекомендация менять работающий драйвер только ради другого префикса.

Карточка старого виджета

$item = fbird_fetch_object($result);
if ($item !== false) { echo $item->title, ": ", $item->qty; }

В трёх формах карточки это Кабель: 0. Для свойства title SQL использует AS "title". Никакой дополнительной бизнес-логики при создании stdClass не исполняется.

Замечание оператора

$item = fbird_fetch_object($result);
if ($item !== false && property_exists($item, "note")) {
    $unsetNote = $item->note === null;
    $clearedNote = $item->note === "";
}

На двух строках видны разные состояния заметки. Одноимённые колонки сталкиваются так же, как у fetch_assoc: последний столбец займёт свойство. Это не ORM: объект не сохраняет изменения обратно в базу при присваивании $item->qty.

Флаг IBASE_UNIXTIME нельзя считать обещанием UTC: в закреплённом Windows-драйвере преобразование использует нативное время процесса; отдельный TIME в проверке дал -1. Разбор времён с явной зоной и список флагов разобраны в настройках Firebird. Для больших вложений используйте порционное чтение BLOB, а не автоматическую загрузку всего содержимого. Форму строки выбирают независимо от её экранирования для HTML или правил экспорта CSV.

fbird_num_fieldsУзнать число выходных колонок, не расходуя строку.

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

fbird_num_fields(resource $result): int

Перед построением таблицы результата полезно узнать, сколько колонок предусмотрено SELECT. Число товаров здесь ни при чём. ibase_num_fields — каноническое имя той же операции. В драйвере FirebirdSQL 3.0.1 fbird_num_fields зарегистрировано как alias того же C-обработчика; собственного формата ресурсов или отдельного SQL-движка у него нет.

Параметр / результатПравило
resultОфициальный контракт принимает результат query/execute. C-реализация 3.0.1 также читает выходной дескриптор подготовленной команды. Ресурс должен быть действующим и подходящего типа.
intКоличество выходных колонок; при отсутствии выходного дескриптора реализация возвращает 0. Даже пустой SELECT сохраняет число своих колонок. Это не row count и не количество входных ?.
ОшибкиНе передавайте false от неуспешного запроса и не считайте ошибку PHP нулём колонок. Вызов не читает и не пропускает строку.

Версии: историческая страница PHP Manual указывает PHP 5 и PHP 7 до 7.4. Старое встроенное расширение удалено из поставки PHP 7.4; в наших примерах эти имена предоставляет отдельный interbase 3.0.1 для PHP 8.2.35. Наличие функции проверяют в том PHP, который исполняет приложение. Это не метод PDO и не рекомендация менять работающий драйвер только ради другого префикса.

Создать заголовки экрана

$columns = fbird_num_fields($result);
for ($i=0; $i<$columns; $i++) {
    $info = fbird_field_info($result, $i);
    // Проверить false, использовать alias для заголовка.
}

В каталоге три колонки: id,title,qty. Затем читаются обе строки, включая первую. Заголовки получили из описания, не пожертвовав товаром ради образца.

Проверить формат выгрузки

$columns = fbird_num_fields($result);
if ($columns !== 3) { throw new DomainException("Ожидались title, qty, note"); }

Для числовой формы карточки ожидаются три колонки. Такая проверка выявит добавленный столбец, но не обмен title и note местами: дополните её проверкой alias через field_info. Две строки и три колонки — независимые числа.

fbird_field_infoПрочитать имя, тип и длину выходной колонки.

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

fbird_field_info(resource $result, int $field_number): array|false

Чтобы подписать или проверить колонки результата, нужно их описание, а не случайные значения первого товара. ibase_field_info — каноническое имя той же операции. В драйвере FirebirdSQL 3.0.1 fbird_field_info зарегистрировано как alias того же C-обработчика; собственного формата ресурсов или отдельного SQL-движка у него нет.

Параметр / результатПравило
result / field_numberДействующий result и целый индекс колонки от 0 до num_fields-1. Реализация 3.0.1 допускает выходной дескриптор prepared-команды.
array / falseМассив описания. При индексе вне диапазона — false, без обязательного предупреждения; при отсутствии выходного дескриптора 3.0.1 также сообщает диагностическую ошибку. Неверный ресурс обрабатывается отдельно.
name / alias / relationИсходное имя, псевдоним и имя отношения из дескриптора. Выражения могут не иметь relation/name; у параметров часто пусты все три значения.
lengthДлина представления в байтах; в 3.0.1 это строка, например "80". Не число символов и не полное правило формы.
typeСтроковое описание типа, например INTEGER/VARCHAR/NUMERIC(18,2). Оно может отражать хранение и масштаб, а не буквально исходный DDL.
Числовые ключиДрайвер добавляет 0…4 с теми же значениями наряду с именованными. Для ясности используйте именованные поля, не считайте десять элементов десятью свойствами.

Версии: историческая страница PHP Manual указывает PHP 5 и PHP 7 до 7.4. Старое встроенное расширение удалено из поставки PHP 7.4; в наших примерах эти имена предоставляет отдельный interbase 3.0.1 для PHP 8.2.35. Наличие функции проверяют в том PHP, который исполняет приложение. Это не метод PDO и не рекомендация менять работающий драйвер только ради другого префикса.

Подпись количества

$info = fbird_field_info($result, 2);
if ($info === false) { throw new RuntimeException("Третьей колонки нет"); }
echo $info["alias"]; // qty для SELECT id,title,qty

Это третья колонка экрана наличия. Она читается без перемещения курсора, поэтому первый товар не пропадёт.

Защитить CSV от перестановки столбцов

$expected = ["title", "qty", "note"];
foreach ($expected as $index=>$alias) {
    $info = fbird_field_info($result, $index);
    if ($info === false || $info["alias"] !== $alias) {
        throw new DomainException("Изменился формат выгрузки");
    }
}

Это прикладная проверка SELECT из сценария форм строки. Метаданные не содержат полного набора CHECK/внешних ключей/прав: не генерируйте по ним всю бизнес-форму. Входные места INSERT описывает другая функция — fbird_param_info.

fbird_num_paramsУзнать число входных мест prepared-команды.

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

fbird_num_params(resource $query): int

До execute можно объяснить, сколько значений нужно подготовленному фильтру или импорту. ibase_num_params — каноническое имя той же операции. В драйвере FirebirdSQL 3.0.1 fbird_num_params зарегистрировано как alias того же C-обработчика; собственного формата ресурсов или отдельного SQL-движка у него нет.

Параметр / результатПравило
queryДействующий prepared-ресурс из prepare, а не result SELECT и не строка SQL.
intЧисло входных мест связывания. 0 допустим для шаблона без ?. Число берётся из разобранного сервером дескриптора, не подсчётом символов. Литерал '?' не является параметром.
Ошибки и пределНеверный/закрытый ресурс может дать ошибку PHP. Число не объясняет порядок, тип и смысл значений; равные количества не доказывают правильность запроса.

Версии: историческая страница PHP Manual указывает PHP 5 и PHP 7 до 7.4. Старое встроенное расширение удалено из поставки PHP 7.4; в наших примерах эти имена предоставляет отдельный interbase 3.0.1 для PHP 8.2.35. Наличие функции проверяют в том PHP, который исполняет приложение. Это не метод PDO и не рекомендация менять работающий драйвер только ради другого префикса.

Две границы вкладки

$needed = fbird_num_params($query);
$values = [2, 8];
if (count($values) !== $needed) { throw new DomainException("Нужны две границы"); }

В диапазоне остатков needed=2. Дополнительно проверяется minimum<=maximum, потому что число мест не обнаруживает перестановку.

Изменившийся импорт

$needed = fbird_num_params($query);
$values = [$item["id"], $item["title"], $item["qty"]];
if (count($values) !== $needed) { throw new DomainException("Список полей не совпал с INSERT"); }

В импорте needed=3. Отсутствие ключа qty проверяется до построения $values; иначе PHP уже сообщит о неизвестном ключе. Имена входных полей сопоставляются с SQL нашим явным списком.

fbird_param_infoПрочитать описание входного параметра по позиции.

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

fbird_param_info(resource $query, int $param_number): array|false

Если prepared-команда ожидает другое представление, описание конкретного входного места поможет локализовать несовпадение до execute. ibase_param_info — каноническое имя той же операции. В драйвере FirebirdSQL 3.0.1 fbird_param_info зарегистрировано как alias того же C-обработчика; собственного формата ресурсов или отдельного SQL-движка у него нет.

Параметр / результатПравило
query / param_numberДействующий prepared-ресурс и целый индекс от 0 до num_params-1. Третье входное место имеет индекс 2.
array / falseОписание входного параметра; при отсутствии входного дескриптора или индексе вне диапазона в 3.0.1 — false, не обязательно с SQL-ошибкой. Историческая сигнатура сокращена до array.
name / alias / relationИсходное имя, псевдоним и имя отношения из дескриптора. Выражения могут не иметь relation/name; у параметров часто пусты все три значения.
lengthДлина представления в байтах; в 3.0.1 это строка, например "80". Не число символов и не полное правило формы.
typeСтроковое описание типа, например INTEGER/VARCHAR/NUMERIC(18,2). Оно может отражать хранение и масштаб, а не буквально исходный DDL.
Числовые ключиДрайвер добавляет 0…4 с теми же значениями наряду с именованными. Для ясности используйте именованные поля, не считайте десять элементов десятью свойствами.

Версии: историческая страница PHP Manual указывает PHP 5 и PHP 7 до 7.4. Старое встроенное расширение удалено из поставки PHP 7.4; в наших примерах эти имена предоставляет отдельный interbase 3.0.1 для PHP 8.2.35. Наличие функции проверяют в том PHP, который исполняет приложение. Это не метод PDO и не рекомендация менять работающий драйвер только ради другого префикса.

Объяснить название товара

$info = fbird_param_info($query, 1);
if ($info === false) { throw new RuntimeException("Второго параметра нет"); }
echo $info["type"], " / ", $info["length"]; // VARCHAR / 80

В импорте title объявлен VARCHAR(20) CHARACTER SET UTF8. Дескриптор 80 байт не означает 80 допустимых символов. Название title известно из контракта приложения, а не гарантируется полем name этого массива.

Диагностика обеих границ

$minimum = fbird_param_info($query, 0);
$maximum = fbird_param_info($query, 1);
// После проверки false: оба type равны INTEGER.

В фильтре остатков это одинаковые типы разных по смыслу мест. Полный разбор метаданных и ограничений формы объясняет также DECIMAL: представление NUMERIC(18,2) не восстанавливает буквальный DDL и не заменяет явный CHECK бизнес-лимита.

fbird_free_queryЗакончить срок жизни подготовленной команды.

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

fbird_free_query(resource $query): bool

После последнего execute нужно освободить prepared-команду, чтобы следующий этап не держал её ресурсы без необходимости. ibase_free_query — каноническое имя той же операции. В драйвере FirebirdSQL 3.0.1 fbird_free_query зарегистрировано как alias того же C-обработчика; собственного формата ресурсов или отдельного SQL-движка у него нет.

Параметр / результатПравило
queryРесурс из prepare. Результаты каждого SELECT сначала читаются и освобождаются отдельно через free_result.
booltrue при освобождении; false по контракту отказа. Неверный аргумент/закрытый ресурс отдельно может вызвать ошибку PHP. После успеха поставьте переменной null и не вызывайте execute повторно.
Что не происходитfree_query не делает commit/rollback, не удаляет товар или таблицу, не закрывает соединение. Скопированные PHP-данные продолжают существовать.

Версии: историческая страница PHP Manual указывает PHP 5 и PHP 7 до 7.4. Старое встроенное расширение удалено из поставки PHP 7.4; в наших примерах эти имена предоставляет отдельный interbase 3.0.1 для PHP 8.2.35. Наличие функции проверяют в том PHP, который исполняет приложение. Это не метод PDO и не рекомендация менять работающий драйвер только ради другого префикса.

Закончить обе вкладки

try {
    // execute первого диапазона → чтение → free_result.
    // execute второго диапазона → чтение → free_result.
} finally {
    $ok = fbird_free_query($query);
    $query = null;
    if ($ok !== true) { throw new RuntimeException("Команда не освобождена"); }
}

В двух вкладках prepare выполняется один раз, а free_query только после обоих результатов. Этот порядок не обещает исправить любую историческую утечку; он явно ограничивает срок жизни ресурсов.

Освободить INSERT перед подтверждением

try {
    // Проверить вход и выполнить INSERT для принятых строк.
} finally {
    fbird_free_query($query);
    $query = null;
}
// Отдельный commit после успешной бизнес-операции.

В импорте после закрытия команды и commit другой SELECT всё ещё видит товары 101 и 102. Полный файл проверяет успешное освобождение; сокращённый фрагмент показывает порядок. При исключении не подменяйте очистку ресурсов подтверждением частично выполненного пакета.

fbird_free_resultОсвободить результат SELECT, сохранив уже прочитанные данные.

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

fbird_free_result(resource $result): bool

Прочитанные строки уже могут лежать в PHP-массиве. Серверный результат SELECT после этого освобождается отдельно. ibase_free_result — каноническое имя той же операции. В драйвере FirebirdSQL 3.0.1 fbird_free_result зарегистрировано как alias того же C-обработчика; собственного формата ресурсов или отдельного SQL-движка у него нет.

Параметр / результатПравило
resultДействующий результат query/execute; не prepared-команда, не соединение и не массив прочитанных строк.
booltrue при освобождении, false по контракту отказа. Ошибки ресурса могут проявиться на уровне PHP. После вызова не fetch и не повторное free; обнулите переменную.
ГраницаФункция не удаляет выбранные записи и не подтверждает транзакцию. Для повторяемого prepared-запроса его query остаётся открытым до free_query.

Версии: историческая страница PHP Manual указывает PHP 5 и PHP 7 до 7.4. Старое встроенное расширение удалено из поставки PHP 7.4; в наших примерах эти имена предоставляет отдельный interbase 3.0.1 для PHP 8.2.35. Наличие функции проверяют в том PHP, который исполняет приложение. Это не метод PDO и не рекомендация менять работающий драйвер только ради другого префикса.

Сохранить данные страницы, закрыть результат

try {
    $items = aliasRows161($result); // Читает ограниченный SELECT.
} finally {
    $ok = fbird_free_result($result);
    $result = null;
    if ($ok !== true) { throw new RuntimeException("Результат не освобождён"); }
}
// $items по-прежнему содержит прочитанные товары.

Это экран наличия. aliasRows161 — показанная в полном файле обёртка над fetch с немедленной диагностикой, не функция расширения. Массив данных и ресурс результата — разные вещи.

Перейти к следующей форме строки

$result = fbird_execute($query, 1);
// Прочитать в форме assoc.
fbird_free_result($result);
$result = null;
$result = fbird_execute($query, 1);
// Новый результат для формы row начинается с первой строки.

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

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

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

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

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

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