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

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

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

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

Глава 86 · После ограниченных SELECT и параметров CUBRID

CUBRID: строки для экрана и экспорта, курсор и длина UTF-8

Каталог, CSV и этикетка; повторный просмотр без потери первой строки; отдельная ячейка и реальный размер UTF-8.

Как одну выборку превратить в понятный результат

Один и тот же товар нужен в разных местах: в карточке на сайте, строке CSV, подписи этикетки и диагностике обмена. SQL сначала выбирает нужные поля, порядок и ограниченное число строк. Затем PHP читает результат в удобной форме. В этой главе вы получите «Кабель — 0 шт.», строку CAB-167;Кабель;0, полный список после предпросмотра и понятное объяснение, почему шесть русских букв занимают двенадцать байт.

Результат SELECT можно представить как небольшой упорядоченный список записей с указателем чтения — курсором. Request хранит связь с этим результатом. Fetch берёт одну запись и передвигает указатель; присваивание $row сохраняет уже полученные данные в PHP. Указатель не удаляет запись из базы. Модель списка объясняет порядок чтения, но не означает, что вся огромная таблица обязана быть загружена в PHP: размер задавайте WHERE и LIMIT ещё в запросе.

От данных к нужному представлению
  1. SQL: SELECT sku,title,qty … ORDER BY id LIMIT 2
  2. Первая строка: CAB-167 / Кабель / "0"; fetch создаёт значение PHP
  3. Варианты назначения: ASSOC для карточки; числовой ряд для CSV; объект для этикетки
  4. Далее читается следующая строка; для повторного просмотра нужна осмысленная позиция

Сначала одна строка

// $request — результат успешного SELECT title,qty ... LIMIT 1.
$row = cubrid_fetch_row($request);
if ($row === null) {
    throw new RuntimeException('Строку не удалось прочитать');
}
if ($row !== false) {
    echo $row[0].' — '.(int)$row[1].' шт.';
}
// Для title=Кабель и qty=0: Кабель — 0 шт.

Имя $request обозначает ресурс запроса. Вызов функции с круглыми скобками получает из него одну строку; [0] и [1] выбирают поля этой строки по позициям SELECT. $row === false означает конец данных, а null в контракте fetch — отказ. Проверка qty на истинность была бы другой операцией и скрыла бы нулевой остаток. Перед выдачей HTML текст нужно экранировать; полные результаты ниже делают это в отчёте. Вызовы и параметры разобраны в главе о функциях, массивы — в главе о массивах.

Что требуетсяНужный механизмРазвитие
Карточка, CSV и этикеткаfetch_array / fetch_row / fetch_objectТри потребителя двух товаров
Предпросмотр и полный списокdata_seek / move_cursorНе потерять первую строку
Одна ячейка малого отчётаresult + строгие сравненияNULL, пустой текст и ноль
Байтовое ограничение обменаfetch_lengths сразу после fetchКабель: 6 кодовых точек, 12 байт

Перед практикой доступны подключение и собственная таблица, параметры и ограниченные выборки и базовые формы результата. Здесь фокус — конкретные потребители и перемещение внутри уже выбранных строк. SQL не использует SELECT *: сортировку и отбор выполняет база, PHP только оформляет нужное.

Программы закреплены на native cubrid 11.2.0.0002, PHP 7.4.33, CCI 11.2.0 и CUBRID 11.3.5 в отдельном Linux-окружении. Это исторический профиль совместимости, не совет понизить PHP действующего сайта. README содержит воспроизводимую подготовку изолированной базы и запуска. c167Request/c167Read в lab.php — открытый код подготовки параметров/чтения, а новые функции fetch и навигации вызваны прямо в example.php.

Одна выборка товаров: подписи карточек, CSV и объект этикетки

Оператору нужен каталог на экране, поставщику — CSV в порядке sku/title/qty, а печати — подпись этикетки. Данные одни и те же, но потребители обращаются к ним по-разному. Карточка получает название по ключу title; CSV получает последовательность значений; объект этикетки соединяет уже прочитанные свойства с переданным префиксом «Склад». Нулевой остаток остаётся видимым: «Кабель — 0 шт.».

Путь конкретных значений
  1. SQL выбирает только sku, title, qty для parts; ORDER BY id, LIMIT 2
  2. Именованный массив: title=Кабель, qty="0" → карточка «Кабель — 0 шт.»
  3. Числовой массив: [CAB-167, Кабель, "0"] → CSV CAB-167;Кабель;0
  4. Объект: свойства из строки + отдельный prefix=Склад → «Склад: Кабель / CAB-167»

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

// $request — успешно выполненный SELECT title,qty ... LIMIT 1.
$row = cubrid_fetch_array($request, CUBRID_ASSOC);
if (is_array($row)) {
    echo $row['title'].' — '.(int)$row['qty'].' шт.';
}
// Для строки Кабель / 0: Кабель — 0 шт.

Квадратные скобки выбирают значение массива по имени столбца; AS может задать это имя явно. Вызов fetch читает одну строку и передвигает курсор. (int) превращает строковый остаток в целое в модели приложения, не меняя SQL. В полной программе NULL/ошибка и false/конец разделены. Для CSV используется fetch_row: позиции 0/1/2 соответствуют порядку SELECT, а fputcsv отвечает за разделители и кавычки. CatalogLabel167 — заранее объявленный класс; -> читает его свойство или вызывает метод. Конструктор получает только переданный префикс, а не весь массив строки.

Результат самостоятельной программы

НаблюдениеРезультат
Карточка 1Кабель — 0 шт.
Карточка 2Зажим — 7 шт.
Строк данных в CSV2
Первая строка CSVCAB-167;Кабель;0
Этикетка объектаСклад: Кабель / CAB-167
Конструктор увидел названиеКабель
Полная программа, файлы и архив для запуска
Одна выборка товаров: подписи карточек, CSV и объект этикеткиСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__.'/lab.php';
$db=null;$owned=$removed=$rows=$checks=$observations=[];
try {
    $config=c167Config();$db=c167Connect();$runtime=c167Runtime($db);

    $name=c167CreateTable($db,$owned,'adapters',[
        ['id'=>10,'sku'=>'CAB-167','title'=>'Кабель','category'=>'parts','price_cents'=>1200,'qty'=>0,'note'=>null],
        ['id'=>20,'sku'=>'CLP-167','title'=>'Зажим','category'=>'parts','price_cents'=>350,'qty'=>7,'note'=>'']
    ]);
    $table=c167Qualified($name);$request=null;$csvStream=null;
    $sql='SELECT sku,title,qty FROM '.$table.' WHERE category=? ORDER BY id LIMIT 2';
    try {
        $request=c167Request($db,$sql,['parts']);$labels=[];$raw=[];
        while (true) {
            $call=c167Capture(static function() use ($request) {return cubrid_fetch_array($request,CUBRID_ASSOC);});
            if ($call['warning'] || $call['code']!==0 || $call['value']===null) {c167Failure('Карточки не прочитаны',$call);}
            if ($call['value']===false) {break;}
            $row=$call['value'];$raw[]=$row;
            $labels[]=$row['title'].' — '.(int)$row['qty'].' шт.';
        }
        c167Release($request);
        $request=c167Request($db,$sql,['parts']);
        $csvStream=fopen('php://temp','w+b');
        if ($csvStream===false) {throw new RuntimeException('Буфер CSV недоступен.');}
        $headerWritten=fputcsv($csvStream,['sku','title','qty'],';','"','')!==false;$exported=0;
        while (true) {
            $call=c167Capture(static function() use ($request) {return cubrid_fetch_row($request);});
            if ($call['warning'] || $call['code']!==0 || $call['value']===null) {c167Failure('Экспорт не прочитан',$call);}
            if ($call['value']===false) {break;}
            if (fputcsv($csvStream,$call['value'],';','"','')===false) {throw new RuntimeException('Строка CSV не записана.');}
            $exported++;
        }
        rewind($csvStream);$csv=stream_get_contents($csvStream);fclose($csvStream);$csvStream=null;
        c167Release($request);
        class CatalogLabel167 {
            public $sku;public $title;public $qty;public $prefix;public $titleSeenByConstructor;
            public function __construct(string $prefix) {
                $this->prefix=$prefix;$this->titleSeenByConstructor=$this->title;
            }
            public function caption():string {return $this->prefix.': '.$this->title.' / '.$this->sku;}
        }
        $request=c167Request($db,'SELECT sku,title,qty FROM '.$table.' WHERE id=? LIMIT 1',[10]);
        $call=c167Capture(static function() use ($request) {return cubrid_fetch_object($request,CatalogLabel167::class,['Склад']);});
        if (!($call['value'] instanceof CatalogLabel167) || $call['warning'] || $call['code']!==0) {c167Failure('Этикетка не создана',$call);}
        $label=$call['value'];$caption=$label->caption();$objectData=get_object_vars($label);
    } finally {
        if (is_resource($csvStream)) {fclose($csvStream);}
        c167Release($request);
    }
    c167Commit($db);
    $expectedCsv="sku;title;qty\nCAB-167;Кабель;0\nCLP-167;Зажим;7\n";
    $rows=[['Карточка 1',$labels[0]],['Карточка 2',$labels[1]],['Строк данных в CSV',$exported],
        ['Первая строка CSV','CAB-167;Кабель;0'],['Этикетка объекта',$caption],['Конструктор увидел название',$label->titleSeenByConstructor]];
    $checks=['two_labels'=>count($labels)===2,'first_zero_shown'=>$labels[0]==='Кабель — 0 шт.',
        'second_quantity'=>$labels[1]==='Зажим — 7 шт.','named_keys'=>array_keys($raw[0])===['sku','title','qty'],
        'raw_quantity_string'=>$raw[0]['qty']==='0','csv_header_written'=>$headerWritten,
        'csv_two_rows'=>$exported===2,'csv_exact'=>$csv===$expectedCsv,'custom_class'=>$label instanceof CatalogLabel167,
        'constructor_argument'=>$label->prefix==='Склад','hydrated_before_constructor'=>$label->titleSeenByConstructor==='Кабель',
        'label_exact'=>$caption==='Склад: Кабель / CAB-167'];
    $observations=['associative_rows'=>$raw,'csv'=>$csv,'csv_bytes'=>strlen($csv),'object'=>$objectData,
        'query_columns'=>['sku','title','qty'],'separate_requests'=>3];

} finally {
    if (is_resource($db)) {try {$removed=c167Cleanup($db,$owned);} finally {c167Close($db);}}
}

c167Report('cub167-row-adapters','Одна выборка товаров: подписи карточек, CSV и объект этикетки',$rows,$checks,$observations,$runtime,$owned,$removed);
Ожидаемый результат
Карточка 1 — Кабель — 0 шт.
Карточка 2 — Зажим — 7 шт.
Строк данных в CSV — 2
Первая строка CSV — CAB-167;Кабель;0
Этикетка объекта — Склад: Кабель / CAB-167
Конструктор увидел название — Кабель

Распакуйте все файлы и выполните example.php по README.txt. lab.php проверяет отдельную базу и namespace, создаёт одну собственную случайную таблицу и удаляет её в finally. result.html показывает полезный результат, result.json сохраняет полученные строки и наблюдения, metrics.json — проверки и очистку. Пароль, приватные пути и сырые серверные сообщения не включаются в публичный результат.

Открыть фактический результат · Открыть данные JSON.

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

Три независимых запроса здесь намеренно показывают три независимых потребителя; их не нужно выполнять подряд в каждом HTTP-запросе сайта. В каждом выбран ровно нужный набор полей и две строки. Объект чтения не является ORM и не сохраняет изменённые свойства в базе. Конструктор может перезаписать загруженное поле: здесь он записывает только prefix и наблюдение. В HTML значения экранируются отчётом; CSV-кавычки решают формат, но не защиту от формул электронных таблиц для произвольного внешнего текста.

Автор вопроса ожидал автоматически передать все поля строки аргументом конструктора своего класса. Исходное обсуждение. Свой класс этикетки отдельно получает свойства и явно заданный prefix. Порядок операций проверяется по CUBRID source и подготовке, а не по поведению mysqli.

Измените требование

В CSV поставщик просит сначала количество, затем артикул. Достаточно ли поменять только заголовок?

Разбор решения

Нет. Для числового массива порядок определяется SELECT. Нужно согласованно выбрать qty,sku и записать заголовок qty;sku либо явно собрать [$row[2],$row[0]]. Именованная карточка title при этом не должна случайно начать показывать sku. Разные потребители могут иметь разные SQL-проекции.

Предварительно показать товар и не потерять его при полном выводе

Над таблицей нужен небольшой предпросмотр первого товара. Если для него прочитать строку, а затем сразу начать обычный цикл fetch, таблица начнётся со второго товара. Это не удаление записи из базы: изменилось положение чтения результата. Самостоятельный пример сначала показывает «Кабель», возвращается к позиции 0 и затем выводит все три товара. Дополнительно он показывает переход к началу, текущей и последней позиции того же небольшого результата.

Путь конкретных значений
  1. Упорядоченная выборка: позиция 0 → id10 Кабель; 1 → id40 Зажим; 2 → id90 Коробка
  2. fetch_object для предпросмотра читает Кабель; следующей становится строка Зажим
  3. data_seek(0) возвращает чтение к Кабелю → цикл выводит все три строки
  4. Отдельные переходы: FIRST1 → Кабель; после fetch CURRENT0 → Зажим; LAST1 → Коробка

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

// Первый fetch уже использован для предпросмотра.
if (cubrid_data_seek($request, 0) !== true) {
    throw new RuntimeException('Не удалось вернуться к началу');
}
$firstAgain = cubrid_fetch_row($request);
// В этой выборке: ["10", "Кабель"].

data_seek получает номер строки результата начиная с нуля. Это не значение первичного ключа id: у строк id 10, 40, 90, но позиции 0, 1, 2. Функция перемещает указатель и возвращает bool; саму строку читает следующий fetch. У move_cursor другое правило: FIRST с offset 1 означает первую строку, LAST с offset 1 — последнюю. CURRENT с offset 0 оставляет текущую позицию; после чтения первой это уже вторая. ORDER BY id делает эту учебную последовательность определённой.

Результат самостоятельной программы

НаблюдениеРезультат
ПредпросмотрКабель
Полный списокКабель → Зажим → Коробка
Идентификаторы списка10, 40, 90
FIRST, 1Кабель
CURRENT, 0 после чтения первогоЗажим
LAST, 1Коробка
Выход за выборку отклонёнда
Полная программа, файлы и архив для запуска
Предварительно показать товар и не потерять его при полном выводеСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__.'/lab.php';
$db=null;$owned=$removed=$rows=$checks=$observations=[];
try {
    $config=c167Config();$db=c167Connect();$runtime=c167Runtime($db);

    $name=c167CreateTable($db,$owned,'preview',[
        ['id'=>10,'sku'=>'CAB-167','title'=>'Кабель','category'=>'parts','price_cents'=>1200,'qty'=>3,'note'=>null],
        ['id'=>40,'sku'=>'CLP-167','title'=>'Зажим','category'=>'parts','price_cents'=>350,'qty'=>7,'note'=>''],
        ['id'=>90,'sku'=>'BOX-167','title'=>'Коробка','category'=>'parts','price_cents'=>500,'qty'=>2,'note'=>null]
    ]);
    $table=c167Qualified($name);$request=null;
    try {
        $request=c167Request($db,'SELECT id,title FROM '.$table.' WHERE category=? ORDER BY id LIMIT 3',['parts']);
        $call=c167Capture(static function() use ($request) {return cubrid_fetch_object($request);});
        if (!($call['value'] instanceof stdClass) || $call['warning']) {c167Failure('Предпросмотр не прочитан',$call);}
        $preview=$call['value'];
        $reset=c167Capture(static function() use ($request) {return cubrid_data_seek($request,0);});
        if ($reset['value']!==true || $reset['warning']) {c167Failure('Начало списка не найдено',$reset);}
        $list=[];
        while (true) {
            $call=c167Capture(static function() use ($request) {return cubrid_fetch_row($request);});
            if ($call['warning'] || $call['code']!==0 || $call['value']===null) {c167Failure('Список не прочитан',$call);}
            if ($call['value']===false) {break;}
            $list[]=$call['value'];
        }
        $first=c167Capture(static function() use ($request) {return cubrid_move_cursor($request,1,CUBRID_CURSOR_FIRST);});
        if ($first['value']!==true || $first['warning']) {c167Failure('Первая позиция недоступна',$first);}
        $firstRow=cubrid_fetch_row($request);
        $current=c167Capture(static function() use ($request) {return cubrid_move_cursor($request,0);});
        if ($current['value']!==true || $current['warning']) {c167Failure('Текущая позиция недоступна',$current);}
        $nextRow=cubrid_fetch_row($request);
        $last=c167Capture(static function() use ($request) {return cubrid_move_cursor($request,1,CUBRID_CURSOR_LAST);});
        if ($last['value']!==true || $last['warning']) {c167Failure('Последняя позиция недоступна',$last);}
        $lastRow=cubrid_fetch_row($request);
        $outside=c167Capture(static function() use ($request) {return cubrid_move_cursor($request,100,CUBRID_CURSOR_FIRST);});
    } finally {c167Release($request);}
    c167Commit($db);
    $ids=array_map(static function(array $row):int{return (int)$row[0];},$list);
    $rows=[['Предпросмотр',$preview->title],['Полный список',implode(' → ',array_column($list,1))],
        ['Идентификаторы списка',implode(', ',$ids)],['FIRST, 1',$firstRow[1]],
        ['CURRENT, 0 после чтения первого',$nextRow[1]],['LAST, 1',$lastRow[1]],['Выход за выборку отклонён',$outside['value']===false]];
    $checks=['preview_stdclass'=>$preview instanceof stdClass,'preview_first'=>$preview->id==='10' && $preview->title==='Кабель',
        'zero_based_reset'=>$reset['value']===true,'all_three_rows'=>count($list)===3,'first_not_lost'=>$ids===[10,40,90],
        'first_origin_return'=>$first['value']===true,'first_origin_row'=>$firstRow===['10','Кабель'],
        'current_origin_return'=>$current['value']===true,'current_is_next'=>$nextRow===['40','Зажим'],
        'last_origin_return'=>$last['value']===true,'last_origin_row'=>$lastRow===['90','Коробка'],
        'outside_false'=>$outside['value']===false,'outside_diagnosed'=>$outside['code']===-20005 && $outside['facility']===3];
    $observations=['preview'=>get_object_vars($preview),'ordered_rows'=>$list,'positions'=>[
        'data_seek_zero'=>$reset['value'],'move_first_one'=>$firstRow,'move_current_zero_after_fetch'=>$nextRow,'move_last_one'=>$lastRow],
        'outside'=>$outside,'selection_limit'=>3,'primary_keys_are_not_offsets'=>true];

} finally {
    if (is_resource($db)) {try {$removed=c167Cleanup($db,$owned);} finally {c167Close($db);}}
}

c167Report('cub167-cursor-preview','Предварительно показать товар и не потерять его при полном выводе',$rows,$checks,$observations,$runtime,$owned,$removed);
Ожидаемый результат
Предпросмотр — Кабель
Полный список — Кабель → Зажим → Коробка
Идентификаторы списка — 10, 40, 90
FIRST, 1 — Кабель
CURRENT, 0 после чтения первого — Зажим
LAST, 1 — Коробка
Выход за выборку отклонён — да

Распакуйте все файлы и выполните example.php по README.txt. lab.php проверяет отдельную базу и namespace, создаёт одну собственную случайную таблицу и удаляет её в finally. result.html показывает полезный результат, result.json сохраняет полученные строки и наблюдения, metrics.json — проверки и очистку. Пароль, приватные пути и сырые серверные сообщения не включаются в публичный результат.

Открыть фактический результат · Открыть данные JSON.

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

Курсор перемещается внутри результата уже выполненного SELECT. Он не выполняет UPDATE, не сортирует таблицу и не заменяет серверную пагинацию. Нельзя выбирать весь огромный каталог ради перехода к последней строке: ограничьте SQL нужной страницей. Пустой результат нельзя произвольно перемотать на строку 0. В проверенной версии драйвера выход move_cursor за границу возвращает false с диагностикой, а не успешную пустую строку; историческое упоминание CUBRID_NO_MORE_DATA на странице документации не является фактическим PHP-возвратом этого профиля.

В исходном вопросе после полного обхода результата следующий fetch уже не выдавал строки; автор искал способ повторного чтения. Исходное обсуждение. Здесь потребность превращена в предпросмотр плюс полный ограниченный список. Положение CUBRID-курсора объясняется на id10/40/90; API mysqli и ответы не копируются.

Измените требование

Какие аргументы нужны, чтобы data_seek выбрал товар id40? А что будет при data_seek(40)?

Разбор решения

В показанном результате нужен data_seek($request,1), затем fetch. Число 40 означает сорок первую строку результата и выйдет за границу трёх строк. Если задача — найти товар по ключу 40, предпочтительнее SELECT нужных полей WHERE id=? с параметром 40 и LIMIT 1, без зависимости от порядка списка.

Прочитать одну ячейку отчёта и различить NULL, пустую строку и ноль

Небольшой отчёт уже содержит две строки, а отдельному блоку требуется только название первого товара или его примечание. cubrid_result обращается к ячейке по номеру строки и имени/номеру поля. Здесь он возвращает «Кабель», строку "0", настоящий null и пустую строку — четыре разных значения. Читатель видит, почему if (!$value) нельзя использовать как универсальную проверку ошибки.

Путь конкретных значений
  1. SELECT id,title AS product_label,qty,note → две строки с id10 и id90
  2. result(row0, product_label) → Кабель; result(row0, field2) → строка "0"
  3. result(row0, note) → null; result(row1, note) → пустая строка
  4. Нужен конкретный id90 вне отчёта → новый WHERE id=? → Зажим, 7

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

// $request содержит SELECT title AS product_label,qty ...
$title = cubrid_result($request, 0, 'product_label');
if ($title === false) { throw new RuntimeException('Ячейка недоступна'); }
echo $title === null ? 'Название не задано' : $title;
// Для первой строки нашего отчёта: Кабель.

Второй аргумент — позиция строки от 0. Третий — строковое имя выбранного поля либо целочисленная позиция от 0; без него читается первое поле. После AS product_label имя title в этой проекции заменено alias. Проверка === false отделяет отказ от null и "0". В таблице примечание null означает отсутствие значения, а "" — явно пустой текст; это не взаимозаменяемые состояния формы.

Результат самостоятельной программы

НаблюдениеРезультат
Название по aliasКабель
Нулевой остаток0
NULL: примечание отсутствуетда
Пустая строка: примечание задано пустымда
Поле по умолчанию (id)10
Строка 9 отсутствуетда
Отдельный поиск id90Зажим
Полная программа, файлы и архив для запуска
Прочитать одну ячейку отчёта и различить NULL, пустую строку и нольСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__.'/lab.php';
$db=null;$owned=$removed=$rows=$checks=$observations=[];
try {
    $config=c167Config();$db=c167Connect();$runtime=c167Runtime($db);

    $name=c167CreateTable($db,$owned,'cells',[
        ['id'=>10,'sku'=>'CAB-167','title'=>'Кабель','category'=>'parts','price_cents'=>1200,'qty'=>0,'note'=>null],
        ['id'=>90,'sku'=>'CLP-167','title'=>'Зажим','category'=>'parts','price_cents'=>350,'qty'=>7,'note'=>'']
    ]);
    $table=c167Qualified($name);$request=null;$cells=[];
    try {
        $request=c167Request($db,'SELECT id,title AS product_label,qty,note FROM '.$table.' ORDER BY id LIMIT 2');
        foreach ([['label',0,'product_label'],['zero',0,2],['absent',0,'note'],['empty',1,'note']] as $lookup) {
            list($key,$rowIndex,$field)=$lookup;
            $call=c167Capture(static function() use ($request,$rowIndex,$field) {return cubrid_result($request,$rowIndex,$field);});
            if ($call['value']===false || $call['warning'] || $call['code']!==0) {c167Failure('Ячейка не прочитана',$call);}
            $cells[$key]=$call['value'];
        }
        $default=c167Capture(static function() use ($request) {return cubrid_result($request,0);});
        if ($default['value']===false || $default['warning']) {c167Failure('Идентификатор не прочитан',$default);}
        $missing=c167Capture(static function() use ($request) {return cubrid_result($request,9,'product_label');});
        $badField=c167Capture(static function() use ($request) {return cubrid_result($request,0,'not_selected');});
        $reset=c167Capture(static function() use ($request) {return cubrid_data_seek($request,0);});
        if ($reset['value']!==true || $reset['warning']) {c167Failure('Строка отчёта недоступна',$reset);}
        $full=c167Capture(static function() use ($request) {return cubrid_fetch_array($request,CUBRID_ASSOC);});
        if (!is_array($full['value']) || $full['warning']) {c167Failure('Строка отчёта не прочитана',$full);}
    } finally {c167Release($request);}
    $byId=c167Read($db,'SELECT title,qty FROM '.$table.' WHERE id=? LIMIT 1',[90]);c167Commit($db);
    $rows=[['Название по alias',$cells['label']],['Нулевой остаток',(int)$cells['zero']],
        ['NULL: примечание отсутствует',$cells['absent']===null],['Пустая строка: примечание задано пустым',$cells['empty']===''],
        ['Поле по умолчанию (id)',(int)$default['value']],['Строка 9 отсутствует',$missing['value']===false],
        ['Отдельный поиск id90',$byId[0]['title']]];
    $checks=['alias_label'=>$cells['label']==='Кабель','zero_is_string'=>$cells['zero']==='0',
        'null_preserved'=>$cells['absent']===null,'empty_preserved'=>$cells['empty']==='',
        'default_first_field'=>$default['value']==='10','missing_row_false'=>$missing['value']===false,
        'missing_row_no_sql_error'=>$missing['code']===0 && !$missing['warning'],
        'unknown_field_false'=>$badField['value']===false,'unknown_field_diagnosed'=>$badField['code']===-20013,
        'full_row_same_label'=>$full['value']['product_label']===$cells['label'],
        'full_row_null_key'=>array_key_exists('note',$full['value']) && $full['value']['note']===null,
        'business_key_search'=>$byId[0]['title']==='Зажим' && $byId[0]['qty']==='7'];
    $observations=['cells'=>$cells,'default_field'=>$default['value'],'missing_row'=>$missing,'unknown_field'=>$badField,
        'whole_row'=>$full['value'],'targeted_by_id'=>$byId,'row_indices'=>[0,1],'primary_keys'=>[10,90]];

} finally {
    if (is_resource($db)) {try {$removed=c167Cleanup($db,$owned);} finally {c167Close($db);}}
}

c167Report('cub167-cell-lookup','Прочитать одну ячейку отчёта и различить NULL, пустую строку и ноль',$rows,$checks,$observations,$runtime,$owned,$removed);
Ожидаемый результат
Название по alias — Кабель
Нулевой остаток — 0
NULL: примечание отсутствует — да
Пустая строка: примечание задано пустым — да
Поле по умолчанию (id) — 10
Строка 9 отсутствует — да
Отдельный поиск id90 — Зажим

Распакуйте все файлы и выполните example.php по README.txt. lab.php проверяет отдельную базу и namespace, создаёт одну собственную случайную таблицу и удаляет её в finally. result.html показывает полезный результат, result.json сохраняет полученные строки и наблюдения, metrics.json — проверки и очистку. Пароль, приватные пути и сырые серверные сообщения не включаются в публичный результат.

Открыть фактический результат · Открыть данные JSON.

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

Для вывода всех полей каждой строки используйте один fetch, а не многократный result для каждой ячейки огромной таблицы. Функция перемещает общий курсор результата; не смешивайте произвольные ячейки и последовательный цикл без явного data_seek. Она не ищет по бизнес-ключу и не выполняет повторный SELECT. В закреплённой версии отсутствующая строка возвращает false без SQL-ошибки, а несуществующее поле — false с диагностикой. Программа сохраняет оба случая отдельно и не выводит сырое серверное сообщение.

Измените требование

Как показать вместо отсутствующего примечания «Не заполнено», сохранив нулевой остаток?

Разбор решения

Сначала проверяйте отказ === false, затем значение === null. Для note null выводите «Не заполнено», для "" решите отдельное правило интерфейса, например «Пустое примечание». Для qty "0" преобразуйте ожидаемый числовой домен и покажите 0. Проверка empty() смешает важные состояния.

Проверить размер поля для обмена данными, сохранив кириллицу

Внешняя система принимает название не длиннее десяти байт. Русское «Кабель» выглядит коротким: шесть букв, но в UTF-8 оно занимает двенадцать байт. ASCII-слово CABLE занимает пять. Пример измеряет именно полученное значение, отмечает неподходящую запись и ничего молча не обрезает. Заодно он показывает, что одинаковая длина 0 не позволяет отличить NULL от пустой строки: нужно смотреть и само значение.

Путь конкретных значений
  1. Успешный fetch_row → [CAB-167, Кабель, "0", null]
  2. fetch_lengths того же request → длины по индексам выбранных полей; title: 12 байт
  3. Правило обмена <=10 байт → Кабель отклоняется, CABLE с 5 байтами проходит
  4. Сохраняем исходное название; оператор выбирает разрешённое сокращение, а не повреждённый UTF-8

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

// $row уже успешно прочитан через cubrid_fetch_row($request).
$lengths = cubrid_fetch_lengths($request);
if ($lengths === false) { throw new RuntimeException('Длины недоступны'); }
echo $lengths[1]; // title — второе поле SELECT; Кабель даёт 12 байт.
// Это не VARCHAR(120) и не шесть видимых букв.

Индекс длины соответствует позиции поля SELECT, начиная с нуля, даже если строка читалась как ассоциативный массив. Сначала получите успешную строку, сразу затем её длины. strlen также считает байты PHP-строки. preg_match_all с флагом u в полном примере нужен только для сравнения с количеством кодовых точек Unicode; это не универсальный подсчёт видимых символов, состоящих из нескольких кодовых точек. Ограничение десяти байт задано договором обмена, а не типом VARCHAR в базе.

Результат самостоятельной программы

НаблюдениеРезультат
Кабель: кодовые точки Unicode6
Кабель: байты UTF-812
Кабель помещается в 10 байтнет
CABLE: байты UTF-85
CABLE помещается в 10 байтда
Длина строкового нуля1
NULL и пустая строка имеют разный смыслда
Полная программа, файлы и архив для запуска
Проверить размер поля для обмена данными, сохранив кириллицуСамостоятельный пример
<?php
declare(strict_types=1);
require __DIR__.'/lab.php';
$db=null;$owned=$removed=$rows=$checks=$observations=[];
try {
    $config=c167Config();$db=c167Connect();$runtime=c167Runtime($db);

    $name=c167CreateTable($db,$owned,'lengths',[
        ['id'=>10,'sku'=>'CAB-167','title'=>'Кабель','category'=>'parts','price_cents'=>1200,'qty'=>0,'note'=>null],
        ['id'=>20,'sku'=>'ENG-167','title'=>'CABLE','category'=>'parts','price_cents'=>1200,'qty'=>7,'note'=>'']
    ]);
    $table=c167Qualified($name);$request=null;$measurements=[];$byteLimit=10;
    try {
        $request=c167Request($db,'SELECT sku,title,qty,note FROM '.$table.' ORDER BY id LIMIT 2');
        while (true) {
            $call=c167Capture(static function() use ($request) {return cubrid_fetch_row($request);});
            if ($call['warning'] || $call['code']!==0 || $call['value']===null) {c167Failure('Строка обмена не прочитана',$call);}
            if ($call['value']===false) {break;}
            $row=$call['value'];
            $lengthCall=c167Capture(static function() use ($request) {return cubrid_fetch_lengths($request);});
            if (!is_array($lengthCall['value']) || $lengthCall['warning'] || $lengthCall['code']!==0) {c167Failure('Длины строки неизвестны',$lengthCall);}
            $lengths=$lengthCall['value'];
            $codepoints=preg_match_all('/./us',$row[1]);
            if ($codepoints===false) {throw new RuntimeException('Название не является UTF-8.');}
            $measurements[]=['sku'=>$row[0],'title'=>$row[1],'qty'=>$row[2],'note'=>$row[3],
                'lengths'=>$lengths,'title_php_bytes'=>strlen($row[1]),'title_codepoints'=>$codepoints,
                'fits_bytes'=>$lengths[1]<=$byteLimit,'null_note'=>$row[3]===null];
        }
        $afterEof=c167Capture(static function() use ($request) {return cubrid_fetch_lengths($request);});
    } finally {c167Release($request);}
    c167Commit($db);$russian=$measurements[0];$ascii=$measurements[1];
    $rows=[['Кабель: кодовые точки Unicode',$russian['title_codepoints']],['Кабель: байты UTF-8',$russian['lengths'][1]],
        ['Кабель помещается в 10 байт',$russian['fits_bytes']],['CABLE: байты UTF-8',$ascii['lengths'][1]],
        ['CABLE помещается в 10 байт',$ascii['fits_bytes']],['Длина строкового нуля',$russian['lengths'][2]],
        ['NULL и пустая строка имеют разный смысл',$russian['note']===null && $ascii['note']==='']];
    $checks=['two_measured_rows'=>count($measurements)===2,'russian_title'=>$russian['title']==='Кабель',
        'russian_codepoints'=>$russian['title_codepoints']===6,'russian_bytes'=>$russian['lengths'][1]===12,
        'matches_php_bytes'=>$russian['lengths'][1]===$russian['title_php_bytes'],
        'russian_not_fit'=>$russian['fits_bytes']===false,'ascii_fits'=>$ascii['fits_bytes']===true,
        'ascii_bytes'=>$ascii['lengths'][1]===5,'numeric_text_bytes'=>$russian['lengths'][2]===1 && $russian['qty']==='0',
        'null_length_zero'=>$russian['lengths'][3]===0 && $russian['note']===null,
        'empty_length_zero'=>$ascii['lengths'][3]===0 && $ascii['note']==='',
        'post_eof_not_current'=>$afterEof['value']===false && $afterEof['code']===-30003];
    $observations=['measurements'=>$measurements,'byte_limit'=>$byteLimit,'after_eof'=>$afterEof,
        'measured_after_successful_fetch_only'=>true,'no_truncation_performed'=>true];

} finally {
    if (is_resource($db)) {try {$removed=c167Cleanup($db,$owned);} finally {c167Close($db);}}
}

c167Report('cub167-row-lengths','Проверить размер поля для обмена данными, сохранив кириллицу',$rows,$checks,$observations,$runtime,$owned,$removed);
Ожидаемый результат
Кабель: кодовые точки Unicode — 6
Кабель: байты UTF-8 — 12
Кабель помещается в 10 байт — нет
CABLE: байты UTF-8 — 5
CABLE помещается в 10 байт — да
Длина строкового нуля — 1
NULL и пустая строка имеют разный смысл — да

Распакуйте все файлы и выполните example.php по README.txt. lab.php проверяет отдельную базу и namespace, создаёт одну собственную случайную таблицу и удаляет её в finally. result.html показывает полезный результат, result.json сохраняет полученные строки и наблюдения, metrics.json — проверки и очистку. Пароль, приватные пути и сырые серверные сообщения не включаются в публичный результат.

Открыть фактический результат · Открыть данные JSON.

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

Не вызывайте fetch_lengths до первого успешного fetch: закреплённая версия драйвера может вернуть ещё не заполненный массив длин без ошибки. После EOF текущей строки также нет; в этом профиле возвращается false/-30003. Перемещение data_seek само по себе не заменяет fetch для измерения новой строки. Длины относятся к переданным значениям, а не к размеру страницы базы, сетевого пакета, JSON/CSV с кавычками или максимальной ширине столбца. Для BLOB/CLOB официальный контракт направляет к cubrid_lob_size, а не к этому массиву.

Вопрос смешивал индекс столбца, его значение и массив байтовых длин текущей строки; в обсуждении также обнаружен неверный аргумент функции. Исходное обсуждение. Оригинальный PHP/CUBRID-сценарий связывает row[1] с lengths[1], передаёт request и сравнивает русский текст с ASCII. C++/MySQL-код не переносится.

Измените требование

Если поле title ограничено шестью символами интерфейса, можно ли отклонить «Кабель» из-за длины 12?

Разбор решения

Нет: сначала уточните единицу правила. Для лимита в байтах 12 действительно больше 6; для шести кодовых точек слово подходит. Видимые символы могут включать несколько кодовых точек, поэтому сложные Unicode-правила требуют соответствующего инструмента. cubrid_fetch_lengths не заменяет эту бизнес-проверку, а даёт размер переданного значения в байтах.

Семь функций: выбрать по действию

ФункцияДля чего
cubrid_fetch_arrayПрочитать одну строку с именованными, числовыми или обоими наборами ключей.
cubrid_fetch_rowПолучить значения в порядке SELECT для последовательного экспорта.
cubrid_fetch_objectПолучить объект чтения с полями строки и при необходимости собственным классом.
cubrid_data_seekПерейти к строке результата по позиции от нуля перед следующим fetch.
cubrid_move_cursorПереместить курсор относительно начала, текущей позиции или конца результата.
cubrid_resultПолучить одну конкретную ячейку небольшого результата.
cubrid_fetch_lengthsИзмерить байтовые длины полей только что прочитанной строки.

От чтения значений к описанию полей

Сначала решите, что нужно потребителю, и выберите SQL-проекцию. Затем читайте строки по одному согласованным способом. Предпросмотр не должен незаметно съедать начало списка; пустое значение не должно превращаться в ошибку, а число байт — в число символов. Не используйте перемотку как замену серверному отбору огромной таблицы.

Следующий шаг — понять описание выбранных полей: aliases, исходные таблицы, типы и объявленные размеры в метаданных CUBRID. Длины текущей строки и метаданные отвечают на разные вопросы. Границы записи остаются отдельной темой: commit, rollback и autocommit. Ресурсы освобождаются через cubrid_close_request, а не с помощью последнего fetch.

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

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

cubrid_fetch_arrayПрочитать одну строку с именованными, числовыми или обоими наборами ключей.

PECL CUBRID >= 8.3.0; отдельное согласование версий PHP/CCI/сервера. · cubrid

cubrid_fetch_array(resource $result, int $type = CUBRID_BOTH): array|false|null

Прочитать одну строку с именованными, числовыми или обоими наборами ключей.

Базовая запись

cubrid_fetch_array(resource $result, int $type = CUBRID_BOTH): array|false|null

В справочной записи resource — открытый request успешно выполненного SELECT, а не соединение $db и не PHP-массив. Знак | перечисляет варианты возврата; это не выполняемая декларация PHP 7.4. Все функции этой страницы представлены в PHP Manual как PECL CUBRID >= 8.3.0. Совместимость PHP, расширения, CCI и сервера проверяют отдельно; закреплённый учебный профиль описан во введении.

ПараметрНазначение
resultRequest от execute; строка берётся из текущей позиции и затем курсор продвигается.
typeCUBRID_NUM — позиции 0..n−1; CUBRID_ASSOC — имена/aliases; CUBRID_BOTH по умолчанию — оба доступа. CUBRID_LOB добавляется битовым |, если нужны LOB-объекты.
ВозвратСмысл
array или object согласно функцииОдна строка; обычные числовые SQL-значения в этом профиле приходят строками, SQL NULL остаётся null.
falseСтрок больше нет. Это нормальный конец SELECT; при сомнении сохраните диагностику сразу после вызова.
nullНе удалось получить строку. Проверьте код/предупреждение и остановите зависимую обработку.

BOTH не удваивает число SQL-строк, но даёт два ключа для каждого обычного выбранного поля. Поэтому count($row) в таком режиме не является числом столбцов. Одинаковые имена полей в именованном представлении создают неоднозначность: назначайте разные AS или выбирайте числовые позиции. Имена ключей не превращают переданные значения в int автоматически.

Применение 1: подпись товара по имени поля

$row = cubrid_fetch_array($request, CUBRID_ASSOC);
if ($row === null) { throw new RuntimeException('Строка не прочитана'); }
if ($row !== false) {
    echo $row['title'].' — '.(int)$row['qty'].' шт.';
}

В карточках каталога это даёт «Кабель — 0 шт.». Проверяется весь результат fetch, а не истинность qty: строковый ноль является полезным остатком. Именованный доступ помогает не перепутать title и sku при изменении порядка SELECT.

Применение 2: единая строка для сравнения ячеек отчёта

$row = cubrid_fetch_array($request, CUBRID_ASSOC);
if (is_array($row) && array_key_exists('note', $row)) {
    $noteLabel = $row['note'] === null ? 'Не заполнено' : $row['note'];
}

В отчёте о NULL и пустом тексте весь ряд читается один раз, чтобы сопоставить отдельно полученные ячейки. array_key_exists сохраняет различие «ключ есть со значением null» и «столбец вообще не выбран». Для чисто позиционного экспорта есть cubrid_fetch_row.

Для LOB режим задают, например, CUBRID_ASSOC | CUBRID_LOB; размер/чтение такого объекта относятся к отдельному LOB API, обычный strlen здесь неприменим. Закрывайте request после обхода. Чтение не записывает ваши изменения массива обратно в SQL и не отменяет необходимости WHERE/ORDER BY/LIMIT. Если нужен только именованный режим без выбора типа, рядом cubrid_fetch_assoc.

cubrid_fetch_rowПолучить значения в порядке SELECT для последовательного экспорта.

PECL CUBRID >= 8.3.0; отдельное согласование версий PHP/CCI/сервера. · cubrid

cubrid_fetch_row(resource $result, int $type = 0): array|false|null

Получить значения в порядке SELECT для последовательного экспорта.

Базовая запись

cubrid_fetch_row(resource $result, int $type = 0): array|false|null

В справочной записи resource — открытый request успешно выполненного SELECT, а не соединение $db и не PHP-массив. Знак | перечисляет варианты возврата; это не выполняемая декларация PHP 7.4. Все функции этой страницы представлены в PHP Manual как PECL CUBRID >= 8.3.0. Совместимость PHP, расширения, CCI и сервера проверяют отдельно; закреплённый учебный профиль описан во введении.

ПараметрНазначение
resultRequest выполненного SELECT. Один вызов читает одну текущую строку и сдвигает курсор.
typeНеобязательный флаг CUBRID_LOB только для работы с LOB-объектами. Без флага действует обычное чтение; 0 в справочной записи отражает отсутствие дополнительных флагов в закреплённом исходнике. Это не переключатель ASSOC/BOTH.
ВозвратСмысл
array или object согласно функцииОдна строка; обычные числовые SQL-значения в этом профиле приходят строками, SQL NULL остаётся null.
falseСтрок больше нет. Это нормальный конец SELECT; при сомнении сохраните диагностику сразу после вызова.
nullНе удалось получить строку. Проверьте код/предупреждение и остановите зависимую обработку.

Индексы начинаются с 0 и соответствуют порядку выбранных столбцов, а не порядку полей полной таблицы. SELECT sku,title,qty даёт [артикул, название, остаток]. Если изменить SELECT на qty,sku, прежнее обращение $row[0] будет означать уже количество. У строки нет ключа title: нужен индекс 1 либо другая функция fetch.

Применение 1: строка CSV с согласованными заголовками

// $csv — уже открытый поток, SELECT возвращает sku,title,qty.
$row = cubrid_fetch_row($request);
if ($row === null) { throw new RuntimeException('Экспорт не прочитан'); }
if ($row !== false) { fputcsv($csv, $row, ';', '"', ''); }

В самостоятельном экспорте получается CAB-167;Кабель;0. fputcsv формирует корректные разделители и кавычки; простой implode не заменяет CSV-формат, если значения содержат разделитель или перевод строки. Числовые ключи удобны, потому что повторных именованных копий в строке нет.

Применение 2: сопоставить поле и его текущую длину

$row = cubrid_fetch_row($request);
if (is_array($row)) {
    $lengths = cubrid_fetch_lengths($request);
    if ($lengths === false) { throw new RuntimeException('Размеры недоступны'); }
    echo $row[1].': '.$lengths[1].' байт';
}

В ограничении обмена данными обе позиции 1 относятся к одному title: «Кабель: 12 байт». Сначала нужен успешный fetch, затем длины именно этой строки. Для перемотки перед новым обходом служит cubrid_data_seek.

Нельзя без проверки разыменовывать false на конце результата. Полный цикл различает null/предупреждение, нормальный EOF и настоящий массив. Получать все строки огромной таблицы ради последующего отбора в PHP не требуется: отбор и порядок задаются SQL, а эта функция лишь читает уже выбранное. LOB-параметр не делает автоматический файл или HTTP-ответ.

cubrid_fetch_objectПолучить объект чтения с полями строки и при необходимости собственным классом.

PECL CUBRID >= 8.3.0; отдельное согласование версий PHP/CCI/сервера. · cubrid

cubrid_fetch_object(resource $result [, string $class_name [, array $params [, int $type]]]): object|false|null

Получить объект чтения с полями строки и при необходимости собственным классом.

Базовая запись

cubrid_fetch_object(resource $result [, string $class_name [, array $params [, int $type]]]): object|false|null

В справочной записи resource — открытый request успешно выполненного SELECT, а не соединение $db и не PHP-массив. Знак | перечисляет варианты возврата; это не выполняемая декларация PHP 7.4. Все функции этой страницы представлены в PHP Manual как PECL CUBRID >= 8.3.0. Совместимость PHP, расширения, CCI и сервера проверяют отдельно; закреплённый учебный профиль описан во введении.

ПараметрНазначение
resultRequest выполненного SELECT, следующий ряд которого нужно представить объектом.
class_nameНеобязательное имя существующего класса; без него получается stdClass. Свойства соответствуют именам/aliases выбранных столбцов.
paramsНеобязательный массив аргументов конструктора указанного класса; это ваши аргументы, а не автоматически составленный массив строки.
typeНеобязательный CUBRID_LOB для получения LOB-объектов; обычные проекты ниже его не требуют.
ВозвратСмысл
array или object согласно функцииОдна строка; обычные числовые SQL-значения в этом профиле приходят строками, SQL NULL остаётся null.
falseСтрок больше нет. Это нормальный конец SELECT; при сомнении сохраните диагностику сразу после вызова.
nullНе удалось получить строку. Проверьте код/предупреждение и остановите зависимую обработку.

Квадратные скобки в сигнатуре обозначают необязательные группы аргументов и не копируются в вызов. Для stdClass запись $item->title читает свойство. Пользовательский класс заранее объявляется, поля в учебном профиле объявлены нетипизированными: числовые данные драйвера ещё могут быть строками. Полученный объект — снимок строки; присваивание его свойству не выполняет UPDATE.

Применение 1: небольшой предпросмотр без собственного класса

$item = cubrid_fetch_object($request);
if ($item === null) { throw new RuntimeException('Предпросмотр не прочитан'); }
if ($item !== false) { echo $item->title; }
// Первый товар примера: Кабель.

В предпросмотре над списком это удобная запись одного объекта. Однако fetch уже продвинул курсор: для полного списка, включающего этот же товар, программа явно возвращается data_seek(0). stdClass не добавляет методов проверки остатка и не знает устройство склада.

Применение 2: объект этикетки с дополнительным префиксом

// CatalogLabel167 заранее объявлен в полном проекте.
$label = cubrid_fetch_object($request, CatalogLabel167::class, ['Склад']);
if (!($label instanceof CatalogLabel167)) {
    throw new RuntimeException('Этикетка не получена');
}
echo $label->caption();
// Склад: Кабель / CAB-167

В полном классе этикетки constructor получает только строку «Склад». Порядок в проверенной версии драйвера: сначала заполнение свойств строкой, затем вызов конструктора. Поэтому constructor видит title=Кабель, но может сам его перезаписать. Мы сохраняем prefix отдельно, не маскируя данные. Если модель требует проверки и преобразования всех полей в конструкторе, проще прочитать массив и явно выполнить new с нужными аргументами.

PHP Manual также показывает короткие LOB-формы, которые поддерживает закреплённый исходник: fetch_object($request, CUBRID_LOB); fetch_object($request, ClassName::class, CUBRID_LOB); либо полная форма с массивом params и четвёртым флагом. Они не означают, что любой int является именем класса. Несуществующий класс, неверный массив аргументов, недостающие обязательные аргументы или исключение конструктора — отдельные PHP-ошибки; не сводите их к EOF. Передача params классу без конструктора также не является полезной формой. Для неизвестных SQL-имён используйте явные aliases и простую модель чтения, а не произвольную гидратацию сложного доменного объекта.

cubrid_data_seekПерейти к строке результата по позиции от нуля перед следующим fetch.

PECL CUBRID >= 8.3.0; отдельное согласование версий PHP/CCI/сервера. · cubrid

cubrid_data_seek(resource $result, int $row_number): bool

Перейти к строке результата по позиции от нуля перед следующим fetch.

Базовая запись

cubrid_data_seek(resource $result, int $row_number): bool

В справочной записи resource — открытый request успешно выполненного SELECT, а не соединение $db и не PHP-массив. Знак | перечисляет варианты возврата; это не выполняемая декларация PHP 7.4. Все функции этой страницы представлены в PHP Manual как PECL CUBRID >= 8.3.0. Совместимость PHP, расширения, CCI и сервера проверяют отдельно; закреплённый учебный профиль описан во введении.

ПараметрНазначение
resultОткрытый результат SELECT с нужными строками. Перемещение действует на его указатель.
row_numberЦелочисленная позиция от 0 до числа строк минус 1. Это не PK и не номер страницы сайта.
ВозвратСмысл
trueУказатель перемещён; следующий fetch прочитает выбранную строку.
falseПозиция недоступна или операция не удалась. Не продолжайте как будто переход состоялся.

Функция не возвращает сам массив. В закреплённом исходнике она преобразует внешний offset в offset+1 для CCI с origin FIRST. Поэтому нулевая позиция API означает первую строку. Пустой результат и выход за границу требуют отдельной обработки; произвольное большое число не создаёт строку и не превращается в поиск по id.

Применение 1: вернуть первую строку после предпросмотра

if (cubrid_data_seek($request, 0) !== true) {
    throw new RuntimeException('Начало списка недоступно');
}
$first = cubrid_fetch_row($request);

В предпросмотре каталога это сохраняет Кабель в полном списке. Без перехода второй fetch увидел бы Зажим. Если предпросмотр вообще не нужен, проще не делать лишнего первого fetch: курсор не требуется перематывать после каждой строки.

Применение 2: перейти к целому ряду после чтения отдельных ячеек

// result() раньше читал ячейки в произвольных строках.
if (cubrid_data_seek($request, 0) !== true) {
    throw new RuntimeException('Строка отчёта недоступна');
}
$row = cubrid_fetch_array($request, CUBRID_ASSOC);

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

Нужно определить порядок ещё в SQL: без ORDER BY «первая строка» не является устойчивой бизнес-позицией. data_seek не делает ORDER BY, LIMIT или повторную выборку актуальных изменений. Для большой таблицы используйте серверную страницу и устойчивый ключ сортировки. Не рассчитывайте, что старый request останется пригоден после close_request, повторного execute или другой границы его жизненного цикла. Отличающиеся аргументы относительного перемещения описаны в cubrid_move_cursor.

cubrid_move_cursorПереместить курсор относительно начала, текущей позиции или конца результата.

PECL CUBRID >= 8.3.0; отдельное согласование версий PHP/CCI/сервера. · cubrid

cubrid_move_cursor(resource $req_identifier, int $offset, int $origin = CUBRID_CURSOR_CURRENT): bool

Переместить курсор относительно начала, текущей позиции или конца результата.

Базовая запись

cubrid_move_cursor(resource $req_identifier, int $offset, int $origin = CUBRID_CURSOR_CURRENT): bool

В справочной записи resource — открытый request успешно выполненного SELECT, а не соединение $db и не PHP-массив. Знак | перечисляет варианты возврата; это не выполняемая декларация PHP 7.4. Все функции этой страницы представлены в PHP Manual как PECL CUBRID >= 8.3.0. Совместимость PHP, расширения, CCI и сервера проверяют отдельно; закреплённый учебный профиль описан во введении.

ПараметрНазначение
req_identifierОткрытый request; перемещается его курсор, не объект PHP-строки.
offsetЦелочисленное смещение, интерпретация которого зависит от origin. FIRST,1 — первая строка; LAST,1 — последняя; CURRENT,0 — текущая.
originCUBRID_CURSOR_FIRST / CUBRID_CURSOR_CURRENT / CUBRID_CURSOR_LAST. Если аргумент пропущен, используется CURRENT.
ВозвратСмысл
trueПереход выполнен; данные затем читаются fetch.
falseПереход не выполнен. В закреплённой версии выход за диапазон тоже false с кодом -20005/facility 3.
Нужная позицияВызовЧто важно
Перваяmove_cursor($r,1,CUBRID_CURSOR_FIRST)Отсчёт отличается от data_seek($r,0).
Последняяmove_cursor($r,1,CUBRID_CURSOR_LAST)1 означает край с этой стороны, не «ещё одну после последней».
Та, которую прочитает следующий fetchmove_cursor($r,0)CURRENT по умолчанию; предыдущий fetch уже передвинул указатель.

Применение 1: крайние позиции ограниченного отчёта

if (cubrid_move_cursor($request, 1, CUBRID_CURSOR_LAST) !== true) {
    throw new RuntimeException('Последняя позиция недоступна');
}
$last = cubrid_fetch_row($request); // ["90", "Коробка"] в нашем отчёте.

В списке трёх товаров крайние позиции известны благодаря ORDER BY id и LIMIT 3. Этот приём подходит для повторного просмотра уже полученного малого отчёта, а не для запроса последнего товара всей базы: последнюю запись базы лучше выбрать отдельным ORDER BY ... DESC LIMIT 1.

Применение 2: снова прочитать только что показанную строку

// Успешный fetch первой строки уже передвинул курсор ко второй.
if (cubrid_move_cursor($request, -1, CUBRID_CURSOR_CURRENT) !== true) {
    throw new RuntimeException('Предыдущая позиция недоступна');
}
$sameAgain = cubrid_fetch_row($request); // Снова первая строка.

Это полезно для повторного форматирования одной строки в пределах той же выборки. Не вычитайте 1 вслепую до первого чтения: позиция должна быть известна. Самостоятельный пример показывает соседний случай CURRENT,0: после первого fetch он читает уже Зажим. В отдельной подготовке закреплённого профиля CURRENT,-1 после fetch действительно повторяет только что прочитанную строку.

На исторической странице PHP Manual есть текст о возврате CUBRID_NO_MORE_DATA при выходе за диапазон, хотя сигнатура и раздел возвратов указывают bool. В 11.2.0.0002 исходник вызывает cci_cursor и превращает отрицательный результат в false с диагностикой; подготовка подтвердила именно false/-20005, а не специальное целое значение. Не сравнивайте результат с выдуманной успешной «пустой строкой». После неудачи выберите допустимую позицию явно или завершите чтение. Курсор не переносится между разными request.

Если нужна абсолютная позиция от нуля, используйте cubrid_data_seek: другой отсчёт: первая строка задаётся 0, а не сочетанием FIRST и 1. Не переносите число offset между двумя API без изменения его смысла.

cubrid_resultПолучить одну конкретную ячейку небольшого результата.

PECL CUBRID >= 8.3.0; отдельное согласование версий PHP/CCI/сервера. · cubrid

cubrid_result(resource $result, int $row, int|string $field = 0): string|null|false

Получить одну конкретную ячейку небольшого результата.

Базовая запись

cubrid_result(resource $result, int $row, int|string $field = 0): string|null|false

В справочной записи resource — открытый request успешно выполненного SELECT, а не соединение $db и не PHP-массив. Знак | перечисляет варианты возврата; это не выполняемая декларация PHP 7.4. Все функции этой страницы представлены в PHP Manual как PECL CUBRID >= 8.3.0. Совместимость PHP, расширения, CCI и сервера проверяют отдельно; закреплённый учебный профиль описан во введении.

ПараметрНазначение
resultRequest уже выполненного SELECT.
rowПозиция строки от 0 в этом результате, не значение id.
fieldПозиция выбранного поля от 0 либо его строковое имя. По умолчанию 0. Документирована форма table.field; для AS используйте alias выбранного поля.
ВозвратСмысл
stringЗначение обычной ячейки, в том числе "0" или "".
nullЯчейка успешно содержит SQL NULL.
falseЯчейку получить не удалось; сохраните диагностику. Отсутствующая строка может не сопровождаться SQL-ошибкой.

Когда результат содержит AS product_label, обращаться нужно к product_label. Для соединений таблиц не полагайтесь на одинаковое title из двух источников: явные разные aliases проще переносить и проверять. Если выбирается table.field, используйте фактическое имя источника/поля метаданных, а не произвольный текст пользователя. Эта функция не интерполирует название поля в новый SQL, но всё равно нуждается в осмысленном разрешённом поле отчёта.

Применение 1: название для шапки малого отчёта

$title = cubrid_result($request, 0, 'product_label');
if ($title === false) { throw new RuntimeException('Название недоступно'); }
echo $title === null ? 'Без названия' : $title;

В табличном отчёте получается Кабель. Третий аргумент можно заменить позицией 1 только при известном SELECT id,title AS product_label,qty,note. По умолчанию вернулся бы id первого столбца — строка "10", а не название.

Применение 2: решение по отсутствующему примечанию

$note = cubrid_result($request, 0, 'note');
if ($note === false) { throw new RuntimeException('Примечание не прочитано'); }
$caption = $note === null ? 'Не заполнено' : ($note === '' ? 'Пустой текст' : $note);

Для второй строки того же примера note равно "", поэтому это «Пустой текст», а первая строка получает «Не заполнено». Ноль qty надо обрабатывать как ноль, не как отказ. Такое различение важно при редактировании формы: «не задано» и «очистить поле» могут быть разными действиями.

Обращение меняет общий курсор. В проверенной версии драйвера после result($r,1,...) следующий fetch читает ту же вторую строку, а не третью; последовательный цикл лучше начинать явным data_seek. Не используйте result в двойном цикле всех строк и столбцов там, где достаточно одного fetch на ряд. Если пользователю нужен товар id90, запрос WHERE id=? надёжнее поиска «строки 90». Отсутствующая строка в проверенном профиле даёт false/code0, неизвестное поле — false/-20013. Сам null ошибкой не является.

Когда нужны все поля одного товара, удобнее прочитать целую именованную строку. После обращений к разным ячейкам сначала задайте известную позицию через cubrid_data_seek, затем выполните один fetch; так имена полей и текущая строка остаются согласованными.

cubrid_fetch_lengthsИзмерить байтовые длины полей только что прочитанной строки.

PECL CUBRID >= 8.3.0; отдельное согласование версий PHP/CCI/сервера. · cubrid

cubrid_fetch_lengths(resource $result): array|false

Измерить байтовые длины полей только что прочитанной строки.

Базовая запись

cubrid_fetch_lengths(resource $result): array|false

В справочной записи resource — открытый request успешно выполненного SELECT, а не соединение $db и не PHP-массив. Знак | перечисляет варианты возврата; это не выполняемая декларация PHP 7.4. Все функции этой страницы представлены в PHP Manual как PECL CUBRID >= 8.3.0. Совместимость PHP, расширения, CCI и сервера проверяют отдельно; закреплённый учебный профиль описан во введении.

ПараметрНазначение
resultТот же request, из которого непосредственно перед этим успешно прочитана строка. Передавайте ресурс, а не массив $row.
ВозвратСмысл
arrayЧисловой массив длин выбранных значений; индексы соответствуют SELECT начиная с 0.
falseДлины текущей строки недоступны; не сравнивайте это с допустимым размером поля.

Обычные текстовые значения в UTF-8 измеряются в байтах переданной строки. Это не предел VARCHAR, не число записей и не размер страницы хранения. Для числового значения "1200" измеряется представление в четыре байта, а не размер SQL INTEGER в памяти. NULL и пустой текст в проверенном профиле оба дают длину 0, поэтому длина не заменяет проверку самого значения.

Применение 1: лимит в байтах внешней системы

$row = cubrid_fetch_row($request);
if (is_array($row)) {
    $sizes = cubrid_fetch_lengths($request);
    if ($sizes === false) { throw new RuntimeException('Длины недоступны'); }
    $accepted = $sizes[1] <= 10;
    // Кабель: 12 байт → false; CABLE: 5 байт → true.
}

В полном сценарии обмена исходные названия сохраняются. Неподходящее значение направляется на исправление; побайтовое отрезание русского слова может повредить UTF-8 и не объяснит оператору, что произошло. Если правило задано в символах, нужен инструмент для соответствующей единицы, а не этот байтовый массив.

Применение 2: диагностика неожиданно большого текстового поля

$row = cubrid_fetch_array($request, CUBRID_ASSOC);
if (is_array($row)) {
    $sizes = cubrid_fetch_lengths($request);
    if ($sizes === false) { throw new RuntimeException('Размеры неизвестны'); }
    // SELECT sku,title,qty,note: note имеет позицию 3.
    $noteBytes = $sizes[3];
    $isAbsent = $row['note'] === null;
}

Именованный доступ к note не меняет числовой индекс массива длин. В той же проверке данных обмена null и "" имеют по 0 байт, но isAbsent отличает их. В журнале можно сохранить размер и признак отсутствия, не копируя чувствительное содержимое; это полезнее предположения «VARCHAR(160) означает, что в каждой записи 160 байт». Максимальная ширина разобрана отдельно в cubrid_field_len.

В 11.2.0.0002 до первого успешного fetch массив длин может быть выделен, но ещё не заполнен: отсутствие false не доказывает достоверность значений. Эта опасная последовательность не используется в примерах. После EOF память текущих длин очищается, наблюдён false/-30003. После data_seek сначала прочитайте новую строку и лишь затем измеряйте её; результат cubrid_result не следует подменять успешным обычным fetch для этого договора. Для BLOB/CLOB PHP Manual прямо указывает cubrid_lob_size. Размер полного CSV/JSON с кавычками, разделителями и кодированием измеряйте после сериализации отдельно.

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

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

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

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

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