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

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

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

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

Глава 101 · После запросов и транзакций CUBRID

CUBRID: выбирать API, передавать ресурсы и разбирать старое приложение

Лист комплектования, именованные профили CSV и безопасная инвентаризация старого кода; четыре вида ресурсов, все официальные константы и три полных каталога CUBRID.

От полезного файла к выбору инструмента

Нужно выдать кладовщику лист комплектования, экспортировать доступные товары и понять, что переделывать в старом приложении. Во всех трёх задачах важно выбрать правильную функцию, форму данных и участок кода, который завершит работу. В этой главе результатом будут три скачиваемых CSV, а справочная часть объяснит весь официальный каталог ресурсов, констант и групп API CUBRID.

ЗадачаПолученный результатСамостоятельный проект
Заявка 501Кабеля не хватает 2, розеток 4; хомутов достаточноЛист комплектования
Один экран и CSVCAB-1 / Кабель / 3; CAB-4 / Кабель O'Reilly / 2Профили результата
Небольшое старое приложение11 прямых вызовов и 2 случая ручного разбораИнвентаризация API

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

Разделение участников: PHP выполняет программу; native-расширение предоставляет cubrid_*; CCI передаёт запрос брокеру; CAS связывает запрос с сервером базы. Первые запросы и транзакции объясняют сами операции с данными. Здесь подробно разобраны подготовка и границы компонентов.

Примеры проверяются в отдельной среде совместимости: PHP 7.4.33, native cubrid 11.2.0.0002, CCI 11.2.0 и сервер 11.3.5.1274. Это не предложение менять PHP существующего сайта. Проверка требований, конфигурации и исходников работает без соединения с базой; три SQL-проекта создают и удаляют только собственную таблицу.

Предпосылки: аргументы и результат функции, ключи массива, try/finally. Здесь новые понятия — ресурс, владелец, заимствование, именованный режим и классификация API — сначала связываются с конкретной задачей. Во всех примерах имена c174* относятся к открытым помощникам лаборатории, а cubrid_* — к расширению.

Connection, request, LOB и LOB2: разные владельцы

Лист комплектования и файл выгрузки должны оставаться полезными после завершения SQL. Для этого разделяйте канал работы и полученные значения. Ресурс PHP — специальный дескриптор расширения, а не содержимое таблицы. Представьте читальный зал: подключение открывает доступ, request относится к конкретному запросу, а массив строки — уже выписанные данные. Аналогия не описывает устройство памяти или сетевого протокола.

Заявка 501: кто держит ресурс и что возвращает
  1. Верхний уровень создаёт $db — ресурс подключения и границу транзакции
  2. pickLines174($db, ..., 501): свой $request → три массива строк → request закрыт
  3. shortage174($db, ..., 501): другой request → positions=2, missing=6 → request закрыт
  4. $db ещё работает; внешний код завершает транзакцию и затем закрывает соединение
  5. $lines содержит обычные значения: CAB-1, Кабель, 5, 3, 2; их можно записать в CSV

Минимальная запись чтения выглядит так:

$request = cubrid_prepare($db, 'SELECT sku,title FROM goods WHERE id=?');
cubrid_bind($request, 1, 1, 'number');
cubrid_execute($request);
$row = cubrid_fetch_assoc($request);
cubrid_close_request($request);
// $row — массив прочитанных значений, если строка найдена.

Здесь $db — ранее открытое соединение; $request — результат подготовки SQL; знак ? отмечает место отдельного значения. $row получает строку с ключами sku и title. Закрытие request не является COMMIT и не превращает массив в ресурс. Это короткий порядок действий; полный проект комплектования проверяет отказы и освобождает request в finally. Параметры, массивы и исключения разобраны в подготовленных запросах, массивах и обработке отказов.

Четыре вида ресурсов из официального раздела

ВидКак появляется и для чего нуженГраница жизни
Connection identifiercubrid_connect, cubrid_connect_with_url, cubrid_pconnect и cubrid_pconnect_with_url дают подключение. Через него выполняются SQL и транзакционные команды.Открывает владелец операции; завершает транзакцию и вызывает cubrid_disconnect или cubrid_close. Заимствовавший его модуль не закрывает подключение. Имя pconnect не является доказательством reuse в конкретном драйвере.
Request identifiercubrid_prepare создаёт подготовленный запрос. Вариант cubrid_execute с соединением и SQL также может вернуть request; выполнение уже подготовленного request возвращает признак выполнения, не второй ресурс.Владелец подготовленного запроса вызывает cubrid_close_request/cubrid_close_prepare. cubrid_free_result освобождает данные результата и имеет иной контракт: возможность повторного execute изучена отдельно.
Legacy LOB identifiercubrid_lob_get получает набор ресурсов BLOB/CLOB из результата SQL. Каждый такой ресурс относится к старому семейству cubrid_lob_*.Передают в cubrid_lob_size, cubrid_lob_export или cubrid_lob_send; массив полученных ресурсов закрывают через cubrid_lob_close. Это не LOB2 handle и не ресурс файла fopen.
LOB2 identifiercubrid_lob2_new создаёт отдельный LOB2; fetch с CUBRID_LOB может получить LOB2 из результата. Ресурс содержит доступ к данным, а не готовую PHP-строку всего файла.Способ получения определяет владельцев. Созданный отдельно LOB2 закрывают через cubrid_lob2_close после завершения использующего его request. Для fetched LOB2 в закреплённом драйвере применяется специальная безопасная последовательность ниже.

Для fetched LOB2 native 11.2.0.0002 request хранит внутреннюю ссылку на дескриптор. Поэтому в проверенных проектах чтения LOB2 данные потребляются при живом request; PHP-ссылки удерживаются до закрытия request, затем удаляются. Явный lob2_close перед очередным fetch/закрытием request может оставить внутренний указатель на освобождённую структуру. Это ограничение закреплённого драйвера, а не общий закон PHP; такой опасный путь здесь не запускается. Отказ импорта в уже заполненный LOB2 также не служит поводом повторно использовать повреждённый handle: импортируем в новый пустой ресурс.

Массив строки, объект stdClass от fetch и строка OID не входят в эти четыре вида. Объект строки результата — PHP-значение с полями; OID обозначает экземпляр данных в CUBRID и не заменяет публичный ID или разрешение на действие. BLOB — SQL-тип данных, LOB2 — один из способов работать с ним в PHP. Передача неверного вида ресурса — ошибка вызова; is_resource сам по себе не удостоверяет нужный тип или активность удалённого сервера.

Два модуля комплектования заимствуют одно соединение

Кладовщик открывает заявку 501. Ему нужны строки для комплектования и отдельная сумма дефицита. Два участка программы получают одно соединение, но каждый отвечает за свой запрос. На выходе — понятный лист: кабеля не хватает 2, розеток 4; хомутов достаточно. Заявка 502 не загружается в список.

Путь данных и полученный результат
  1. Внешний код открывает $db и выбирает order_no = 501
  2. pickLines174 создаёт request → 3 строки → закрывает request
  3. shortage174 создаёт другой request → 2 позиции, 6 единиц → закрывает request
  4. Владелец $db выполняет commit; PHP-массивы сохраняют прочитанные значения
  5. CSV: CAB-1 / 5 / 3 / 2; CLP-2 / 2 / 8 / 0; SOC-3 / 4 / 0 / 4

Минимальный вызов

$lines = pickLines174($db, $table, 501, $trace);
$shortage = shortage174($db, $table, 501, $trace);
cubrid_commit($db);

pickLines174 и shortage174 — наши функции, не функции расширения. Аргумент $db передаёт существующий ресурс подключения. Возвращаемые $lines и $shortage — обычные массивы, а $trace записывает порядок завершения. Внутри каждой функции создаётся свой request; finally закрывает его даже при исключении. Внешний код решает, когда закончить транзакцию и закрыть подключение; самостоятельный файл ниже содержит проверки каждого шага.

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

НаблюдениеРезультат
Заявка501
Кабельнужно 5; доступно 3; не хватает 2
Хомутнужно 2; доступно 8; не хватает 0
Розетканужно 4; доступно 0; не хватает 4
Дефицит2 позиции; 6 единиц
Завершениеpick-list: request closed → shortage: request closed → caller: commit
CSVpick-list.csv
Самостоятельный проект, исходник и архив
Два модуля комплектования заимствуют одно соединениеСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__.'/lab.php';
$db=null;$owned=$removed=$rows=$checks=$observations=[];

try {
    $config=c174Config();$db=c174Connect();$runtime=c174Runtime($db);

    function saveCsv174(string $filename,array $records): void {
        $file=fopen(__DIR__.'/'.$filename,'wb');
        if($file===false){throw new RuntimeException('Не удалось создать CSV');}
        try {
            foreach($records as $record){
                if(fputcsv($file,$record,';','"','')===false){throw new RuntimeException('Строка CSV не записана');}
            }
        } finally {fclose($file);}
    }
    function readCsv174(string $filename): array {
        $file=fopen(__DIR__.'/'.$filename,'rb');
        if($file===false){throw new RuntimeException('Созданный CSV не найден');}
        $records=[];
        try {
            while(($record=fgetcsv($file,0,';','"',''))!==false){
                $records[]=$record;
                if(count($records)>30){throw new RuntimeException('Учебный файл неожиданно велик');}
            }
        } finally {fclose($file);}
        return $records;
    }

    // This function owns only the request it creates. The caller owns $db.
    function borrowedRows174($db,string $sql,int $orderNo,string $label,array &$trace): array {
        $request=null;
        $call=c174Capture(static function()use($db,$sql){return cubrid_prepare($db,$sql);});
        if(!is_resource($call['value']) || $call['warning']){c174Failure('Запрос не подготовлен',$call);}
        $request=$call['value'];
        try {
            $bound=c174Capture(static function()use($request,$orderNo){return cubrid_bind($request,1,$orderNo,'number');});
            if($bound['value']!==true || $bound['warning']){c174Failure('Номер заявки не связан',$bound);}
            $run=c174Capture(static function()use($request){return cubrid_execute($request);});
            if($run['value']!==true || $run['warning']){c174Failure('Данные заявки не прочитаны',$run);}
            $result=[];
            while(true){
                $next=c174Capture(static function()use($request){return cubrid_fetch_assoc($request);});
                if($next['value']===false && $next['code']===0 && !$next['warning']){break;}
                if(!is_array($next['value']) || $next['warning']){c174Failure('Строка заявки не получена',$next);}
                $result[]=$next['value'];
            }
            return $result;
        } finally {
            c174Release($request);
            $trace[]=$label.': request closed';
        }
    }
    function pickLines174($db,string $table,int $orderNo,array &$trace): array {
        return borrowedRows174($db,'SELECT sku,title,wanted,available,CASE WHEN wanted>available THEN wanted-available ELSE 0 END AS missing FROM '.$table.' WHERE order_no=? ORDER BY line_no',$orderNo,'pick-list',$trace);
    }
    function shortage174($db,string $table,int $orderNo,array &$trace): array {
        return borrowedRows174($db,'SELECT COUNT(*) AS positions,SUM(wanted-available) AS missing FROM '.$table.' WHERE order_no=? AND wanted>available',$orderNo,'shortage',$trace);
    }
    $name=c174OwnName($db,$owned,'TABLE','ownership');$table=c174Qualified($name);
    c174Ddl($db,'CREATE TABLE '.$table.' (line_no INTEGER PRIMARY KEY,order_no INTEGER NOT NULL,sku VARCHAR(24) NOT NULL,title VARCHAR(80) NOT NULL,wanted INTEGER NOT NULL,available INTEGER NOT NULL)');
    c174Commit($db);
    foreach([[1,501,'CAB-1','Кабель',5,3],[2,501,'CLP-2','Хомут',2,8],[3,501,'SOC-3','Розетка',4,0],[4,502,'BOX-4','Коробка',9,9]] as $line){
        c174Command($db,'INSERT INTO '.$table.' (line_no,order_no,sku,title,wanted,available) VALUES (?,?,?,?,?,?)',$line);
    }
    c174Commit($db);
    $trace=[];
    try {
        $lines=pickLines174($db,$table,501,$trace);
        $summary=shortage174($db,$table,501,$trace);
        // The request resources are gone; the connection remains usable.
        $stillOpen=c174Read($db,'SELECT COUNT(*) AS total FROM '.$table);
        c174Commit($db);$trace[]='caller: commit';
    } catch(Throwable $error) {c174Rollback($db);throw $error;}
    $records=[['sku','title','wanted','available','missing']];
    foreach($lines as $line){$records[]=[$line['sku'],$line['title'],$line['wanted'],$line['available'],$line['missing']];}
    saveCsv174('pick-list.csv',$records);
    $expected=[['sku','title','wanted','available','missing'],['CAB-1','Кабель','5','3','2'],['CLP-2','Хомут','2','8','0'],['SOC-3','Розетка','4','0','4']];
    $rows=[['Заявка','501'],['Кабель','нужно 5; доступно 3; не хватает 2'],
        ['Хомут','нужно 2; доступно 8; не хватает 0'],['Розетка','нужно 4; доступно 0; не хватает 4'],
        ['Дефицит',$summary[0]['positions'].' позиции; '.$summary[0]['missing'].' единиц'],
        ['Завершение',implode(' → ',$trace)],['CSV','pick-list.csv']];
    $checks=['chosen_order'=>count($lines)===3,'actual_items'=>$records===$expected,
        'summary_in_sql'=>$summary===[['positions'=>'2','missing'=>'6']],
        'borrowed_connection_survives'=>$stillOpen===[['total'=>'4']],
        'requests_released'=>$trace===['pick-list: request closed','shortage: request closed','caller: commit'],
        'ordinary_rows_after_commit'=>is_array($lines[0])&&$lines[0]['title']==='Кабель',
        'csv_exact'=>readCsv174('pick-list.csv')===$expected];
    $observations=['selected_order'=>501,'items'=>$lines,'shortage'=>$summary,'ownership_trace'=>$trace,
        'other_order_not_selected'=>true,'transactions_owned_by'=>'caller','csv_rows'=>readCsv174('pick-list.csv')];

} finally {

    if (is_resource($db)) {try {$removed=c174Cleanup($db,$owned);} finally {c174Close($db);}}
}

c174Report('cub174-resource-ownership','Два модуля комплектования заимствуют одно соединение',$rows,$checks,$observations,$runtime,$owned,$removed);
Ожидаемый результат
Заявка — 501
Кабель — нужно 5; доступно 3; не хватает 2
Хомут — нужно 2; доступно 8; не хватает 0
Розетка — нужно 4; доступно 0; не хватает 4
Дефицит — 2 позиции; 6 единиц
Завершение — pick-list: request closed → shortage: request closed → caller: commit
CSV — pick-list.csv

Распакуйте весь ZIP и прочитайте README.txt. example.php содержит логику урока, lab.php — проверку отдельной среды и работу с собственными ресурсами. Этот проект выполняет SQL и удаляет только свою учебную таблицу. После запуска откройте result.html и скачиваемый ниже JSON или CSV.

Открыть результат выполнения · Посмотреть данные JSON · Скачать лист комплектования CSV

Границы решения

Это чтение учебного снимка, а не резервирование остатка. При реальных конкурентных изменениях одна транзакция при READ COMMITTED не обещает неизменный снимок между двумя SELECT: выбирайте нужную изоляцию или общую SQL-операцию. Заимствовавшая соединение функция не должна тайно делать disconnect, commit или rollback. Возвращённый массив не обновляется сам при изменении базы. Четыре строки здесь — небольшая фикстура; условие WHERE и агрегирование остаются в SQL.

Попробуйте изменить условие

Добавьте в заявку 501 товар, которого нужно 3, а доступно 1. Кто должен менять общий код закрытия соединения?

Разбор

Никто: функции по-прежнему освобождают свои requests, соединение принадлежит внешнему коду. В SQL-сводке будет 3 дефицитные позиции и 8 единиц, в CSV появится новая строка с missing=2. Меняются данные и ожидаемый результат, а не договор владения.

Именованный профиль выбирает форму результата, а SQL — строки

Один экран обращается к строке по имени поля, а CSV требует стабильного порядка. Не заставляйте пользователя угадывать номера констант. Названия screen и csv выбирают два разрешённых способа получения одних данных: два доступных товара категории «электрика». Для этого не нужно получать весь каталог и сортировать его в PHP.

Путь данных и полученный результат
  1. Профиль screen → CUBRID_ASSOC → ключи sku/title/qty
  2. Профиль csv → CUBRID_NUM → позиции 0/1/2
  3. SQL: category = электрика, qty > 0, ORDER BY id, LIMIT 2
  4. Получены CAB-1 / Кабель / 3 и CAB-4 / Кабель O'Reilly / 2
  5. Неизвестный профиль не выполняет третий SELECT

Минимальный вызов

$profiles = ['screen' => CUBRID_ASSOC, 'csv' => CUBRID_NUM];
$row = cubrid_fetch($request, $profiles['csv']);
// Для первого товара: ['CAB-1', 'Кабель', '3']

CUBRID_NUM — имя числовой константы, которую определяет загруженное расширение; кавычки вокруг самого имени не нужны. $profiles — наш массив соответствий, а строка csv — ключ в нём. cubrid_fetch получает request и выбранный режим. Режим меняет ключи PHP-массива, но не порядок SQL-строк и не условия WHERE. В полном проекте неизвестный ключ вызывает исключение до подготовки запроса.

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

НаблюдениеРезультат
Экран: ключиsku, title, qty
CSV: ключи0, 1, 2
Первая позицияCAB-1 | Кабель | 3
Вторая позицияCAB-4 | Кабель O'Reilly | 2
Неизвестный профильотклонён до SQL
Выполнено SELECT2
CSVselected-export.csv
Самостоятельный проект, исходник и архив
Именованный профиль выбирает форму результата, а SQL — строкиСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__.'/lab.php';
$db=null;$owned=$removed=$rows=$checks=$observations=[];

try {
    $config=c174Config();$db=c174Connect();$runtime=c174Runtime($db);

    function saveCsv174(string $filename,array $records): void {
        $file=fopen(__DIR__.'/'.$filename,'wb');
        if($file===false){throw new RuntimeException('Не удалось создать CSV');}
        try {
            foreach($records as $record){
                if(fputcsv($file,$record,';','"','')===false){throw new RuntimeException('Строка CSV не записана');}
            }
        } finally {fclose($file);}
    }
    function readCsv174(string $filename): array {
        $file=fopen(__DIR__.'/'.$filename,'rb');
        if($file===false){throw new RuntimeException('Созданный CSV не найден');}
        $records=[];
        try {
            while(($record=fgetcsv($file,0,';','"',''))!==false){
                $records[]=$record;
                if(count($records)>30){throw new RuntimeException('Учебный файл неожиданно велик');}
            }
        } finally {fclose($file);}
        return $records;
    }

    function preset174(string $name): array {
        $allowed=['screen'=>['mode'=>CUBRID_ASSOC,'symbol'=>'CUBRID_ASSOC'],
                  'csv'=>['mode'=>CUBRID_NUM,'symbol'=>'CUBRID_NUM']];
        if(!isset($allowed[$name])){throw new InvalidArgumentException('Неизвестный профиль результата');}
        return $allowed[$name];
    }
    function exportRows174($db,string $table,string $profile,string $category,int $limit,int &$executions): array {
        $choice=preset174($profile);
        if($limit<1 || $limit>20){throw new InvalidArgumentException('Предел строк должен быть от 1 до 20');}
        $sql='SELECT sku,title,qty FROM '.$table.' WHERE category=? AND qty>0 ORDER BY id LIMIT '.$limit;
        $call=c174Capture(static function()use($db,$sql){return cubrid_prepare($db,$sql);});
        if(!is_resource($call['value']) || $call['warning']){c174Failure('Профильный запрос не подготовлен',$call);}
        $request=$call['value'];$records=[];
        try {
            $bound=c174Capture(static function()use($request,$category){return cubrid_bind($request,1,$category,'string');});
            if($bound['value']!==true || $bound['warning']){c174Failure('Категория не связана',$bound);}
            $run=c174Capture(static function()use($request){return cubrid_execute($request);});$executions++;
            if($run['value']!==true || $run['warning']){c174Failure('Выгрузка не выполнена',$run);}
            while(true){
                $next=c174Capture(static function()use($request,$choice){return cubrid_fetch($request,$choice['mode']);});
                if($next['value']===false && $next['code']===0 && !$next['warning']){break;}
                if(!is_array($next['value']) || $next['warning']){c174Failure('Строка выгрузки не прочитана',$next);}
                $records[]=$next['value'];
            }
        } finally {c174Release($request);}
        return $records;
    }
    $name=c174CreateTable($db,$owned,'presets',[
        ['id'=>1,'sku'=>'CAB-1','title'=>'Кабель','category'=>'электрика','price_cents'=>1000,'qty'=>3],
        ['id'=>2,'sku'=>'CLP-2','title'=>'Хомут','category'=>'крепёж','price_cents'=>100,'qty'=>8],
        ['id'=>3,'sku'=>'SOC-3','title'=>'Розетка','category'=>'электрика','price_cents'=>2000,'qty'=>0],
        ['id'=>4,'sku'=>'CAB-4','title'=>"Кабель O'Reilly",'category'=>'электрика','price_cents'=>1100,'qty'=>2]]);
    $table=c174Qualified($name);$executions=0;
    $screen=exportRows174($db,$table,'screen','электрика',2,$executions);
    $csv=exportRows174($db,$table,'csv','электрика',2,$executions);
    $before=$executions;$rejected=false;
    try {exportRows174($db,$table,'CUBRID_BOTH','электрика',2,$executions);}
    catch(InvalidArgumentException $expected){$rejected=true;}
    c174Commit($db);
    $normalized=[];foreach($screen as $record){$normalized[]=[$record['sku'],$record['title'],$record['qty']];}
    $expected=[['CAB-1','Кабель','3'],['CAB-4',"Кабель O'Reilly",'2']];
    saveCsv174('selected-export.csv',array_merge([['sku','title','qty']],$csv));
    $rows=[['Экран: ключи',implode(', ',array_keys($screen[0]))],['CSV: ключи',implode(', ',array_keys($csv[0]))],
        ['Первая позиция',implode(' | ',$csv[0])],['Вторая позиция',implode(' | ',$csv[1])],
        ['Неизвестный профиль',$rejected?'отклонён до SQL':'не отклонён'],['Выполнено SELECT',$executions],['CSV','selected-export.csv']];
    $checks=['screen_shape'=>array_keys($screen[0])===['sku','title','qty'],
        'csv_shape'=>array_keys($csv[0])===[0,1,2],'same_values'=>$normalized===$csv&&$csv===$expected,
        'sql_selects_available'=>count($csv)===2,'unknown_before_sql'=>$rejected&&$before===$executions&&$executions===2,
        'named_modes'=>preset174('screen')['mode']===CUBRID_ASSOC&&preset174('csv')['mode']===CUBRID_NUM,
        'utf8_apostrophe'=>$csv[1][1]==="Кабель O'Reilly",'csv_exact'=>readCsv174('selected-export.csv')===array_merge([['sku','title','qty']],$expected)];
    $observations=['selected_category'=>'электрика','limit'=>2,'screen'=>$screen,'csv'=>$csv,
        'profiles'=>['screen'=>'CUBRID_ASSOC','csv'=>'CUBRID_NUM'],'invalid_rejected_before_sql'=>$rejected&&$before===$executions];

} finally {

    if (is_resource($db)) {try {$removed=c174Cleanup($db,$owned);} finally {c174Close($db);}}
}

c174Report('cub174-preset-export','Именованный профиль выбирает форму результата, а SQL — строки',$rows,$checks,$observations,$runtime,$owned,$removed);
Ожидаемый результат
Экран: ключи — sku, title, qty
CSV: ключи — 0, 1, 2
Первая позиция — CAB-1 | Кабель | 3
Вторая позиция — CAB-4 | Кабель O'Reilly | 2
Неизвестный профиль — отклонён до SQL
Выполнено SELECT — 2
CSV — selected-export.csv

Распакуйте весь ZIP и прочитайте README.txt. example.php содержит логику урока, lab.php — проверку отдельной среды и работу с собственными ресурсами. Этот проект выполняет SQL и удаляет только свою учебную таблицу. После запуска откройте result.html и скачиваемый ниже JSON или CSV.

Открыть результат выполнения · Посмотреть данные JSON · Скачать выбранные товары CSV

Границы решения

Таблица профилей — небольшой разрешённый набор для задачи, а не полный каталог возможностей CUBRID. Не вычисляйте constant() по произвольной строке из HTTP и не объединяйте все режимы через |: значения могут быть альтернативами, а не независимыми флагами. Здесь оба SELECT выполняются над неизменяемой фикстурой; при конкурентном обновлении два чтения не обязаны совпадать. Запись значений в CSV не превращает их автоматически в безопасные формулы для Excel; реальный экспорт пользовательских значений требует отдельной политики.

Попробуйте изменить условие

Добавьте профиль screen_with_price. Нужно ли заменять CUBRID_ASSOC на новый числовой флаг?

Разбор

Нет. Режим массива останется CUBRID_ASSOC. В разрешённом SQL-профиле добавьте price_cents и скорректируйте заголовки/проверки. Выбранные колонки — договор запроса, а NUM/ASSOC — форма одной уже выбранной строки.

Полный каталог констант: смысл, получатель и версия

Оператор выбирает «CSV», а программа должна передать функции правильный режим. Константа — именованное значение: в CUBRID_NUM нет знака $, значение поставляет расширение. Она доступна только после загрузки/встраивания соответствующего модуля. Это позволяет читать намерение кода, но имя из справочника всё равно нужно сверять с установленной версией.

Одно имя имеет смысл только у своего получателя
  1. csv → CUBRID_NUM → cubrid_fetch(request, mode) → [CAB-1, Кабель, 3]
  2. CUBRID_PARAM_LOCK_TIMEOUT → set_db_parameter(connection, selector, 1000) → ожидание блокировки
  3. CUBRID_SCH_PRIMARY_KEY → cubrid_schema(connection, selector, table) → описание ключа
  4. Одинаковый тип int не делает эти три значения взаимозаменяемыми
$mode = CUBRID_ASSOC;
$row = cubrid_fetch($request, $mode);
// $row['title'] содержит название товара, если строка найдена.

Здесь первое присваивание выбирает форму результата, второе читает одну строку; оно не меняет WHERE, сортировку или доступные права. Полная программа с профилями выгрузки показывает те же значения с числовыми и именованными ключами. Побитовое | применяйте только для разрешённой комбинации: например CUBRID_ASSOC | CUBRID_LOB. Уровни изоляции, направления курсора и типы schema выбираются как отдельные варианты, не складываются произвольно.

Ниже сохранены все 47 имён официальной страницы. «Есть в 11.2» означает регистрацию в native 11.2.0.0002, а не запуск каждого режима в этой главе. Исходник также регистрирует один дополнительный schema-флаг. Историческое описание оставлено рядом с ограничением версии; отсутствующее имя нельзя просто использовать в современном PHP как строку.

Подготовка и выполнение SQL

Получатель: cubrid_prepare / cubrid_execute.

ИмяНазначениеВерсия и границы
CUBRID_INCLUDE_OIDПодготовить результат с OID для дальнейшего обращения к выбранному экземпляру данных.Есть в 11.2; требуется подходящий SELECT и таблица DONT_REUSE_OID. Не означает наличие прав.
CUBRID_ASYNCЗапросить асинхронный режим выполнения в поддерживающем его вызове.Есть в 11.2. Не делает весь PHP-код неблокирующим и не доказывает отсутствие буферов; разбирать контракт execute/unbuffered отдельно.
CUBRID_EXEC_QUERY_ALLВключить обработку всех SQL-команд, чтобы затем переходить между результатами.Официальная страница описывает синхронное выполнение нескольких команд. В 173 проверен именно ограниченный multi-result сценарий с next_result.

Форма одной строки

Получатель: cubrid_fetch / cubrid_fetch_array; CUBRID_LOB также в fetch_row/assoc/object.

ИмяНазначениеВерсия и границы
CUBRID_NUMЧисловые ключи: [0 => CAB-1, 1 => Кабель, 2 => 3]; порядок полей задаёт SELECT.В pinned source значение 1. Фраза старой страницы «0-default» не означает числовое значение этой константы.
CUBRID_ASSOCКлючи по именам/алиасам выбранных колонок: sku, title, qty.Есть в 11.2; одинаковые имена колонок требуют разных SQL-алиасов.
CUBRID_BOTHОдна строка имеет и числовые, и именованные ключи.Есть в 11.2; default у fetch_array и cubrid_fetch. Для экспорта задавайте режим явно, чтобы не записать значения дважды.
CUBRID_OBJECTСтрока становится объектом PHP с полями выбранных колонок.Есть в 11.2. Это не CUBRID OID и не экземпляр SQL-класса.
CUBRID_LOBВместо преобразования содержимого BLOB/CLOB получить доступ через LOB2 resource.Есть в 11.2; комбинируется с документированной формой fetch, например ASSOC | LOB. Нужно соблюдать срок жизни request и полученных LOB2.

Начало отсчёта курсора и старые коды

Получатель: cubrid_move_cursor.

ИмяНазначениеВерсия и границы
CUBRID_CURSOR_FIRSTСмещение считается от начала выбранного результата.Есть в 11.2; это начало результата SQL, не минимальный PK всей таблицы.
CUBRID_CURSOR_CURRENTСмещение отсчитывается от текущей позиции; используется при отсутствии origin.Есть в 11.2. Нельзя считать курсор независимым от предыдущих fetch/move.
CUBRID_CURSOR_LASTСмещение задаётся относительно конца результата.Есть в 11.2; направление/границы смотрите в карточке move_cursor. Константа также применяется в LOB2 seek, но правила смещения там отдельные.
CUBRID_CURSOR_SUCCESSИсторический код успешного перемещения.Удалён начиная с 8.4.1, не регистрируется в 11.2. Современный move_cursor возвращает bool, не сравнивайте его с этим отсутствующим именем.
CUBRID_NO_MORE_DATAИсторическое обозначение отсутствия следующей позиции.Удалён начиная с 8.4.1. Для современного fetch различайте false/конец/диагностику по контракту функции.
CUBRID_CURSOR_ERRORИсторический код отказа курсора.Удалён начиная с 8.4.1. Сохраняйте действительную диагностику после отказа.

Границы автоматического сохранения

Получатель: cubrid_get_autocommit / cubrid_set_autocommit.

ИмяНазначениеВерсия и границы
CUBRID_AUTOCOMMIT_TRUEИменованное представление включённого автосохранения.Зарегистрирована в 11.2 как int. При strict_types setter этого драйвера требует bool: передавайте true, а не int-константу.
CUBRID_AUTOCOMMIT_FALSEИменованное представление выключенного автосохранения.Зарегистрирована в 11.2 как int. В рабочих примерах используется literal false; затем обязателен явный commit/rollback.

Какую настройку соединения читать/менять

Получатель: cubrid_get_db_parameter / cubrid_set_db_parameter.

ИмяНазначениеВерсия и границы
CUBRID_PARAM_ISOLATION_LEVELВыбирает параметр уровня изоляции; само значение уровня передаётся отдельно.Есть в 11.2; меняйте на границе транзакции, а не посередине готового отчёта.
CUBRID_PARAM_LOCK_TIMEOUTВыбирает предел ожидания блокировки соединением.PHP Manual исторически пишет секунды; CCI начиная с 9.0 использует миллисекунды. В профиле CCI 11.2 значение 1000 означало около 1 с; это не request query timeout.

Историческая шкала CLASS / INSTANCE

Получатель: значение для CUBRID_PARAM_ISOLATION_LEVEL.

ИмяНазначениеВерсия и границы
TRAN_COMMIT_CLASS_UNCOMMIT_INSTANCEУровень 1: описание схемы может измениться между чтениями; для строк возможны грязные, неповторяемые и фантомные чтения.Историческая шкала. Это имя не регистрируется pinned PHP-драйвером 11.2.
TRAN_COMMIT_CLASS_COMMIT_INSTANCEУровень 2: читаются подтверждённые строки; повторное чтение схемы/строк и новые подходящие строки могут отличаться.Историческое имя отсутствует в MINIT 11.2; не подставляйте число 2 только ради обхода отсутствующей константы.
TRAN_REP_CLASS_UNCOMMIT_INSTANCEУровень 3: схема повторяема, но чтение данных допускает неподтверждённые значения; старая страница называет его исходным.Историческое имя не зарегистрировано в 11.2. В нашей современной среде исходно наблюдён уровень 4, а не 3.
TRAN_REP_CLASS_COMMIT_INSTANCEУровень 4: повторяемое описание схемы и подтверждённые значения строк; два чтения строки могут отличаться.Имя зарегистрировано. В 168 чтение после чужого COMMIT увидело новое значение; это READ COMMITTED для данных.
TRAN_REP_CLASS_REP_INSTANCEУровень 5: повторяемое чтение схемы и строки; исторический каталог отдельно допускает фантомы.Имя зарегистрировано. В 168 уже прочитанная строка сохраняла прежнее значение до окончания транзакции; это не тест всех фантомных сценариев.
TRAN_SERIALIZABLEУровень 6: исторический максимальный контракт исключает грязные, неповторяемые и фантомные чтения.Имя зарегистрировано, но регистрация не гарантирует поддержку режима конкретным сервером. Не объявляем его исполненным сценарием этого пакета.

Формат описания схемы

Получатель: cubrid_schema.

ИмяНазначениеВерсия и границы
CUBRID_SCH_CLASSИмена и виды классов/таблиц базы.Есть в 11.2; это метаданные CUBRID, не классы PHP.
CUBRID_SCH_VCLASSИмена виртуальных классов — представлений.Есть в 11.2; наличие имени не означает разрешения на чтение.
CUBRID_SCH_QUERY_SPECSQL-определение представления.Есть в 11.2; схема отчёта отличается от выборки его бизнес-строк.
CUBRID_SCH_ATTRIBUTEОписание колонок: имена, типы, ограничения, значения по умолчанию.Есть в 11.2; необязательный шаблон атрибута и точное имя различаются.
CUBRID_SCH_CLASS_ATTRIBUTEАтрибуты уровня класса CUBRID.Есть в 11.2; не смешивать с полями строки или static-свойствами PHP.
CUBRID_SCH_METHODМетоды экземпляров класса CUBRID.Есть в 11.2; пустой список допустим, на обычной учебной таблице методов может не быть.
CUBRID_SCH_CLASS_METHODМетоды класса CUBRID.Есть в 11.2; это отдельный формат схемы, а не вызов PHP-метода.
CUBRID_SCH_METHOD_FILEФайлы, связанные с методами класса базы.Есть в 11.2; каталог не должен превращаться в выдачу закрытых серверных путей.
CUBRID_SCH_SUPERCLASSПредки выбранного класса базы.Есть в 11.2; направление от дочернего к родительскому.
CUBRID_SCH_SUBCLASSПотомки выбранного класса базы.Есть в 11.2; направление от родительского к дочернему.
CUBRID_SCH_CONSTRAINTОписание ограничений данного формата и относящихся к ним колонок/индексов.В 169 получены UNIQUE; это не обещание всех ограничений таблицы. Первичный ключ читается отдельно через PRIMARY_KEY, внешние — через семейство KEYS.
CUBRID_SCH_TRIGGERТриггеры и их условия/действия.Есть в 11.2; читать описание и запускать действие — разные операции.
CUBRID_SCH_CLASS_PRIVILEGEРазрешения на класс/таблицу и возможность передавать разрешение.Есть в 11.2; не заменяет проверку прав самим SQL-сервером.
CUBRID_SCH_ATTR_PRIVILEGEИсторический формат разрешений атрибута.Имя есть в 11.2; из этого не следует поддержка произвольного column-level GRANT современным сервером.
CUBRID_SCH_DIRECT_SUPER_CLASSТолько непосредственные предки класса.Есть в 11.2; отличается от общего маршрута SUPERCLASS.
CUBRID_SCH_PRIMARY_KEYКолонки первичного ключа выбранной таблицы.Есть в 11.2; составной ключ нужно читать с порядком колонок.
CUBRID_SCH_IMPORTED_KEYSВнешние ключи, которые данная таблица импортирует от родительских.Есть в 11.2; помогает узнать, на какие родительские записи ссылаются строки.
CUBRID_SCH_EXPORTED_KEYSВнешние ключи других таблиц, ссылающиеся на данную.Есть в 11.2; обратная сторона связи относительно IMPORTED_KEYS.
CUBRID_SCH_CROSS_REFERENCEСвязь между заданными родительской и дочерней таблицами.Есть в 11.2; четвёртый аргумент здесь — дочерний класс, а не шаблон имени колонки.

Откуда пришёл отказ

Получатель: cubrid_error_code_facility.

ИмяНазначениеВерсия и границы
CUBRID_FACILITY_DBMSИсточник диагностики — сервер базы данных.Есть в 11.2; номер facility не является SQL-кодом конкретной ошибки.
CUBRID_FACILITY_CASИсточник — CAS брокера.Есть в 11.2; при расследовании нужны также error_code и безопасное описание ситуации.
CUBRID_FACILITY_CCIИсточник — клиентская библиотека CCI.Есть в 11.2; это не то же самое, что версия PHP-модуля.
CUBRID_FACILITY_CLIENTИсточник — клиентское PHP-расширение.Есть в 11.2; сохраните диагностику сразу, до следующего вызова драйвера.

Дополнение закреплённого драйвера

ИмяНазначениеПроверенный контекст
CUBRID_SCH_ATTR_WITH_SYNONYMОписание атрибутов с разрешением имени synonym в cubrid_schema.Не входит в 47 строк официальной страницы constants, но зарегистрировано в 11.2.0.0002 и использовано в карте схемы. Synonym не обходит права доступа.

Подробные получатели: cubrid_prepare / cubrid_execute; cubrid_fetch / cubrid_fetch_array; cubrid_move_cursor; cubrid_get_autocommit / cubrid_set_autocommit; cubrid_get_db_parameter / cubrid_set_db_parameter; cubrid_schema; cubrid_error_code_facility. У каждой карточки есть собственные параметры, результат и ограничения, которых один список констант не заменяет.

Практика: формы строк, позиции курсора, изоляция и пределы ожидания, описание структуры. Для получения полного списка констант PHP можно использовать get_defined_constants(true), но вывод списка не объясняет совместимость режимов и не заменяет этот разбор.

Официальная основа: прочитать сводку и сохранить запись

Официальная страница Examples учит двум действиям: получить сводку и вставить запись. Её данные — демонстрационная база demodb, таблицы спортивных событий и олимпиад. Перед таким примером должны работать сервер CUBRID и Broker, а сама demodb должна существовать. Адрес и порт относятся к брокеру; порт 33000 — пример исходной конфигурации, не обязательное значение для каждого проекта.

Официальный путьЧто человек должен понятьСовременное применение
Получение данных: event → sports / count(players) → HTML-таблицаАгрегат и группировка вычисляются SQL-сервером. column_names и num_cols описывают выбранные колонки; fetch обходит строки. После чтения завершается транзакция и соединение.В отчёте склада группируются категории товаров. В заявке 501 отдельно вычисляется дефицит нужного заказа; нет загрузки всей таблицы с последующим суммированием в PHP.
Добавление данных: INSERT olympic с годом, страной, городом и датамиSQL INSERT и чтение результата — разные операции. При успехе требуется выбранная граница commit, при отказе — немедленная диагностика и rollback. to_date — SQL-функция с SQL-форматом, не PHP date().В создании своей заявки результатом служат конкретная сохранённая запись и её AUTO_INCREMENT ID. Подготовка строк в проектах этой главы также использует связанные значения и явный commit.
Та же задача с понятными данными склада
  1. Данные каталога → SELECT category, агрегаты → уже небольшая сводка
  2. Описание выбранных колонок → подписи, fetch → значения
  3. HTML escaping для экрана; отдельный CSV для скачивания
  4. При ручной транзакции: успешное окончание commit, отказ rollback; затем закрытие собственных ресурсов

Самая маленькая запись вставки со связанным значением

$request = cubrid_prepare($db, 'INSERT INTO goods (title,qty) VALUES (?,?)');
cubrid_bind($request, 1, 'Кабель', 'string');
cubrid_bind($request, 2, 3, 'number');
$ok = cubrid_execute($request);
// Ожидаемая новая строка после успешного commit: Кабель, 3.

Круглые скобки после goods перечисляют колонки, а VALUES — значения в том же порядке. Два ? получают позиции 1 и 2 через bind. $ok подтверждает только выполнение данного вызова, не последующий commit. Это контекстный фрагмент для уже созданной таблицы; реальные самостоятельные файлы комплектования и экспорта проверяют создание, вставки, чтение и завершение. Для отказа второго изменения без частичного сохранения есть транзакционные проекты.

Старый HTML-пример содержит euc-kr, each(), подавление предупреждения через @ и прямой вывод значений. Смысл операций сохранён; новый путь использует UTF-8, foreach, явную обработку отказа и htmlspecialchars для HTML. Сохранённое сообщение SQL не следует безусловно показывать посетителю: оно может содержать внутренние имена. Настройка autocommit определяет границу транзакции; слово «rollback» в старом комментарии рядом с commit не делает эти команды взаимозаменяемыми. Объяснение отложенного установления соединения в старом примере не заменяет проверку фактического вызова и профиля драйвера.

При переносе примера не требуется заново создавать чужую demodb или повторять её данные. Самостоятельные проекты имеют собственные имена таблиц, понятные записи, точный ожидаемый результат и очистку. Прикладной SELECT выбирает нужные колонки, WHERE, ORDER BY и ограничение. Тип false, пустая выборка и нулевой результат агрегата различаются по контракту API.

Нативные функции, совместимый интерфейс и старый GLO

Выбор начинается с задачи. Для нового параметризованного запроса удобно оставить структуру SQL в prepare, а значения в bind. При сопровождении существующего файла может встретиться query с порядком аргументов, похожим на прежний MySQL API. При переносе GLO сначала нужно получить старые данные в совместимой старой среде. Все эти маршруты нельзя объединить заменой одного префикса.

ЗадачаМаршрутЧто не следует предполагать
Новая форма поиска или измененияNative cubrid_prepare → cubrid_bind → cubrid_execute → нужный fetch.Подготовка не выбирает данные сама; параметры не заменяют whitelist идентификаторов/ORDER BY.
Поддержка старого PHP-файла с обычным querycubrid_query с выбранными SQL-колонками; cubrid_errno / cubrid_error сразу после отказа.Название MySQL Compatibility описывает интерфейс PHP, а не подключение к серверу MySQL и не полную совместимость его SQL.
Бинарные/текстовые вложенияДля новых операций LOB2; для существующего cubrid_lob_* — отдельный legacy-контракт.LOB, LOB2 и GLO не являются взаимозаменяемыми ресурсами.
Очень старые GLO-вызовыПлан извлечения и сверки с переходом на LOB2.Четыре GLO-функции отсутствуют в проверенном модуле; статическое обнаружение не является их запуском.
Приложение уже использует PDOРаздел PDO_CUBRID.PDO — другой API и ресурсный договор. Здесь не выдаём выполнение native cubrid за запуск PDO.

Сравните только форму вызова:

$prepared = cubrid_prepare($db, 'SELECT title FROM goods WHERE id=?');
// native: сначала соединение, затем SQL с отдельным параметром
$compatible = cubrid_query('SELECT title FROM goods WHERE id=1', $db);
// compatibility: сначала SQL, затем соединение; здесь значение фиксированное

Эти строки показывают разные интерфейсы, не сравнение скорости. Обе требуют проверки результата, корректного чтения и освобождения. Для пользовательского ID используйте параметризованный путь, а не конкатенацию. Статическая инвентаризация помогает найти место вызова в старом файле, после чего карточка объясняет его точный контракт.

Составить план поддержки старого PHP-кода без его запуска

При сопровождении старого приложения сначала нужен список мест, которые следует разобрать. Поиск текста cubrid_ находит комментарии, строковые примеры и методы с похожими именами. Программа читает три синтетических PHP-файла, различает прямые вызовы и выдаёт план: нативные вызовы, совместимые функции, legacy LOB, отсутствующий GLO и два случая для ручного разбора. Исходники остаются файлами данных — include и eval не используются.

Путь данных и полученный результат
  1. sample/catalog.php: 7 прямых вызовов, комментарий/строка не считаются
  2. sample/legacy.php: query + legacy LOB + отсутствующий GLO; $callable требует ручного разбора
  3. sample/namespaced.php: не угадываем разрешение имён
  4. 11 прямых вызовов + 2 пометки → api-audit.csv
  5. В наблюдениях — точные ссылки на карточки; SQL и исходники не исполнялись

Минимальный вызов

$tokens = token_get_all('<?php cubrid_query($sql, $db);');
// T_STRING cubrid_query, затем символ (, аргументы и )
// Строка 'cubrid_query(...)' и комментарий имеют другие типы токенов.

token_get_all принимает текст программы с открывающим тегом <?php. Токен — либо одиночный знак, либо массив [тип, текст, строка]. Мы пропускаем пробелы/комментарии, смотрим T_STRING и следующий знак «(», затем исключаем объявления, методы и qualified names. Это ограниченный разбор для инвентаризации, не полноценный PHP-парсер. function_exists проверяет доступность самого имени в текущем CLI, а не его корректность для старых данных.

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

НаблюдениеРезультат
Прямые вызовы CUBRID11
Нативная группа7
Совместимая группа2
Legacy LOBcubrid_lob_export: доступен; план перехода к LOB2
GLOcubrid_send_glo: отсутствует; нужен экспорт в старой среде
Ручной разбор$callable() и файл с namespace
Исходникине изменены и не исполнены
CSVapi-audit.csv
Самостоятельный проект, исходник и архив
Составить план поддержки старого PHP-кода без его запускаСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__.'/lab.php';
$db=null;$owned=$removed=$rows=$checks=$observations=[];
$runtime=c174LocalRuntime();

    function saveCsv174(string $filename,array $records): void {
        $file=fopen(__DIR__.'/'.$filename,'wb');
        if($file===false){throw new RuntimeException('Не удалось создать CSV');}
        try {
            foreach($records as $record){
                if(fputcsv($file,$record,';','"','')===false){throw new RuntimeException('Строка CSV не записана');}
            }
        } finally {fclose($file);}
    }
    function readCsv174(string $filename): array {
        $file=fopen(__DIR__.'/'.$filename,'rb');
        if($file===false){throw new RuntimeException('Созданный CSV не найден');}
        $records=[];
        try {
            while(($record=fgetcsv($file,0,';','"',''))!==false){
                $records[]=$record;
                if(count($records)>30){throw new RuntimeException('Учебный файл неожиданно велик');}
            }
        } finally {fclose($file);}
        return $records;
    }

    if(!function_exists('token_get_all')){throw new RuntimeException('Для разбора исходников нужен модуль tokenizer');}
    $map=json_decode(file_get_contents(__DIR__.'/api-map.json'),true,512,JSON_THROW_ON_ERROR);
    function auditTokens174(string $source): array {
        $result=[];$line=1;
        foreach(token_get_all($source) as $token){
            $id=is_array($token)?$token[0]:null;
            $text=is_array($token)?$token[1]:$token;
            $start=is_array($token)?$token[2]:$line;
            if(!in_array($id,[T_WHITESPACE,T_COMMENT,T_DOC_COMMENT],true)){
                $result[]=['id'=>$id,'text'=>$text,'line'=>$start];
            }
            $line=$start+substr_count($text,"\n");
        }
        return $result;
    }
    function inspectFile174(string $filename,string $source,array $map): array {
        $tokens=auditTokens174($source);
        // Resolving namespaces and imported function aliases requires a parser.
        foreach($tokens as $token){
            if(in_array($token['id'],[T_NAMESPACE,T_USE],true)){
                return [[$filename,(string)$token['line'],'*namespace*','scope','manual','manual-namespace']];
            }
        }
        $findings=[];$count=count($tokens);
        for($i=0;$i<$count;$i++){
            $token=$tokens[$i];$next=$tokens[$i+1]??null;$previous=$tokens[$i-1]??null;
            if($next===null || $next['text']!=='('){continue;}
            if($previous!==null && in_array($previous['id'],[T_OBJECT_OPERATOR,T_DOUBLE_COLON,T_NEW,T_NS_SEPARATOR],true)){continue;}
            if($token['id']===T_VARIABLE){
                $findings[]=[$filename,(string)$token['line'],$token['text'],'dynamic','manual','manual-call-target'];continue;
            }
            if($token['id']!==T_STRING){continue;}
            if($previous!==null && ($previous['id']===T_FUNCTION || ($previous['text']==='&' && ($tokens[$i-2]['id']??null)===T_FUNCTION))){continue;}
            $name=strtolower($token['text']);
            if(strpos($name,'cubrid_')!==0){continue;}
            $family=$map[$name]['family']??'unknown';
            $available=function_exists($name)?'yes':'no';
            $actions=['native'=>'keep-native','compatibility'=>'review-compatible','legacy_lob'=>'plan-lob2','obsolete'=>'export-on-old-system','unknown'=>'manual-api'];
            $findings[]=[$filename,(string)$token['line'],$name,$family,$available,$actions[$family]];
        }
        return $findings;
    }
    $records=[['file','line','call','family','available','next_step']];$unchanged=true;$links=[];
    foreach(['catalog.php','legacy.php','namespaced.php'] as $filename){
        $path=__DIR__.'/sample/'.$filename;
        $before=hash_file('sha256',$path);$source=file_get_contents($path);
        if($source===false){throw new RuntimeException('Синтетический исходник не прочитан');}
        foreach(inspectFile174($filename,$source,$map) as $finding){
            $records[]=$finding;
            if(isset($map[$finding[2]])){$links[$finding[2]]='?phpchapter='.$map[$finding[2]]['chapter'].'#'.$map[$finding[2]]['anchor'];}
        }
        $unchanged=$unchanged&&hash_file('sha256',$path)===$before;
    }
    saveCsv174('api-audit.csv',$records);
    $counts=['native'=>0,'compatibility'=>0,'legacy_lob'=>0,'obsolete'=>0,'dynamic'=>0,'scope'=>0,'unknown'=>0];
    foreach(array_slice($records,1) as $record){$counts[$record[3]]++;}
    $rows=[['Прямые вызовы CUBRID',$counts['native']+$counts['compatibility']+$counts['legacy_lob']+$counts['obsolete']],
        ['Нативная группа',$counts['native']],['Совместимая группа',$counts['compatibility']],
        ['Legacy LOB','cubrid_lob_export: доступен; план перехода к LOB2'],
        ['GLO','cubrid_send_glo: отсутствует; нужен экспорт в старой среде'],
        ['Ручной разбор','$callable() и файл с namespace'],['Исходники',$unchanged?'не изменены и не исполнены':'изменены'],['CSV','api-audit.csv']];
    $expected=json_decode(file_get_contents(__DIR__.'/audit-expected.json'),true,512,JSON_THROW_ON_ERROR);
    $checks=['exact_call_sites'=>$records===$expected,'native_count'=>$counts['native']===7,
        'compatibility_count'=>$counts['compatibility']===2,'legacy_available'=>$records[10][4]==='yes',
        'glo_absent'=>$records[11][2]==='cubrid_send_glo'&&$records[11][4]==='no',
        'manual_cases'=>$counts['dynamic']===1&&$counts['scope']===1,
        'source_unchanged'=>$unchanged,'no_database'=>$owned===[]&&$runtime['transport']==='no_database_connection',
        'csv_exact'=>readCsv174('api-audit.csv')===$expected];
    $observations=['findings'=>array_slice($records,1),'counts'=>$counts,'reference_links'=>$links,
        'source_files_executed'=>false,'scope'=>'PHP 7.4 direct unqualified calls; namespace/use and variable calls need review'];

c174Report('cub174-api-audit','Составить план поддержки старого PHP-кода без его запуска',$rows,$checks,$observations,$runtime,$owned,$removed);
Ожидаемый результат
Прямые вызовы CUBRID — 11
Нативная группа — 7
Совместимая группа — 2
Legacy LOB — cubrid_lob_export: доступен; план перехода к LOB2
GLO — cubrid_send_glo: отсутствует; нужен экспорт в старой среде
Ручной разбор — $callable() и файл с namespace
Исходники — не изменены и не исполнены
CSV — api-audit.csv

Распакуйте весь ZIP и прочитайте README.txt. example.php содержит логику урока, lab.php — проверку отдельной среды и работу с собственными ресурсами. Этот проект читает локальные файлы и возможности CLI, не открывая соединение с базой. После запуска откройте result.html и скачиваемый ниже JSON или CSV.

Открыть результат выполнения · Посмотреть данные JSON · Скачать план разбора API CSV

Границы решения

Нельзя назвать этот сканер доказательством отсутствия других вызовов. Он рассчитан на синтаксис PHP 7.4 и прямые имена без namespace/import; динамические строки, callbacks, алиасы use function, variable methods и код, генерируемый в runtime, требуют парсера и ручной проверки. T_USE также консервативно отправляет файл на ручной разбор. Метод с именем cubrid_query не является глобальной функцией расширения. Доступность в CLI не доказывает доступность веб-окружения; GLO здесь только обнаруживается, а не извлекается и не мигрируется.

Попробуйте изменить условие

В legacy.php добавьте комментарий с cubrid_new_glo и настоящий прямой вызов cubrid_lob2_size($lob). Что должно измениться?

Разбор

Комментарий не добавит строку отчёта. Прямой вызов добавит native/yes/keep-native с фактическим номером строки; исходник всё равно не исполняется. Если этот вызов находится в namespace, весь файл останется в ручной группе — нужно расширять парсер, а не объявлять разрешение имён известным.

Все 68 функций нативного каталога

Нативный каталог включает 68 функций: подключения, подготовленные команды, транзакции, результаты, описания схемы, OID, коллекции и оба семейства LOB. Ниже перечислен каждый официальный пункт. Ссылка ведёт к полной карточке; существующая карточка сама по себе не означает, что проверены все возможные сочетания параметров.

Функция и подробная карточкаНазначение
cubrid_bindПередать значение конкретному маркеру, сохранив его смысл и тип.
cubrid_close_prepareОсвободить request handle, не подменяя commit/rollback
cubrid_close_requestОсвободить запрос после работы, не закрывая всё соединение.
cubrid_col_getПрочитать элементы одного атрибута коллекции в числовой PHP-массив.
cubrid_col_sizeУзнать количество элементов, включая повторения и NULL-элементы, не передавая их все в PHP.
cubrid_column_namesУзнать имена выбранных колонок и сохранить порядок заголовков.
cubrid_column_typesУзнать SQL-типы результата и выбрать осмысленный формат данных.
cubrid_commitПодтвердить все изменения одной законченной бизнес-операции.
cubrid_connectОткрыть соединение с нужной базой через broker.
cubrid_connect_with_urlПередать адрес, базу и свойства входа в URL.
cubrid_current_oidПолучить внутренний идентификатор экземпляра, на который сейчас установлен курсор результата.
cubrid_disconnectЗакрыть выбранное соединение и его оставшиеся запросы.
cubrid_dropУдалить один выбранный экземпляр с дальнейшим подтверждением или отменой транзакции.
cubrid_error_codeСохранить номер последнего отказа CUBRID API.
cubrid_error_code_facilityОпределить слой последней ошибки: DBMS, CAS, CCI или клиент.
cubrid_error_msgПрочитать пояснение последнего отказа для диагностики.
cubrid_executeВыполнить прямой SQL либо подготовленный запрос и проверить его результат.
cubrid_fetchПолучить следующую строку как числовой или именованный массив либо объект.
cubrid_free_resultОсвободить клиентский буфер чтения, сохранив сам request до отдельного закрытия.
cubrid_getПрочитать одно, несколько или все свойства одного выбранного экземпляра базы.
cubrid_get_autocommitУзнать, фиксируются ли команды автоматически, и отличить выключенный режим от ошибки.
cubrid_get_charsetУзнать кодировку конкретного соединения перед диагностикой текстового обмена.
cubrid_get_class_nameУзнать имя структуры CUBRID, которой принадлежит найденный экземпляр.
cubrid_get_client_infoОпределить версию клиентской библиотеки CCI, через которую PHP общается с CUBRID.
cubrid_get_db_parameterУзнать фактический режим соединения: изоляцию, ожидание блокировки, предельную строку и autocommit.
cubrid_get_query_timeoutПроверить установленный бюджет конкретного request в миллисекундах.
cubrid_get_server_infoПолучить версию сервера по конкретному открытому соединению.
cubrid_insert_idСразу после INSERT получить сгенерированный ключ своей новой записи.
cubrid_is_instanceРазличить существующий экземпляр, его отсутствие и ошибку проверки.
cubrid_lob_closeОсвободить весь массив legacy LOB
cubrid_lob_exportСохранить выбранный LOB в локальный файл
cubrid_lob_getПолучить legacy-дескрипторы выбранных BLOB/CLOB
cubrid_lob_sendПередать содержимое LOB в вывод PHP
cubrid_lob_sizeУзнать размер содержимого строкой цифр
cubrid_lob2_bindПередать содержимое LOB в конкретный параметр подготовленного SQL.
cubrid_lob2_closeОсвободить самостоятельно созданный дескриптор LOB в правильный момент.
cubrid_lob2_exportВыгрузить целое вложение из BLOB/CLOB в новый файл на машине PHP.
cubrid_lob2_importПеренести целый локальный файл в подготовленный LOB.
cubrid_lob2_newСоздать отдельный BLOB/CLOB перед записью и привязкой к строке.
cubrid_lob2_readПолучить ограниченную порцию байтов BLOB/CLOB для обработки, передачи или записи в файл.
cubrid_lob2_seekПереставить байтовый курсор LOB перед чтением заголовка, середины или хвоста.
cubrid_lob2_seek64Переставить курсор LOB, передав смещение десятичной строкой вместо ограниченного PHP int.
cubrid_lob2_sizeУзнать размер и отличить существующее пустое вложение от отказа.
cubrid_lob2_size64Получить размер десятичной строкой без сужения до PHP int.
cubrid_lob2_tellУзнать текущую байтовую позицию LOB как целое число, например после чтения заголовка.
cubrid_lob2_tell64Получить позицию LOB десятичной строкой для закладки и сопоставления с большим размером.
cubrid_lob2_writeЗаписать очередную порцию двоичных данных или текста.
cubrid_lock_readПолучить разделяемую блокировку чтения конкретного экземпляра в короткой операции.
cubrid_lock_writeПолучить блокировку изменения, затем перечитать данные и выполнить согласованную правку.
cubrid_move_cursorПереместить курсор относительно начала, текущей позиции или конца результата.
cubrid_next_resultПерейти к следующему результату SQL-пакета
cubrid_num_colsПосчитать поля SELECT, в том числе у пустого результата.
cubrid_num_rowsПосчитать строки выполненной выборки, отделив страницу от общего количества.
cubrid_pconnectПодключение через persistent API и проверка реального срока жизни
cubrid_pconnect_with_urlURL-параметры persistent-подключения
cubrid_prepareОтделить постоянный SQL от значений и создать запрос для повторного выполнения.
cubrid_putИзменить одно поле или явную карту полей выбранного экземпляра.
cubrid_rollbackОтменить незавершённые изменения после отказа условия или SQL.
cubrid_schemaПолучить описание классов, колонок, PK/FK, представлений, триггеров, методов и прав; превратить его в понятное решение импорта или сопровождения.
cubrid_seq_dropУдалить конкретное вхождение по позиции, сохранив одинаковые значения в других местах.
cubrid_seq_insertВставить новый элемент на определённую позицию последовательности.
cubrid_seq_putЗаменить значение на выбранной позиции последовательности.
cubrid_set_addДобавить одно значение в SET/MULTISET; для SEQUENCE проверенный вызов добавляет его в конец.
cubrid_set_autocommitВыбрать границы подтверждения изменений в конкретном соединении.
cubrid_set_db_parameterЗадать соединению ожидание блокировки либо изоляцию следующей операции.
cubrid_set_dropУдалить одно совпадающее значение из SET или одно вхождение из MULTISET.
cubrid_set_query_timeoutОграничить допустимое исполнение выбранного request, не меняя автоматически соседние запросы.
cubrid_versionУзнать версию загруженного PHP-модуля cubrid и не спутать её с сервером.

Маршрут по задачам: подключиться и получить нужные строки → подставить значения → сохранить операцию целиком → выбрать форму результата. Затем структура схемы, экземпляры данных по OID, коллекции и файлы/потоки LOB2.

Все 26 функций каталога совместимости

В каталоге совместимости 26 функций. Это операции CUBRID с привычной формой интерфейса для сопровождения PHP-кода. Есть отдельные функции полей/курсора, кодировки, диагностики, обычных запросов. Не переносите автоматически MySQL-типы ошибок, правила транзакций или границы памяти.

Функция и подробная карточкаНазначение
cubrid_affected_rowsОтличить изменённые строки от случая, когда условие не выбрало ни одной.
cubrid_client_encodingПрочитать кодировку соединения через имя из совместимого с MySQL набора CUBRID.
cubrid_closeЗакрыть соединение после явного завершения транзакции
cubrid_data_seekПерейти к строке результата по позиции от нуля перед следующим fetch.
cubrid_db_nameДостать одно имя по позиции из массива, возвращённого cubrid_list_dbs.
cubrid_errnoСохранить числовой код последнего отказа выбранного соединения.
cubrid_errorПолучить диагностический текст соединения, отделив его от сообщения посетителю.
cubrid_fetch_arrayПрочитать одну строку с именованными, числовыми или обоими наборами ключей.
cubrid_fetch_assocПолучить следующую строку с именованными ячейками и отличить EOF от ошибки.
cubrid_fetch_fieldПолучить объект описания одного поля и понимать ограничения его свойств.
cubrid_fetch_lengthsИзмерить байтовые длины полей только что прочитанной строки.
cubrid_fetch_objectПолучить объект чтения с полями строки и при необходимости собственным классом.
cubrid_fetch_rowПолучить значения в порядке SELECT для последовательного экспорта.
cubrid_field_flagsПрочитать признаки ограничения и индекса, не подменяя ими правила приложения.
cubrid_field_lenУзнать типовую ширину поля отдельно от длины конкретного значения.
cubrid_field_nameПрочитать имя или псевдоним выбранной позиции SELECT.
cubrid_field_seekВыбрать следующее описание колонки без перемещения товарных строк.
cubrid_field_tableПосмотреть доступное происхождение поля и отличить его от выражения.
cubrid_field_typeУзнать SQL-тип конкретного поля для понятного описания или формата.
cubrid_list_dbsПолучить список имён баз для служебного выбора и сопоставления с разрешённой конфигурацией.
cubrid_num_fieldsПосчитать поля результата и безопасно ограничить обход метаданных.
cubrid_pingПроверить ответ соединения, отдельно от результата конкретной бизнес-операции.
cubrid_queryОтправить один готовый SQL-запрос и различить набор строк, успех команды и отказ.
cubrid_real_escape_stringПодготовить содержимое строкового SQL-литерала в существующем коде CUBRID.
cubrid_resultПолучить одну конкретную ячейку небольшого результата.
cubrid_unbuffered_queryНачать последовательное получение результата и постепенно записывать нужные строки.

Функции cubrid_fetch_assoc и другие fetch-варианты могут читать request, созданный нативным prepare/execute: группы справочника не создают два разных сервера. Но различия return/default/error остаются у каждой функции. Поиск, CSV и диагностика совместимого API и жизнь request показывают реальные границы.

Все четыре исторические GLO-функции

Исторический каталог состоит из четырёх GLO-функций. Их параметры и прежнее назначение сохранены в карточках. В native 11.2.0.0002 function_exists для всех четырёх вернул false в подготовке 172; ни один GLO-вызов не исполнялся. Работающие проекты миграции используют синтетический manifest и современный LOB2, а не извлечение из настоящего старого GLO.

Функция и подробная карточкаНазначение
cubrid_load_from_gloИсторически получить содержимое GLO в назначенный файл для архива или переноса.
cubrid_new_gloИсторически создать экземпляр GLO указанного класса и получить OID для связи с документом.
cubrid_save_to_gloИсторически сохранить указанный файл в уже существующем экземпляре GLO.
cubrid_send_gloИсторически направить содержимое GLO в стандартный вывод PHP, не возвращая его строкой.

Порядок переноса: найти старые вызовы → сохранить сведения об исходной версии/данных → экспортировать в подходящей старой среде → получить manifest с бизнес-ключами и хешами → импортировать новые LOB2 → сверить записи. Новые проекты не обещают автоматически распознать старые классы GLO по одному имени функции; из исходников ничего не удаляется.

Применить договор владения и выбрать следующий шаг

Сначала определите полезный результат: строки для экрана, файл, сохранённую запись или план миграции. Затем выберите API и договор ресурсов; параметры и константы относятся к конкретному получателю. Прямая ссылка на карточку нужна для точных параметров, но не заменяет понимания, кто выполняет commit и закрывает request.

Человек закрывает соединение в подключаемом файле и не понимает, почему определённая там функция не получает живое подключение. Вопрос на Stack Overflow. В нашем листе комплектования соединение явно передаётся аргументом, функции владеют только своими requests, а внешняя операция завершает транзакцию. Глобальные mysql_* и SQL-конкатенация из обсуждения не перенесены.

Произвольные числовые значения пытаются объединять как независимые битовые флаги. Вопрос на Stack Overflow. В профилях выбираются осмысленные разрешённые режимы CUBRID; отдельные selectors не объединяются через |. Только контракт конкретной функции определяет допустимую комбинацию.

Один токен T_STRING может быть именем функции, объявлением, константой или частью другой конструкции. Вопрос на Stack Overflow. Наш ограниченный сканер проверяет соседние токены, пропускает комментарии/строки/методы и оставляет namespace и переменные вызовы на ручной разбор. Полного AST или разрешения имён он не обещает.

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

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

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

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

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

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