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

Дерево руководства
Глава 127 · После таблиц и чтения SQL через X Protocol
MySQL X DevAPI: столбцы результата, типы и понятный отчёт
От имени одной колонки до готового HTML-каталога: aliases и исходные поля, типы DECIMAL и BIGINT, Unicode, пустая выдача и безопасный вывод реальных строк.
От ответа базы к понятному отчёту
Каталог уже умеет читать товары из MySQL. Следующая потребность — показать их понятно: в какой колонке название, какая цена пришла точной строкой, откуда взялось поле после JOIN и какие заголовки оставить, если фильтр ничего не нашёл. Здесь мы получим эти сведения от результата запроса, а затем соберём настоящий HTML-отчёт с двумя товарами.
- SELECT name AS product_name выбирает поле и имя ответа
- Result хранит описание проекции и строки
- getColumns → ColumnResult: название, происхождение, тип
- fetchOne / fetchAll → значения товаров
- словарь интерфейса + значения → таблица для человека
Представьте ведомость: заголовок «Цена, руб.» объясняет колонку, а 12,30 — значение в строке конкретного товара. Metadata — техническое описание колонки; данные — сами значения. Это сравнение помогает разделить два слоя, но сервер не знает всех правил интерфейса: валюту, русский заголовок и смысл пустого примечания задаёт приложение.
Первый понятный вызов: имя одного столбца
Пусть в подготовленной таблице есть name со значением «Светильник». AS product_name даёт этому полю имя только в ответе запроса: таблица на сервере не переименовывается. getColumns возвращает массив описаний; [0] выбирает первое описание. Вызов метода через стрелку читает его свойство, а fetchOne отдельно читает строку.
$result = $table->select('name AS product_name')->limit(1)->execute();
$columns = $result->getColumns();
$label = $columns[0]->getColumnLabel();
$row = $result->fetchOne();
echo $label, ': ', $row[$label]; // product_name: СветильникЗдесь $table — уже полученный объект учебной таблицы, и строка заведомо существует. Полные программы ниже создают все свои исходные данные; отдельный пример показывает нулевой ответ. До этой главы нужны выбор полей таблицы, передача SQL-параметров и чтение строки. Короткий вызов не заменяет подключение: оно находится в скачиваемом файле вместе с подготовкой базы.
Что именно узнаём, а что задаём сами
| Слой | Пример | Для чего |
|---|---|---|
| SQL-источник | products.name | Узнать происхождение прямого поля |
| Метка результата | product_name | Обратиться к ключу ассоциативного массива |
| Человеческий заголовок | Товар | Объяснить содержание читателю |
| Описание типа | DECIMAL, scale 2 | Проверить контракт точной цены |
| Реальное значение | строка «12.30» | Показать «12,30» без промежуточного float |
Описание не заменяет SQL-схему целиком и не является указанием загружать все данные. SELECT перечисляет только нужные поля; WHERE, ORDER BY и LIMIT ограничивают строки на сервере. Здесь мы не выгружаем большой каталог, чтобы сортировать его в PHP. Сборка таблицы проходит по уже выбранным строкам экрана. Наличие metadata также не доказывает наличие хотя бы одного товара.
Как запустить и открыть результат
Нужны отдельная учебная схема и X Protocol: подготовка лаборатории. Профиль этой главы — PHP 8.3.35, PECL mysql_xdevapi 8.0.30 и MySQL 8.4.13. Параметры подключения берутся из PHPMAN_MYSQLX_URI и PHPMAN_MYSQLX_SCHEMA; пароль не хранится в архиве. README каждого архива описывает запуск и необходимые права. Сценарии создают только собственные таблицы mysql142_… и удаляют их после чтения результата.
Сначала выполните CLI-файл из архива. result.html покажет фактические наблюдения, result.json сохранит значения, их типы и metadata, metrics.json зафиксирует проверки и очистку. Последняя программа дополнительно создаёт report.html — сам каталог, который можно открыть в браузере. Это разные файлы: техническая таблица помогает понять действие API, а каталог показывает его полезный результат. Проверка подключения и выполнение SQL происходят в PHP, не имитируются браузером.
В выбранной версии объект ColumnResult ссылается на внутреннее описание исходного Result. Держите $result живым до завершения всех getter-вызовов. Самостоятельный metadata.php превращает описания в обычный массив строк, чисел, bool и null, который затем безопасно сохраняется в JSON. Сохранение такого массива не означает сохранение работающего соединения.
Есть расхождения с краткими страницами Manual: фактический класс — ColumnResult; пустой TableSelect сохраняет описания полей, а пустой SQL SELECT в этой связке их не предоставляет. Для такого пустого экрана заголовки берутся из собственного контракта проекции. isNumberSigned/isPadded дают bool, а имена кодировки числовых полей могут быть null. Официальные сигнатуры сохранены в справочнике ниже, фактические различия отмечены явно. Эти сведения относятся к названному профилю и не объявляются контрактом любого MySQL-коннектора.
Связать имя в таблице, псевдоним и значение товара
Для карточки товара база хранит name, а интерфейс ожидает product_name. Нужно увидеть, какое имя поменялось и по какому ключу искать значение. Получим не просто список свойств объекта, а три строки соответствия вместе с реальными id, названием и ценой.
- В таблице: id=1, name=Светильник, price=12.34
- SELECT назначает product_id, product_name, unit_price
- getColumns возвращает 3 описания, не читая товар
- getColumnName даёт name; getColumnLabel даёт product_name
- fetchOne даёт строку; по label читаем Светильник
Сначала короткое действие
$result = $table->select('name AS product_name')->execute();
$columns = $result->getColumns();
$row = $result->fetchOne();
echo $columns[0]->getColumnName(); // name
echo $columns[0]->getColumnLabel(); // product_name
echo $row['product_name']; // СветильникAS назначает имя только в результате этого SELECT. Исходная колонка на сервере не переименовывается. $columns — нумерованный массив объектов описания, поэтому первый элемент имеет индекс 0. $row — ассоциативный массив данных одной строки: ключом является label. $result сохраняется в переменной всё время чтения metadata. В полной программе сначала создаётся собственная таблица и одна строка; SQL с фиксированными исходными значениями служит только подготовкой лаборатории.
Что получится
| Наблюдение | Результат |
|---|---|
| id → product_id | 1 |
| name → product_name | Светильник |
| price → unit_price | 12.34 |
| Класс описания первого столбца | mysql_xdevapi\ColumnResult |
| Ключи строки | product_id, product_name, unit_price |
Самостоятельный файл и архив для запуска
<?php
declare(strict_types=1);
require __DIR__.'/lab.php';
require __DIR__.'/metadata.php';
$config = x142Config();
$session = mysql_xdevapi\getSession($config['uri']);
$schema = null;
$owned = [];
$removed = [];
$rows = $checks = $observations = [];
try {
$schema = $session->getSchema($config['schema']);
if (!$schema->existsInDatabase()) {
throw new RuntimeException('Учебная схема не существует: подготовьте её отдельно.');
}
$runtime = ['php'=>PHP_VERSION, 'extension'=>phpversion('mysql_xdevapi'),
'server_version'=>$session->getServerVersion(), 'schema'=>$config['schema'],
'host'=>$config['host'], 'port'=>$config['port']];
$name = x142OwnTable($session,$schema,$owned,'labels',
'(id INT PRIMARY KEY, name VARCHAR(40) NOT NULL, price DECIMAL(10,2) NOT NULL) ENGINE=InnoDB');
$qualified = x142Qualified($schema,$name);
$table = $schema->getTable($name);
$session->sql("INSERT INTO ".$qualified." (id,name,price) VALUES (1,'Светильник',12.34)")->execute();
$result = $table->select('id AS product_id','name AS product_name','price AS unit_price')->execute();
$columns = $result->getColumns();
$meta = metadata142Columns($columns);
$row = $result->fetchOne();
$rows = [];
foreach ($meta as $column) {
$label = $column['label'];
$rows[] = [$column['name'].' → '.$label,$row[$label]];
}
$rows[] = ['Класс описания первого столбца',$meta[0]['class']];
$rows[] = ['Ключи строки',implode(', ',array_keys($row))];
$checks = ['metadata_classes'=>array_column($meta,'class') === array_fill(0,3,'mysql_xdevapi\\ColumnResult'),
'original_names'=>array_column($meta,'name') === ['id','name','price'],
'labels'=>array_column($meta,'label') === ['product_id','product_name','unit_price'],
'row_keys'=>array_keys($row) === array_column($meta,'label'),
'row_values'=>$row === ['product_id'=>1,'product_name'=>'Светильник','unit_price'=>'12.34'],
'metadata_no_read'=>$result->fetchOne() === null];
$observations = ['metadata'=>$meta,'row'=>$row,'value_types'=>array_map('get_debug_type',$row)];
} finally {
try {
if ($schema instanceof mysql_xdevapi\Schema) {
$removed = x142Cleanup($schema, $owned);
}
} finally {
$session->close();
}
}
x142Report('mysql142-x-metadata-column-labels', 'Связать имя в таблице, псевдоним и значение товара', $rows, $checks, $observations, $runtime, $owned, $removed);
Ожидаемый результат
id → product_id — 1
name → product_name — Светильник
price → unit_price — 12.34
Класс описания первого столбца — mysql_xdevapi\ColumnResult
Ключи строки — product_id, product_name, unit_price
Выполните PHP-файл в отдельной лаборатории по README. lab.php создаёт и удаляет только собственные объекты; metadata.php сохраняет описание колонок, пока жив исходный Result. Откройте result.html и result.json; в последней программе также report.html.
Наблюдения сохранённого запуска · Значения, типы и metadata.
Границы и ошибки
В PECL 8.0.30 фактически получен ColumnResult. На странице RowResult::getColumns Manual до сих пор указан FieldMetadata: старое описание нельзя принимать за класс текущего объекта. Не путайте label с русским текстом интерфейса: надпись «Товар» можно задать отдельным словарём. Для пустого ответа fetchOne даёт null; обращение к $row[label] допускается только после проверки наличия строки. Такой случай разобран далее.
Попробуйте изменить
Измените только AS product_name на AS caption. Что останется прежним, что нужно поменять при чтении строки?
Ожидаемое решение
Исходное имя name и сохранённое значение Светильник останутся прежними. Метка, ключ результата и обращение к $row должны стать caption; иначе код обратится к отсутствующему ключу.
Показать происхождение полей после JOIN
В отчёте нужны название товара и название категории. Обе таблицы содержат колонку name; без ясных aliases читателю и коду будет трудно различить их. Покажем товар вместе с категорией и ценой двух штук, а затем объясним, откуда пришло каждое поле.
- Категория 7 → Освещение
- Товар 1 хранит category_id=7 и name=Светильник
- JOIN связывает p.category_id с c.id
- p.name → product_name, c.name → category_name
- p.price * 2 → pair_price: 24.68, без одного исходного столбца
Сначала короткое действие
$sql = 'SELECT p.name AS product_name,c.name AS category_name '
.'FROM '.$productQualified.' AS p '
.'JOIN '.$categoryQualified.' AS c ON c.id=p.category_id WHERE p.id=?';
$result = $session->sql($sql)->bind(1)->execute();
$columns = $result->getColumns();
echo $columns[0]->getTableLabel(); // p
echo $columns[0]->getColumnName(); // nameВ JOIN буквы p и c — короткие роли таблиц в этом запросе. ON определяет соответствие строк, а AS у колонки задаёт имя ключа ответа. getTableLabel читает роль p; getTableName возвращает настоящее созданное имя таблицы. Вместе со схемой и исходным именем поля это объясняет прямое происхождение. В полном архиве внешний ключ дополнительно проверяет, что category_id ссылается на существующую категорию.
Что получится
| Наблюдение | Результат |
|---|---|
| Товар | Светильник |
| Категория | Освещение |
| Цена пары | 24.68 |
| product_name: роль и поле | p.name |
| category_name: роль и поле | c.name |
| Исходная таблица товара определена | да |
| Исходная таблица категории определена | да |
| Обе прямые колонки из учебной схемы | да |
| pair_price: источник | Вычисление, единого исходного поля нет |
Самостоятельный файл и архив для запуска
<?php
declare(strict_types=1);
require __DIR__.'/lab.php';
require __DIR__.'/metadata.php';
$config = x142Config();
$session = mysql_xdevapi\getSession($config['uri']);
$schema = null;
$owned = [];
$removed = [];
$rows = $checks = $observations = [];
try {
$schema = $session->getSchema($config['schema']);
if (!$schema->existsInDatabase()) {
throw new RuntimeException('Учебная схема не существует: подготовьте её отдельно.');
}
$runtime = ['php'=>PHP_VERSION, 'extension'=>phpversion('mysql_xdevapi'),
'server_version'=>$session->getServerVersion(), 'schema'=>$config['schema'],
'host'=>$config['host'], 'port'=>$config['port']];
$categoryName = x142OwnTable($session,$schema,$owned,'categories',
'(id INT PRIMARY KEY, name VARCHAR(40) NOT NULL) ENGINE=InnoDB');
$categoryQualified = x142Qualified($schema,$categoryName);
$productName = x142OwnTable($session,$schema,$owned,'products',
'(id INT PRIMARY KEY, category_id INT NOT NULL, name VARCHAR(40) NOT NULL, price DECIMAL(10,2) NOT NULL, '
.'FOREIGN KEY (category_id) REFERENCES '.$categoryQualified.' (id)) ENGINE=InnoDB');
$productQualified = x142Qualified($schema,$productName);
$session->sql("INSERT INTO ".$categoryQualified." (id,name) VALUES (7,'Освещение')")->execute();
$session->sql("INSERT INTO ".$productQualified." (id,category_id,name,price) VALUES (1,7,'Светильник',12.34)")->execute();
$sql = 'SELECT p.name AS product_name,c.name AS category_name,p.price * 2 AS pair_price '
.'FROM '.$productQualified.' AS p JOIN '.$categoryQualified.' AS c ON c.id=p.category_id WHERE p.id=?';
$result = $session->sql($sql)->bind(1)->execute();
$columns = $result->getColumns();
$meta = metadata142Columns($columns);
$row = $result->fetchOne();
$rows = [['Товар',$row['product_name']],['Категория',$row['category_name']],['Цена пары',$row['pair_price']],
['product_name: роль и поле',$meta[0]['table_label'].'.'.$meta[0]['name']],
['category_name: роль и поле',$meta[1]['table_label'].'.'.$meta[1]['name']],
['Исходная таблица товара определена',$meta[0]['table_name'] === $productName],
['Исходная таблица категории определена',$meta[1]['table_name'] === $categoryName],
['Обе прямые колонки из учебной схемы',$meta[0]['schema'] === $config['schema'] && $meta[1]['schema'] === $config['schema']],
['pair_price: источник',$meta[2]['table_name'] === '' && $meta[2]['name'] === '' ? 'Вычисление, единого исходного поля нет' : 'Неожиданные метаданные']];
$checks = ['joined_row'=>$row === ['product_name'=>'Светильник','category_name'=>'Освещение','pair_price'=>'24.68'],
'unique_labels'=>array_column($meta,'label') === ['product_name','category_name','pair_price'],
'table_aliases'=>array_column($meta,'table_label') === ['p','c',''],
'column_origins'=>array_column($meta,'name') === ['name','name',''],
'physical_tables'=>$meta[0]['table_name'] === $productName && $meta[1]['table_name'] === $categoryName,
'physical_schema'=>$meta[0]['schema'] === $config['schema'] && $meta[1]['schema'] === $config['schema'],
'expression_origin_empty'=>$meta[2]['schema'] === '' && $meta[2]['table_name'] === ''];
$observations = ['metadata'=>$meta,'row'=>$row,'owned_tables'=>['p'=>$productName,'c'=>$categoryName]];
} finally {
try {
if ($schema instanceof mysql_xdevapi\Schema) {
$removed = x142Cleanup($schema, $owned);
}
} finally {
$session->close();
}
}
x142Report('mysql142-x-metadata-column-origins', 'Показать происхождение полей после JOIN', $rows, $checks, $observations, $runtime, $owned, $removed);
Ожидаемый результат
Товар — Светильник
Категория — Освещение
Цена пары — 24.68
product_name: роль и поле — p.name
category_name: роль и поле — c.name
Исходная таблица товара определена — да
Исходная таблица категории определена — да
Обе прямые колонки из учебной схемы — да
pair_price: источник — Вычисление, единого исходного поля нет
Выполните PHP-файл в отдельной лаборатории по README. lab.php создаёт и удаляет только собственные объекты; metadata.php сохраняет описание колонок, пока жив исходный Result. Откройте result.html и result.json; в последней программе также report.html.
Наблюдения сохранённого запуска · Значения, типы и metadata.
Границы и ошибки
Происхождение вычисления не восстанавливается по последнему столбцу: price * 2 имеет собственный label, но пустые schema/table/name в metadata. Это нормальная граница результата, не повреждение базы. Для сложных вычислений зависимости задаются самим запросом. Metadata не даёт безопасного универсального способа превратить показанную ячейку в UPDATE: нужны ключ строки, права и предметные правила. Обе таблицы собственной лаборатории удаляются в обратном порядке, сначала дочерняя.
После SQL-псевдонимов разработчик видит имена результата, но хочет узнать исходные таблицу и колонку. Реальная задача на Stack Overflow. В X DevAPI отдельно читаем label и name из ColumnResult и не пытаемся восстановить SQL регулярным выражением. Вопрос относится к PDO; его ответы не задают контракт этого расширения.
Попробуйте изменить
Добавьте ещё один товар той же категории и замените WHERE по id на ORDER BY p.id. Какие имена metadata изменятся?
Ожидаемое решение
Количество строк станет два, а состав metadata прежний: три столбца, те же исходные имена и aliases. Метаданные описывают форму всех строк, а не отдельный товар.
Сохранить точную цену, большой ID и отсутствие значения
Экспорт учёта должен сохранить точную цену и длинный внешний идентификатор. Если привести все похожие на число строки к float, можно изменить значимые цифры. Получим отрицательную корректировку, unsigned остаток, DECIMAL цену, большой ID и NULL — с их фактическими типами PHP.
- adjustment=-2 → int, signed true
- stock=4 → int, signed false
- DECIMAL(12,2) → строка 1234567890.12
- BIGINT UNSIGNED → строка 18446744073709551615
- SQL NULL → PHP null, не ноль и не пустая строка
Сначала короткое действие
$columns = $result->getColumns();
$row = $result->fetchOne();
echo $columns[2]->getFractionalDigits(); // 2
echo $row['price']; // 1234567890.12
echo get_debug_type($row['price']); // stringDECIMAL(12,2) задаёт всего 12 десятичных цифр, из них 2 после точки. Это точное SQL-значение; возвращённая строка сохраняет его написание без промежуточного float. BIGINT UNSIGNED допускает число больше максимального PHP int в этом 64-битном профиле, поэтому ID тоже остаётся строкой. isNumberSigned описывает допустимый знак типа, а не проверяет, отрицательна ли текущая ячейка. getType сравнивается с MYSQLX_TYPE_*; get_debug_type читает тип уже полученного PHP-значения.
Что получится
| Наблюдение | Результат |
|---|---|
| Корректировка | -2 |
| Тип PHP корректировки | int |
| Корректировка signed | да |
| Остаток | 4 |
| Остаток signed | нет |
| Точная цена | 1234567890.12 |
| Тип PHP цены | string |
| Дробных знаков | 2 |
| Внешний ID | 18446744073709551615 |
| Тип PHP внешнего ID | string |
| Примечание | NULL: значение не задано |
| Тип PHP примечания | null |
Самостоятельный файл и архив для запуска
<?php
declare(strict_types=1);
require __DIR__.'/lab.php';
require __DIR__.'/metadata.php';
$config = x142Config();
$session = mysql_xdevapi\getSession($config['uri']);
$schema = null;
$owned = [];
$removed = [];
$rows = $checks = $observations = [];
try {
$schema = $session->getSchema($config['schema']);
if (!$schema->existsInDatabase()) {
throw new RuntimeException('Учебная схема не существует: подготовьте её отдельно.');
}
$runtime = ['php'=>PHP_VERSION, 'extension'=>phpversion('mysql_xdevapi'),
'server_version'=>$session->getServerVersion(), 'schema'=>$config['schema'],
'host'=>$config['host'], 'port'=>$config['port']];
$name = x142OwnTable($session,$schema,$owned,'numbers',
'(adjustment INT NOT NULL, stock INT UNSIGNED NOT NULL, price DECIMAL(12,2) NOT NULL, '
.'external_id BIGINT UNSIGNED NOT NULL, note VARCHAR(20) NULL) ENGINE=InnoDB');
$qualified = x142Qualified($schema,$name);
$session->sql('INSERT INTO '.$qualified.' (adjustment,stock,price,external_id,note) '
.'VALUES (-2,4,1234567890.12,18446744073709551615,NULL)')->execute();
$result = $session->sql('SELECT adjustment,stock,price,external_id,note FROM '.$qualified)->execute();
$columns = $result->getColumns();
$meta = metadata142Columns($columns);
$row = $result->fetchOne();
$rows = [['Корректировка',$row['adjustment']],['Тип PHP корректировки',get_debug_type($row['adjustment'])],
['Корректировка signed',$meta[0]['signed']],['Остаток',$row['stock']],['Остаток signed',$meta[1]['signed']],
['Точная цена',$row['price']],['Тип PHP цены',get_debug_type($row['price'])],['Дробных знаков',$meta[2]['fractional_digits']],
['Внешний ID',$row['external_id']],['Тип PHP внешнего ID',get_debug_type($row['external_id'])],
['Примечание',$row['note'] === null ? 'NULL: значение не задано' : $row['note']],
['Тип PHP примечания',get_debug_type($row['note'])]];
$checks = ['signed_value'=>$row['adjustment'] === -2 && $meta[0]['signed'] === true,
'unsigned_value'=>$row['stock'] === 4 && $meta[1]['signed'] === false,
'integer_metadata'=>$meta[0]['type'] === MYSQLX_TYPE_INT && $meta[1]['type'] === MYSQLX_TYPE_INT,
'exact_decimal'=>$row['price'] === '1234567890.12' && $meta[2]['type'] === MYSQLX_TYPE_DECIMAL,
'decimal_scale'=>$meta[2]['fractional_digits'] === 2,
'large_id'=>$row['external_id'] === '18446744073709551615' && $meta[3]['type'] === MYSQLX_TYPE_BIGINT,
'null_preserved'=>array_key_exists('note',$row) && $row['note'] === null,
'flags_boolean'=>is_bool($meta[0]['signed']) && is_bool($meta[0]['padded']),
'numeric_charset_absent'=>$meta[2]['charset'] === null && $meta[2]['collation'] === null];
$observations = ['metadata'=>$meta,'row'=>$row,'php_types'=>array_map('get_debug_type',$row),
'type_constants'=>['INT'=>MYSQLX_TYPE_INT,'DECIMAL'=>MYSQLX_TYPE_DECIMAL,'BIGINT'=>MYSQLX_TYPE_BIGINT]];
} finally {
try {
if ($schema instanceof mysql_xdevapi\Schema) {
$removed = x142Cleanup($schema, $owned);
}
} finally {
$session->close();
}
}
x142Report('mysql142-x-metadata-numeric-shape', 'Сохранить точную цену, большой ID и отсутствие значения', $rows, $checks, $observations, $runtime, $owned, $removed);
Ожидаемый результат
Корректировка — -2
Тип PHP корректировки — int
Корректировка signed — да
Остаток — 4
Остаток signed — нет
Точная цена — 1234567890.12
Тип PHP цены — string
Дробных знаков — 2
Внешний ID — 18446744073709551615
Тип PHP внешнего ID — string
Примечание — NULL: значение не задано
Тип PHP примечания — null
Выполните PHP-файл в отдельной лаборатории по README. lab.php создаёт и удаляет только собственные объекты; metadata.php сохраняет описание колонок, пока жив исходный Result. Откройте result.html и result.json; в последней программе также report.html.
Наблюдения сохранённого запуска · Значения, типы и metadata.
Границы и ошибки
Не используйте JSON_NUMERIC_CHECK для такого экспорта: строковый ID и точная десятичная сумма должны сохранить контракт. Для расчётов задайте единицы и способ точной арифметики отдельно — например, целые копейки в подходящем диапазоне либо BCMath; этот урок не выполняет денежные вычисления через float. NULL отличается от пустого текста и нуля. Metadata числового поля не имеет текстовой кодировки в этом профиле: getters возвращают null, несмотря на краткие string-сигнатуры Manual.
Значение DECIMAL приходит строкой, и возникает желание автоматически превратить каждую такую строку во float. Реальная задача на Stack Overflow. Разделяем SQL-тип, фактический PHP-тип и формат показа. Точную десятичную сумму и большой ID сохраняем строками; числовое оформление не требует потери разрядов.
Попробуйте изменить
Замените NULL в note на пустую строку. Изменится ли SQL-тип столбца?
Ожидаемое решение
Столбец останется VARCHAR(20), но фактическое значение станет string длины 0. Строка отчёта должна отличать пустой текст от отсутствия значения; проверка null_preserved закономерно перестанет подходить.
Различить ширину колонки, символы и байты
При создании формы легко перепутать VARCHAR(12), значение getLength и длину уже введённого текста. Покажем все три вещи рядом: строка «Кот🙂» содержит четыре символа и занимает десять байтов UTF-8, а метаданные VARCHAR(12) этого профиля сообщают ширину 48.
- DDL разрешает 12 символов для label
- В строке Кот🙂: 3 кириллических символа и 1 emoji
- CHAR_LENGTH даёт 4, OCTET_LENGTH даёт 10
- getLength даёт 48 — ширину поля в полученных metadata
- CHAR(5) отдельно показывает rightpad; это не команда trim
Сначала короткое действие
$columns = $result->getColumns();
echo $columns[1]->getLength(); // 48
echo $columns[1]->getCharacterSetName(); // utf8mb4
echo $columns[1]->getCollationName(); // utf8mb4_binВ определении CHAR/VARCHAR число задаётся в символах. В наблюдаемом ответе MySQL 8.4.13 и PECL 8.0.30 getLength равен 5×4 и 12×4: серверная ширина учитывает максимальные 4 байта utf8mb4. Фактическая строка короче. CHAR_LENGTH считает символы SQL, OCTET_LENGTH — байты; визуально один знак может состоять из нескольких Unicode-кодов, поэтому это не универсальный счётчик пользовательских графем. Кодировка задаёт представление, collation — сравнение. Они не заменяют друг друга.
Что получится
| Наблюдение | Результат |
|---|---|
| Значение CHAR(5) | AB |
| Значение VARCHAR(12) | Кот🙂 |
| Ширина metadata CHAR(5) | 20 |
| Ширина metadata VARCHAR(12) | 48 |
| Символов в текущем label | 4 |
| Байтов в текущем label | 10 |
| Набор символов | utf8mb4 |
| Правило сравнения | utf8mb4_bin |
| CHAR: isPadded | да |
| VARCHAR: isPadded | нет |
Самостоятельный файл и архив для запуска
<?php
declare(strict_types=1);
require __DIR__.'/lab.php';
require __DIR__.'/metadata.php';
$config = x142Config();
$session = mysql_xdevapi\getSession($config['uri']);
$schema = null;
$owned = [];
$removed = [];
$rows = $checks = $observations = [];
try {
$schema = $session->getSchema($config['schema']);
if (!$schema->existsInDatabase()) {
throw new RuntimeException('Учебная схема не существует: подготовьте её отдельно.');
}
$runtime = ['php'=>PHP_VERSION, 'extension'=>phpversion('mysql_xdevapi'),
'server_version'=>$session->getServerVersion(), 'schema'=>$config['schema'],
'host'=>$config['host'], 'port'=>$config['port']];
$name = x142OwnTable($session,$schema,$owned,'text_width',
'(code CHAR(5) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL, '
.'label VARCHAR(12) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL) ENGINE=InnoDB');
$qualified = x142Qualified($schema,$name);
$session->sql('INSERT INTO '.$qualified.' (code,label) VALUES (?,?)')->bind('AB')->bind('Кот🙂')->execute();
$result = $session->sql('SELECT code,label,CHAR_LENGTH(label) AS characters,OCTET_LENGTH(label) AS bytes FROM '.$qualified)->execute();
$columns = $result->getColumns();
$meta = metadata142Columns($columns);
$row = $result->fetchOne();
$rows = [['Значение CHAR(5)',$row['code']],['Значение VARCHAR(12)',$row['label']],
['Ширина metadata CHAR(5)',$meta[0]['length']],['Ширина metadata VARCHAR(12)',$meta[1]['length']],
['Символов в текущем label',$row['characters']],['Байтов в текущем label',$row['bytes']],
['Набор символов',$meta[1]['charset']],['Правило сравнения',$meta[1]['collation']],
['CHAR: isPadded',$meta[0]['padded']],['VARCHAR: isPadded',$meta[1]['padded']]];
$checks = ['exact_text'=>$row['code'] === 'AB' && $row['label'] === 'Кот🙂',
'length_is_metadata'=>$meta[0]['length'] === 20 && $meta[1]['length'] === 48,
'value_characters'=>$row['characters'] === 4,'value_bytes'=>$row['bytes'] === 10,
'character_set'=>$meta[0]['charset'] === 'utf8mb4' && $meta[1]['charset'] === 'utf8mb4',
'collation'=>$meta[0]['collation'] === 'utf8mb4_bin' && $meta[1]['collation'] === 'utf8mb4_bin',
'padding_flags'=>$meta[0]['padded'] === true && $meta[1]['padded'] === false,
'text_type'=>$meta[0]['type'] === MYSQLX_TYPE_BYTES && $meta[1]['type'] === MYSQLX_TYPE_BYTES];
$observations = ['metadata'=>$meta,'row'=>$row,'declared_characters'=>['code'=>5,'label'=>12],
'php_bytes'=>strlen($row['label']),'type_constant_bytes'=>MYSQLX_TYPE_BYTES];
} finally {
try {
if ($schema instanceof mysql_xdevapi\Schema) {
$removed = x142Cleanup($schema, $owned);
}
} finally {
$session->close();
}
}
x142Report('mysql142-x-metadata-text-width', 'Различить ширину колонки, символы и байты', $rows, $checks, $observations, $runtime, $owned, $removed);
Ожидаемый результат
Значение CHAR(5) — AB
Значение VARCHAR(12) — Кот🙂
Ширина metadata CHAR(5) — 20
Ширина metadata VARCHAR(12) — 48
Символов в текущем label — 4
Байтов в текущем label — 10
Набор символов — utf8mb4
Правило сравнения — utf8mb4_bin
CHAR: isPadded — да
VARCHAR: isPadded — нет
Выполните PHP-файл в отдельной лаборатории по README. lab.php создаёт и удаляет только собственные объекты; metadata.php сохраняет описание колонок, пока жив исходный Result. Откройте result.html и result.json; в последней программе также report.html.
Наблюдения сохранённого запуска · Значения, типы и metadata.
Границы и ошибки
Не переносите это соотношение автоматически на числовые, двоичные или вычисленные поля. getLength не является готовым maxlength и не проверяет корректность ввода. В CHAR прочитано AB без добавленных пробелов, хотя isPadded true: флаг metadata и реальное значение — разные наблюдения. Не обрезайте значимые пробелы VARCHAR. Таблица задаёт свою collation; глобальные настройки сервера и sql_mode не меняются.
Ширина поля в metadata отличается от числа в CHAR(n), поэтому её ошибочно принимают за длину текущего значения. Реальная задача на Stack Overflow. Сравниваем объявленный размер, метаданные, CHAR_LENGTH и OCTET_LENGTH на одной строке. Проблема описана для C API: единицы X DevAPI проверяются отдельно, а не переносятся из ответа другого драйвера.
Попробуйте изменить
Замените Кот🙂 на ABCD. Какие наблюдения изменятся?
Ожидаемое решение
Символов останется 4, фактических байтов станет 4. Ширина metadata 48, объявленный VARCHAR(12), кодировка и collation останутся прежними: они описывают поле, а не одно его значение.
Показать заголовки, когда товаров нет
Менеджер ввёл фильтр, который не нашёл ни одного товара. Экран должен сохранить понятные заголовки и сказать, что совпадений нет. Если строить их из array_keys($rows[0]), код обратится к несуществующей строке. Сравним пустые TableSelect и SQL: в проверенном профиле первый сохраняет metadata, второй её не предоставляет. Поэтому заголовки также принадлежат явному контракту экрана.
- В таблице есть только товар id=1
- Фильтр id=999 → 0 строк в обоих ответах
- TableSelect сохраняет 2 описания; SQL getters дают [], [] и 0
- Фиксированная проекция sku/product_name → словарь Артикул/Товар
- Пустые данные → сообщение об отсутствии совпадений, не ошибка подключения
Сначала короткое действие
$displayColumns = ['sku'=>'Артикул','product_name'=>'Товар'];
$result = $session->sql('SELECT sku,name AS product_name FROM '.$qualified.' WHERE id=?')
->bind(999)->execute();
$names = $result->getColumnNames();
$labels = $names === [] ? array_keys($displayColumns) : $names;
$headers = array_map(static fn(string $label): string => $displayColumns[$label], $labels);
$rows = $result->fetchAll();
if ($rows === []) {
echo implode(', ', $headers), PHP_EOL;
echo 'По фильтру товары не найдены';
}getColumnsCount, getColumnNames и getColumns читают доступное описание результата, а не текст SQL. В связке PECL mysql_xdevapi 8.0.30/MySQL 8.4.13 нулевой TableSelect сохраняет две колонки; такой же SQL SELECT возвращает count=0, names=[] и metadata=[]. Обе выборки данных — []. Словарь $displayColumns заранее соответствует явному SELECT и сохраняет человеческие заголовки. Полная программа отдельно фиксирует отсутствие SQL metadata, выбор fallback и отображаемые заголовки; не подменяет пустую metadata придуманными ColumnResult.
В коротком фрагменте $qualified — доверенное полное имя собственной таблицы: `схема`.`таблица`. Полный файл получает его через x142Qualified после создания таблицы. Получение объекта Schema само по себе не выбирает SQL default database; пользовательский текст в имя не подставляется.
Что получится
| Наблюдение | Результат |
|---|---|
| Заголовки TableSelect | sku, product_name |
| Колонок TableSelect | 2 |
| Строк TableSelect | 0 |
| Имена полей SQL | Не предоставлены: [] |
| Колонок SQL | 0 |
| Строк SQL | 0 |
| Сообщение экрана | По фильтру товары не найдены |
| Описаний SQL | 0 |
| Класс metadata TableSelect | mysql_xdevapi\ColumnResult |
| Заголовки экрана | Артикул, Товар |
| Взяты из контракта проекции | да |
Экран оставляет заголовки «Артикул» и «Товар», а под ними — «По фильтру товары не найдены». При отсутствии SQL metadata их сохраняет собственный словарь фиксированной проекции; первой строки данных в этом сценарии нет.
Самостоятельный файл и архив для запуска
<?php
declare(strict_types=1);
require __DIR__.'/lab.php';
require __DIR__.'/metadata.php';
$config = x142Config();
$session = mysql_xdevapi\getSession($config['uri']);
$schema = null;
$owned = [];
$removed = [];
$rows = $checks = $observations = [];
try {
$schema = $session->getSchema($config['schema']);
if (!$schema->existsInDatabase()) {
throw new RuntimeException('Учебная схема не существует: подготовьте её отдельно.');
}
$runtime = ['php'=>PHP_VERSION, 'extension'=>phpversion('mysql_xdevapi'),
'server_version'=>$session->getServerVersion(), 'schema'=>$config['schema'],
'host'=>$config['host'], 'port'=>$config['port']];
$name = x142OwnTable($session,$schema,$owned,'empty_screen',
'(id INT PRIMARY KEY, sku VARCHAR(12) NOT NULL, name VARCHAR(40) NOT NULL) ENGINE=InnoDB');
$qualified = x142Qualified($schema,$name);
$table = $schema->getTable($name);
$session->sql("INSERT INTO ".$qualified." (id,sku,name) VALUES (1,'LED','Светильник')")->execute();
$rowResult = $table->select('sku','name AS product_name')->where('id = :id')->bind(['id'=>999])->execute();
$rowColumns = $rowResult->getColumns();
$rowMeta = metadata142Columns($rowColumns);
$rowNames = $rowResult->getColumnNames();
$rowCount = $rowResult->getColumnsCount();
$rowData = $rowResult->fetchAll();
$sqlResult = $session->sql('SELECT sku,name AS product_name FROM '.$qualified.' WHERE id=?')->bind(999)->execute();
$sqlColumns = $sqlResult->getColumns();
$sqlMeta = metadata142Columns($sqlColumns);
$sqlNames = $sqlResult->getColumnNames();
$sqlCount = $sqlResult->getColumnsCount();
$sqlData = $sqlResult->fetchAll();
$displayColumns = ['sku'=>'Артикул','product_name'=>'Товар'];
$expectedLabels = array_keys($displayColumns);
$fallbackUsed = $sqlNames === [];
$displayLabels = $fallbackUsed ? $expectedLabels : $sqlNames;
if ($displayLabels !== $expectedLabels) {
throw new RuntimeException('Неожиданные поля экрана');
}
$headers = array_map(static fn(string $label): string => $displayColumns[$label],$displayLabels);
$message = $sqlData === [] ? 'По фильтру товары не найдены' : 'Найдены товары';
$rows = [['Заголовки TableSelect',implode(', ',$rowNames)],['Колонок TableSelect',$rowCount],
['Строк TableSelect',count($rowData)],['Имена полей SQL',$sqlNames === [] ? 'Не предоставлены: []' : implode(', ',$sqlNames)],
['Колонок SQL',$sqlCount],['Строк SQL',count($sqlData)],['Сообщение экрана',$message],
['Описаний SQL',count($sqlMeta)],['Класс metadata TableSelect',$rowMeta[0]['class']],
['Заголовки экрана',implode(', ',$headers)],['Взяты из контракта проекции',$fallbackUsed]];
$checks = ['row_empty'=>$rowData === [],'sql_empty'=>$sqlData === [],
'row_names_available'=>$rowNames === ['sku','product_name'],
'row_count_available'=>$rowCount === 2,
'row_metadata_available'=>count($rowMeta) === 2,
'sql_names_absent'=>$sqlNames === [],'sql_count_zero'=>$sqlCount === 0,'sql_metadata_absent'=>$sqlMeta === [],
'row_labels_match'=>array_column($rowMeta,'label') === $expectedLabels,
'real_class'=>$rowMeta[0]['class'] === 'mysql_xdevapi\\ColumnResult',
'explicit_fallback'=>$fallbackUsed === true && $displayLabels === $expectedLabels,
'headers_preserved'=>$headers === ['Артикул','Товар'],
'clear_empty_message'=>$message === 'По фильтру товары не найдены'];
$observations = ['row_metadata'=>$rowMeta,'sql_metadata'=>$sqlMeta,'row_rows'=>$rowData,'sql_rows'=>$sqlData,
'row_names'=>$rowNames,'sql_names'=>$sqlNames,'row_count'=>$rowCount,'sql_count'=>$sqlCount,
'expected_projection'=>$expectedLabels,'display_labels'=>$displayLabels,'headers'=>$headers,
'fallback_used'=>$fallbackUsed,'message'=>$message];
} finally {
try {
if ($schema instanceof mysql_xdevapi\Schema) {
$removed = x142Cleanup($schema, $owned);
}
} finally {
$session->close();
}
}
x142Report('mysql142-x-metadata-empty-screen', 'Показать заголовки, когда товаров нет', $rows, $checks, $observations, $runtime, $owned, $removed);
Ожидаемый результат
Заголовки TableSelect — sku, product_name
Колонок TableSelect — 2
Строк TableSelect — 0
Имена полей SQL — Не предоставлены: []
Колонок SQL — 0
Строк SQL — 0
Сообщение экрана — По фильтру товары не найдены
Описаний SQL — 0
Класс metadata TableSelect — mysql_xdevapi\ColumnResult
Заголовки экрана — Артикул, Товар
Взяты из контракта проекции — да
Выполните PHP-файл в отдельной лаборатории по README. lab.php создаёт и удаляет только собственные объекты; metadata.php сохраняет описание колонок, пока жив исходный Result. Откройте result.html и result.json; в последней программе также report.html.
Наблюдения сохранённого запуска · Значения, типы и metadata.
Границы и ошибки
Это наблюдаемое различие конкретного профиля, не универсальная гарантия всех MySQL-драйверов. RowResult читает metadata из результата напрямую, а SqlStatementResult в исходнике 8.0.30 ищет её в rowset. Manual обещает пустую metadata для нулевого RowResult, однако именно TableSelect здесь её сохраняет. Не обращайтесь к $sqlMeta[0], если массив пуст. Fallback подходит фиксированной проекции экрана, не произвольному пользовательскому SQL. Ошибка соединения/синтаксиса не должна превращаться в сообщение «товаров нет».
Попробуйте изменить
Измените фильтр на id=1. Какие части результата останутся прежними?
Ожидаемое решение
Договорённые поля sku/product_name и заголовки Артикул/Товар останутся прежними; в обоих массивах данных появится строка. У непустого SQL-ответа появятся доступные metadata, имена и count=2, fallback не потребуется. Проверки, специально ожидающие пустой ответ, нужно заменить проверками нового сценария; сообщение «товаров нет» больше не показывается.
Собрать настоящий HTML-каталог по проверенным колонкам
Теперь результат нужен человеку: открыть каталог и увидеть товар, точную цену, остаток и примечание. Получим только четыре нужных поля и две строки, проверим их структуру и сохраним отдельный report.html. В названии первого товара специально есть <script> и &: они должны читаться как текст. Второй товар показывает разницу между нулём, пустой строкой и SQL NULL.
- SELECT задаёт 4 разрешённых поля и не более 2 строк
- metadata проверяет aliases, тип цены и scale 2
- словарь интерфейса задаёт надписи и правила показа
- каждая реальная строка проходит formatter без float
- htmlspecialchars выводит текст ячеек безопасно
- report.html открывается как готовый каталог
Сначала короткое действие
$columns = $result->getColumns();
$priceColumn = $columns[1];
$rawRows = $result->fetchAll();
$priceText = str_replace('.', ',', $rawRows[0]['unit_price']);
echo htmlspecialchars($priceText, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); // 12,30Сначала строка DECIMAL 12.30 становится строкой 12,30 заменой разделителя. Ни арифметики, ни float для этого не нужно. В полном файле metadata подтверждает DECIMAL и два дробных знака, а функция formatter проверяет фактическое значение. Словарь $displayColumns связывает alias с человеческим заголовком и видом ячейки. Вложенный foreach проходит по товарам и выбранным полям; он обрабатывает уже ограниченный SQL-ответ. SQL NULL проверяется раньше пустого текста. Остаток 0 сохраняется как «0», поэтому здесь не применяется empty().
Что получится
| Наблюдение | Результат |
|---|---|
| Товаров в HTML | 2 |
| Название первого товара | Лампа <script> & свет |
| Цена без float | 12,30 |
| Нулевая цена | 0,00 |
| Нулевой остаток | 0 |
| SQL NULL | — (нет значения) |
| Пустой текст | (пустая строка) |
| HTML-символы выведены текстом | да |
| Полезный файл | report.html |
Сам каталог
| Товар | Цена, руб. | Остаток | Примечание |
|---|---|---|---|
| Лампа <script> & свет | 12,30 | 4 | — (нет значения) |
| Кабель | 0,00 | 0 | (пустая строка) |
Эта таблица показывает ожидаемые ячейки. Открыть отдельный HTML-каталог, полученный программой — полный сохранённый report.html с результатом чтения базы.
Самостоятельный файл и архив для запуска
<?php
declare(strict_types=1);
require __DIR__.'/lab.php';
require __DIR__.'/metadata.php';
$config = x142Config();
$session = mysql_xdevapi\getSession($config['uri']);
$schema = null;
$owned = [];
$removed = [];
$rows = $checks = $observations = [];
try {
$schema = $session->getSchema($config['schema']);
if (!$schema->existsInDatabase()) {
throw new RuntimeException('Учебная схема не существует: подготовьте её отдельно.');
}
$runtime = ['php'=>PHP_VERSION, 'extension'=>phpversion('mysql_xdevapi'),
'server_version'=>$session->getServerVersion(), 'schema'=>$config['schema'],
'host'=>$config['host'], 'port'=>$config['port']];
require __DIR__.'/report-renderer.php';
$name = x142OwnTable($session,$schema,$owned,'report',
'(id INT PRIMARY KEY, name VARCHAR(80) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL, '
.'price DECIMAL(10,2) NOT NULL, stock INT UNSIGNED NOT NULL, note VARCHAR(40) NULL) ENGINE=InnoDB');
$qualified = x142Qualified($schema,$name);
$session->sql('INSERT INTO '.$qualified.' (id,name,price,stock,note) VALUES (?,?,?,?,?)')
->bind(1)->bind('Лампа <script> & свет')->bind('12.30')->bind(4)->bind(null)->execute();
$session->sql('INSERT INTO '.$qualified.' (id,name,price,stock,note) VALUES (?,?,?,?,?)')
->bind(2)->bind('Кабель')->bind('0.00')->bind(0)->bind('')->execute();
$result = $session->sql('SELECT p.name AS product_name,p.price AS unit_price,p.stock AS stock,p.note AS note '
.'FROM '.$qualified.' AS p ORDER BY p.id LIMIT 2')->execute();
$displayColumns = [
'product_name'=>['title'=>'Товар','kind'=>'text'],
'unit_price'=>['title'=>'Цена, руб.','kind'=>'money'],
'stock'=>['title'=>'Остаток','kind'=>'integer'],
'note'=>['title'=>'Примечание','kind'=>'text'],
];
$columns = $result->getColumns();
$meta = metadata142Columns($columns);
$names = $result->getColumnNames();
if ($names !== array_keys($displayColumns) || $result->getColumnsCount() !== count($displayColumns)) {
throw new RuntimeException('Изменилась структура отчёта');
}
$priceColumn = $columns[1];
if ($priceColumn->getType() !== MYSQLX_TYPE_DECIMAL || $priceColumn->getFractionalDigits() !== 2) {
throw new RuntimeException('Изменился тип или точность цены');
}
$rawRows = $result->fetchAll();
$reportColumns = array_column($displayColumns,'title');
$reportRows = [];
foreach ($rawRows as $rawRow) {
$displayRow = [];
foreach ($displayColumns as $label=>$settings) {
$displayRow[] = metadata142Display($rawRow[$label],$settings['kind']);
}
$reportRows[] = $displayRow;
}
$html = metadata142Html($reportColumns,$reportRows);
if (file_put_contents(__DIR__.'/report.html',$html) === false) {
throw new RuntimeException('Не удалось записать report.html');
}
$expectedReportRows = [
['Лампа <script> & свет','12,30','4','— (нет значения)'],
['Кабель','0,00','0','(пустая строка)'],
];
$rows = [['Товаров в HTML',count($reportRows)],['Название первого товара',$reportRows[0][0]],
['Цена без float',$reportRows[0][1]],['Нулевая цена',$reportRows[1][1]],
['Нулевой остаток',$reportRows[1][2]],['SQL NULL',$reportRows[0][3]],
['Пустой текст',$reportRows[1][3]],['HTML-символы выведены текстом',!str_contains($html,'<script>')],
['Полезный файл','report.html']];
$checks = ['exact_report_rows'=>$reportRows === $expectedReportRows,
'header_labels'=>$reportColumns === ['Товар','Цена, руб.','Остаток','Примечание'],
'raw_values'=>$rawRows === [
['product_name'=>'Лампа <script> & свет','unit_price'=>'12.30','stock'=>4,'note'=>null],
['product_name'=>'Кабель','unit_price'=>'0.00','stock'=>0,'note'=>'']],
'real_origins'=>$meta[0]['schema'] === $config['schema'] && $meta[0]['table_name'] === $name && $meta[0]['name'] === 'name',
'money_string'=>get_debug_type($rawRows[0]['unit_price']) === 'string',
'zero_preserved'=>$reportRows[1][2] === '0',
'null_and_empty_distinct'=>$reportRows[0][3] !== $reportRows[1][3],
'escaped_payload'=>str_contains($html,'Лампа <script> & свет') && !str_contains($html,'<script>'),
'file_written'=>is_file(__DIR__.'/report.html') && file_get_contents(__DIR__.'/report.html') === $html];
$observations = ['metadata'=>$meta,'column_names'=>$names,'raw_rows'=>$rawRows,
'report_columns'=>$reportColumns,'report_rows'=>$reportRows,'report_file'=>'report.html'];
} finally {
try {
if ($schema instanceof mysql_xdevapi\Schema) {
$removed = x142Cleanup($schema, $owned);
}
} finally {
$session->close();
}
}
x142Report('mysql142-x-metadata-rendered-report', 'Собрать настоящий HTML-каталог по проверенным колонкам', $rows, $checks, $observations, $runtime, $owned, $removed);
Ожидаемый результат
Товаров в HTML — 2
Название первого товара — Лампа <script> & свет
Цена без float — 12,30
Нулевая цена — 0,00
Нулевой остаток — 0
SQL NULL — — (нет значения)
Пустой текст — (пустая строка)
HTML-символы выведены текстом — да
Полезный файл — report.html
Выполните PHP-файл в отдельной лаборатории по README. lab.php создаёт и удаляет только собственные объекты; metadata.php сохраняет описание колонок, пока жив исходный Result. Откройте result.html и result.json; в последней программе также report.html.
Наблюдения сохранённого запуска · Значения, типы и metadata.
Границы и ошибки
Это formatter конкретного отчёта: он не выбирает валюту из SQL-типа и не обещает автоматически показать любой SQL-тип. Для денежных расчётов требуется отдельно выбранная точная арифметика, для локализации — правила приложения. HTML-экранирование выполняется в момент вывода текста, а SQL bind — в момент передачи значений серверу: одно не заменяет другое. Metadata не используется для построения произвольного UPDATE. Таблица прокручивается внутри контейнера на узком экране; исходный JSON сохраняет реальные типы и происхождение колонок.
Попробуйте изменить
Добавьте третьему товару примечание «Проверить <упаковку>». Что должно появиться в HTML и JSON?
Ожидаемое решение
Сначала увеличьте SQL LIMIT до нужного размера и добавьте строку. В JSON останется исходный текст с угловыми скобками. В HTML-коде будут < и >, а человек увидит буквальные <упаковку>. Новое значение не должно становиться HTML-тегом; перечень четырёх колонок остаётся прежним.
Выбрать описание колонки под задачу
| API | Для чего |
|---|---|
| ColumnResult | Понять, что означает столбец отчёта, до чтения его значений. |
| RowResult::getColumns | Получить описание колонок выбранного через TableSelect экрана. |
| SqlStatementResult::getColumns | Получить описание колонок обычного SQL SELECT. |
| SqlStatementResult::getColumnsCount | Узнать ширину результата независимо от количества товаров. |
| SqlStatementResult::getColumnNames | Получить ключи полей SQL-ответа в порядке проекции. |
| ColumnResult::getColumnLabel | Узнать имя поля именно в полученном ответе. |
| ColumnResult::getColumnName | Прочитать исходное имя поля до назначения псевдонима. |
| ColumnResult::getTableName | Узнать исходную таблицу прямого поля. |
| ColumnResult::getTableLabel | Узнать псевдоним таблицы, использованный запросом. |
| ColumnResult::getSchemaName | Узнать схему происхождения прямого поля результата. |
| ColumnResult::getType | Различить тип поля в протоколе от значения и типа переменной PHP. |
| ColumnResult::isNumberSigned | Понять, допускает ли числовой столбец отрицательные значения. |
| ColumnResult::getFractionalDigits | Прочитать объявленную дробную точность числового результата. |
| ColumnResult::getLength | Прочитать описанную сервером ширину поля, не длину текущей строки. |
| ColumnResult::getCharacterSetName | Узнать набор символов поля результата. |
| ColumnResult::getCollationName | Узнать правило сравнения, связанное с полем результата. |
| ColumnResult::isPadded | Узнать, обозначено ли правое дополнение поля в metadata. |
От формы ответа к предупреждениям и квитанциям
Теперь известны заголовки, происхождение и типы выбранных полей; можно показать настоящий отчёт и корректный пустой экран. Следующий шаг — отличить успешную операцию, предупреждение и следующий набор результатов. Для запросов под экран вернитесь к проекции и ограничению данных, а к чтению обычных строк — в каталог через таблицы и SQL.
Функции и методы этой темы
Откройте карточку, чтобы увидеть договор функции, примеры, ограничения и связанные инструменты.
mysql_xdevapi\ColumnResultПонять, что означает столбец отчёта, до чтения его значений.
Класс указан в PHP Manual начиная с PECL mysql_xdevapi 8.0.11. · mysql_xdevapi
class mysql_xdevapi\ColumnResultПонять, что означает столбец отчёта, до чтения его значений.
- SELECT name AS product_name
- Result хранит описание столбца и строки
- getColumns()[0] → ColumnResult
- label product_name связывается с ключом строки; name объясняет происхождение
Запись, вход и результат
class mysql_xdevapi\ColumnResultКласс получают из результата, он не принимает данные товара для сохранения.
Возвращаемое значение. Один объект описывает один столбец конкретного результата: имя и псевдоним, происхождение, тип, длину и другие свойства. Это не товар и не вся таблица базы.
Два применения
Подписать одну колонку каталога.
$result = $table->select('name AS product_name')->execute();
$columns = $result->getColumns();
echo $columns[0]->getColumnLabel();Исходные данные, полный запуск и результат.
Разобрать происхождение полей после JOIN.
$columns = $result->getColumns();
foreach ($columns as $column) {
echo $column->getColumnLabel(), ' ← ', $column->getColumnName(), PHP_EOL;
}Исходные данные, полный запуск и результат.
Условия и ограничения
Метаданные описывают проекцию запроса, не всю DDL-схему. Они не доказывают право записи, уникальность строк или смысл денег. В 8.0.30 ColumnResult содержит ссылку на внутренние данные результата: держите исходный $result живым, пока вызываете getters. Для дальнейшей работы сохраните обычные строки/числа metadata в массив. Не получайте объекты из временного execute()->getColumns(), если Result сразу уничтожается.
Объект создаёт расширение при чтении описания столбцов. Приложение получает его через getColumns, а не через new ColumnResult. Конструктор закрыт в реализации 8.0.30; отдельный узел официального оглавления сохранён ниже.
class mysql_xdevapi\ColumnResult {
public function getCharacterSetName(): string;
public function getCollationName(): string;
public function getColumnLabel(): string;
public function getColumnName(): string;
public function getFractionalDigits(): int;
public function getLength(): int;
public function getSchemaName(): string;
public function getTableLabel(): string;
public function getTableName(): string;
public function getType(): int;
public function isNumberSigned(): int;
public function isPadded(): int;
}Это полный состав методов официального обзора, а не исполняемое повторное определение встроенного класса. В краткой документации isNumberSigned/isPadded указаны как int, хотя описание обещает true и реализация возвращает bool. Эти расхождения разобраны в соответствующих карточках.
- ColumnResult::__construct
- ColumnResult::getCharacterSetName
- ColumnResult::getCollationName
- ColumnResult::getColumnLabel
- ColumnResult::getColumnName
- ColumnResult::getFractionalDigits
- ColumnResult::getLength
- ColumnResult::getSchemaName
- ColumnResult::getTableLabel
- ColumnResult::getTableName
- ColumnResult::getType
- ColumnResult::isNumberSigned
- ColumnResult::isPadded
Версии. Класс указан в PHP Manual начиная с PECL mysql_xdevapi 8.0.11.
mysql_xdevapi\RowResult::getColumnsПолучить описание колонок выбранного через TableSelect экрана.
Страница метода не указывает первую версию. Здесь рассматривается PECL mysql_xdevapi 8.0.30; версия PHP и сервера записана в результатах. · mysql_xdevapi
RowResult::getColumns(): arrayПолучить описание колонок выбранного через TableSelect экрана.
- TableSelect выбирает sku и name AS product_name
- execute возвращает RowResult
- getColumns описывает 2 колонки
- fetchAll отдельно читает значения товаров
Запись, вход и результат
RowResult::getColumns(): arrayМетоды этой карточки вызываются без параметров.
Возвращаемое значение. Массив описаний столбцов проекции. Manual называет объекты FieldMetadata и обещает [] при пустом наборе строк. В проверяемой связке PECL 8.0.30 и MySQL 8.4.13 TableSelect возвращает ColumnResult и сохраняет описания при нуле строк. У SqlStatementResult пустой SELECT ведёт себя иначе; оба пути показаны отдельно.
Два применения
Сопоставить идентификатор и название товара с их описаниями.
$result = $table->select('id','name AS product_name')->execute();
$columns = $result->getColumns();Исходные данные, полный запуск и результат.
Оставить заголовки при отсутствии товаров, не обращаться к $rows[0].
$result = $table->select('sku','name AS product_name')->where('id = :id')->bind(['id'=>999])->execute();
$columns = $result->getColumns();
$rows = $result->fetchAll();Исходные данные, полный запуск и результат.
Условия и ограничения
Параметров нет. Получение metadata не является проверкой непустой выдачи. Не копируйте разрушительный DROP DATABASE из демонстрации Manual: наши архивы создают собственные объекты в готовой лаборатории и удаляют только их. Не объявляйте FieldMetadata и ColumnResult взаимозаменяемыми классами без проверки используемого расширения.
Версии. Страница метода не указывает первую версию. Здесь рассматривается PECL mysql_xdevapi 8.0.30; версия PHP и сервера записана в результатах.
mysql_xdevapi\SqlStatementResult::getColumnsПолучить описание колонок обычного SQL SELECT.
Страница метода не указывает первую версию. Здесь рассматривается PECL mysql_xdevapi 8.0.30; версия PHP и сервера записана в результатах. · mysql_xdevapi
SqlStatementResult::getColumns(): arrayПолучить описание колонок обычного SQL SELECT.
- SQL JOIN выбирает товар и категорию
- execute возвращает SqlStatementResult
- getColumns сохраняет порядок SELECT
- исходные имена и aliases объясняют каждый столбец
Запись, вход и результат
SqlStatementResult::getColumns(): arrayМетоды этой карточки вызываются без параметров.
Возвращаемое значение. Массив объектов описания результата. Страница Manual не заполняет семантику return. В непустом SELECT выбранного профиля это ColumnResult; при нуле строк SQL getters в этом профиле не предоставляют metadata и возвращают пустые массивы.
Два применения
Разобрать два поля name из разных таблиц.
$result = $session->sql($sql)->execute();
$columns = $result->getColumns();Исходные данные, полный запуск и результат.
Сверить ожидаемые поля перед созданием HTML-отчёта.
$columns = $result->getColumns();
$labels = array_map(static fn($column) => $column->getColumnLabel(), $columns);Исходные данные, полный запуск и результат.
Условия и ограничения
Параметров нет. Объекты читаются, пока существует их Result. При пустом SQL-ответе PECL 8.0.30/MySQL 8.4.13 не обращайтесь к $columns[0]: массива описаний может не быть. Для пустого экрана держите собственный контракт выбранных полей и заголовков. Не используйте произвольные имена из metadata для генерации новых SQL-команд. Для вычисления может отсутствовать физическая таблица происхождения.
Версии. Страница метода не указывает первую версию. Здесь рассматривается PECL mysql_xdevapi 8.0.30; версия PHP и сервера записана в результатах.
mysql_xdevapi\SqlStatementResult::getColumnsCountУзнать ширину результата независимо от количества товаров.
В PECL 8.0.14 getColumnCount переименован в getColumnsCount. Исторический URL официальной страницы остаётся getcolumncount; вызывайте существующее имя с s. · mysql_xdevapi
SqlStatementResult::getColumnsCount(): intУзнать ширину результата независимо от количества товаров.
- SELECT sku, name AS product_name задаёт 2 поля
- Фильтр оставляет 0 строк
- SQL getter в этом профиле даёт 0: metadata не предоставлена
- Два заголовка пустого экрана берём из собственного контракта проекции
Запись, вход и результат
SqlStatementResult::getColumnsCount(): intМетоды этой карточки вызываются без параметров.
Возвращаемое значение. Число колонок; 0, если колонок нет. Это число полей каждой строки, а не число строк результата.
Два применения
Показать фактическое отсутствие SQL metadata отдельно от нуля найденных товаров.
$columnCount = $sqlResult->getColumnsCount();
$rows = $sqlResult->fetchAll();Исходные данные, полный запуск и результат.
Обнаружить расхождение фиксированного renderer и запроса.
if ($result->getColumnsCount() !== count($displayColumns)) {
throw new RuntimeException('Изменилась структура отчёта');
}Исходные данные, полный запуск и результат.
Условия и ограничения
Параметров нет. Проверка count не заменяет проверку имён: два разных SELECT могут иметь одинаковую ширину. В пустом SQL SELECT связки PECL 8.0.30/MySQL 8.4.13 getter возвращает 0, хотя текст запроса перечисляет два поля: metadata текущего SQL rowset не предоставлена. Это не число товаров. COUNT(*) в SQL возвращает значение в одной колонке; не смешивайте два механизма.
Версии. В PECL 8.0.14 getColumnCount переименован в getColumnsCount. Исторический URL официальной страницы остаётся getcolumncount; вызывайте существующее имя с s.
mysql_xdevapi\SqlStatementResult::getColumnNamesПолучить ключи полей SQL-ответа в порядке проекции.
Страница метода не указывает первую версию. Здесь рассматривается PECL mysql_xdevapi 8.0.30; версия PHP и сервера записана в результатах. · mysql_xdevapi
SqlStatementResult::getColumnNames(): arrayПолучить ключи полей SQL-ответа в порядке проекции.
- name AS product_name задаёт имя результата
- непустой ответ сообщает product_name в getColumnNames
- пустой SQL в этом профиле возвращает []
- собственный словарь сохраняет заголовки без первой строки
Запись, вход и результат
SqlStatementResult::getColumnNames(): arrayМетоды этой карточки вызываются без параметров.
Возвращаемое значение. Массив имён результата, включая aliases. Не перечень исходных колонок всей таблицы; подробный return в Manual не заполнен.
Два применения
При отсутствии SQL metadata взять известные имена из контракта проекции.
$names = $sqlResult->getColumnNames();
$labels = $names === [] ? array_keys($displayColumns) : $names;
$rows = $sqlResult->fetchAll();Исходные данные, полный запуск и результат.
Сверить точный порядок разрешённых колонок.
if ($result->getColumnNames() !== array_keys($displayColumns)) {
throw new RuntimeException('Неожиданные поля отчёта');
}Исходные данные, полный запуск и результат.
Условия и ограничения
Параметров нет. Пустой массив не следует превращать в выдуманные технические metadata: fallback хранится отдельно как контракт приложения. Названия для человека выбирайте в собственном словаре: alias product_name полезен как ключ данных, но надпись «Товар» управляется интерфейсом. Одинаковые aliases создают неоднозначность ассоциативной строки, поэтому задавайте уникальные имена до execute.
Версии. Страница метода не указывает первую версию. Здесь рассматривается PECL mysql_xdevapi 8.0.30; версия PHP и сервера записана в результатах.
mysql_xdevapi\ColumnResult::getColumnLabelУзнать имя поля именно в полученном ответе.
Страница метода не указывает первую версию. Здесь рассматривается PECL mysql_xdevapi 8.0.30; версия PHP и сервера записана в результатах. · mysql_xdevapi
ColumnResult::getColumnLabel(): stringУзнать имя поля именно в полученном ответе.
- В таблице name
- SELECT name AS product_name
- label product_name
- значение доступно как $row[product_name]
Запись, вход и результат
ColumnResult::getColumnLabel(): stringМетоды этой карточки вызываются без параметров.
Возвращаемое значение. Метка колонки результата: при явном AS — заданный псевдоним. Реализация читает поле metadata name.
Два применения
Соединить описание заголовка с фактическим значением.
$label = $columns[1]->getColumnLabel();
$value = $row[$label];Исходные данные, полный запуск и результат.
Различить product_name и category_name после JOIN.
foreach ($columns as $column) {
echo $column->getColumnLabel(), PHP_EOL;
}Исходные данные, полный запуск и результат.
Условия и ограничения
Параметров нет. Label не обязательно совпадает с исходным именем и не является безопасным HTML сам по себе. Даже доверенный технический alias при выводе проходит экранирование. Для происхождения нужен getColumnName, а не попытка вырезать часть псевдонима.
Версии. Страница метода не указывает первую версию. Здесь рассматривается PECL mysql_xdevapi 8.0.30; версия PHP и сервера записана в результатах.
Описание объекта: ColumnResult. Соседние свойства полезно читать вместе, сохраняя исходный Result до последнего getter-вызова.
mysql_xdevapi\ColumnResult::getColumnNameПрочитать исходное имя поля до назначения псевдонима.
Страница метода не указывает первую версию. Здесь рассматривается PECL mysql_xdevapi 8.0.30; версия PHP и сервера записана в результатах. · mysql_xdevapi
ColumnResult::getColumnName(): stringПрочитать исходное имя поля до назначения псевдонима.
- products.name AS product_name
- getColumnLabel → product_name
- getColumnName → name
- computed label может не иметь одного исходного name
Запись, вход и результат
ColumnResult::getColumnName(): stringМетоды этой карточки вызываются без параметров.
Возвращаемое значение. Исходное имя из original_name metadata. Для выражения исходная физическая колонка может отсутствовать: пустая строка не является именем несуществующей таблицы.
Два применения
Объяснить, какое поле переименовано для экрана.
echo $columns[1]->getColumnName(), ' → ', $columns[1]->getColumnLabel();Исходные данные, полный запуск и результат.
Не приписывать вычисленному результату выдуманный столбец.
$original = $column->getColumnName();
$originText = $original === '' ? 'Вычисленное поле' : $original;Исходные данные, полный запуск и результат.
Условия и ограничения
Параметров нет. Одинаковое name может принадлежать разным таблицам. Для полного происхождения сопоставляйте schema, table name и column name; для обращения к PHP-массиву используйте label. Метаданные запроса не являются полной моделью зависимостей сложного выражения.
Версии. Страница метода не указывает первую версию. Здесь рассматривается PECL mysql_xdevapi 8.0.30; версия PHP и сервера записана в результатах.
Описание объекта: ColumnResult. Соседние свойства полезно читать вместе, сохраняя исходный Result до последнего getter-вызова.
mysql_xdevapi\ColumnResult::getTableNameУзнать исходную таблицу прямого поля.
Страница метода не указывает первую версию. Здесь рассматривается PECL mysql_xdevapi 8.0.30; версия PHP и сервера записана в результатах. · mysql_xdevapi
ColumnResult::getTableName(): stringУзнать исходную таблицу прямого поля.
- FROM products AS p
- p.name AS product_name
- table label p
- table name — фактическое имя созданной таблицы
Запись, вход и результат
ColumnResult::getTableName(): stringМетоды этой карточки вызываются без параметров.
Возвращаемое значение. Имя таблицы колонки; в реализации берётся original_table до применения table alias.
Два применения
Понять, откуда пришло имя товара в JOIN.
$sourceTable = $columns[0]->getTableName();Исходные данные, полный запуск и результат.
Проверить происхождение прямого поля в ограниченном отчёте.
$fromExpectedTable = $column->getTableName() === $name;Исходные данные, полный запуск и результат.
Условия и ограничения
Параметров нет. Выражение или сложная проекция не обязаны иметь одну исходную таблицу. Пустое значение следует показать как отсутствие данного атрибута, а не подставлять последнюю увиденную таблицу. Это информация о результате, не разрешение UPDATE.
Версии. Страница метода не указывает первую версию. Здесь рассматривается PECL mysql_xdevapi 8.0.30; версия PHP и сервера записана в результатах.
Описание объекта: ColumnResult. Соседние свойства полезно читать вместе, сохраняя исходный Result до последнего getter-вызова.
mysql_xdevapi\ColumnResult::getTableLabelУзнать псевдоним таблицы, использованный запросом.
Страница метода не указывает первую версию. Здесь рассматривается PECL mysql_xdevapi 8.0.30; версия PHP и сервера записана в результатах. · mysql_xdevapi
ColumnResult::getTableLabel(): stringУзнать псевдоним таблицы, использованный запросом.
- Одна таблица названа p, другая c
- p.name и c.name имеют одинаковые исходные имена
- table label отличает p от c
- column label отличает product_name от category_name
Запись, вход и результат
ColumnResult::getTableLabel(): stringМетоды этой карточки вызываются без параметров.
Возвращаемое значение. Метка таблицы из metadata table; при AS обычно отличается от original_table.
Два применения
Показать p.name рядом с c.name.
echo $columns[0]->getTableLabel(), '.', $columns[0]->getColumnName();Исходные данные, полный запуск и результат.
Сопоставить прямой столбец с известной ролью таблицы в конкретном JOIN.
$isCategoryColumn = $column->getTableLabel() === 'c';Исходные данные, полный запуск и результат.
Условия и ограничения
Параметров нет. Alias локален запросу и не переименовывает таблицу на сервере. Не используйте его как универсальный физический адрес; для этого существуют getTableName и getSchemaName. У вычисленного поля table label может быть пустым.
Версии. Страница метода не указывает первую версию. Здесь рассматривается PECL mysql_xdevapi 8.0.30; версия PHP и сервера записана в результатах.
Описание объекта: ColumnResult. Соседние свойства полезно читать вместе, сохраняя исходный Result до последнего getter-вызова.
mysql_xdevapi\ColumnResult::getSchemaNameУзнать схему происхождения прямого поля результата.
Страница метода не указывает первую версию. Здесь рассматривается PECL mysql_xdevapi 8.0.30; версия PHP и сервера записана в результатах. · mysql_xdevapi
ColumnResult::getSchemaName(): stringУзнать схему происхождения прямого поля результата.
- Учебная схема содержит две таблицы
- JOIN использует обе с полными именами
- прямые поля сообщают выбранную схему
- вычисление не обязано сообщать схему
Запись, вход и результат
ColumnResult::getSchemaName(): stringМетоды этой карточки вызываются без параметров.
Возвращаемое значение. Имя схемы, где хранится исходная колонка; реализация возвращает поле schema metadata.
Два применения
Проверить, что прямое поле пришло из выбранной лаборатории.
$sameSchema = $columns[0]->getSchemaName() === $config['schema'];Исходные данные, полный запуск и результат.
Сохранить техническое происхождение поля в JSON отчёта.
$origin = [$column->getSchemaName(),$column->getTableName(),$column->getColumnName()];Исходные данные, полный запуск и результат.
Условия и ограничения
Параметров нет. Имя схемы не является идентификатором клиента для проверки доступа. Не показывайте внутренние имена БД на публичном сайте без предметной необходимости; учебный JSON сохраняет только собственные объекты. Отсутствие schema у вычисленного поля не означает потерю соединения.
Версии. Страница метода не указывает первую версию. Здесь рассматривается PECL mysql_xdevapi 8.0.30; версия PHP и сервера записана в результатах.
Описание объекта: ColumnResult. Соседние свойства полезно читать вместе, сохраняя исходный Result до последнего getter-вызова.
mysql_xdevapi\ColumnResult::getTypeРазличить тип поля в протоколе от значения и типа переменной PHP.
Страница метода не указывает первую версию. Здесь рассматривается PECL mysql_xdevapi 8.0.30; версия PHP и сервера записана в результатах. · mysql_xdevapi
ColumnResult::getType(): intРазличить тип поля в протоколе от значения и типа переменной PHP.
- DECIMAL цена, INT остаток, BIGINT номер
- getType описывает колонку
- get_debug_type описывает конкретное прочитанное значение
- renderer сохраняет десятичную строку и большой ID
Запись, вход и результат
ColumnResult::getType(): intМетоды этой карточки вызываются без параметров.
Возвращаемое значение. Числовой код типа, отображённый расширением в MYSQLX_TYPE_*; это не строка SQL DDL и не результат get_debug_type(value).
Два применения
Отличить точную десятичную цену от количества.
$isDecimal = $columns[2]->getType() === MYSQLX_TYPE_DECIMAL;Исходные данные, полный запуск и результат.
Сверить технический тип с заранее заданной ролью столбца.
$type = $column->getType();
$kind = $displayColumns[$column->getColumnLabel()]['kind'];Исходные данные, полный запуск и результат.
Условия и ограничения
Параметров нет. Сравнивайте с константами реально загруженного расширения, не с угаданными числами. Код X Protocol и отображённый код MYSQLX_TYPE_* — разные шкалы. Тип сам по себе не сообщает валюту, единицы измерения и правила округления. SQL NULL в конкретной ячейке не превращает объявленный DECIMAL-столбец в отдельный бизнес-тип.
Версии. Страница метода не указывает первую версию. Здесь рассматривается PECL mysql_xdevapi 8.0.30; версия PHP и сервера записана в результатах.
Описание объекта: ColumnResult. Соседние свойства полезно читать вместе, сохраняя исходный Result до последнего getter-вызова.
mysql_xdevapi\ColumnResult::isNumberSignedПонять, допускает ли числовой столбец отрицательные значения.
Страница метода не указывает первую версию. Здесь рассматривается PECL mysql_xdevapi 8.0.30; версия PHP и сервера записана в результатах. · mysql_xdevapi
ColumnResult::isNumberSigned(): intПонять, допускает ли числовой столбец отрицательные значения.
- adjustment INT допускает -2
- stock INT UNSIGNED хранит 4
- флаг signed различает их диапазоны
- текущий положительный элемент не меняет описание колонки
Запись, вход и результат
ColumnResult::isNumberSigned(): intМетоды этой карточки вызываются без параметров.
Возвращаемое значение. Manual в сигнатуре пишет int и в return — true для signed. Реализация 8.0.30 возвращает bool: true для знакового числа, false для unsigned либо нечисловой категории.
Два применения
Различить корректировку и остаток.
$signedAdjustment = $columns[0]->isNumberSigned();
$unsignedStock = $columns[1]->isNumberSigned();Исходные данные, полный запуск и результат.
Сохранить диагностический флаг вместе с типом, не трактуя любой false как unsigned int.
$signed = $column->isNumberSigned();
$numericType = $column->getType();Исходные данные, полный запуск и результат.
Условия и ограничения
Параметров нет. Это признак типа, не проверка value < 0. false у текста не доказывает, что в ячейке целое число. Проверяйте тип и предметный диапазон; metadata не заменяет валидацию пользовательского количества.
Версии. Страница метода не указывает первую версию. Здесь рассматривается PECL mysql_xdevapi 8.0.30; версия PHP и сервера записана в результатах.
Описание объекта: ColumnResult. Соседние свойства полезно читать вместе, сохраняя исходный Result до последнего getter-вызова.
mysql_xdevapi\ColumnResult::getFractionalDigitsПрочитать объявленную дробную точность числового результата.
Страница метода не указывает первую версию. Здесь рассматривается PECL mysql_xdevapi 8.0.30; версия PHP и сервера записана в результатах. · mysql_xdevapi
ColumnResult::getFractionalDigits(): intПрочитать объявленную дробную точность числового результата.
- price DECIMAL(12,2)
- значение 1234567890.12
- metadata fractional_digits 2
- значение сохраняется строкой без промежуточного float
Запись, вход и результат
ColumnResult::getFractionalDigits(): intМетоды этой карточки вызываются без параметров.
Возвращаемое значение. Число дробных десятичных знаков в metadata. У DECIMAL(12,2) scale равен 2; это описание столбца, а не подсчёт символов после точки в произвольной строке.
Два применения
Убедиться, что отчёт получает цену с двумя десятичными знаками.
$scale = $columns[2]->getFractionalDigits();Исходные данные, полный запуск и результат.
Обнаружить изменение контракта конкретного денежного отчёта.
if ($priceColumn->getFractionalDigits() !== 2) {
throw new RuntimeException('Изменилась точность цены');
}Исходные данные, полный запуск и результат.
Условия и ограничения
Параметров нет. Не округляйте автоматически любой numeric столбец до этого количества без знания задачи. Для FLOAT/DOUBLE метаданные не превращают приближённое хранение в точное. Для вычисленных выражений тип и scale могут отличаться от исходных колонок; читайте описание именно результата.
Версии. Страница метода не указывает первую версию. Здесь рассматривается PECL mysql_xdevapi 8.0.30; версия PHP и сервера записана в результатах.
Описание объекта: ColumnResult. Соседние свойства полезно читать вместе, сохраняя исходный Result до последнего getter-вызова.
mysql_xdevapi\ColumnResult::getLengthПрочитать описанную сервером ширину поля, не длину текущей строки.
Страница метода не указывает первую версию. Здесь рассматривается PECL mysql_xdevapi 8.0.30; версия PHP и сервера записана в результатах. · mysql_xdevapi
ColumnResult::getLength(): intПрочитать описанную сервером ширину поля, не длину текущей строки.
- Колонка VARCHAR(12)
- в ячейке короткая кириллица и emoji
- metadata length описывает возможность поля
- фактические символы и байты вычисляются для значения отдельно
Запись, вход и результат
ColumnResult::getLength(): intМетоды этой карточки вызываются без параметров.
Возвращаемое значение. Значение length из metadata поля. Интерпретация зависит от типа и протокола; рядом в примере отдельно показываются объявленная ширина, CHAR_LENGTH и OCTET_LENGTH.
Два применения
Не растягивать поле ввода по текущему короткому слову.
$declaredMetadataLength = $columns[1]->getLength();Исходные данные, полный запуск и результат.
Сохранить числовую ширину отдельно от дробной точности.
$numberWidth = $columns[2]->getLength();
$scale = $columns[2]->getFractionalDigits();Исходные данные, полный запуск и результат.
Условия и ограничения
Параметров нет. Нельзя универсально присвоить это значение HTML maxlength или считать его strlen(value). В исходнике 8.0.30 на 32-битном PHP длина сверх PHP_INT_MAX может возвращаться double, несмотря на краткий int в Manual; наш профиль 64-битный. Metadata не заменяет явные ограничения ввода, SQL-тип и корректную Unicode-валидацию.
Версии. Страница метода не указывает первую версию. Здесь рассматривается PECL mysql_xdevapi 8.0.30; версия PHP и сервера записана в результатах.
Описание объекта: ColumnResult. Соседние свойства полезно читать вместе, сохраняя исходный Result до последнего getter-вызова.
mysql_xdevapi\ColumnResult::getCharacterSetNameУзнать набор символов поля результата.
Страница метода не указывает первую версию. Здесь рассматривается PECL mysql_xdevapi 8.0.30; версия PHP и сервера записана в результатах. · mysql_xdevapi
ColumnResult::getCharacterSetName(): stringУзнать набор символов поля результата.
- Название товара содержит кириллицу и emoji
- колонка использует utf8mb4
- getCharacterSetName сообщает кодировку результата
- длина байтов отличается от числа символов
Запись, вход и результат
ColumnResult::getCharacterSetName(): stringМетоды этой карточки вызываются без параметров.
Возвращаемое значение. Имя character set по collation-id metadata. Исходник 8.0.30 может вернуть null, если mysqlnd не распознал этот идентификатор; краткая сигнатура Manual этого не показывает.
Два применения
Объяснить, почему текст допускает emoji и занимает несколько байтов на символ.
$charset = $columns[1]->getCharacterSetName();Исходные данные, полный запуск и результат.
Сохранить диагностическое отсутствие имени без придумывания UTF-8.
$charset = $column->getCharacterSetName();
$charsetLabel = $charset ?? 'Не распознана';Исходные данные, полный запуск и результат.
Условия и ограничения
Параметров нет. Getter ничего не перекодирует и не исправляет повреждённый ввод. Кодировка соединения, хранение в колонке и HTML charset должны быть согласованы отдельно. Для бинарного поля смысл отличается от текста; нельзя автоматически применять текстовые операции к любым BYTES.
Версии. Страница метода не указывает первую версию. Здесь рассматривается PECL mysql_xdevapi 8.0.30; версия PHP и сервера записана в результатах.
Описание объекта: ColumnResult. Соседние свойства полезно читать вместе, сохраняя исходный Result до последнего getter-вызова.
mysql_xdevapi\ColumnResult::getCollationNameУзнать правило сравнения, связанное с полем результата.
Страница метода не указывает первую версию. Здесь рассматривается PECL mysql_xdevapi 8.0.30; версия PHP и сервера записана в результатах. · mysql_xdevapi
ColumnResult::getCollationName(): stringУзнать правило сравнения, связанное с полем результата.
- utf8mb4 задаёт представление символов
- utf8mb4_bin задаёт выбранное правило сравнения
- metadata показывает правило поля
- текст значения при этом остаётся тем же
Запись, вход и результат
ColumnResult::getCollationName(): stringМетоды этой карточки вызываются без параметров.
Возвращаемое значение. Имя collation по её идентификатору; реализация может дать null, если идентификатор не найден в таблице mysqlnd.
Два применения
Зафиксировать настройки текстовой колонки отчёта.
$collation = $columns[1]->getCollationName();Исходные данные, полный запуск и результат.
Сохранить технические сведения для разбора отличий сортировки.
$diagnostics[$column->getColumnLabel()] = $column->getCollationName();Исходные данные, полный запуск и результат.
Условия и ограничения
Параметров нет. Collation и character set — разные свойства. Имя правила не заменяет ORDER BY и не задаёт порядок строк само по себе. Не меняйте глобальную конфигурацию сервера ради чтения metadata; учебные таблицы задают нужную collation собственным определением.
Версии. Страница метода не указывает первую версию. Здесь рассматривается PECL mysql_xdevapi 8.0.30; версия PHP и сервера записана в результатах.
Описание объекта: ColumnResult. Соседние свойства полезно читать вместе, сохраняя исходный Result до последнего getter-вызова.
mysql_xdevapi\ColumnResult::isPaddedУзнать, обозначено ли правое дополнение поля в metadata.
Страница метода не указывает первую версию. Здесь рассматривается PECL mysql_xdevapi 8.0.30; версия PHP и сервера записана в результатах. · mysql_xdevapi
ColumnResult::isPadded(): intУзнать, обозначено ли правое дополнение поля в metadata.
- CHAR и VARCHAR имеют разные правила хранения
- описание поля может содержать rightpad
- isPadded читает флаг
- полученная строка проверяется отдельно, пробелы не добавляются наугад
Запись, вход и результат
ColumnResult::isPadded(): intМетоды этой карточки вызываются без параметров.
Возвращаемое значение. Manual показывает int, а описание и реализация 8.0.30 — bool. true означает BYTES_RIGHTPAD у поля категории BYTES; false — отсутствие этой комбинации.
Два применения
Сравнить описание фиксированной и переменной текстовой колонки.
$fixedPadded = $columns[0]->isPadded();
$variablePadded = $columns[1]->isPadded();Исходные данные, полный запуск и результат.
Отделить характеристику поля от реального содержимого ячейки.
$padded = $column->isPadded();
$rawValue = $row[$column->getColumnLabel()];Исходные данные, полный запуск и результат.
Условия и ограничения
Параметров нет. Флаг не равен str_ends_with(value," ") и не является указанием безусловно применять trim: значимые пробелы могут быть частью VARCHAR. Также это не PAD SPACE/NO PAD атрибут collation. Для двоичных данных возможен другой заполняющий байт; UI не должен самостоятельно дополнять строку до metadata length без собственного контракта.
Версии. Страница метода не указывает первую версию. Здесь рассматривается PECL mysql_xdevapi 8.0.30; версия PHP и сервера записана в результатах.
Описание объекта: ColumnResult. Соседние свойства полезно читать вместе, сохраняя исходный Result до последнего getter-вызова.
Источники и обсуждения
- PHP Manual — ColumnResult
- PHP Manual — RowResult::getColumns
- PHP Manual — SqlStatementResult::getColumns
- PHP Manual — SqlStatementResult::getColumnsCount
- PHP Manual — SqlStatementResult::getColumnNames
- PHP Manual — ColumnResult::getColumnLabel
- PHP Manual — ColumnResult::getColumnName
- PHP Manual — ColumnResult::getTableName
- PHP Manual — ColumnResult::getTableLabel
- PHP Manual — ColumnResult::getSchemaName
- PHP Manual — ColumnResult::getType
- PHP Manual — ColumnResult::isNumberSigned
- PHP Manual — ColumnResult::getFractionalDigits
- PHP Manual — ColumnResult::getLength
- PHP Manual — ColumnResult::getCharacterSetName
- PHP Manual — ColumnResult::getCollationName
- PHP Manual — ColumnResult::isPadded
- PECL mysql_xdevapi 8.0.30: исходная реализация ColumnResult и результатов
- PHP Manual: константы mysql_xdevapi
- MySQL 8.4: точные DECIMAL и NUMERIC
- MySQL 8.4: CHAR и VARCHAR
Код и руководство с собой
Скачайте самостоятельные примеры с исходными данными, инструкциями и ожидаемыми результатами. Примеры HTTP и фрагменты для проекта помечены отдельно: им нужно указанное окружение.
Код не исполняется на сайте. Скачайте нужный пример и запускайте его в своём локальном окружении.
Материалы сверены 2026-10-12. Примеры и объяснения написаны для этого руководства; источники и обсуждения доступны в каждой главе.


