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

Дерево руководства
Глава 101 · После запросов и транзакций CUBRID
CUBRID: выбирать API, передавать ресурсы и разбирать старое приложение
Лист комплектования, именованные профили CSV и безопасная инвентаризация старого кода; четыре вида ресурсов, все официальные константы и три полных каталога CUBRID.
От полезного файла к выбору инструмента
Нужно выдать кладовщику лист комплектования, экспортировать доступные товары и понять, что переделывать в старом приложении. Во всех трёх задачах важно выбрать правильную функцию, форму данных и участок кода, который завершит работу. В этой главе результатом будут три скачиваемых CSV, а справочная часть объяснит весь официальный каталог ресурсов, констант и групп API CUBRID.
| Задача | Полученный результат | Самостоятельный проект |
|---|---|---|
| Заявка 501 | Кабеля не хватает 2, розеток 4; хомутов достаточно | Лист комплектования |
| Один экран и CSV | CAB-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 относится к конкретному запросу, а массив строки — уже выписанные данные. Аналогия не описывает устройство памяти или сетевого протокола.
- Верхний уровень создаёт $db — ресурс подключения и границу транзакции
- pickLines174($db, ..., 501): свой $request → три массива строк → request закрыт
- shortage174($db, ..., 501): другой request → positions=2, missing=6 → request закрыт
- $db ещё работает; внешний код завершает транзакцию и затем закрывает соединение
- $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 identifier | cubrid_connect, cubrid_connect_with_url, cubrid_pconnect и cubrid_pconnect_with_url дают подключение. Через него выполняются SQL и транзакционные команды. | Открывает владелец операции; завершает транзакцию и вызывает cubrid_disconnect или cubrid_close. Заимствовавший его модуль не закрывает подключение. Имя pconnect не является доказательством reuse в конкретном драйвере. |
| Request identifier | cubrid_prepare создаёт подготовленный запрос. Вариант cubrid_execute с соединением и SQL также может вернуть request; выполнение уже подготовленного request возвращает признак выполнения, не второй ресурс. | Владелец подготовленного запроса вызывает cubrid_close_request/cubrid_close_prepare. cubrid_free_result освобождает данные результата и имеет иной контракт: возможность повторного execute изучена отдельно. |
| Legacy LOB identifier | cubrid_lob_get получает набор ресурсов BLOB/CLOB из результата SQL. Каждый такой ресурс относится к старому семейству cubrid_lob_*. | Передают в cubrid_lob_size, cubrid_lob_export или cubrid_lob_send; массив полученных ресурсов закрывают через cubrid_lob_close. Это не LOB2 handle и не ресурс файла fopen. |
| LOB2 identifier | cubrid_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 не загружается в список.
- Внешний код открывает $db и выбирает order_no = 501
- pickLines174 создаёт request → 3 строки → закрывает request
- shortage174 создаёт другой request → 2 позиции, 6 единиц → закрывает request
- Владелец $db выполняет commit; PHP-массивы сохраняют прочитанные значения
- 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 |
| CSV | pick-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.
- Профиль screen → CUBRID_ASSOC → ключи sku/title/qty
- Профиль csv → CUBRID_NUM → позиции 0/1/2
- SQL: category = электрика, qty > 0, ORDER BY id, LIMIT 2
- Получены CAB-1 / Кабель / 3 и CAB-4 / Кабель O'Reilly / 2
- Неизвестный профиль не выполняет третий 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 |
| Выполнено SELECT | 2 |
| CSV | selected-export.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;
}
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 нет знака $, значение поставляет расширение. Она доступна только после загрузки/встраивания соответствующего модуля. Это позволяет читать намерение кода, но имя из справочника всё равно нужно сверять с установленной версией.
- csv → CUBRID_NUM → cubrid_fetch(request, mode) → [CAB-1, Кабель, 3]
- CUBRID_PARAM_LOCK_TIMEOUT → set_db_parameter(connection, selector, 1000) → ожидание блокировки
- CUBRID_SCH_PRIMARY_KEY → cubrid_schema(connection, selector, table) → описание ключа
- Одинаковый тип 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_SPEC | SQL-определение представления. | Есть в 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. |
- Данные каталога → SELECT category, агрегаты → уже небольшая сводка
- Описание выбранных колонок → подписи, fetch → значения
- HTML escaping для экрана; отдельный CSV для скачивания
- При ручной транзакции: успешное окончание 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-файла с обычным query | cubrid_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 не используются.
- sample/catalog.php: 7 прямых вызовов, комментарий/строка не считаются
- sample/legacy.php: query + legacy LOB + отсутствующий GLO; $callable требует ручного разбора
- sample/namespaced.php: не угадываем разрешение имён
- 11 прямых вызовов + 2 пометки → api-audit.csv
- В наблюдениях — точные ссылки на карточки; 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, а не его корректность для старых данных.
Что получится после запуска
| Наблюдение | Результат |
|---|---|
| Прямые вызовы CUBRID | 11 |
| Нативная группа | 7 |
| Совместимая группа | 2 |
| Legacy LOB | cubrid_lob_export: доступен; план перехода к LOB2 |
| GLO | cubrid_send_glo: отсутствует; нужен экспорт в старой среде |
| Ручной разбор | $callable() и файл с namespace |
| Исходники | не изменены и не исполнены |
| CSV | api-audit.csv |
Самостоятельный проект, исходник и архив
<?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_url | URL-параметры 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. Примеры и объяснения написаны для этого руководства; источники и обсуждения доступны в каждой главе.


