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

Дерево руководства
Глава 135 · После первого подключения, простой таблицы и ассоциативного массива
MySQL X DevAPI: учебная база, список схем и собственные подключения
Создать изолированную базу импорта, перечитать товар, выбрать одну из двух схем и убрать свою, сохранив соседнюю. Полный контракт listClients с честной проверкой отсутствующего метода и реальной диагностикой двух соединений.
Учебный импорт в отдельной базе: от создания до уборки
Нужно проверить импорт товаров, не смешав пробные строки с рабочим каталогом. Для этого приготовим отдельную базу, запишем кабель с остатком 7 и убедимся чтением, что данные действительно сохранились. Затем сравним два учебных импорта и покажем, как убрать один, сохранив контрольную запись во втором. Последняя задача — понять, какие соединения открыла наша программа, даже если найденного в документации метода нет в установленном расширении.
- MySQL-сервер принимает соединение Session
- База phpmanualx146alpha объединяет свои таблицы и коллекции
- Таблица products задаёт столбцы sku, product_name, price_kopecks, stock
- Одна строка содержит CABLE, Кабель, 12000 копеек, 7 штук
- SELECT возвращает только нужную строку; отчёт показывает прочитанное значение
В MySQL слова schema и database в этих командах обозначают базу данных. Представьте подписанную папку с таблицами: alpha и beta — разные папки, products — таблица внутри alpha, CABLE — строка этой таблицы. Это модель организации имён, а не инструкция создавать папки вручную и не описание физического хранения MySQL. Один PHP-объект Schema представляет выбранную базу; само наличие такого объекта ещё не доказывает, что база существует на сервере.
Сначала один вызов и его результат
Предположим, отдельный учебный аккаунт уже подключён, а имя alpha свободно. $session хранит объект открытого соединения. Стрелка -> вызывает у него метод; строка в круглых скобках — имя новой базы. Знак = сохраняет возвращённый объект в $schema. Точка с запятой завершает инструкцию.
$schema = $session->createSchema('phpmanualx146alpha');
echo $schema->getName(); // phpmanualx146alphaТеперь на сервере есть база, но пока нет ни таблицы products, ни кабеля. getName возвращает название, а echo выводит его. Полная программа ниже добавляет подготовку таблицы, запись и чтение. Этот короткий фрагмент — объяснение действия, не замена самостоятельному архиву: он требует свободного учебного имени и прав CREATE. Если база уже существует, createSchema не выбирает её, а получает отказ.
| Намерение | Вызов | Что меняется |
|---|---|---|
| Создать новую базу | Session::createSchema(name) | Сервер создаёт базу; PHP получает Schema. |
| Получить объект известной базы | Session::getSchema(name) | Получаем адресный объект; создания базы нет. |
| Перечислить доступные базы | Session::getSchemas() | Получаем массив объектов Schema для текущей Session. |
| Удалить свою учебную базу | Session::dropSchema(name) | Удаляются база и её постоянные таблицы с данными. |
Разницу между объектом-адресом и существующим хранилищем раскрывает навигация по объектам. Если слова объект и метод ещё незнакомы, начните с класса и экземпляра; ниже они всегда привязаны к конкретным именам и значениям.
Имя базы и значение товара — разные входы
Имя базы является идентификатором: оно выбирает хранилище. Артикул CABLE является значением строки. В примерах SQL имена берутся только из точного разрешённого списка, а значения передаются через отдельные bind. Вопросительный знак ? обозначает место одного значения. Он не может заменить имя схемы или таблицы.
$qualified = x146SchemaName(X146_ALPHA).'.products';
$row = $session->sql('SELECT sku, stock FROM '.$qualified.' WHERE sku = ?')
->bind('CABLE')->execute()->fetchOne();
echo $row['stock']; // 7 после записи из первого полного примераЗдесь x146SchemaName — видимая в lab.php учебная функция: она разрешает только alpha или beta и оформляет имя для SQL. Точка . соединяет строки. execute посылает подготовленную команду, fetchOne получает одну строку как ассоциативный массив, а ключ stock выбирает остаток. В этом фрагменте таблица уже создана и заполнена первой программой. В каждом скачиваемом архиве вся подготовка включена. Для устройства таблиц и условий запросов продолжите таблицы, выборку и bind.
Подготовка ограниченного учебного окружения
Самостоятельные архивы используют один отдельный локальный MySQL-аккаунт и только два точных имени: phpmanualx146alpha и phpmanualx146beta. До начала оба должны отсутствовать. README содержит команды администратора для CREATE, DROP, SELECT, INSERT, UPDATE и DELETE на каждой из этих баз, без глобальных прав и без PROCESS. Если такие имена уже заняты, программа прекращает работу до создания и ничего не удаляет. Не меняйте проверку на автоматическое удаление существующей базы.
Подключение передаётся через PHPMAN_MYSQLX_URI с собственным паролем, loopback-адресом, явным X-портом и требованием TLS. Имя базы в URI не выбирается: запросы используют явно квалифицированную таблицу. Реальных учётных данных в архиве нет. Проверенный профиль — PHP 8.3.35 NTS x64, PECL mysql_xdevapi 8.0.30 и MySQL 8.4.13; это условия опыта, а не утверждение о последних версиях. Проверка расширения, X Protocol и настроек PHP помогает найти неверный исполняемый PHP или порт до работы с базами.
Почему finally здесь не означает rollback
- Проверяем отсутствие двух учебных имён
- Создаём только нужные этому сценарию базы
- Сразу записываем собственный список и закрытый маркер владения
- Выполняем операции и перечитываем данные
- finally проверяет точное имя и маркер, затем явно удаляет собственные базы
- Проверяем отсутствие alpha и beta; закрываем Session
DDL — команды изменения структуры, в том числе CREATE DATABASE и DROP DATABASE. MySQL выполняет для таких команд неявный commit: оборачивание их обычной транзакцией не даёт возможности вернуть удалённую базу через rollback. finally лишь выполняет предусмотренную явную уборку при выходе из блока. История создания и маркер нужны, чтобы не принять заранее существующее содержимое за своё. Пароль и маркер не включаются в результат.
Восстановление после аварии процесса и параллельная работа нескольких администраторов — отдельные задачи. Наши два имени выделены одному учебному запуску; не запускайте архивы одновременно. При удалении базы её права не исчезают автоматически. Если лаборатория больше не нужна, администратор отдельно удаляет выделенную учётную запись по README. У рабочего сайта такую базу не создают и не удаляют при каждом открытии страницы.
Какие результаты можно открыть
Каждый архив запускается самостоятельно. Первые три создают и заполняют свои схемы, четвёртый вообще не создаёт баз: он исследует только два собственных подключения. result.html показывает читаемую таблицу, result.json — фактически прочитанные строки, типы и очищенные наблюдения. metrics.json проверяет значения и отсутствие учебных баз после завершения. Код не заменяет недоступный сервер заранее подготовленным ответом; неуспешное подключение остаётся ошибкой.
Создать базу пробного импорта и перечитать сохранённый товар
Перед настоящим импортом поставщика хочется проверить формат товара в отдельной базе: кабель за 120 рублей, остаток 7 штук. Создадим именно новую учебную базу, запишем строку и прочитаем её обратно. Затем намеренно повторим создание того же имени: отказ не должен превращаться в удаление базы с уже сохранённым товаром.
- Имя alpha свободно и разрешено отдельному учебному аккаунту
- createSchema создаёт базу, Register отмечает владение текущего запуска
- Внутри alpha создаётся products; INSERT сохраняет CABLE
- SELECT возвращает название, цену 12000 копеек и остаток 7
- Повторный createSchema даёт отказ; чтение подтверждает прежний товар
- finally удаляет только созданную и отмеченную alpha; повторная проверка подтверждает отсутствие
Сначала короткий вызов
$schema = $session->createSchema('phpmanualx146alpha');
echo $schema->getName(); // phpmanualx146alphaЭтот фрагмент предназначен только для подготовленного учебного аккаунта и свободного имени. Строка в скобках — имя создаваемой базы. Стрелка -> вызывает метод у открытой Session; = сохраняет возвращённый Schema. getName читает имя, а не строки товаров. В полной программе перед вызовом проверяется отсутствие базы, сразу после него регистрируется владение, затем отдельно создаётся таблица. INSERT передаёт четыре значения через bind; SELECT выбирает одну нужную строку, поэтому вывод доказывает запись, а не только создание пустого контейнера.
Что получится
| Наблюдение | Результат |
|---|---|
| Создана учебная база | phpmanualx146alpha |
| Артикул сохранённого товара | CABLE |
| Название из базы | Кабель |
| Цена, коп. | 12000 |
| Остаток | 7 |
| Повторное создание отклонено | да |
| Товар сохранился после отказа | да |
| Оба учебных имени свободны после запуска | да |
Самостоятельный файл и архив для запуска
<?php
declare(strict_types=1);
require __DIR__.'/lab.php';
$session = null;
$config = null;
$owned = $tokens = $removed = $rows = $checks = [];
$observations = ['schema_absence_before'=>null, 'schema_absence_after'=>null];
$runtime = x146Runtime();
try {
$config=x146Config();
$session=mysql_xdevapi\getSession($config['uri']);
$runtime=x146Runtime($session,$config);
$observations['schema_absence_before']=x146Absence($session);
if (in_array(false,$observations['schema_absence_before'],true)) {
throw new RuntimeException('Одно из учебных имён занято; запуск прекращён без удаления.');
}
x146AssertVacant($session, X146_ALPHA);
$alpha = $session->createSchema(X146_ALPHA);
x146Register($session, X146_ALPHA, $owned, $tokens);
$qualified = x146SchemaName(X146_ALPHA).'.products';
$session->sql('CREATE TABLE '.$qualified
.' (sku VARCHAR(20) PRIMARY KEY, product_name VARCHAR(60) NOT NULL, price_kopecks INT NOT NULL, stock INT NOT NULL) ENGINE=InnoDB')->execute();
$session->sql('INSERT INTO '.$qualified.' (sku, product_name, price_kopecks, stock) VALUES (?, ?, ?, ?)')
->bind('CABLE')->bind('Кабель')->bind(12000)->bind(7)->execute();
$saved = $session->sql('SELECT sku, product_name, price_kopecks, stock FROM '.$qualified.' WHERE sku = ?')
->bind('CABLE')->execute()->fetchOne();
$duplicate = x146Failure(static fn() => $session->createSchema(X146_ALPHA));
$codes = array_column($duplicate, 'code');
$afterDuplicate = $session->sql('SELECT sku, product_name, price_kopecks, stock FROM '.$qualified.' WHERE sku = ?')
->bind('CABLE')->execute()->fetchOne();
$rows = [['Создана учебная база', $alpha->getName()], ['Артикул сохранённого товара', $saved['sku']],
['Название из базы', $saved['product_name']], ['Цена, коп.', $saved['price_kopecks']], ['Остаток', $saved['stock']],
['Повторное создание отклонено', in_array(1007, $codes, true)],
['Товар сохранился после отказа', $afterDuplicate === $saved]];
$checks = ['schema_object'=>$alpha instanceof mysql_xdevapi\Schema,
'schema_name'=>$alpha->getName() === X146_ALPHA, 'schema_exists'=>$alpha->existsInDatabase(),
'saved_product'=>$saved === ['sku'=>'CABLE','product_name'=>'Кабель','price_kopecks'=>12000,'stock'=>7],
'duplicate_error'=>in_array(1007, $codes, true), 'duplicate_preserves_data'=>$afterDuplicate === $saved];
$observations += ['schema_class'=>get_debug_type($alpha), 'created_schema'=>$alpha->getName(),
'saved_product'=>$saved, 'duplicate_create'=>$duplicate, 'after_duplicate'=>$afterDuplicate];
} finally {
if ($session instanceof mysql_xdevapi\Session) {
try {
$removed=x146Cleanup($session,$owned,$tokens,$removed);
$observations['schema_absence_after']=x146Absence($session);
} finally {$session->close();}
}
}
$absentAfter = !in_array(false, $observations['schema_absence_after'], true);
$rows[] = ['Оба учебных имени свободны после запуска', $absentAfter];
$checks['schemas_absent_after'] = $absentAfter;
x146Report('mysql146-x-schema-lifecycle', 'Создать базу пробного импорта и перечитать сохранённый товар', $rows, $checks, $observations, $runtime, $owned, $removed);
Ожидаемый результат
Создана учебная база — phpmanualx146alpha
Артикул сохранённого товара — CABLE
Название из базы — Кабель
Цена, коп. — 12000
Остаток — 7
Повторное создание отклонено — да
Товар сохранился после отказа — да
Оба учебных имени свободны после запуска — да
Запускайте отдельным процессом PHP по README и только последовательно. Файл создаёт лишь свои разрешённые учебные схемы, если это требуется сценарию, проверяет фактические записи и в finally удаляет собственные объекты. result.html и result.json показывают результат; metrics.json — проверки. Для сценария подключений никаких схем не создаётся.
Таблица сохранённого запуска · Фактические строки и наблюдения.
Границы и ошибки
createSchema не является способом выбрать существующую базу: для этого есть getSchema. В проверенном профиле повторное создание даёт mysql_xdevapi Exception с кодом 1007; цепочка очищенных сообщений сохранена в JSON. Иная причина отказа — например недостаточные права — не должна объявляться «база уже существует». CREATE DATABASE относится к DDL и не отменяется обычным rollback. После проверки finally удаляет всю созданную учебную базу, включая products; для рабочего импорта такой автоматической уборки быть не должно.
Попробуйте изменить
Что следует сделать, если phpmanualx146alpha уже существовала до запуска, а не появилась в текущей программе?
Предлагаемое решение
Остановиться до createSchema и ничего не удалять. Имя из разрешённого списка ещё не означает, что содержимое принадлежит этому запуску. Если задача — читать существующую базу, это отдельный режим с getSchema и другими правилами жизненного цикла.
Показать две доступные учебные базы и открыть запись из выбранной
У приложения есть два независимых учебных каталога. Оператор выбирает beta и должен увидеть её карточку BETA-BOX с остатком 11, а не карточку из alpha. Составим список доступных учебных баз из настоящих объектов Schema и используем выбранный объект для точного чтения.
- Созданы alpha с ALPHA-BOX: 3 и beta с BETA-BOX: 11
- getSchemas возвращает массив объектов, видимых текущей Session
- getName извлекает имя; разрешённый список оставляет только alpha и beta
- Сортируем имена явно, сохраняем связь имя → Schema
- Выбор beta даёт её объект, а запрос читает BETA-BOX: 11
- finally удаляет две базы, созданные именно этим запуском
Сначала короткий вызов
$schemas = $session->getSchemas();
$names = [];
foreach ($schemas as $schema) {
$name = $schema->getName();
if (in_array($name, X146_SCHEMAS, true)) {
$names[] = $name;
}
}
sort($names, SORT_STRING);getSchemas вызывается без аргументов. foreach рассматривает каждый объект массива как $schema, а getName возвращает строку имени. in_array с true сравнивает её с двумя точными разрешёнными именами. [] добавляет имя в новый массив; sort задаёт порядок интерфейса. Полная программа дополнительно сохраняет сами объекты по имени и выбирает beta: индекс исходного массива не служит идентификатором базы.
Что получится
| Наблюдение | Результат |
|---|---|
| Доступных учебных баз | 2 |
| Первое имя после сортировки | phpmanualx146alpha |
| Второе имя после сортировки | phpmanualx146beta |
| Выбрана база | phpmanualx146beta |
| Артикул из выбранной базы | BETA-BOX |
| Остаток в beta | 11 |
| Остаток отдельной карточки alpha | 3 |
| getSchemas вернул объекты Schema | да |
| Оба учебных имени свободны после запуска | да |
Самостоятельный файл и архив для запуска
<?php
declare(strict_types=1);
require __DIR__.'/lab.php';
$session = null;
$config = null;
$owned = $tokens = $removed = $rows = $checks = [];
$observations = ['schema_absence_before'=>null, 'schema_absence_after'=>null];
$runtime = x146Runtime();
try {
$config=x146Config();
$session=mysql_xdevapi\getSession($config['uri']);
$runtime=x146Runtime($session,$config);
$observations['schema_absence_before']=x146Absence($session);
if (in_array(false,$observations['schema_absence_before'],true)) {
throw new RuntimeException('Одно из учебных имён занято; запуск прекращён без удаления.');
}
$setup = [
X146_ALPHA=>['sku'=>'ALPHA-BOX','stock'=>3],
X146_BETA=>['sku'=>'BETA-BOX','stock'=>11],
];
foreach ($setup as $name=>$product) {
x146AssertVacant($session, $name);
$created = $session->createSchema($name);
x146Register($session, $name, $owned, $tokens);
$qualified = x146SchemaName($name).'.inventory';
$session->sql('CREATE TABLE '.$qualified.' (sku VARCHAR(20) PRIMARY KEY, stock INT NOT NULL) ENGINE=InnoDB')->execute();
$session->sql('INSERT INTO '.$qualified.' (sku, stock) VALUES (?, ?)')->bind($product['sku'])->bind($product['stock'])->execute();
}
$all = $session->getSchemas();
$byName = $ownObjects = [];
foreach ($all as $key=>$schemaObject) {
$name = $schemaObject->getName();
if (!in_array($name, X146_SCHEMAS, true)) { continue; }
$byName[$name] = $schemaObject;
$ownObjects[] = ['key'=>$key, 'key_type'=>get_debug_type($key),
'class'=>get_debug_type($schemaObject), 'name'=>$name];
}
$names = array_keys($byName);
sort($names, SORT_STRING);
$selected = $byName[X146_BETA];
$qualified = x146SchemaName($selected->getName()).'.inventory';
$selectedProduct = $session->sql('SELECT sku, stock FROM '.$qualified.' WHERE sku = ?')
->bind('BETA-BOX')->execute()->fetchOne();
$alphaProduct = $session->sql('SELECT sku, stock FROM '.x146SchemaName(X146_ALPHA).'.inventory WHERE sku = ?')
->bind('ALPHA-BOX')->execute()->fetchOne();
$objectTypesOk = true;
foreach ($byName as $schemaObject) { $objectTypesOk = $objectTypesOk && $schemaObject instanceof mysql_xdevapi\Schema; }
$rows = [['Доступных учебных баз', count($names)], ['Первое имя после сортировки', $names[0]],
['Второе имя после сортировки', $names[1]], ['Выбрана база', $selected->getName()],
['Артикул из выбранной базы', $selectedProduct['sku']], ['Остаток в beta', $selectedProduct['stock']],
['Остаток отдельной карточки alpha', $alphaProduct['stock']], ['getSchemas вернул объекты Schema', $objectTypesOk]];
$checks = ['array_result'=>is_array($all), 'own_names'=>$names === [X146_ALPHA, X146_BETA],
'objects'=>$objectTypesOk && count($ownObjects) === 2,
'selected_name'=>$selected->getName() === X146_BETA,
'selected_product'=>$selectedProduct === ['sku'=>'BETA-BOX','stock'=>11],
'separate_alpha'=>$alphaProduct === ['sku'=>'ALPHA-BOX','stock'=>3]];
$observations += ['getschemas_container'=>get_debug_type($all), 'own_schema_objects'=>$ownObjects,
'sorted_own_names'=>$names, 'selected_schema'=>$selected->getName(),
'selected_product'=>$selectedProduct, 'alpha_product'=>$alphaProduct];
} finally {
if ($session instanceof mysql_xdevapi\Session) {
try {
$removed=x146Cleanup($session,$owned,$tokens,$removed);
$observations['schema_absence_after']=x146Absence($session);
} finally {$session->close();}
}
}
$absentAfter = !in_array(false, $observations['schema_absence_after'], true);
$rows[] = ['Оба учебных имени свободны после запуска', $absentAfter];
$checks['schemas_absent_after'] = $absentAfter;
x146Report('mysql146-x-schema-directory', 'Показать две доступные учебные базы и открыть запись из выбранной', $rows, $checks, $observations, $runtime, $owned, $removed);
Ожидаемый результат
Доступных учебных баз — 2
Первое имя после сортировки — phpmanualx146alpha
Второе имя после сортировки — phpmanualx146beta
Выбрана база — phpmanualx146beta
Артикул из выбранной базы — BETA-BOX
Остаток в beta — 11
Остаток отдельной карточки alpha — 3
getSchemas вернул объекты Schema — да
Оба учебных имени свободны после запуска — да
Запускайте отдельным процессом PHP по README и только последовательно. Файл создаёт лишь свои разрешённые учебные схемы, если это требуется сценарию, проверяет фактические записи и в finally удаляет собственные объекты. result.html и result.json показывают результат; metrics.json — проверки. Для сценария подключений никаких схем не создаётся.
Таблица сохранённого запуска · Фактические строки и наблюдения.
Границы и ошибки
getSchemas показывает доступные этому сеансу схемы, а не гарантированно все базы сервера и не перечень их владельцев. В исходном массиве могут быть системные схемы; их имена здесь не публикуются. Порядок и числовые ключи не фиксируются: в подготовительном запуске наши объекты имели ключи 2 и 3, но другие права или набор схем изменят их. Видимость имени не доказывает право создавать или удалять все объекты внутри. Если выбранного имени нет, интерфейс должен сообщить об этом, а не открывать первый элемент массива.
Нужно перечислить базы, доступные пользователю; при этом легко спутать видимость текущей учётной записи с административной проверкой чужих прав. Задача на Stack Overflow. Читаем getSchemas в собственной Session, извлекаем имена и оставляем два точных учебных имени. Не запрашиваем таблицы привилегий и не выдаём право видеть весь сервер только ради списка приложения.
Попробуйте изменить
Поменяйте выбранное имя с beta на alpha. Достаточно ли поменять только число индекса в $names?
Предлагаемое решение
Выбирайте объект по точному имени в $byName и используйте соответствующий артикул ALPHA-BOX. Получится остаток 3. Позиция в отсортированном списке нужна интерфейсу, но не должна подменять устойчивый выбор базы по имени.
Повторить удаление alpha и сохранить соседнюю beta
После пробного импорта нужно удалить только его отдельную базу alpha. Рядом находится другая учебная база beta с квитанцией KEEP на 9 позиций. Проверим первое удаление, честный отказ при повторе и сохранность соседней квитанции; beta удалится позже, только в своей процедуре завершения.
- Alpha содержит TRIAL: 2, beta содержит KEEP: 9
- AssertOwned проверяет точное имя, запись текущего запуска и приватный маркер alpha
- dropSchema(alpha) возвращает true, проверка подтверждает отсутствие
- Повторный drop отсутствующей alpha возвращает false и E_WARNING
- SELECT из beta снова даёт KEEP: 9
- finally отдельно проверяет владение beta, удаляет её и подтверждает отсутствие обеих схем
Сначала короткий вызов
x146AssertOwned($session, X146_ALPHA, $owned, $tokens);
$dropped = $session->dropSchema(X146_ALPHA);
x146MarkRemoved($session, X146_ALPHA, $removed);
var_dump($dropped); // bool(true) при успешном удаленииКороткий фрагмент предполагает, что alpha уже создана и зарегистрирована этой программой. dropSchema получает строковое имя, удаляет базу с постоянными таблицами и возвращает bool. AssertOwned и MarkRemoved — открытые функции lab.php: первая проверяет наше право на автоматическую уборку, вторая проверяет отсутствие после удаления. В полном файле повторный вызов специально идёт для уже отсутствующего собственного имени. Локальный обработчик принимает только E_WARNING с ожидаемой фразой и восстанавливается в finally; другие предупреждения не скрываются.
Что получится
| Наблюдение | Результат |
|---|---|
| Первое удаление alpha | да |
| Alpha отсутствует после первого удаления | да |
| Повторное удаление отсутствующей alpha | нет |
| Перехвачено ожидаемых предупреждений | 1 |
| Уровень предупреждения | 2 |
| Beta пока существует | да |
| Код квитанции в beta | KEEP |
| Позиций в квитанции beta | 9 |
| Квитанция beta не изменилась | да |
| Оба учебных имени свободны после запуска | да |
Самостоятельный файл и архив для запуска
<?php
declare(strict_types=1);
require __DIR__.'/lab.php';
$session = null;
$config = null;
$owned = $tokens = $removed = $rows = $checks = [];
$observations = ['schema_absence_before'=>null, 'schema_absence_after'=>null];
$runtime = x146Runtime();
try {
$config=x146Config();
$session=mysql_xdevapi\getSession($config['uri']);
$runtime=x146Runtime($session,$config);
$observations['schema_absence_before']=x146Absence($session);
if (in_array(false,$observations['schema_absence_before'],true)) {
throw new RuntimeException('Одно из учебных имён занято; запуск прекращён без удаления.');
}
foreach ([X146_ALPHA, X146_BETA] as $name) {
x146AssertVacant($session, $name);
$created = $session->createSchema($name);
x146Register($session, $name, $owned, $tokens);
$qualified = x146SchemaName($name).'.import_receipts';
$session->sql('CREATE TABLE '.$qualified.' (code VARCHAR(20) PRIMARY KEY, items INT NOT NULL) ENGINE=InnoDB')->execute();
$session->sql('INSERT INTO '.$qualified.' (code, items) VALUES (?, ?)')
->bind($name === X146_ALPHA ? 'TRIAL' : 'KEEP')->bind($name === X146_ALPHA ? 2 : 9)->execute();
}
$betaSql = 'SELECT code, items FROM '.x146SchemaName(X146_BETA).'.import_receipts WHERE code = ?';
$betaBefore = $session->sql($betaSql)->bind('KEEP')->execute()->fetchOne();
x146AssertOwned($session, X146_ALPHA, $owned, $tokens);
$firstDrop = $session->dropSchema(X146_ALPHA);
x146MarkRemoved($session, X146_ALPHA, $removed);
$alphaAbsent = !$session->getSchema(X146_ALPHA)->existsInDatabase();
x146AssertVacant($session, X146_ALPHA);
$warnings = [];
set_error_handler(static function(int $level, string $message) use (&$warnings): bool {
if ($level !== E_WARNING || !str_contains($message, "cannot drop schema 'phpmanualx146alpha'")) {
return false;
}
$warnings[] = ['level'=>$level, 'message'=>x146SafeMessage($message)];
return true;
});
try { $secondDrop = $session->dropSchema(X146_ALPHA); }
finally { restore_error_handler(); }
$betaAfter = $session->sql($betaSql)->bind('KEEP')->execute()->fetchOne();
$betaStillExists = $session->getSchema(X146_BETA)->existsInDatabase();
$rows = [['Первое удаление alpha', $firstDrop], ['Alpha отсутствует после первого удаления', $alphaAbsent],
['Повторное удаление отсутствующей alpha', $secondDrop], ['Перехвачено ожидаемых предупреждений', count($warnings)],
['Уровень предупреждения', $warnings[0]['level']], ['Beta пока существует', $betaStillExists],
['Код квитанции в beta', $betaAfter['code']], ['Позиций в квитанции beta', $betaAfter['items']],
['Квитанция beta не изменилась', $betaAfter === $betaBefore]];
$checks = ['first_drop'=>$firstDrop === true, 'alpha_absent'=>$alphaAbsent,
'second_drop'=>$secondDrop === false, 'expected_warning'=>count($warnings) === 1 && $warnings[0]['level'] === E_WARNING
&& str_contains($warnings[0]['message'], "cannot drop schema 'phpmanualx146alpha'"),
'beta_exists'=>$betaStillExists, 'beta_readback'=>$betaAfter === ['code'=>'KEEP','items'=>9],
'beta_unchanged'=>$betaBefore === $betaAfter, 'one_removed_before_finally'=>count($removed) === 1];
$observations += ['first_drop'=>$firstDrop, 'alpha_absent_after_first'=>$alphaAbsent,
'second_drop'=>$secondDrop, 'expected_missing_warnings'=>$warnings,
'beta_before'=>$betaBefore, 'beta_after'=>$betaAfter, 'beta_exists_before_finally'=>$betaStillExists];
} finally {
if ($session instanceof mysql_xdevapi\Session) {
try {
$removed=x146Cleanup($session,$owned,$tokens,$removed);
$observations['schema_absence_after']=x146Absence($session);
} finally {$session->close();}
}
}
$absentAfter = !in_array(false, $observations['schema_absence_after'], true);
$rows[] = ['Оба учебных имени свободны после запуска', $absentAfter];
$checks['schemas_absent_after'] = $absentAfter;
x146Report('mysql146-x-schema-cleanup-boundaries', 'Повторить удаление alpha и сохранить соседнюю beta', $rows, $checks, $observations, $runtime, $owned, $removed);
Ожидаемый результат
Первое удаление alpha — да
Alpha отсутствует после первого удаления — да
Повторное удаление отсутствующей alpha — нет
Перехвачено ожидаемых предупреждений — 1
Уровень предупреждения — 2
Beta пока существует — да
Код квитанции в beta — KEEP
Позиций в квитанции beta — 9
Квитанция beta не изменилась — да
Оба учебных имени свободны после запуска — да
Запускайте отдельным процессом PHP по README и только последовательно. Файл создаёт лишь свои разрешённые учебные схемы, если это требуется сценарию, проверяет фактические записи и в finally удаляет собственные объекты. result.html и result.json показывают результат; metrics.json — проверки. Для сценария подключений никаких схем не создаётся.
Таблица сохранённого запуска · Фактические строки и наблюдения.
Границы и ошибки
false — не сообщение об успешном удалении: схема отсутствует либо её удалить не удалось. Причину нужно отличать по контексту и диагностике. Показанное сообщение и уровень 2 относятся к проверенному PECL 8.0.30; в других версиях текст может отличаться, его нельзя подавлять без разбора. x146MarkRemoved вызывается один раз после настоящего удаления, а не после повторного false. DROP DATABASE не отменяется обычным rollback и не отзывает выданные на эту базу права. Защитный маркер не является распределённой блокировкой: в учебной лаборатории эти два имени не используют параллельно другими программами.
Разработчик хочет отменить создание нескольких баз и выдачу прав одним rollback, если следующий шаг не удался. Задача на Stack Overflow. По правилам MySQL DDL отделён от откатываемых изменений строк. Ведём список созданных этим запуском схем и выполняем явную ограниченную уборку; это не обещание транзакционного rollback схемы. Возможности сторонних GUI из ответов не подменяют контракт сервера.
Попробуйте изменить
Можно ли в обработчике ошибки просто перебрать все имена из getSchemas и удалить базы, начинающиеся с phpmanual?
Предлагаемое решение
Нет. Совпадение префикса не доказывает владение и может затронуть соседние данные. Удаляются только два точных разрешённых имени, успешно созданные текущим запуском, с совпадающим маркером. Если имя или маркер изменились, автоматическая очистка останавливается для разбора.
Проверить доступный API и увидеть два собственных соединения
В документации найден Session::listClients, но нужно понять, доступен ли он в установленном PHP-расширении. Сначала проверим сам API. Затем решим более узкую полезную задачу: увидим два соединения, которые открыла наша программа, и подтвердим, что закрытие второго не мешает первому выполнять запросы.
- Реальная Session → method_exists(listClients): проверка реализации, не прав базы
- Первая Session возвращает свой CONNECTION_ID
- Вторая отдельная Session возвращает другой CONNECTION_ID
- SHOW PROCESSLIST фильтруется только по двум собственным ID
- close закрывает вторую Session, SELECT 1 подтверждает пригодность первой
- Внешний finally закрывает первую; схемы вообще не создавались
Сначала короткий вызов
$hasMethod = method_exists($session, 'listClients');
var_dump($hasMethod); // bool(false) в PECL mysql_xdevapi 8.0.30
$row = $session->sql('SELECT CONNECTION_ID() AS id')->execute()->fetchOne();
echo $row['id']; // реальный SQL ID текущего соединенияmethod_exists принимает объект и строку имени метода, возвращая true или false. В выбранном расширении метода нет, поэтому мы его не вызываем и не подставляем выдуманный массив клиентов. SQL CONNECTION_ID возвращает идентификатор только текущего SQL-соединения. В полном файле две отдельно открытые Session получают два реальных значения; они меняются между запусками и сохраняются в JSON. Сопоставление с PROCESSLIST выполняется по Id, а не по порядку строк или ожидаемому состоянию Query/Sleep.
Что получится
| Наблюдение | Результат |
|---|---|
| Метод listClients доступен | нет |
| Открыто собственных Session | 2 |
| SQL connection ID различаются | да |
| Тип первого SQL ID | int |
| Тип второго SQL ID | int |
| Собственных строк найдено в PROCESSLIST | 2 |
| Строки соответствуют двум полученным ID | да |
| Вторая Session закрыта | да |
| Первая Session отвечает после закрытия второй | 1 |
| Оба учебных имени свободны после запуска | да |
Самостоятельный файл и архив для запуска
<?php
declare(strict_types=1);
require __DIR__.'/lab.php';
$session = null;
$config = null;
$owned = $tokens = $removed = $rows = $checks = [];
$observations = ['schema_absence_before'=>null, 'schema_absence_after'=>null];
$runtime = x146Runtime();
try {
$config=x146Config();
$session=mysql_xdevapi\getSession($config['uri']);
$runtime=x146Runtime($session,$config);
$observations['schema_absence_before']=x146Absence($session);
if (in_array(false,$observations['schema_absence_before'],true)) {
throw new RuntimeException('Одно из учебных имён занято; запуск прекращён без удаления.');
}
$available = method_exists($session, 'listClients');
$firstId = $session->sql('SELECT CONNECTION_ID() AS id')->execute()->fetchOne()['id'];
$second = mysql_xdevapi\getSession($config['uri']);
try {
$secondId = $second->sql('SELECT CONNECTION_ID() AS id')->execute()->fetchOne()['id'];
$processRows = $session->sql('SHOW PROCESSLIST')->execute()->fetchAll();
$ownRows = [];
foreach ($processRows as $row) {
if (in_array($row['Id'], [$firstId, $secondId], true)) {
$ownRows[] = ['id'=>$row['Id'], 'user'=>$row['User'], 'command'=>$row['Command']];
}
}
} finally { $secondClosed = $second->close(); }
$idsFromProcess = array_column($ownRows, 'id');
$ownIds = [$firstId, $secondId];
sort($idsFromProcess, SORT_NUMERIC);
sort($ownIds, SORT_NUMERIC);
$afterClose = $session->sql('SELECT 1 AS alive')->execute()->fetchOne();
$rows = [['Метод listClients доступен', $available], ['Открыто собственных Session', count($ownIds)],
['SQL connection ID различаются', $firstId !== $secondId],
['Тип первого SQL ID', get_debug_type($firstId)], ['Тип второго SQL ID', get_debug_type($secondId)],
['Собственных строк найдено в PROCESSLIST', count($ownRows)],
['Строки соответствуют двум полученным ID', $idsFromProcess === $ownIds],
['Вторая Session закрыта', $secondClosed], ['Первая Session отвечает после закрытия второй', $afterClose['alive']]];
$checks = ['capability_profile'=>$available === false, 'two_ids'=>is_int($firstId) && is_int($secondId) && $firstId > 0 && $secondId > 0 && $firstId !== $secondId,
'own_process_rows'=>count($ownRows) === 2 && $idsFromProcess === $ownIds,
'same_account'=>count(array_unique(array_column($ownRows, 'user'))) === 1,
'second_closed'=>$secondClosed === true, 'first_alive'=>$afterClose === ['alive'=>1], 'no_schemas_created'=>$owned === []];
$observations += ['listclients_available'=>$available, 'alternative'=>'SQL CONNECTION_ID plus filtered own SHOW PROCESSLIST rows; not listClients output',
'connection_ids'=>[$firstId,$secondId], 'id_types'=>[get_debug_type($firstId),get_debug_type($secondId)],
'own_process_rows'=>$ownRows, 'second_closed'=>$secondClosed, 'after_second_close'=>$afterClose];
} finally {
if ($session instanceof mysql_xdevapi\Session) {
try {
$removed=x146Cleanup($session,$owned,$tokens,$removed);
$observations['schema_absence_after']=x146Absence($session);
} finally {$session->close();}
}
}
$absentAfter = !in_array(false, $observations['schema_absence_after'], true);
$rows[] = ['Оба учебных имени свободны после запуска', $absentAfter];
$checks['schemas_absent_after'] = $absentAfter;
x146Report('mysql146-x-client-capabilities', 'Проверить доступный API и увидеть два собственных соединения', $rows, $checks, $observations, $runtime, $owned, $removed);
Ожидаемый результат
Метод listClients доступен — нет
Открыто собственных Session — 2
SQL connection ID различаются — да
Тип первого SQL ID — int
Тип второго SQL ID — int
Собственных строк найдено в PROCESSLIST — 2
Строки соответствуют двум полученным ID — да
Вторая Session закрыта — да
Первая Session отвечает после закрытия второй — 1
Оба учебных имени свободны после запуска — да
Запускайте отдельным процессом PHP по README и только последовательно. Файл создаёт лишь свои разрешённые учебные схемы, если это требуется сценарию, проверяет фактические записи и в finally удаляет собственные объекты. result.html и result.json показывают результат; metrics.json — проверки. Для сценария подключений никаких схем не создаётся.
Таблица сохранённого запуска · Фактические строки и наблюдения.
Границы и ошибки
Официальный контракт listClients сохранён в карточке ниже: он описывает client_id, user, host и sql_session. Полученные здесь SQL ID нельзя переименовать в X client_id. Наш fallback отвечает только на вопрос о собственных соединениях и не является реализацией отсутствующего метода. Без глобального PROCESS обычная учётная запись видит свои потоки; программа дополнительно отбирает только два ID и не сохраняет чужие запросы. Команда и состояние процесса меняются со временем, поэтому Query/Sleep записываются как наблюдения, а не как обязательный результат. Успешный close не используется для обещания мгновенного исчезновения строки из любого внешнего монитора.
Диагностика с ограниченными правами не показывает чужие соединения, хотя автор ожидает полный список сервера. Задача на Stack Overflow. Для нашей задачи достаточно двух Session, которые открыл сам сценарий. Сопоставляем их CONNECTION_ID с разрешёнными собственными строками SHOW PROCESSLIST. Предложенные в обсуждении глобальные права здесь не нужны.
Попробуйте изменить
Можно ли исправить отсутствие listClients выдачей PROCESS или заменой второго соединения переменной $second = $session?
Предлагаемое решение
Нет. Права сервера не добавляют отсутствующий метод PHP. А присваивание другой переменной того же объекта не создаёт отдельное соединение: для двух SQL ID нужны два реальных сеанса. Если требуется полный серверный мониторинг, это отдельная административная задача с согласованными правами и другим объёмом раскрываемых данных.
Четыре метода: создать, перечислить, убрать и проверить поддержку
| Метод справочника | Практическая задача |
|---|---|
| Session::createSchema | Создать отдельную базу для пробного импорта, прежде чем помещать в неё товары. |
| Session::dropSchema | Убрать свою базу после пробного импорта и убедиться, что соседний результат сохранился. |
| Session::getSchemas | Построить список доступных учебных баз и прочитать товар из выбранной, не угадывая индекс массива. |
| Session::listClients | Отличить описанный в справочнике список клиентов от реально доступного API и увидеть собственные соединения. |
Следующий шаг: подготовка сервиса и рабочие данные
База пробного импорта создана и проверена, выбор нужного хранилища не зависит от случайного индекса, а уборка не принимает соседнюю базу за свою. Теперь переходите к структуре таблиц и запросам для нужного экрана или каталогу документов. Для подготовки окружения используйте проверку PHP, настроек и X Protocol. Для нескольких рабочих сеансов продолжите жизненный цикл Client и Session.
Функции и методы этой темы
Откройте карточку, чтобы увидеть договор функции, примеры, ограничения и связанные инструменты.
mysql_xdevapi\Session::createSchemaСоздать отдельную базу для пробного импорта, прежде чем помещать в неё товары.
Официальная страница не указывает версию появления метода. Фактические различия ниже относятся к PHP 8.3.35, PECL mysql_xdevapi 8.0.30 и MySQL 8.4.13; номер расширения не является номером PHP. · mysql_xdevapi
public function mysql_xdevapi\Session::createSchema(string $schema_name): mysql_xdevapi\SchemaСоздать отдельную базу для пробного импорта, прежде чем помещать в неё товары.
- Открытая Session знает сервер и учётную запись
- createSchema получает свободное имя alpha
- Сервер создаёт базу и возвращает Schema
- Отдельные CREATE TABLE и INSERT сохраняют CABLE
- SELECT перечитывает реальный товар; в учебном finally удаляется только своя база
Сигнатура, вход и возвращаемое значение
public function mysql_xdevapi\Session::createSchema(string $schema_name): mysql_xdevapi\Schema| Параметр | Назначение |
|---|---|
| schema_name | Обязательная строка: имя новой базы. Это имя контейнера таблиц и коллекций, а не имя таблицы и не значение товара. В наших запусках разрешены только две заранее выделенные базы. |
При успехе возвращается объект Schema созданной базы. Документация описывает исключение при отказе. Полученный объект позволяет обратиться к содержимому; сам факт его создания не добавляет таблицы и товары.
Два практических применения
Пробный импорт получает собственный контейнер для проверки формата поставщика. В полном запуске перед этим стоит проверка свободного имени, после — таблица products и чтение кабеля за 12000 копеек с остатком 7. Register — открытая служебная функция lab.php, а не метод расширения.
$schema = $session->createSchema(X146_ALPHA);
x146Register($session, X146_ALPHA, $owned, $tokens);
echo $schema->getName(); // phpmanualx146alphaПолный файл, исходные данные и ожидаемый результат.
Для сравнения двух вариантов импорта создаётся отдельная beta. Полный сценарий сохраняет остаток 3 в alpha и 11 в beta, выбирает beta по имени и читает именно её строку. Это разделение учебных данных, а не обещание изоляции ресурсов сервера.
$beta = $session->createSchema(X146_BETA);
x146Register($session, X146_BETA, $owned, $tokens);
echo $beta->getName(); // phpmanualx146betaПолный файл, исходные данные и ожидаемый результат.
Ошибки, границы и другие варианты
Метод действительно меняет сервер, в отличие от getSchema. У учётной записи должно быть CREATE для нужного имени. В проверенной реализации повторное создание даёт код 1007 и не превращается в получение существующей базы. Не считайте любую ошибку подключения или прав таким повтором. Метод не имеет параметра IF NOT EXISTS: это отдельная конструкция SQL. Имя нельзя брать из неограниченного поля формы. Создание базы — DDL с неявным commit, поэтому обычный rollback окружающей транзакции не отменяет создание. В рабочем приложении подготовка схемы обычно относится к установке или миграции, а не к каждому запросу пользователя.
Сначала различите получение объекта существующей базы и создание. Таблицы, строки и запросы раскрыты в реляционном каталоге. Официальный пример тоже отдельно открывает Session, создаёт именованную базу и обрабатывает исключение; наши архивы дополнительно задают точные права и подтверждают запись чтением.
Версии. Официальная страница не указывает версию появления метода. Фактические различия ниже относятся к PHP 8.3.35, PECL mysql_xdevapi 8.0.30 и MySQL 8.4.13; номер расширения не является номером PHP.
mysql_xdevapi\Session::dropSchemaУбрать свою базу после пробного импорта и убедиться, что соседний результат сохранился.
Официальная страница не указывает версию появления метода. Фактические различия ниже относятся к PHP 8.3.35, PECL mysql_xdevapi 8.0.30 и MySQL 8.4.13; номер расширения не является номером PHP. · mysql_xdevapi
public function mysql_xdevapi\Session::dropSchema(string $schema_name): boolУбрать свою базу после пробного импорта и убедиться, что соседний результат сохранился.
- alpha содержит пробную квитанцию TRIAL, beta — KEEP
- Проверяем точное имя и маркер владения alpha
- dropSchema(alpha) возвращает true; alpha отсутствует
- Повтор даёт false и ожидаемый E_WARNING
- Чтение beta всё ещё возвращает KEEP и 9; отдельно убираем свою beta
Сигнатура, вход и возвращаемое значение
public function mysql_xdevapi\Session::dropSchema(string $schema_name): bool| Параметр | Назначение |
|---|---|
| schema_name | Обязательная строка с точным именем удаляемой базы. Вызов относится ко всей базе, а не к одной строке или выбранной коллекции. |
true означает, что база удалена. false означает, что она не существует или не может быть удалена. При отсутствующей базе официально указан E_WARNING. Причину false нужно различать по контексту и диагностике, а не объявлять любую неудачу успешной уборкой.
Два практических применения
После проверки импорта нужно убрать только alpha. Полная программа затем читает KEEP и 9 из соседней beta. Две функции lab.php проверяют владение и подтверждают отсутствие; они не являются встроенными средствами защиты dropSchema.
x146AssertOwned($session, X146_ALPHA, $owned, $tokens);
$removedNow = $session->dropSchema(X146_ALPHA);
x146MarkRemoved($session, X146_ALPHA, $removed);
var_dump($removedNow); // bool(true)Полный файл, исходные данные и ожидаемый результат.
Уборка нужна и при отказе шага после успешного создания. Полный первый сценарий сохраняет товар, разбирает намеренную ошибку повторного создания и завершается finally. Открытая функция Cleanup проверяет маркер и вызывает dropSchema для объектов своего списка; фрагмент показывает размещение cleanup, а не самостоятельную программу создания.
try {
// Создать и проверить только собственный учебный импорт.
} finally {
$removed = x146Cleanup($session, $owned, $tokens, $removed);
}Полный файл, исходные данные и ожидаемый результат.
Ошибки, границы и другие варианты
Удаление базы уничтожает её постоянные таблицы и данные; DROP требует соответствующего права. Это DDL, а не откатываемое удаление строки. Удаление базы само по себе не отменяет ранее выданные на её имя привилегии. Метод не предлагает IF EXISTS. В нашем профиле повтор возвращает false и одно предупреждение уровня 2 с фразой cannot drop schema и точным именем. Только это преднамеренное предупреждение перехватывается локально; обработчик всегда восстанавливается. Незнакомые предупреждения не подавляются. Проверки имени и маркера рассчитаны на два эксклюзивных учебных имени; это не распределённый замок против одновременной работы другого администратора.
Для отдельной коллекции есть dropCollection, а для выбранных документов — ограниченное удаление черновиков. Не заменяйте удаление одного объекта удалением всей базы. Важное различие с транзакциями строк разобрано в транзакциях базы данных.
Версии. Официальная страница не указывает версию появления метода. Фактические различия ниже относятся к PHP 8.3.35, PECL mysql_xdevapi 8.0.30 и MySQL 8.4.13; номер расширения не является номером PHP.
mysql_xdevapi\Session::getSchemasПостроить список доступных учебных баз и прочитать товар из выбранной, не угадывая индекс массива.
Официальная страница не указывает версию появления метода. Фактические различия ниже относятся к PHP 8.3.35, PECL mysql_xdevapi 8.0.30 и MySQL 8.4.13; номер расширения не является номером PHP. · mysql_xdevapi
public function mysql_xdevapi\Session::getSchemas(): arrayПостроить список доступных учебных баз и прочитать товар из выбранной, не угадывая индекс массива.
- getSchemas возвращает массив Schema
- foreach получает один объект за шаг
- getName даёт имя; оставляем только точные alpha и beta
- Сортируем собственные имена для понятного списка
- Выбираем объект beta по имени; читаем BETA-BOX с остатком 11
Сигнатура, вход и возвращаемое значение
public function mysql_xdevapi\Session::getSchemas(): array| Параметр | Назначение |
|---|---|
| Параметров нет | Видимость определяется текущей Session и её учётной записью. Здесь нет аргумента имени другого пользователя или маски баз. |
Массив объектов Schema, представляющих доступные сеансу схемы. Каждый элемент — объект, а не строка имени. Имя извлекается getName; затем объект можно использовать для работы с содержимым. В официальном примере показан print_r такого массива, включая системную схему; это иллюстрация формы, а не обязательный список каждой установки.
Два практических применения
Для переключателя двух учебных импортов нужен предсказуемый список имён. X146_SCHEMAS в lab.php содержит только alpha и beta. Серверный порядок и числовые ключи не используются как идентификаторы базы.
$names = [];
foreach ($session->getSchemas() as $schema) {
$name = $schema->getName();
if (in_array($name, X146_SCHEMAS, true)) {
$names[] = $name;
}
}
sort($names, SORT_STRING);Полный файл, исходные данные и ожидаемый результат.
Для экрана результата импорта нужно сохранить не только название, но и соответствующий объект. Полная программа получает имя выбранной beta, квалифицирует её таблицу и читает BETA-BOX; затем отдельно подтверждает, что alpha содержит ALPHA-BOX и остаток 3.
$byName = [];
foreach ($session->getSchemas() as $schema) {
$name = $schema->getName();
if (in_array($name, X146_SCHEMAS, true)) {
$byName[$name] = $schema;
}
}
$chosen = $byName[X146_BETA];
echo $chosen->getName(); // phpmanualx146betaПолный файл, исходные данные и ожидаемый результат.
Ошибки, границы и другие варианты
Доступность в списке не доказывает владение и не даёт разрешения на удаление. В MySQL видимость зависит от прав; глобальные привилегии и серверная настройка skip_show_database могут менять поведение. Код не должен обещать, что getSchemas всегда вернёт только прикладные базы. Поэтому публичный результат фильтруется по двум точным именам. В проверенном расширении это массив с числовыми ключами, но конкретные ключи и порядок не закрепляем. Между перечислением и запросом база или права могут измениться: список не блокирует сервер. Ошибка запроса не должна подменяться выдуманным списком; отсутствие нужного имени обрабатывайте как отдельное состояние.
Запись $byName[$name] использует ассоциативный массив, а foreach выполняет перебор элементов. Если известно одно имя и перечень не нужен, используйте getSchema и отдельную проверку существования. Подключение к выбранной базе не обязано требовать нового сетевого сеанса.
Версии. Официальная страница не указывает версию появления метода. Фактические различия ниже относятся к PHP 8.3.35, PECL mysql_xdevapi 8.0.30 и MySQL 8.4.13; номер расширения не является номером PHP.
mysql_xdevapi\Session::listClientsОтличить описанный в справочнике список клиентов от реально доступного API и увидеть собственные соединения.
Официальная страница не указывает версию появления метода. Фактические различия ниже относятся к PHP 8.3.35, PECL mysql_xdevapi 8.0.30 и MySQL 8.4.13; номер расширения не является номером PHP. · mysql_xdevapi
public function mysql_xdevapi\Session::listClients(): arrayОтличить описанный в справочнике список клиентов от реально доступного API и увидеть собственные соединения.
- На странице есть контракт listClients
- method_exists проверяет установленную реализацию
- В PECL 8.0.30 результат false: вызов отсутствующего метода не выполняется
- Для собственной диагностики две Session получают SQL CONNECTION_ID
- Из SHOW PROCESSLIST оставляем только два собственных SQL ID и проверяем независимое закрытие
Сигнатура, вход и возвращаемое значение
public function mysql_xdevapi\Session::listClients(): array| Параметр | Назначение |
|---|---|
| Параметров нет | Официально метод относится к клиентским подключениям на MySQL-сервере текущей Session. Параметров фильтра или выдачи прав на странице нет. |
Официальный контракт описывает массив текущих клиентов с полями client_id, user, host и sql_session. В примере client_id и sql_session — разные числа; они обозначают разные идентификаторы. Конкретные примерные значения и число клиентов не фиксированы. В нашей PECL mysql_xdevapi 8.0.30 метод отсутствует, поэтому такой массив от него не получен и не имитируется.
Два практических применения
При подготовке административного экрана сначала выясните, есть ли API в том PHP, которым реально запущена программа. Наличие страницы документации не является результатом этой проверки. В нашем полном примере отсутствие отмечено явно; установка дополнительных прав сервера не добавит метод в PHP-класс.
$available = method_exists($session, 'listClients');
var_dump($available); // bool(false) в выбранном профилеПолный файл, исходные данные и ожидаемый результат.
Чтобы связать собственную рабочую задачу с SQL-соединением, достаточно получить его ID. Полный запуск открывает два отдельных сеанса и сопоставляет их ID с собственными строками PROCESSLIST; после закрытия второго первый ещё выполняет SELECT 1. Это полезная узкая альтернатива, а не выданная за listClients подделка.
$row = $session->sql('SELECT CONNECTION_ID() AS id')->execute()->fetchOne();
$mySqlId = $row['id'];
echo $mySqlId; // фактическое число текущего соединенияПолный файл, исходные данные и ожидаемый результат.
Ошибки, границы и другие варианты
Справочная сигнатура сохранена для полноты, но копируемые примеры не вызывают отсутствующий метод. В исходниках PECL 8.0.30 listClients не зарегистрирован в Session, и реальная проверка подтверждает false. Другую сборку нужно проверять заново: method_exists сообщает наличие реализации, а не достаточность прав или успех будущего запроса. Поля SQL PROCESSLIST Id/User/Host не следует механически переименовывать в X client_id/user/host/sql_session. Без глобального PROCESS MySQL позволяет обычному пользователю видеть собственные потоки; приложение здесь отбирает ещё уже — только две открытые им Session. Не выводите чужие SQL-тексты и не выдавайте глобальные права ради этого урока.
| Поле официального контракта | Что различать |
|---|---|
| client_id | Идентификатор клиента; его не получаем подменой из CONNECTION_ID. |
| user | Пользователь клиентского подключения, а не имя прикладного посетителя. |
| host | Узел подключения; не адрес страницы сайта. |
| sql_session | Идентификатор SQL-сеанса, отдельный от client_id. |
Жизненный цикл настоящих соединений показан в Client и Session. Проверка версии расширения и доступности класса — в диагностике установленного PHP.
Версии. Официальная страница не указывает версию появления метода. Фактические различия ниже относятся к PHP 8.3.35, PECL mysql_xdevapi 8.0.30 и MySQL 8.4.13; номер расширения не является номером PHP.
Источники и обсуждения
- PHP Manual — Session::createSchema
- PHP Manual — Session::dropSchema
- PHP Manual — Session::getSchemas
- PHP Manual — Session::listClients
- MySQL 8.4: CREATE DATABASE
- MySQL 8.4: DROP DATABASE
- MySQL 8.4: видимость SHOW DATABASES
- MySQL 8.4: собственные подключения и SHOW PROCESSLIST
- MySQL 8.4: неявный commit для DDL
- PHP Manual: method_exists
- PECL mysql_xdevapi 8.0.30: реализация Session
Код и руководство с собой
Скачайте самостоятельные примеры с исходными данными, инструкциями и ожидаемыми результатами. Примеры HTTP и фрагменты для проекта помечены отдельно: им нужно указанное окружение.
Код не исполняется на сайте. Скачайте нужный пример и запускайте его в своём локальном окружении.
Материалы сверены 2026-10-12. Примеры и объяснения написаны для этого руководства; источники и обсуждения доступны в каждой главе.


