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

Дерево руководства
Глава 92 · После таблиц, параметров, транзакций и OID CUBRID
CUBRID: признаки товара, комплекты и состояния коллекций SET/MULTISET
Полезные признаки товара, состав комплекта с повторами, три состояния пустого поля и подтверждение набора изменений.
Набор признаков и повторяющиеся детали — разные задачи
У кабеля может быть несколько признаков: медный, гибкий, для помещений. У монтажного комплекта — несколько деталей, причём две одинаковые шайбы нельзя случайно превратить в одну. Здесь вы увидите сохранённые признаки товара, точный состав комплекта, подсказки формы для пустых состояний и смену способов выдачи с подтверждением или отменой.
Коллекция CUBRID — значение одного столбца, внутри которого находится несколько элементов. Представьте коробку с карточками значений: SET оставляет только разные карточки, MULTISET сохраняет все экземпляры, SEQUENCE дополнительно закрепляет порядок. Коробка относится к одной записи базы. Эта модель не даёт элементам самостоятельных прав, цен или внешних ключей и не заменяет проектирование связей.
| Модель | Пример значения | Смысл результата | Когда подходит |
|---|---|---|---|
| SET | {'copper','indoor','copper'} | Два различных признака | Маленький набор независимых признаков товара |
| MULTISET | {'washer','washer','bolt'} | Три детали, два названия | Короткий состав, где повтор обозначает экземпляр |
| SEQUENCE | {'Склад','Цех','Склад'} | Три остановки в данном порядке | Маршруты и последовательности действий |
Сначала один столбец и одно чтение
// SQL-форма объявления столбца внутри CREATE TABLE:
// tags SET(VARCHAR(40))
// SQL-значение: {'copper','indoor'}
// $db уже подключён; $oid выбранной записи получен способом ниже.
$tags = cubrid_col_get($db, $oid, 'tags');
if ($tags === false) { throw new RuntimeException('Признаки не прочитаны'); }
// Получен массив ['copper','indoor'] (порядок SET не обещан).
echo count($tags); // 2, если атрибут содержит эти два признака.VARCHAR(40) задаёт домен строковых элементов, SET — правило уникальности. В SQL фигурные скобки задают значение коллекции; в PHP квадратные скобки обозначают другую структуру — массив в памяти программы. Оператор = сохраняет возвращённый массив в переменной, а === false отличает отказ от корректного пустого массива. count здесь считает уже прочитанную копию для объяснения; когда нужен только размер, используйте cubrid_col_size. Основа синтаксиса разобрана в массивах и вызовах функций.
В коротких фрагментах $db — открытое соединение, $oid — внутренний идентификатор уже найденной записи, а имя атрибута, например tags, задано приложением. OID получают через SELECT с CUBRID_INCLUDE_OID, позиционирование курсора и cubrid_current_oid; полный исходник повторяет эти шаги. Таблица явно создаётся DONT_REUSE_OID, чтобы поддерживать такой доступ. Внешний пользователь передаёт бизнес-выбор, а приложение проверяет доступ к записи; знание OID разрешением не является.
В SQL объявление tags SET(VARCHAR(40)) означает набор строк длиной до 40 символов. Литерал {'copper','indoor'} задаёт его элементы. В PHP те же прочитанные значения выглядят как ['copper','indoor']; фигурные скобки SQL и квадратные скобки PHP принадлежат разным языкам. Имена таблиц и атрибутов выбираются из доверенной схемы, значения запроса передаются параметрами.
Эти уроки продолжают подключение и первые SQL-запросы, параметры запроса и выбор одной записи по OID. Коллекция CUBRID хранится внутри одного атрибута строки базы. Полученный PHP-массив — отдельный снимок: изменение $items[0] само по себе ничего не записывает на сервер.
Вызовы проверены в отдельной среде PHP 7.4.33, native-драйвер cubrid 11.2.0.0002, CCI 11.2.0 и сервер CUBRID 11.3.5.1274, UTF-8. Это специальный профиль совместимости расширения, а не подтверждение его сборки для основного PHP 8.5.11. Сам сайт лишь показывает сохранённые результаты. Семь функций появились в PECL CUBRID 8.3.0; различия старого и проверенного поведения указаны отдельно.
- Выбрать доступный Кабель: SELECT id,title WHERE id=? → его OID
- Прочитать tags или передать одно значение set_add/set_drop
- Проверить состояние приложения → при успехе commit, при отказе rollback
- Перечитать сохранённую коллекцию → показать метки или количество деталей
Коллекции допускают NULL как значение всего атрибута и как отдельный элемент; это разные случаи. SET и MULTISET не гарантируют порядок, SEQUENCE сохраняет его. В CUBRID 11.3 коллекционные типы не поддерживают COLLATE; BLOB/CLOB не используются как их элементы. В этой главе каждый домен однородный VARCHAR(40), а в приложении дополнительно проверяется словарь и отсутствие неполных меток. Не следует предполагать, что произвольный PHP-массив или объект можно передать вместо одного строкового аргумента native API.
| Практическая задача | Что будет видно |
|---|---|
| Признаки товара | Медь не дублируется, старая метка снята, кириллица и апостроф сохранены |
| Состав комплекта | Одна шайба снята из трёх; итог — две детали одного вида |
| Состояние формы | NULL, {} и {NULL} дают разные подсказки, ошибки не скрываются |
| Сохранение нескольких изменений | Противоречивый предпросмотр отменён; корректная замена сохранена |
Признаки кабеля: добавить метку без дубля и убрать старую
В карточке кабеля надо оставить медную жилу (copper), добавить гибкость (flexible) и снять устаревшую пометку для помещения (indoor). Повторный щелчок «медь» не должен создавать второй одинаковый признак. SET подходит для небольшого набора уникальных признаков одной записи. После commit программа снова читает данные, а Хомут остаётся со своим признаком steel.
- Кабель: {copper, indoor}; Хомут: {steel}
- Кабель: добавить flexible; повторить copper → {copper, indoor, flexible}
- Кабель: убрать indoor; добавить Серия O'Reilly → commit
- Повторное чтение: copper, flexible, Серия O'Reilly; Хомут: steel
Минимальный вызов
$tags = cubrid_col_get($db, $oid, 'tags');
if ($tags === false) { throw new RuntimeException('Метки не прочитаны'); }
// До изменения: ['copper', 'indoor']; порядок SET не гарантируется.
if (!cubrid_set_add($db, $oid, 'tags', 'flexible')) {
throw new RuntimeException('Метка не добавлена');
}
if (!cubrid_commit($db)) { throw new RuntimeException('Сохранение не подтверждено'); }$tags получает обычный числовой PHP-массив. Второй вызов изменяет атрибут tags записи с данным OID; строка flexible — отдельный аргумент, её не склеивают с SQL. commit подтверждает изменение в ручном режиме транзакций. В полной программе перед этим создаются две строки и выбирается OID именно кабеля. Для вывода небольшой копии SET используется sort: это оформление трёх меток, а не сортировка выгруженного каталога.
Что получится после запуска
| Наблюдение | Результат |
|---|---|
| Товар | Кабель |
| До изменения | copper, indoor |
| После сохранения | copper, flexible, Серия O'Reilly |
| Количество признаков | 3 |
| Повторное добавление copper успешно | да |
| Кириллица и апостроф сохранены | да |
| Хомут не изменён | да |
| Изменение массива PHP не записалось | да |
Самостоятельный проект, исходник и архив
<?php
declare(strict_types=1);
require __DIR__.'/lab.php';
$db=null;$owned=$removed=$rows=$checks=$observations=[];
try {
$config=c170Config();$db=c170Connect();$runtime=c170Runtime($db);
function selectedOid170($db,string $table,int $id): string {
$q=cubrid_prepare($db,'SELECT id,title FROM '.$table.' WHERE id=?',CUBRID_INCLUDE_OID);
if($q===false){throw new RuntimeException('Не подготовлен выбор записи');}
try {
if(!cubrid_bind($q,1,$id,'int')||!cubrid_execute($q)||cubrid_num_rows($q)!==1
||!cubrid_move_cursor($q,1,CUBRID_CURSOR_FIRST)){
throw new RuntimeException('Нужна ровно одна доступная запись');
}
$oid=cubrid_current_oid($q);
if(!is_string($oid)||$oid===''){throw new RuntimeException('Не получен OID');}
return $oid;
} finally {c170Release($q);}
}
function collection170($db,string $oid,string $attr): array {
$value=cubrid_col_get($db,$oid,$attr);
if($value===false){throw new RuntimeException('Не прочитана коллекция');}
return $value;
}
function change170(bool $ok): void {
if(!$ok){throw new RuntimeException('Изменение коллекции не выполнено');}
}
function sorted170(array $items): array {sort($items,SORT_STRING);return $items;}
$name=c170OwnName($db,$owned,'TABLE','product_tags');$t=c170Qualified($name);
c170Ddl($db,'CREATE TABLE '.$t.' (id INT PRIMARY KEY,title VARCHAR(80),tags SET(VARCHAR(40))) DONT_REUSE_OID');
c170Command($db,"INSERT INTO $t (id,title,tags) VALUES (?, ?, {'copper','indoor'})",[10,'Кабель']);
c170Command($db,"INSERT INTO $t (id,title,tags) VALUES (?, ?, {'steel'})",[20,'Хомут']);
c170Commit($db);
$oid=selectedOid170($db,$t,10);
$before=sorted170(collection170($db,$oid,'tags'));
change170(cubrid_set_add($db,$oid,'tags','flexible'));
$duplicate=cubrid_set_add($db,$oid,'tags','copper');change170($duplicate);
$afterDuplicate=sorted170(collection170($db,$oid,'tags'));
change170(cubrid_set_drop($db,$oid,'tags','indoor'));
$series="Серия O'Reilly";
change170(cubrid_set_add($db,$oid,'tags',$series));
c170Commit($db);
$saved=sorted170(collection170($db,selectedOid170($db,$t,10),'tags'));
$size=cubrid_col_size($db,$oid,'tags');
if($size===false){throw new RuntimeException('Размер набора не прочитан');}
$neighbor=collection170($db,selectedOid170($db,$t,20),'tags');
// PHP-массив — копия. Эта строка не вызывает ни одного изменения базы.
$local=$saved;$local[]='local-only';
$unchanged=sorted170(collection170($db,$oid,'tags'));
c170Commit($db);
$rows=[['Товар','Кабель'],['До изменения',implode(', ',$before)],
['После сохранения',implode(', ',$saved)],['Количество признаков',$size],
['Повторное добавление copper успешно',$duplicate],['Кириллица и апостроф сохранены',in_array($series,$saved,true)],
['Хомут не изменён',$neighbor===['steel']],['Изменение массива PHP не записалось',$unchanged===$saved]];
$checks=['initial_members'=>$before===['copper','indoor'],'duplicate_success'=>$duplicate===true,
'duplicate_not_multiplied'=>$afterDuplicate===['copper','flexible','indoor'],
'saved_members'=>$saved===['copper','flexible',"Серия O'Reilly"],'saved_size'=>$size===3,
'utf8_quote_roundtrip'=>in_array($series,$saved,true),'neighbor_unchanged'=>$neighbor===['steel'],
'local_snapshot_not_write'=>$unchanged===$saved&&count($local)===4];
$observations=['before'=>$before,'after_duplicate'=>$afterDuplicate,'saved'=>$saved,
'size'=>$size,'neighbor'=>$neighbor,'php_copy'=>$local,'reread'=>$unchanged,
'series_bytes'=>strlen($series),'series_sha256'=>hash('sha256',$series),
'display_order'=>'SORT_STRING on a small copy; SET storage has no promised order'];
} finally {
if (is_resource($db)) {try {$removed=c170Cleanup($db,$owned);} finally {c170Close($db);}}
}
c170Report('cub170-product-tags','Признаки кабеля: добавить метку без дубля и убрать старую',$rows,$checks,$observations,$runtime,$owned,$removed);
Ожидаемый результат
Товар — Кабель
До изменения — copper, indoor
После сохранения — copper, flexible, Серия O'Reilly
Количество признаков — 3
Повторное добавление copper успешно — да
Кириллица и апостроф сохранены — да
Хомут не изменён — да
Изменение массива PHP не записалось — да
Распакуйте весь ZIP; порядок запуска и создание отдельного стенда описаны в README.txt. example.php содержит операции урока, lab.php — подключение, создание только собственных таблиц и очистку. result.html показывает итог, result.json — исходные наблюдения. В finally удаляется только созданная этим запуском таблица. Это реальный CLI-проект с CUBRID, не PHP-обработчик публичного сайта.
Открыть результат выполнения · Посмотреть данные JSON
Границы решения
true от set_add означает успешную операцию, а не обязательный рост размера. Коллекции SET и MULTISET не обещают порядка; сортируется только маленькая копия для экрана. Названия характеристик с отдельной ценой, переводами, правами или массовым поиском удобнее хранить в таблицах со связями. Апостроф передаётся значением функции, поэтому вручную удваивать его здесь не нужно. HTML результата экранирует текст, но это отдельная задача от передачи значения в базу.
Попробуйте изменить условие
Добавьте flexible ещё раз и объясните, изменятся ли размер и список. Затем добавьте к PHP-массиву строку local-only без вызова CUBRID.
Разбор
Повтор flexible завершится успешно, но SET сохранит одно вхождение. Массив PHP изменится только в памяти процесса. Для изменения записи нужны отдельный вызов записи и подтверждение транзакции.
Комплект крепежа: считать все детали и вынуть только одну шайбу
В наборе крепежа две одинаковые шайбы — две настоящие детали. SET потерял бы это количество. MULTISET сохраняет повторения: сначала добавим третью шайбу, затем вынем ровно одну и уберём ошибочно положенный болт. Сохранённый комплект содержит две шайбы, а не один вид детали, ошибочно посчитанный как одна штука.
- Исходно: bolt + washer + washer = 3 детали
- Добавить washer → bolt + washer + washer + washer = 4
- Вынуть одну washer → bolt + washer + washer = 3
- Убрать bolt, подтвердить → washer + washer = 2 детали одного вида
Минимальный вызов
// parts — MULTISET(VARCHAR(40)), исходно {'washer','washer','bolt'}.
if (!cubrid_set_drop($db, $oid, 'parts', 'washer')) {
throw new RuntimeException('Деталь не удалена');
}
$parts = cubrid_col_get($db, $oid, 'parts');
if ($parts === false) { throw new RuntimeException('Комплект не прочитан'); }
// Одна washer и один bolt: удалено одно вхождение, не все washer.set_drop выбирает элемент по значению. В проверенном MULTISET один успешный вызов удаляет одно совпадающее вхождение. col_size считает все оставшиеся элементы, включая повторения. array_count_values в полном проекте только оформляет маленький комплект после чтения; это не команда изменения базы и не количество всех строк таблицы.
Что получится после запуска
| Наблюдение | Результат |
|---|---|
| До комплектации | bolt, washer, washer |
| После добавления шайбы | bolt, washer, washer, washer |
| Деталей после добавления | 4 |
| После извлечения одной шайбы | bolt, washer, washer |
| Сохранённый комплект | washer, washer |
| Осталось шайб | 2 |
| Всего деталей | 2 |
| Удаление отсутствующей nut успешно | да |
Самостоятельный проект, исходник и архив
<?php
declare(strict_types=1);
require __DIR__.'/lab.php';
$db=null;$owned=$removed=$rows=$checks=$observations=[];
try {
$config=c170Config();$db=c170Connect();$runtime=c170Runtime($db);
function selectedOid170($db,string $table,int $id): string {
$q=cubrid_prepare($db,'SELECT id,title FROM '.$table.' WHERE id=?',CUBRID_INCLUDE_OID);
if($q===false){throw new RuntimeException('Не подготовлен выбор записи');}
try {
if(!cubrid_bind($q,1,$id,'int')||!cubrid_execute($q)||cubrid_num_rows($q)!==1
||!cubrid_move_cursor($q,1,CUBRID_CURSOR_FIRST)){
throw new RuntimeException('Нужна ровно одна доступная запись');
}
$oid=cubrid_current_oid($q);
if(!is_string($oid)||$oid===''){throw new RuntimeException('Не получен OID');}
return $oid;
} finally {c170Release($q);}
}
function collection170($db,string $oid,string $attr): array {
$value=cubrid_col_get($db,$oid,$attr);
if($value===false){throw new RuntimeException('Не прочитана коллекция');}
return $value;
}
function change170(bool $ok): void {
if(!$ok){throw new RuntimeException('Изменение коллекции не выполнено');}
}
function sorted170(array $items): array {sort($items,SORT_STRING);return $items;}
$name=c170OwnName($db,$owned,'TABLE','kit_parts');$t=c170Qualified($name);
c170Ddl($db,'CREATE TABLE '.$t.' (id INT PRIMARY KEY,title VARCHAR(80),parts MULTISET(VARCHAR(40))) DONT_REUSE_OID');
c170Command($db,"INSERT INTO $t (id,title,parts) VALUES (?, ?, {'washer','washer','bolt'})",[10,'Монтажный комплект']);
c170Commit($db);$oid=selectedOid170($db,$t,10);
$initial=sorted170(collection170($db,$oid,'parts'));
change170(cubrid_set_add($db,$oid,'parts','washer'));
$packed=sorted170(collection170($db,$oid,'parts'));$packedSize=cubrid_col_size($db,$oid,'parts');
change170(cubrid_set_drop($db,$oid,'parts','washer'));
$afterOne=sorted170(collection170($db,$oid,'parts'));
change170(cubrid_set_drop($db,$oid,'parts','bolt'));
$missing=cubrid_set_drop($db,$oid,'parts','nut');change170($missing);
c170Commit($db);
$saved=sorted170(collection170($db,selectedOid170($db,$t,10),'parts'));
$savedSize=cubrid_col_size($db,$oid,'parts');
if($packedSize===false||$savedSize===false){throw new RuntimeException('Размер комплекта не прочитан');}
$counts=array_count_values($saved);c170Commit($db);
$rows=[['До комплектации',implode(', ',$initial)],['После добавления шайбы',implode(', ',$packed)],
['Деталей после добавления',$packedSize],['После извлечения одной шайбы',implode(', ',$afterOne)],
['Сохранённый комплект',implode(', ',$saved)],['Осталось шайб',$counts['washer']??0],
['Всего деталей',$savedSize],['Удаление отсутствующей nut успешно',$missing]];
$checks=['initial_duplicates'=>$initial===['bolt','washer','washer'],
'multiset_add_keeps_repeat'=>$packed===['bolt','washer','washer','washer'],'four_packed'=>$packedSize===4,
'one_occurrence_removed'=>$afterOne===['bolt','washer','washer'],'bolt_removed'=>$saved===['washer','washer'],
'two_actual_items'=>$savedSize===2,'one_kind_two_instances'=>count($counts)===1&&($counts['washer']??0)===2,
'missing_drop_success'=>$missing===true];
$observations=['initial'=>$initial,'packed'=>$packed,'after_one_drop'=>$afterOne,'saved'=>$saved,
'saved_size'=>$savedSize,'counts_by_value'=>$counts,'missing_drop'=>$missing,
'business_meaning'=>'Each repeated washer represents one physical piece; no guaranteed storage order'];
} finally {
if (is_resource($db)) {try {$removed=c170Cleanup($db,$owned);} finally {c170Close($db);}}
}
c170Report('cub170-kit-multiset','Комплект крепежа: считать все детали и вынуть только одну шайбу',$rows,$checks,$observations,$runtime,$owned,$removed);
Ожидаемый результат
До комплектации — bolt, washer, washer
После добавления шайбы — bolt, washer, washer, washer
Деталей после добавления — 4
После извлечения одной шайбы — bolt, washer, washer
Сохранённый комплект — washer, washer
Осталось шайб — 2
Всего деталей — 2
Удаление отсутствующей nut успешно — да
Распакуйте весь ZIP; порядок запуска и создание отдельного стенда описаны в README.txt. example.php содержит операции урока, lab.php — подключение, создание только собственных таблиц и очистку. result.html показывает итог, result.json — исходные наблюдения. В finally удаляется только созданная этим запуском таблица. Это реальный CLI-проект с CUBRID, не PHP-обработчик публичного сайта.
Открыть результат выполнения · Посмотреть данные JSON
Границы решения
true при удалении nut, которой нет, не означает, что что-то исчезло: проверенный драйвер считает такой вызов успешным. Если важен факт снятия позиции, сравните нужное число вхождений до и после в одной корректно защищённой транзакции. Повторный запрос на удаление шайбы уже может удалить вторую: такой бизнес-запрос нельзя бесконтрольно повторять после сетевой неопределённости. Для состава с ценами, партиями и большим количеством деталей нужна отдельная таблица строк комплекта с количеством.
Попробуйте изменить условие
Как получить три шайбы и ни одного болта, если исходный комплект тот же? Почему нельзя заменить MULTISET на SET?
Разбор
Добавьте washer один раз, уберите bolt и подтвердите транзакцию. SET оставит единственное значение washer, поэтому по нему нельзя восстановить число физических деталей.
Форма заполнения: не перепутать неизвестный набор, пустой набор и NULL-элемент
Редактор ещё не заполнил признаки первого товара, для второго осознанно выбрал «меток нет», а в третьем остался незаполненный элемент. Если показать все три как «пусто», интерфейс потеряет смысл. Программа выводит три разных подсказки, а также отделяет ошибочное имя поля и обычную строку от корректной коллекции.
- id=10: tags=NULL → IS NULL=1; col_get=[]; col_size=−1
- id=20: tags={} → IS NULL=0; col_get=[]; col_size=0
- id=30: tags={NULL} → IS NULL=0; col_get=[null]; col_size=1
- missing170 или plain VARCHAR → false + сохранённая диагностика, не «пустой набор»
Минимальный вызов
$size = cubrid_col_size($db, $oid, 'tags');
if ($size === false) {
throw new RuntimeException('Не удалось прочитать размер');
}
if ($size === -1) { echo 'Набор ещё не заполнен'; }
elseif ($size === 0) { echo 'Явно сохранён пустой набор'; }
else { echo 'Элементов: ', $size; }
// -1 для SQL NULL наблюдается в закреплённой версии; это не false.=== сравнивает и значение, и тип: целое 0, целое −1 и логическое false имеют разный смысл. get возвращает PHP-массив, но для SQL NULL и {} в этом профиле получается одинаковый []. Поэтому полный пример дополнительно читает tags IS NULL в SQL и размер. {NULL} содержит один элемент null: этот элемент не является отсутствием самой коллекции.
Что получится после запуска
| Наблюдение | Результат |
|---|---|
| SQL NULL | не заполнено; размер -1 |
| Пустая коллекция {} | подтверждено: меток нет; размер 0 |
| Коллекция {NULL} | требуется заполнить один элемент; размер 1 |
| Массивы первых двух одинаковы | да |
| Неизвестный атрибут: код | -202 |
| Обычная строка вместо коллекции: код | -10020 |
Самостоятельный проект, исходник и архив
<?php
declare(strict_types=1);
require __DIR__.'/lab.php';
$db=null;$owned=$removed=$rows=$checks=$observations=[];
try {
$config=c170Config();$db=c170Connect();$runtime=c170Runtime($db);
function selectedOid170($db,string $table,int $id): string {
$q=cubrid_prepare($db,'SELECT id,title FROM '.$table.' WHERE id=?',CUBRID_INCLUDE_OID);
if($q===false){throw new RuntimeException('Не подготовлен выбор записи');}
try {
if(!cubrid_bind($q,1,$id,'int')||!cubrid_execute($q)||cubrid_num_rows($q)!==1
||!cubrid_move_cursor($q,1,CUBRID_CURSOR_FIRST)){
throw new RuntimeException('Нужна ровно одна доступная запись');
}
$oid=cubrid_current_oid($q);
if(!is_string($oid)||$oid===''){throw new RuntimeException('Не получен OID');}
return $oid;
} finally {c170Release($q);}
}
function collection170($db,string $oid,string $attr): array {
$value=cubrid_col_get($db,$oid,$attr);
if($value===false){throw new RuntimeException('Не прочитана коллекция');}
return $value;
}
function change170(bool $ok): void {
if(!$ok){throw new RuntimeException('Изменение коллекции не выполнено');}
}
function sorted170(array $items): array {sort($items,SORT_STRING);return $items;}
$name=c170OwnName($db,$owned,'TABLE','state');$t=c170Qualified($name);
c170Ddl($db,'CREATE TABLE '.$t.' (id INT PRIMARY KEY,title VARCHAR(80),tags SET(VARCHAR(40)),plain VARCHAR(40)) DONT_REUSE_OID');
c170Command($db,"INSERT INTO $t (id,title,tags,plain) VALUES (?, ?, NULL, ?)",[10,'Не заполнено','обычный текст']);
c170Command($db,"INSERT INTO $t (id,title,tags,plain) VALUES (?, ?, {}, ?)",[20,'Подтверждено: меток нет','обычный текст']);
c170Command($db,"INSERT INTO $t (id,title,tags,plain) VALUES (?, ?, {NULL}, ?)",[30,'Нужна правка элемента','обычный текст']);
c170Commit($db);$states=[];
$selected=c170Read($db,"SELECT id,title,CASE WHEN tags IS NULL THEN 1 ELSE 0 END AS collection_is_null FROM $t WHERE id IN (10,20,30) ORDER BY id LIMIT 3");
foreach($selected as $record){
$oid=selectedOid170($db,$t,(int)$record['id']);
$get=c170Capture(static function()use($db,$oid){return cubrid_col_get($db,$oid,'tags');});
$size=c170Capture(static function()use($db,$oid){return cubrid_col_size($db,$oid,'tags');});
if($get['value']===false||$get['warning']||$size['value']===false||$size['warning']){c170Failure('Состояние не прочитано');}
$status=(int)$record['collection_is_null']===1?'не заполнено':($size['value']===0?'подтверждено: меток нет':'требуется заполнить один элемент');
$states[]=['id'=>(int)$record['id'],'sql_null'=>(int)$record['collection_is_null']===1,
'items'=>$get['value'],'size'=>$size['value'],'status'=>$status];
}
$oid=selectedOid170($db,$t,10);
$wrong=c170Capture(static function()use($db,$oid){return cubrid_col_get($db,$oid,'missing170');});
$scalar=c170Capture(static function()use($db,$oid){return cubrid_col_size($db,$oid,'plain');});
// These controlled refusals are diagnostics, not empty collections.
c170Rollback($db);
$rows=[['SQL NULL',$states[0]['status'].'; размер '.$states[0]['size']],
['Пустая коллекция {}',$states[1]['status'].'; размер '.$states[1]['size']],
['Коллекция {NULL}',$states[2]['status'].'; размер '.$states[2]['size']],
['Массивы первых двух одинаковы',$states[0]['items']===$states[1]['items']],
['Неизвестный атрибут: код',$wrong['code']],['Обычная строка вместо коллекции: код',$scalar['code']]];
$checks=['three_bounded_records'=>count($states)===3,'null_is_unknown'=>$states[0]['sql_null']&&$states[0]['items']===[]&&$states[0]['size']===-1,
'empty_is_known'=>!$states[1]['sql_null']&&$states[1]['items']===[]&&$states[1]['size']===0,
'one_null_is_one'=>!$states[2]['sql_null']&&$states[2]['items']===[null]&&$states[2]['size']===1,
'get_alone_loses_distinction'=>$states[0]['items']===$states[1]['items'],
'unknown_attribute_refused'=>$wrong['value']===false&&$wrong['code']===-202&&$wrong['facility']===1&&$wrong['warning']&&$wrong['message_present'],
'scalar_refused'=>$scalar['value']===false&&$scalar['code']===-10020&&$scalar['facility']===2&&$scalar['warning']&&$scalar['message_present']];
$observations=['states'=>$states,'wrong_attribute'=>$wrong,'not_collection'=>$scalar,
'nullable_form_policy'=>'SQL NULL unknown; empty collection explicitly no tags; NULL member needs correction'];
} finally {
if (is_resource($db)) {try {$removed=c170Cleanup($db,$owned);} finally {c170Close($db);}}
}
c170Report('cub170-collection-state','Форма заполнения: не перепутать неизвестный набор, пустой набор и NULL-элемент',$rows,$checks,$observations,$runtime,$owned,$removed);
Ожидаемый результат
SQL NULL — не заполнено; размер -1
Пустая коллекция {} — подтверждено: меток нет; размер 0
Коллекция {NULL} — требуется заполнить один элемент; размер 1
Массивы первых двух одинаковы — да
Неизвестный атрибут: код — -202
Обычная строка вместо коллекции: код — -10020
Распакуйте весь ZIP; порядок запуска и создание отдельного стенда описаны в README.txt. example.php содержит операции урока, lab.php — подключение, создание только собственных таблиц и очистку. result.html показывает итог, result.json — исходные наблюдения. В finally удаляется только созданная этим запуском таблица. Это реальный CLI-проект с CUBRID, не PHP-обработчик публичного сайта.
Открыть результат выполнения · Посмотреть данные JSON
Границы решения
Значение −1 для NULL подтверждено CCI и проверенной сборкой; не путайте его со старым возвратом −1 при ошибке col_size до версии 8.3.1. В новых версиях ошибка — false. Конкретные коды отказа привязаны к стенду, а приложение должно прежде всего проверять false и сохранять текущую диагностику. Наличие NULL-элемента не даёт универсального правила формы: здесь это наша явная политика, запрещающая неопределённые метки.
Попробуйте изменить условие
Сделайте поле необязательным: разрешите неизвестный набор, но сохраните различие между «ещё не заполнено» и «меток нет».
Разбор
Не заменяйте NULL на {} автоматически и не вызывайте empty($items) как единственный классификатор. Храните отдельный SQL NULL и показывайте его состояние через IS NULL; пустой известный набор оставляйте {}.
Изменение способов выдачи: отменить противоречивый набор и сохранить корректный
Магазин меняет выдачу шкафа: вместо самовывоза остаётся только доставка. Две метки pickup и delivery-only одновременно противоречат правилу магазина. Сначала покажем, что успешный set_add ещё не делает набор допустимым: предпросмотр отменяется. Затем удалим старый признак и добавим новый в одной транзакции, подтвердим и перечитаем. Полка сохранит самовывоз.
- Шкаф: {pickup}; Полка: {pickup}
- Предпросмотр шкафа {pickup, delivery-only} → конфликт → rollback → {pickup}
- Новый выбор: удалить pickup + добавить delivery-only → commit
- Перечитать: Шкаф {delivery-only}; Полка {pickup}
Минимальный вызов
// $db работает с cubrid_set_autocommit($db, false).
try {
if (!cubrid_set_drop($db, $oid, 'tags', 'pickup') ||
!cubrid_set_add($db, $oid, 'tags', 'delivery-only')) {
throw new RuntimeException('Способ выдачи не изменён');
}
if (!cubrid_commit($db)) { throw new RuntimeException('Изменение не подтверждено'); }
} catch (Throwable $failure) {
cubrid_rollback($db);
throw $failure;
}try объединяет работу, catch обрабатывает отказ и отменяет незавершённые изменения; разбор этой конструкции связан с главой транзакций. В простом фрагменте показана граница двух операций, а полный проект сначала проверяет допустимое значение и затем итоговый набор. Временный предпросмотр выполняется в рамках короткой транзакции внутри одного запуска, а не во время ожидания действий человека в браузере.
Что получится после запуска
| Наблюдение | Результат |
|---|---|
| Исходный способ выдачи | pickup |
| Предпросмотр | delivery-only, pickup |
| Конфликт обнаружен | да |
| После отмены | pickup |
| Сохранённый способ для шкафа | delivery-only |
| Способ для полки | pickup |
Самостоятельный проект, исходник и архив
<?php
declare(strict_types=1);
require __DIR__.'/lab.php';
$db=null;$owned=$removed=$rows=$checks=$observations=[];
try {
$config=c170Config();$db=c170Connect();$runtime=c170Runtime($db);
function selectedOid170($db,string $table,int $id): string {
$q=cubrid_prepare($db,'SELECT id,title FROM '.$table.' WHERE id=?',CUBRID_INCLUDE_OID);
if($q===false){throw new RuntimeException('Не подготовлен выбор записи');}
try {
if(!cubrid_bind($q,1,$id,'int')||!cubrid_execute($q)||cubrid_num_rows($q)!==1
||!cubrid_move_cursor($q,1,CUBRID_CURSOR_FIRST)){
throw new RuntimeException('Нужна ровно одна доступная запись');
}
$oid=cubrid_current_oid($q);
if(!is_string($oid)||$oid===''){throw new RuntimeException('Не получен OID');}
return $oid;
} finally {c170Release($q);}
}
function collection170($db,string $oid,string $attr): array {
$value=cubrid_col_get($db,$oid,$attr);
if($value===false){throw new RuntimeException('Не прочитана коллекция');}
return $value;
}
function change170(bool $ok): void {
if(!$ok){throw new RuntimeException('Изменение коллекции не выполнено');}
}
function sorted170(array $items): array {sort($items,SORT_STRING);return $items;}
$name=c170OwnName($db,$owned,'TABLE','membership');$t=c170Qualified($name);
c170Ddl($db,'CREATE TABLE '.$t.' (id INT PRIMARY KEY,title VARCHAR(80),tags SET(VARCHAR(40))) DONT_REUSE_OID');
c170Command($db,"INSERT INTO $t (id,title,tags) VALUES (?, ?, {'pickup'})",[10,'Шкаф']);
c170Command($db,"INSERT INTO $t (id,title,tags) VALUES (?, ?, {'pickup'})",[20,'Полка']);
c170Commit($db);
$oid=selectedOid170($db,$t,10);$original=collection170($db,$oid,'tags');
change170(cubrid_set_add($db,$oid,'tags','delivery-only'));
$preview=sorted170(collection170($db,$oid,'tags'));
$conflict=in_array('pickup',$preview,true)&&in_array('delivery-only',$preview,true);
if(!$conflict){throw new RuntimeException('Ожидался конфликт правил выдачи');}
c170Rollback($db);
$afterCancel=collection170($db,selectedOid170($db,$t,10),'tags');
// A new allowed choice is validated before the two writes.
$requested='delivery-only';$allowed=['pickup','delivery-only'];
if(!in_array($requested,$allowed,true)){throw new InvalidArgumentException('Неизвестный способ выдачи');}
$oid=selectedOid170($db,$t,10);
try{
change170(cubrid_set_drop($db,$oid,'tags','pickup'));
change170(cubrid_set_add($db,$oid,'tags',$requested));
$candidate=collection170($db,$oid,'tags');
if($candidate!==['delivery-only']){throw new RuntimeException('Неверный итоговый набор');}
c170Commit($db);
}catch(Throwable $failure){c170Rollback($db);throw $failure;}
$saved=collection170($db,selectedOid170($db,$t,10),'tags');
$neighbor=collection170($db,selectedOid170($db,$t,20),'tags');c170Commit($db);
$rows=[['Исходный способ выдачи',implode(', ',$original)],['Предпросмотр',implode(', ',$preview)],
['Конфликт обнаружен',$conflict],['После отмены',implode(', ',$afterCancel)],
['Сохранённый способ для шкафа',implode(', ',$saved)],['Способ для полки',implode(', ',$neighbor)]];
$checks=['original_pickup'=>$original===['pickup'],'preview_conflict'=>$preview===['delivery-only','pickup']&&$conflict,
'rollback_restored'=>$afterCancel===$original,'requested_allowed'=>in_array($requested,$allowed,true),
'saved_new_policy'=>$saved===['delivery-only'],'no_removed_pickup'=>!in_array('pickup',$saved,true),
'neighbor_unchanged'=>$neighbor===['pickup']];
$observations=['original'=>$original,'preview'=>$preview,'after_rollback'=>$afterCancel,
'saved'=>$saved,'neighbor'=>$neighbor,'allowed_values'=>$allowed,
'policy'=>'pickup and delivery-only are mutually exclusive for this item'];
} finally {
if (is_resource($db)) {try {$removed=c170Cleanup($db,$owned);} finally {c170Close($db);}}
}
c170Report('cub170-membership-save','Изменение способов выдачи: отменить противоречивый набор и сохранить корректный',$rows,$checks,$observations,$runtime,$owned,$removed);
Ожидаемый результат
Исходный способ выдачи — pickup
Предпросмотр — delivery-only, pickup
Конфликт обнаружен — да
После отмены — pickup
Сохранённый способ для шкафа — delivery-only
Способ для полки — pickup
Распакуйте весь ZIP; порядок запуска и создание отдельного стенда описаны в README.txt. example.php содержит операции урока, lab.php — подключение, создание только собственных таблиц и очистку. result.html показывает итог, result.json — исходные наблюдения. В finally удаляется только созданная этим запуском таблица. Это реальный CLI-проект с CUBRID, не PHP-обработчик публичного сайта.
Открыть результат выполнения · Посмотреть данные JSON
Границы решения
Атомарность нескольких изменений не заменяет разрешение на запись, проверку актуальности формы и защиту от одновременного редактирования. Здесь один процесс и собственная запись; для конкурентной формы нужны блокировка либо проверяемая версия строки. Если способ выдачи всегда только один, обычный столбец с одним значением проще SET; пример показывает смену правил внутри уже существующей модели меток. SQL-успех и допустимость бизнес-состояния проверяются отдельно.
Попробуйте изменить условие
Что произойдёт, если после удаления pickup второй вызов вернёт false? Почему нельзя делать commit сразу после удаления?
Разбор
В ручном режиме catch откатит первое изменение. Ранний commit сохранил бы промежуточное состояние без способа выдачи, и последующий rollback его уже не отменил бы.
Когда вместо коллекции нужна таблица со связями
Вопрос о пользовательских метках и отдельной таблице полезен именно выбором модели: строка с запятыми, коллекция и таблица связей дают разные возможности. Здесь применяется типизированная коллекция CUBRID, а не разбор строки explode. Для меток с собственными названиями, переводами и поиском по многим товарам рассмотрите таблицы tags и product_tags с ключами; схема и составные ключи показывают основы такого решения.
Обсуждения заранее заданных меток и слабых сравнений NULL подсказывают две отдельные обязанности приложения: проверять допустимые значения и различать отсутствие данных. Синтаксис MySQL SET и его битовые представления сюда не переносятся: SQL-коллекция CUBRID имеет собственный контракт. Решения этой главы сверены с PHP Manual, CCI и закреплённым native-драйвером.
Четыре функции по задаче
| Функция | Зачем нужна |
|---|---|
| cubrid_col_get | Прочитать элементы одного атрибута коллекции в числовой PHP-массив. |
| cubrid_col_size | Узнать количество элементов, включая повторения и NULL-элементы, не передавая их все в PHP. |
| cubrid_set_add | Добавить одно значение в SET/MULTISET; для SEQUENCE проверенный вызов добавляет его в конец. |
| cubrid_set_drop | Удалить одно совпадающее значение из SET или одно вхождение из MULTISET. |
Следующий шаг: порядок элементов
Теперь признаки и экземпляры различаются по смыслу, NULL не теряется при отображении, а несколько изменений подтверждаются общей границей транзакции. Следующий связанный шаг — порядок шагов и маршрутов SEQUENCE: вставить проверку перед упаковкой, изменить адрес, убрать только одну повторяющуюся остановку и перенести этап без потери остальных.
Если важны одновременные изменения, вернитесь к доступу по OID и блокировкам и транзакциям. Для обычного каталога строк продолжайте выбирать нужные поля, WHERE, ORDER BY и LIMIT в SQL: работа с коллекцией одной записи не является поводом загружать всю таблицу в PHP.
Функции и методы этой темы
Откройте карточку, чтобы увидеть договор функции, примеры, ограничения и связанные инструменты.
cubrid_col_getПрочитать элементы одного атрибута коллекции в числовой PHP-массив.
PECL CUBRID >= 8.3.0; у col_size возврат ошибки изменён в 8.3.1. · cubrid
cubrid_col_get(resource $conn_identifier, string $oid, string $attr_name): array|falseЭкран карточки должен показать признаки выбранного товара. Функция читает содержимое одного атрибута SET, MULTISET или SEQUENCE по OID, а не все строки таблицы.
| Параметр | Что передать |
|---|---|
| conn_identifier | Открытое native-соединение cubrid. |
| oid | OID выбранной доступной записи referable-таблицы. |
| attr_name | Точное имя атрибута коллекции; это имя столбца, не индекс PHP-массива. |
Результат: Числовой массив с ключами 0, 1, …; false при отказе. В проверенном native-драйвере элементы читаются через CCI как строки, SQL NULL-элементы становятся PHP null. Результат уже является массивом, не resource для fetch/close_request.
При false сразу считайте cubrid_error_code(), cubrid_error_code_facility() и cubrid_error_msg(), пока следующий вызов не заменил диагностику. В полном проекте c170Capture перехватывает предупреждение и сохраняет эти признаки вместе. Не проверяйте результат только через if (!$value): пустой массив и число 0 могут быть нормальным ответом.
Пустая коллекция даёт []. Существенная граница профиля 11.2.0.0002/CCI 11.2.0: SQL NULL всего атрибута также даёт [], а {NULL} — [null]. Не восстанавливайте состояние формы по одному empty($items). Размер различает −1, 0 и 1, а SQL tags IS NULL явно проверяет отсутствие значения атрибута. Подробности и отказы собраны в трёх состояниях поля.
Применение 1: подписи признаков одной карточки
$items = cubrid_col_get($db, $oid, 'tags');
if ($items === false) { throw new RuntimeException('Признаки недоступны'); }
sort($items, SORT_STRING); // Только небольшой набор для стабильного вывода.
echo htmlspecialchars(implode(', ', $items), ENT_QUOTES, 'UTF-8');Перед этим приложение знает, что tags — заполненный SET строк без NULL. В карточке кабеля после сохранения видны copper, flexible и Серия O'Reilly. sort не задаёт порядок хранения SET и не записывает его обратно. Для NULL-элемента нужна отдельная подпись, а не неявное превращение в пустую строку.
Применение 2: посчитать повторяющиеся детали комплекта
$parts = cubrid_col_get($db, $oid, 'parts');
if ($parts === false) { throw new RuntimeException('Состав не прочитан'); }
// Здесь схема и проверка приложения допускают только непустые строки.
$byPart = array_count_values($parts);
echo $byPart['washer'] ?? 0; // 2 у сохранённого комплекта из урока.В комплекте крепежа два одинаковых элемента не теряются. Это локальное оформление небольшой коллекции. Для заказов со многими строками, количеством, стоимостью и партиями используйте связанные таблицы и SQL-агрегацию, а не загрузку огромных коллекций для расчёта в PHP.
SET/MULTISET не обещают порядок, а SEQUENCE сохраняет позиции: её массив нельзя сортировать для последующей позиционной записи. Чтение делает снимок, не живую связь с базой. После изменения массива данные сервера остаются прежними. После добавления или удаления перечитайте атрибут в нужной границе транзакции. Внутренний CCI-request функция освобождает сама.
Ошибка имени атрибута, неколлекционный тип или недоступный OID дают отказ; не представляйте его пользователю пустым списком. Проверенный неизвестный атрибут дал −202/facility 1, VARCHAR вместо коллекции — −10020/facility 2. Это наблюдения конкретного профиля, а не полный перечень возможных кодов. Вызов выполняется на существующем соединении; он не открывает базу и не проверяет бизнес-разрешение пользователя.
cubrid_col_sizeУзнать количество элементов, включая повторения и NULL-элементы, не передавая их все в PHP.
PECL CUBRID >= 8.3.0; у col_size возврат ошибки изменён в 8.3.1. · cubrid
cubrid_col_size(resource $conn_identifier, string $oid, string $attr_name): int|falseНадпись «в комплекте 4 детали» требует общего числа элементов, а не полного списка. Функция получает размер одного атрибута коллекции; это не количество строк результата SELECT и не число уникальных названий.
| Параметр | Что передать |
|---|---|
| conn_identifier | Открытое native-соединение cubrid. |
| oid | OID выбранной доступной записи referable-таблицы. |
| attr_name | Точное имя атрибута коллекции; это имя столбца, не индекс PHP-массива. |
Результат: Целое число элементов SET/MULTISET/SEQUENCE; false при ошибке начиная с PECL CUBRID 8.3.1. До 8.3.1 документация указывает −1 как возврат ошибки. В проверенном новом профиле размер −1 без ошибки означает SQL NULL коллекции по контракту CCI.
При false сразу считайте cubrid_error_code(), cubrid_error_code_facility() и cubrid_error_msg(), пока следующий вызов не заменил диагностику. В полном проекте c170Capture перехватывает предупреждение и сохраняет эти признаки вместе. Не проверяйте результат только через if (!$value): пустой массив и число 0 могут быть нормальным ответом.
| Сохранено в атрибуте | col_get | col_size | Что сказать человеку |
|---|---|---|---|
| NULL | [] | −1 | Набор ещё не задан |
| {} | [] | 0 | Явно известно, что элементов нет |
| {NULL} | [null] | 1 | Есть один неизвестный элемент |
| MULTISET {'washer','washer'} | ['washer','washer'] | 2 | Две детали одного вида |
Применение 1: количество деталей, а не видов
$count = cubrid_col_size($db, $oid, 'parts');
if ($count === false) { throw new RuntimeException('Количество не получено'); }
if ($count === -1) { echo 'Состав не задан'; }
else { echo 'Деталей: ', $count; }В комплекте после добавления третьей шайбы число равно 4, после финального сохранения — 2. Если нужен только счётчик, col_get и подсчёт большого PHP-массива не требуются. Число видов — другой вопрос; оно не равно размеру MULTISET.
Применение 2: не обнулить незаполненное поле формы
$count = cubrid_col_size($db, $oid, 'tags');
if ($count === false) { throw new RuntimeException('Не удалось загрузить поле'); }
$caption = $count === -1 ? 'Не заполнено'
: ($count === 0 ? 'Метки не требуются' : 'Выбрано элементов: '.$count);Самостоятельная программа сверяет это с SQL IS NULL и показывает, почему if (!$count) смешивает корректный ноль с false. Не перепутайте старый ошибочный −1 и нынешний NULL-размер: версия драйвера, тип возвращённого значения и немедленная диагностика имеют значение.
Здесь элементы измеряются штуками, не символами и не байтами текста. Размер до изменения и после него может совпасть, например, при повторном добавлении SET-метки. При параллельных изменениях раздельные вызовы get/size не являются автоматически общим снимком: выбирайте подходящую изоляцию и ограничение ожидания. Для числа выбранных строк предназначен cubrid_num_rows, а не этот API.
cubrid_set_addДобавить одно значение в SET/MULTISET; для SEQUENCE проверенный вызов добавляет его в конец.
PECL CUBRID >= 8.3.0; у col_size возврат ошибки изменён в 8.3.1. · cubrid
cubrid_set_add(resource $conn_identifier, string $oid, string $attr_name, string $set_element): boolДобавить один признак товару или одну физическую деталь комплекту можно без пересборки всей коллекции. Функция принимает значение отдельным аргументом и изменяет атрибут выбранной записи.
| Параметр | Что передать |
|---|---|
| conn_identifier | Открытое native-соединение cubrid. |
| oid | OID выбранной доступной записи referable-таблицы. |
| attr_name | Точное имя атрибута коллекции; это имя столбца, не индекс PHP-массива. |
| set_element | Один добавляемый элемент, переданный строкой. Тип элемента должен подходить объявленному домену коллекции; в strict_types профиле не подменяйте строку массивом или целым числом. |
Результат: true при успешной операции; false при ошибке. true не гарантирует, что размер увеличился: SET не хранит второй такой же элемент. В MULTISET повтор является отдельным элементом.
При false сразу считайте cubrid_error_code(), cubrid_error_code_facility() и cubrid_error_msg(), пока следующий вызов не заменил диагностику. В полном проекте c170Capture перехватывает предупреждение и сохраняет эти признаки вместе. Не проверяйте результат только через if (!$value): пустой массив и число 0 могут быть нормальным ответом.
PHP Manual относит вызов к SET, MULTISET и SEQUENCE. Для SEQUENCE подготовка подтвердила добавление в конец; когда важна конкретная позиция, используйте cubrid_seq_insert. Для SET порядок добавления не является порядком будущего вывода.
Применение 1: поставить признак один раз
$ok = cubrid_set_add($db, $oid, 'tags', 'copper');
if ($ok !== true) { throw new RuntimeException('Признак не установлен'); }
// Повтор того же вызова в SET не создаёт второй copper.
if (!cubrid_commit($db)) { throw new RuntimeException('Не подтверждено'); }Кабель с признаками показывает повторный вызов и реальное чтение без дубля. Имя tags задано схемой приложения, а не произвольным полем запроса. Для контролируемых признаков проверьте допустимый словарь значений: коллекция строк сама по себе не запрещает опечатку coppre.
Применение 2: добавить ещё одну одинаковую деталь
$ok = cubrid_set_add($db, $oid, 'parts', 'washer');
if ($ok !== true) { throw new RuntimeException('Шайба не добавлена'); }
$count = cubrid_col_size($db, $oid, 'parts');
if ($count === false) { throw new RuntimeException('Количество недоступно'); }
// Две washer + bolt становятся тремя washer + bolt: размер 4.В MULTISET-комплекте этот повтор нужен. Функция не является универсальной идемпотентной командой: повтор после неопределённого сетевого исхода способен добавить лишнюю физическую деталь. Сохраняйте бизнес-идентификатор операции, если требуется безопасное повторение запроса.
API передаёт значение через CCI, а не склеивает SQL: строка с апострофом передаётся как есть. Экранирование HTML выполняется отдельно при выводе. Политика приложения может запретить технически успешный набор: пример способов выдачи отменяет конфликт и подтверждает только допустимое состояние. Выключенный autocommit требует явного commit, а при отказе всей операции — rollback. Функция не заменяет проверку прав, блокировку либо контроль версии редактируемой строки.
cubrid_set_dropУдалить одно совпадающее значение из SET или одно вхождение из MULTISET.
PECL CUBRID >= 8.3.0; у col_size возврат ошибки изменён в 8.3.1. · cubrid
cubrid_set_drop(resource $conn_identifier, string $oid, string $attr_name, string $set_element): boolСнять устаревший признак или вынуть одну одинаковую деталь — это удаление элемента по значению, а не удаление строки товара. Документированные коллекции этой функции: SET и MULTISET.
| Параметр | Что передать |
|---|---|
| conn_identifier | Открытое native-соединение cubrid. |
| oid | OID выбранной доступной записи referable-таблицы. |
| attr_name | Точное имя атрибута коллекции; это имя столбца, не индекс PHP-массива. |
| set_element | Удаляемое значение строкой. Оно сравнивается с элементами указанного атрибута; это не номер позиции и не весь PHP-массив. |
Результат: true при успешной операции; false при ошибке. В проверенном MULTISET удаляется одно совпадающее вхождение. Если значения не было, проверенный вызов тоже возвращает true, сохраняя коллекцию без изменений.
При false сразу считайте cubrid_error_code(), cubrid_error_code_facility() и cubrid_error_msg(), пока следующий вызов не заменил диагностику. В полном проекте c170Capture перехватывает предупреждение и сохраняет эти признаки вместе. Не проверяйте результат только через if (!$value): пустой массив и число 0 могут быть нормальным ответом.
Применение 1: снять старую метку товара
if (!cubrid_set_drop($db, $oid, 'tags', 'indoor')) {
throw new RuntimeException('Метка не снята');
}
if (!cubrid_commit($db)) { throw new RuntimeException('Сохранение не подтверждено'); }
$tags = cubrid_col_get($db, $oid, 'tags');
if ($tags === false) { throw new RuntimeException('Результат не прочитан'); }В наборе признаков кабеля indoor исчезает, остальные метки сохраняются. Функция не удаляет сам столбец, строку или всю таблицу. Если интерфейсу нужен факт «метка присутствовала и была снята», true без проверки состояния не отвечает на этот вопрос.
Применение 2: вынуть одну деталь из повторяющегося состава
// parts содержит три washer и один bolt.
if (!cubrid_set_drop($db, $oid, 'parts', 'washer')) {
throw new RuntimeException('Деталь не снята');
}
// После успешного чтения останутся две washer и один bolt.
// Ещё один такой вызов снимет ещё одну washer.Комплект с повторениями отличает эту операцию от удаления всех одинаковых значений и показывает отсутствующую nut. Нельзя считать повторный запрос безопасным только потому, что он тоже возвращает true. При недоступной связи после записи прежде выясняют исход бизнес-операции; бесконечный retry способен снять лишние детали.
Для SEQUENCE значение может встречаться в нескольких позициях с разным смыслом; используйте позиционное удаление cubrid_seq_drop. Если удаление — часть замены одного правила другим, объедините шаги, как в сохранении способа выдачи. NULL атрибута, отсутствующее значение, неправильный атрибут и false от функции — разные состояния; для классификации смотрите состояния коллекции и сохраняйте диагностику до следующего CUBRID-вызова.
Код и руководство с собой
Скачайте самостоятельные примеры с исходными данными, инструкциями и ожидаемыми результатами. Примеры HTTP и фрагменты для проекта помечены отдельно: им нужно указанное окружение.
Код не исполняется на сайте. Скачайте нужный пример и запускайте его в своём локальном окружении.
Материалы сверены 2026-10-12. Примеры и объяснения написаны для этого руководства; источники и обсуждения доступны в каждой главе.


