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

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

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

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

Глава 135 · После первого подключения, простой таблицы и ассоциативного массива

MySQL X DevAPI: учебная база, список схем и собственные подключения

Создать изолированную базу импорта, перечитать товар, выбрать одну из двух схем и убрать свою, сохранив соседнюю. Полный контракт listClients с честной проверкой отсутствующего метода и реальной диагностикой двух соединений.

Учебный импорт в отдельной базе: от создания до уборки

Нужно проверить импорт товаров, не смешав пробные строки с рабочим каталогом. Для этого приготовим отдельную базу, запишем кабель с остатком 7 и убедимся чтением, что данные действительно сохранились. Затем сравним два учебных импорта и покажем, как убрать один, сохранив контрольную запись во втором. Последняя задача — понять, какие соединения открыла наша программа, даже если найденного в документации метода нет в установленном расширении.

Где находится товар
  1. MySQL-сервер принимает соединение Session
  2. База phpmanualx146alpha объединяет свои таблицы и коллекции
  3. Таблица products задаёт столбцы sku, product_name, price_kopecks, stock
  4. Одна строка содержит CABLE, Кабель, 12000 копеек, 7 штук
  5. 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

От создания до явной уборки
  1. Проверяем отсутствие двух учебных имён
  2. Создаём только нужные этому сценарию базы
  3. Сразу записываем собственный список и закрытый маркер владения
  4. Выполняем операции и перечитываем данные
  5. finally проверяет точное имя и маркер, затем явно удаляет собственные базы
  6. Проверяем отсутствие alpha и beta; закрываем Session

DDL — команды изменения структуры, в том числе CREATE DATABASE и DROP DATABASE. MySQL выполняет для таких команд неявный commit: оборачивание их обычной транзакцией не даёт возможности вернуть удалённую базу через rollback. finally лишь выполняет предусмотренную явную уборку при выходе из блока. История создания и маркер нужны, чтобы не принять заранее существующее содержимое за своё. Пароль и маркер не включаются в результат.

Восстановление после аварии процесса и параллельная работа нескольких администраторов — отдельные задачи. Наши два имени выделены одному учебному запуску; не запускайте архивы одновременно. При удалении базы её права не исчезают автоматически. Если лаборатория больше не нужна, администратор отдельно удаляет выделенную учётную запись по README. У рабочего сайта такую базу не создают и не удаляют при каждом открытии страницы.

Какие результаты можно открыть

Каждый архив запускается самостоятельно. Первые три создают и заполняют свои схемы, четвёртый вообще не создаёт баз: он исследует только два собственных подключения. result.html показывает читаемую таблицу, result.json — фактически прочитанные строки, типы и очищенные наблюдения. metrics.json проверяет значения и отсутствие учебных баз после завершения. Код не заменяет недоступный сервер заранее подготовленным ответом; неуспешное подключение остаётся ошибкой.

Создать базу пробного импорта и перечитать сохранённый товар

Перед настоящим импортом поставщика хочется проверить формат товара в отдельной базе: кабель за 120 рублей, остаток 7 штук. Создадим именно новую учебную базу, запишем строку и прочитаем её обратно. Затем намеренно повторим создание того же имени: отказ не должен превращаться в удаление базы с уже сохранённым товаром.

Путь действия и проверки
  1. Имя alpha свободно и разрешено отдельному учебному аккаунту
  2. createSchema создаёт базу, Register отмечает владение текущего запуска
  3. Внутри alpha создаётся products; INSERT сохраняет CABLE
  4. SELECT возвращает название, цену 12000 копеек и остаток 7
  5. Повторный createSchema даёт отказ; чтение подтверждает прежний товар
  6. 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 и используем выбранный объект для точного чтения.

Путь действия и проверки
  1. Созданы alpha с ALPHA-BOX: 3 и beta с BETA-BOX: 11
  2. getSchemas возвращает массив объектов, видимых текущей Session
  3. getName извлекает имя; разрешённый список оставляет только alpha и beta
  4. Сортируем имена явно, сохраняем связь имя → Schema
  5. Выбор beta даёт её объект, а запрос читает BETA-BOX: 11
  6. 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
Остаток в beta11
Остаток отдельной карточки alpha3
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 удалится позже, только в своей процедуре завершения.

Путь действия и проверки
  1. Alpha содержит TRIAL: 2, beta содержит KEEP: 9
  2. AssertOwned проверяет точное имя, запись текущего запуска и приватный маркер alpha
  3. dropSchema(alpha) возвращает true, проверка подтверждает отсутствие
  4. Повторный drop отсутствующей alpha возвращает false и E_WARNING
  5. SELECT из beta снова даёт KEEP: 9
  6. 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 пока существуетда
Код квитанции в betaKEEP
Позиций в квитанции beta9
Квитанция beta не измениласьда
Оба учебных имени свободны после запускада
Самостоятельный файл и архив для запуска
Повторить удаление alpha и сохранить соседнюю 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. Затем решим более узкую полезную задачу: увидим два соединения, которые открыла наша программа, и подтвердим, что закрытие второго не мешает первому выполнять запросы.

Путь действия и проверки
  1. Реальная Session → method_exists(listClients): проверка реализации, не прав базы
  2. Первая Session возвращает свой CONNECTION_ID
  3. Вторая отдельная Session возвращает другой CONNECTION_ID
  4. SHOW PROCESSLIST фильтруется только по двум собственным ID
  5. close закрывает вторую Session, SELECT 1 подтверждает пригодность первой
  6. Внешний 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 доступеннет
Открыто собственных Session2
SQL connection ID различаютсяда
Тип первого SQL IDint
Тип второго SQL IDint
Собственных строк найдено в PROCESSLIST2
Строки соответствуют двум полученным IDда
Вторая Session закрытада
Первая Session отвечает после закрытия второй1
Оба учебных имени свободны после запускада
Самостоятельный файл и архив для запуска
Проверить доступный API и увидеть два собственных соединенияСамостоятельный пример
<?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

Создать отдельную базу для пробного импорта, прежде чем помещать в неё товары.

Как действие связано с результатом
  1. Открытая Session знает сервер и учётную запись
  2. createSchema получает свободное имя alpha
  3. Сервер создаёт базу и возвращает Schema
  4. Отдельные CREATE TABLE и INSERT сохраняют CABLE
  5. 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

Убрать свою базу после пробного импорта и убедиться, что соседний результат сохранился.

Как действие связано с результатом
  1. alpha содержит пробную квитанцию TRIAL, beta — KEEP
  2. Проверяем точное имя и маркер владения alpha
  3. dropSchema(alpha) возвращает true; alpha отсутствует
  4. Повтор даёт false и ожидаемый E_WARNING
  5. Чтение 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

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

Как действие связано с результатом
  1. getSchemas возвращает массив Schema
  2. foreach получает один объект за шаг
  3. getName даёт имя; оставляем только точные alpha и beta
  4. Сортируем собственные имена для понятного списка
  5. Выбираем объект 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 и увидеть собственные соединения.

Как действие связано с результатом
  1. На странице есть контракт listClients
  2. method_exists проверяет установленную реализацию
  3. В PECL 8.0.30 результат false: вызов отсутствующего метода не выполняется
  4. Для собственной диагностики две Session получают SQL CONNECTION_ID
  5. Из 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.

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

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

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

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

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