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

Дерево руководства
Глава 136 · Перед первым подключением; сборка — отдельный расширенный маршрут
MySQL X DevAPI: установка, настройки PHP и проверка соединения
Проверить конкретный PHP и ini, прочитать все настройки X DevAPI, отличить отказ порта и собрать модуль с реальной загрузкой.
От сообщения об ошибке к работающему подключению
Новый проект должен обратиться к MySQL, но прежде чем писать запросы, нужно собрать четыре работающих звена: исполняющий PHP, его расширение mysql_xdevapi, X Protocol на сервере и подходящую учётную запись. Здесь результатом будут конкретные отчёты: какой PHP запущен, какие настройки действуют, почему соединение отклонено и загрузился ли действительно собранный модуль.
- example.php выполняет конкретный php.exe / php-fpm
- При запуске PHP читает ini и загружает DLL / SO
- mysql_xdevapi открывает Session через X Protocol
- MySQL проверяет соединение и выполняет SELECT
- Строка ready = 1 возвращается в PHP
Расширение — модуль, добавляющий функции и классы в PHP. DLL в Windows и SO в Linux — разные форматы такого модуля; переименование файла не делает их взаимозаменяемыми. SAPI — способ связи PHP с окружением: консоль, PHP-FPM или серверный модуль. ini — текстовая конфигурация, читаемая при запуске PHP. Протокол — правила общения клиента и сервера; X Protocol и классический MySQL используют разные точки входа.
Три отчёта не подключаются к базе: окружение, настройки и инструменты и совместимость. Проверка соединения выполняет только чтение и контролируемый отказ локального порта. Ни одна из четырёх программ не создаёт базу, таблицу или коллекцию.
Архив каждого урока содержит example.php, lab.php и README.txt. Запускайте файл в отдельном каталоге: он сохранит result.html для чтения человеком, result.json с фактическими наблюдениями и metrics.json со сводными проверками. Полные личные пути, URI и пароль в опубликованные отчёты не включаются. Для первого отчёта mysql_xdevapi может отсутствовать: это штатно объясняемый результат. Остальные уроки требуют загруженного расширения; только проверке соединения нужен PHPMAN_MYSQLX_URI.
Входные знания: переменные, условия, массивы, try/finally и исключения. Короткий пример перед полной программой объясняет новое действие; диагностические обёртки полного файла нужны для сохранения результата и гарантированного закрытия ресурсов. Первая коллекция объясняет исходный mysqlx URI, а отдельные учебные базы — их создание, владение и удаление.
Основные PHP-примеры имеют профиль Windows: PHP 8.3.35 NTS x64, mysql_xdevapi 8.0.30 и MySQL 8.4.13. Дополнительно ниже показана отдельная сборка расширения под Linux/PHP 8.3.6. Это два разных бинарных окружения. Успешная загрузка Linux-модуля не означает, что в Linux был проверен доступ к базе; соединение проверяется отдельно в Windows-примере.
Установка и настройка: полный маршрут
Нужно запустить импорт на новом компьютере и понять, на каком шаге он готов к работе. Официальный раздел Installing/Configuring объединяет требования, установку, настройки и сборку. Пройдём эти четыре шага в том порядке, в котором они помогают получить первый осмысленный результат.
| Шаг | Что требуется | Чем подтверждается |
|---|---|---|
| Требования | MySQL 8+ с X Plugin; для сборки — Boost 1.53+, OpenSSL и Protobuf | Версии процесса и серверный X-порт проверяются раздельно |
| Установка | Внешнее расширение mysql_xdevapi, подходящее данному PHP | extension_loaded и phpversion в нужном SAPI |
| Конфигурация | Правильный ini и все шесть директив | Прочитанные local/global/access, изменение и восстановление INI_ALL |
| Сборка из исходников | Совместимый SDK или phpize, компилятор и библиотеки | Собранный модуль действительно загружен отдельным PHP |
Самая короткая проверка перед установкой
php -v
php --ini
php -m
php --ri mysql_xdevapiЭти команды обращаются к php, найденному оболочкой. -v показывает версию и сборку, --ini — основной и сканируемые ini, -m — загруженные модули, --ri — сведения конкретного расширения. Если в PATH несколько PHP, укажите полный путь к нужному исполняемому файлу. Отсутствие расширения в --ri не доказывает отсутствие DLL на диске: оно доказывает, что этот процесс её не загрузил. Первый самостоятельный отчёт сохраняет те же различия в читаемой таблице.
Готовое расширение: установить файл и включить его — два шага
mysql_xdevapi не входит в базовую поставку PHP как обязательная встроенная возможность. Официальный архив расширения доступен в PECL. Установка библиотеки Composer с похожим названием не добавляет нативные классы mysql_xdevapi в PHP. Сначала нужен совместимый бинарный модуль либо сборка исходника; затем его должен загрузить конкретный PHP-процесс.
Для Windows сопоставьте версию PHP, разрядность x86/x64, TS/NTS, debug/release и компиляторную совместимость. Выбирайте файл с официальной страницы расширения, если для вашего профиля он доступен. DLL для другой ветки или архитектуры нельзя исправить переименованием. Отсутствующая зависимая DLL тоже может проявиться как ошибка загрузки основного файла; читайте startup-журнал, а не только проверяйте существование php_mysql_xdevapi.dll.
; Фрагмент php.ini своего PHP; путь замените своим.
extension_dir = "C:\PATH\TO\PHP\ext"
extension = mysql_xdevapiИмена расширений без префикса php_ и суффикса .dll поддерживаются PHP начиная с 7.2. Полное имя файла и абсолютный путь также применяются в подходящей конфигурации. Не добавляйте модуль дважды в основной и дополнительный ini. После изменения настройку должен прочитать новый процесс: CLI запускается заново, а PHP-FPM или модуль веб-сервера надо перезапустить штатным способом.
В Debian/Ubuntu модуль часто включают отдельным файлом в mods-available и утилитой phpenmod, которая создаёт ссылки для выбранных SAPI. Это механизм дистрибутива, а не универсальная команда PHP для Windows и всех Linux. Официальная страница mysql_xdevapi.installation содержит исторический пример Ubuntu 18.04/PHP 7.2 и pecl install; он показывает этапы, но его пакеты и сторонний PPA не нужно копировать на Ubuntu 24.04. Общая документация PHP теперь рекомендует PIE вместо устаревающего установщика PECL; конкретную PIE-команду для этого пакета мы без подтверждённой поддержки не придумываем. Ниже используется точный архив 8.0.30 и ручной phpize-маршрут.
Как не перепутать ini сайта и консоли
PHP читает настройки в порядке своей конфигурации запуска. Специальный php-SAPI.ini может иметь приоритет над обычным php.ini; дополнительные ini берутся из каталога сканирования, на который может влиять PHP_INI_SCAN_DIR. --ini показывает именно CLI. Для сайта проверяйте значения внутри обслуживающего его процесса, например защищённым временным диагностическим файлом. CLI-ключ -c явно выбирает файл или каталог, -n отключает чтение ini, -d задаёт настройку только этому запуску. Подробнее — выбор конфигурационного файла.
& 'C:\PATH\TO\PHP\php.exe' -c 'C:\PATH\TO\PHP\php.ini' .\example.php
# Изолированная загрузка конкретной DLL без чтения ini:
& 'C:\PATH\TO\PHP\php.exe' -n -d 'extension=C:\PATH\TO\PHP\ext\php_mysql_xdevapi.dll' .\example.phpПервый вариант проверяет вашу обычную конфигурацию. Второй нужен для локализации проблемы и должен учитывать зависимости конкретной сборки; это не команда установки на веб-сервер. В наших Windows-запусках DLL загружается абсолютным путём. Поэтому проверка существования extension_dir и проверка extension_loaded намеренно показаны разными строками.
Два реальных применения
При переносе CLI-импорта сравните сохранённые отчёты старого и нового PHP, затем выполните контрольный SELECT. При отказе только сайта начните с SAPI и его ini: рабочая консоль не доказывает загрузку модуля PHP-FPM. Если модуль загрузился, но getSession не сработал, переходите к адресу X Protocol, TLS и учётной записи — переустановка DLL уже не является первым объяснением.
Официальная структура Installing/Configuring: PHP Manual. Отчёт окружения даёт начальную точку, контрольное соединение завершает маршрут.
Понять, какой PHP выполняет файл и загружен ли нужный модуль
В консоли запрос работает, а сайт пишет «Class mysql_xdevapi\Session not found». Сначала надо узнать, одним ли PHP они выполняются и загружен ли модуль именно в проблемном процессе. Отчёт работает и без mysql_xdevapi: он показывает причину остановки, а не пытается подключиться несуществующим классом.
- Запущенный PHP → версия, SAPI, TS/NTS и разрядность
- Его startup-настройки → основной и дополнительные ini
- extension_loaded → модуль действительно есть или отсутствует
- Отчёт → настройка модуля либо проверка соединения
Начните с одного действия
<?php
$loaded = extension_loaded('mysql_xdevapi');
echo $loaded ? 'Модуль загружен' : 'Модуль не загружен';Строка mysql_xdevapi — имя расширения. extension_loaded возвращает логическое true или false. Знак ? выбирает первый текст при true, а часть после : — второй. Это только проверка текущего процесса. PHP_SAPI показывает способ запуска: cli для консоли, например fpm-fcgi для PHP-FPM. PHP_ZTS различает сборки TS и NTS, PHP_INT_SIZE * 8 — разрядность целого числа. Полный файл отдельно спрашивает PHP о прочитанных ini и наличии класса, не вызывая ни одного метода Session.
Результат в указанном Windows-профиле
| Наблюдение | Результат |
|---|---|
| PHP | 8.3.35 |
| Режим запуска | cli |
| Разрядность | 64 |
| Сборка | NTS |
| Имя PHP | php.exe |
| mysql_xdevapi загружен | да |
| Версия расширения | 8.0.30 |
| Класс Session доступен | да |
| Основной ini прочитан | нет |
| Дополнительные ini прочитаны | нет |
| Каталог extension_dir существует | нет |
| Следующий шаг | Можно проверять соединение |
В другом PHP значения версии, SAPI, ini и инструментов могут отличаться: это и есть полезная часть диагностики. Сравнивайте наблюдения и причину, а не подгоняйте свой компьютер под текст таблицы.
Самостоятельная программа и архив
<?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 {
$loaded=extension_loaded('mysql_xdevapi');
$version=$loaded ? phpversion('mysql_xdevapi') : false;
$sessionClass=class_exists('mysql_xdevapi\Session',false);
$ini=php_ini_loaded_file();
$scanned=php_ini_scanned_files();
$extensionDirectory=ini_get('extension_dir');
$directoryExists=is_string($extensionDirectory) && is_dir($extensionDirectory);
$binaryName=basename(str_replace('\\','/',PHP_BINARY));
$iniName=$ini===false ? null : basename(str_replace('\\','/',$ini));
$scannedNames=[];
if (is_string($scanned)) {
foreach (preg_split('/,\s*/',trim($scanned)) as $path) {
if ($path!=='') {$scannedNames[]=basename(str_replace('\\','/',$path));}
}
}
$decision=$loaded ? 'Можно проверять соединение' : 'Сначала установите и включите совместимый модуль';
$rows=[['PHP',PHP_VERSION],['Режим запуска',PHP_SAPI],['Разрядность',PHP_INT_SIZE*8],
['Сборка',PHP_ZTS ? 'TS' : 'NTS'],['Имя PHP',$binaryName],
['mysql_xdevapi загружен',$loaded],['Версия расширения',$version ?: 'не загружено'],
['Класс Session доступен',$sessionClass],['Основной ini прочитан',$ini!==false],
['Дополнительные ini прочитаны',is_string($scanned) && trim($scanned)!==''],
['Каталог extension_dir существует',$directoryExists],['Следующий шаг',$decision]];
$checks=['report_matches_loaded_state'=>$loaded ? is_string($version) && $sessionClass : $version===false && !$sessionClass,
'binary_identity_available'=>$binaryName!=='','no_database_contact'=>$session===null,
'ini_discovery_recorded'=>is_array($scannedNames)];
$observations+=['extension_loaded'=>$loaded,'extension_version'=>$version ?: null,
'binary_name'=>$binaryName,'binary_absolute_path_available_locally'=>PHP_BINARY!=='',
'sapi'=>PHP_SAPI,'bits'=>PHP_INT_SIZE*8,'zts'=>(bool)PHP_ZTS,'debug'=>(bool)PHP_DEBUG,
'loaded_ini_name'=>$iniName,'scanned_ini_names'=>$scannedNames,
'extension_dir_exists'=>$directoryExists,'private_paths_omitted'=>true,
'database_connection_attempted'=>false];
} finally {
if ($session instanceof mysql_xdevapi\Session) {
try {
$removed=x146Cleanup($session,$owned,$tokens,$removed);
$observations['schema_absence_after']=x146Absence($session);
} finally {$session->close();}
}
}
x146Report('mysql146-x-environment-report', 'Понять, какой PHP выполняет файл и загружен ли нужный модуль', $rows, $checks, $observations, $runtime, $owned, $removed);
Ожидаемый результат
PHP — 8.3.35
Режим запуска — cli
Разрядность — 64
Сборка — NTS
Имя PHP — php.exe
mysql_xdevapi загружен — да
Версия расширения — 8.0.30
Класс Session доступен — да
Основной ini прочитан — нет
Дополнительные ini прочитаны — нет
Каталог extension_dir существует — нет
Следующий шаг — Можно проверять соединение
CLI-файл сохраняет result.html/result.json. Ничего не устанавливает и не создаёт объектов базы. Не открывает соединение с MySQL.
Таблица фактического запуска · Наблюдения и проверки JSON.
Что этот результат подтверждает
Установленный файл DLL/SO ещё не означает, что этот процесс его загрузил. Даже одинаковый путь ini не доказывает одинаковые бинарные файлы, рабочий каталог и найденные зависимости. В проверенном Windows-профиле ini не читается из-за -n, а DLL передана абсолютным путём через -d extension=...; поэтому модуль доступен при несуществующем extension_dir. Это наблюдение конкретного запуска, а не совет оставить неверную настройку веб-сервера. В опубликованном JSON пути скрыты; для локального расследования можно отдельно вывести PHP_BINARY, php_ini_loaded_file() и php_ini_scanned_files(). Не оставляйте такой диагностический обработчик открытым в интернете.
Вопрос показывает, почему одного совпадения ini недостаточно: разработчик видел разные результаты запуска. Мы сопоставляем исполняющий PHP, SAPI, загрузку модуля и пути поиска; старые ограничения путей Windows 7 не выдаём за универсальное правило современных PHP. Расширение работает в CLI, но отсутствует в Apache при том же ini.
Попробуйте другое условие
Запустите файл другим php.exe или в PHP-FPM. Какие поля могут измениться, даже если example.php остался тем же?
Разбор
Сравните версию и имя PHP, SAPI, TS/NTS, разрядность, загруженный модуль и имена ini. После изменения конфигурации перезапустите обслуживающий сайт процесс; новый CLI-процесс перечитает настройки при своём запуске. При отсутствии расширения отчёт должен завершиться с полезным следующим шагом; это не успешное соединение с базой.
Прочитать все настройки X DevAPI и восстановить временное изменение
Длинный CLI-импорт должен временно изменить разрешённую настройку и затем вернуть прежний режим. Перед изменением посмотрим, какие параметры принадлежат расширению и какие из них вообще доступны из PHP-кода. Отказ ini_set тоже будет видимым результатом.
- ini_get_all → 6 зарегистрированных директив
- INI_ALL collect_statistics: 1 → 0
- finally → восстановлено прежнее 1
- INI_SYSTEM: ini_set вернул false → значение осталось 0
Начните с одного действия
<?php
$before = ini_get('xmysqlnd.collect_statistics');
$previous = ini_set('xmysqlnd.collect_statistics', '0');
var_dump($previous); // В нашем профиле string(1) "1".
if ($previous !== false) {
ini_set('xmysqlnd.collect_statistics', $before);
}ini_get читает текущее значение в виде строки либо false для неизвестного имени. ini_set возвращает предыдущее значение либо false при отказе; сравниваем строго через !== false, иначе строка "0" вводит в заблуждение. Массив ini_get_all содержит global_value, local_value и числовой access. В полной программе try/finally гарантирует восстановление прежнего локального значения после блока. Мы проверяем изменения настройки, а не объявляем ускорение импорта.
Результат в указанном Windows-профиле
| Наблюдение | Результат |
|---|---|
| Зарегистрировано настроек | 6 |
| collect_statistics до изменения | 1 |
| collect_statistics внутри блока | 0 |
| collect_statistics восстановлена | да |
| INI_SYSTEM изменена через ini_set | нет |
| collect_memory_statistics осталось прежним | да |
В другом PHP значения версии, SAPI, ini и инструментов могут отличаться: это и есть полезная часть диагностики. Сравнивайте наблюдения и причину, а не подгоняйте свой компьютер под текст таблицы.
Самостоятельная программа и архив
<?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 {
if (!extension_loaded('mysql_xdevapi')) {throw new RuntimeException('Для этого урока включите mysql_xdevapi. Сначала запустите environment-report.');}
$beforeAll=ini_get_all('mysql_xdevapi',true);
if (!is_array($beforeAll)) {throw new RuntimeException('Не удалось получить настройки расширения.');}
$option='xmysqlnd.collect_statistics';
$before=ini_get($option);
if ($before===false) {throw new RuntimeException('Настройка не зарегистрирована.');}
$previous=false; $during=null;
try {
$previous=ini_set($option,'0');
if ($previous===false) {throw new RuntimeException('Текущее окружение запретило изменение INI_ALL.');}
$during=ini_get($option);
} finally {if ($previous!==false) {ini_set($option,$before);}}
$after=ini_get($option);
$systemOption='xmysqlnd.collect_memory_statistics';
$systemBefore=ini_get($systemOption);
$systemAttempt=ini_set($systemOption,'1');
$systemAfter=ini_get($systemOption);
$rows=[['Зарегистрировано настроек',count($beforeAll)],
['collect_statistics до изменения',$before],['collect_statistics внутри блока',$during],
['collect_statistics восстановлена',$after===$before],
['INI_SYSTEM изменена через ini_set',$systemAttempt!==false],
['collect_memory_statistics осталось прежним',$systemAfter===$systemBefore]];
$checks=['all_six_directives'=>count($beforeAll)===6,
'temporary_override_and_restore'=>$previous===$before && $during==='0' && $after===$before,
'system_change_rejected'=>$systemAttempt===false && $systemAfter===$systemBefore,
'no_database_contact'=>$session===null];
$observations+=['directives'=>$beforeAll,'collect_statistics'=>['before'=>$before,'returned_previous'=>$previous,'during'=>$during,'after'=>$after],
'system_change'=>['name'=>$systemOption,'before'=>$systemBefore,'returned'=>$systemAttempt,'after'=>$systemAfter],
'database_connection_attempted'=>false];
} finally {
if ($session instanceof mysql_xdevapi\Session) {
try {
$removed=x146Cleanup($session,$owned,$tokens,$removed);
$observations['schema_absence_after']=x146Absence($session);
} finally {$session->close();}
}
}
x146Report('mysql146-x-runtime-configuration', 'Прочитать все настройки X DevAPI и восстановить временное изменение', $rows, $checks, $observations, $runtime, $owned, $removed);
Ожидаемый результат
Зарегистрировано настроек — 6
collect_statistics до изменения — 1
collect_statistics внутри блока — 0
collect_statistics восстановлена — да
INI_SYSTEM изменена через ini_set — нет
collect_memory_statistics осталось прежним — да
CLI-файл сохраняет result.html/result.json. Ничего не устанавливает и не создаёт объектов базы. Не открывает соединение с MySQL.
Таблица фактического запуска · Наблюдения и проверки JSON.
Что этот результат подтверждает
Пример рассчитан на шесть директив PECL 8.0.30; иная версия может зарегистрировать другой набор. Для INI_SYSTEM правка php.ini и перезапуск PHP-процесса отличаются от ini_set. Не путайте глобальное значение конфигурации этого процесса с общей переменной всех серверов. В release-сборке debug/trace не превращаются в обещание полноценной отладки. Не меняйте net_read_timeout в надежде исправить неслушающий TCP-порт: время подключения и чтение — разные этапы. Полный перечень и ограничения исходника разобраны ниже.
Попробуйте другое условие
Внешний код уже установил collect_statistics в "0". Какое значение надо вернуть после вашей функции?
Разбор
Сохраните значение перед входом и верните именно его. Не устанавливайте безусловно "1" и не заменяйте это ini_restore: тот восстанавливает исходную настройку конфигурации, а не обязательно локальное значение вызывающего кода.
Отличить рабочий X Protocol от отказа порта и проверить TLS
Расширение уже загружено, но приложение не соединяется с базой. Теперь отделим PHP-конфигурацию от доступности X-порта, а доступность порта — от работающего запроса и шифрования. В одном файле получим настоящий SELECT, TLS-параметры и контролируемый отказ другого локального порта.
- mysqlx URI → X Protocol, а не классический MySQL-порт
- Session → SELECT ready = 1
- Mysqlx_ssl_* → TLSv1.3 и согласованный шифр
- Зарезервированный порт без listen → ошибка 2002
- Прежняя Session → снова ready = 1
Начните с одного действия
$session = mysql_xdevapi\getSession(getenv('PHPMAN_MYSQLX_URI'));
$row = $session->sql('SELECT 1 AS ready')->execute()->fetchOne();
echo $row['ready']; // 1
$session->close();URI — строка с протоколом, учётной записью, адресом и параметрами; getenv берёт её из окружения, а не из опубликованного исходника. getSession создаёт соединение. sql подготавливает команду, execute выполняет её, fetchOne читает строку; AS ready даёт столбцу понятное имя. Полный файл также использует fetchAll для таблицы TLS и защищает закрытие через finally. bind тестового TCP-сокета резервирует адрес, но без STREAM_SERVER_LISTEN не принимает соединения: так отказ воспроизводится без обращения к чужому серверу.
Результат в указанном Windows-профиле
| Наблюдение | Результат |
|---|---|
| Первый контрольный SELECT | 1 |
| X Protocol использует TLS | да |
| Версия TLS | TLSv1.3 |
| Шифр X Protocol | TLS_AES_256_GCM_SHA384 |
| Код отказа неслушающего порта | 2002 |
| Рабочее соединение после отказа | 1 |
В другом PHP значения версии, SAPI, ini и инструментов могут отличаться: это и есть полезная часть диагностики. Сравнивайте наблюдения и причину, а не подгоняйте свой компьютер под текст таблицы.
Самостоятельная программа и архив
<?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('Одно из учебных имён занято; запуск прекращён без удаления.');
}
$first=$session->sql('SELECT 1 AS ready')->execute()->fetchAll();
$statusRows=$session->sql("SHOW SESSION STATUS LIKE 'Mysqlx_ssl%'")->execute()->fetchAll();
$tls=[];
foreach ($statusRows as $row) {$tls[$row['Variable_name']]=$row['Value'];}
$bound=null; $failure=null; $badPort=null;
try {
$bound=stream_socket_server('tcp://127.0.0.1:0',$socketError,$socketMessage,STREAM_SERVER_BIND);
if ($bound===false) {throw new RuntimeException('Не удалось зарезервировать локальный тестовый порт.');}
$address=stream_socket_get_name($bound,false);
$badPort=(int)substr($address,strrpos($address,':')+1);
if ($badPort<1 || $badPort>65535) {throw new RuntimeException('Не удалось определить тестовый порт.');}
$parts=parse_url($config['uri']);
$badUri='mysqlx://'.($parts['user']??'').':'.($parts['pass']??'').'@127.0.0.1:'.$badPort.
'?ssl-mode=required&connect-timeout=1000';
$failure=x146Failure(static function() use($badUri): void {
$unexpectedSession=mysql_xdevapi\getSession($badUri);
$unexpectedSession->close();
});
} finally {
if (is_resource($bound)) {fclose($bound);}
}
$last=$session->sql('SELECT 1 AS ready')->execute()->fetchAll();
$errorCode=$failure[0]['code']??null;
$rows=[['Первый контрольный SELECT',$first[0]['ready']],
['X Protocol использует TLS',($tls['Mysqlx_ssl_active']??'')==='ON'],
['Версия TLS',$tls['Mysqlx_ssl_version']??''],
['Шифр X Protocol',$tls['Mysqlx_ssl_cipher']??''],
['Код отказа неслушающего порта',$errorCode],
['Рабочее соединение после отказа',$last[0]['ready']]];
$checks=['real_query_succeeded'=>$first===[['ready'=>1]],
'x_protocol_tls_observed'=>($tls['Mysqlx_ssl_active']??'')==='ON' && ($tls['Mysqlx_ssl_cipher']??'')!=='',
'bounded_expected_failure'=>$failure!==null && $errorCode===2002,
'original_session_still_usable'=>$last===[['ready'=>1]],
'no_schema_objects_created'=>$owned===[]];
$observations+=['first_query'=>$first,'x_protocol_tls'=>[
'active'=>$tls['Mysqlx_ssl_active']??null,'cipher'=>$tls['Mysqlx_ssl_cipher']??null,'version'=>$tls['Mysqlx_ssl_version']??null],
'failed_endpoint'=>['host'=>'127.0.0.1','port'=>$badPort,'bound_without_listen'=>true,'connect_timeout_ms'=>1000,'failure'=>$failure],
'last_query'=>$last,'uri_omitted'=>true];
} finally {
if ($session instanceof mysql_xdevapi\Session) {
try {
$removed=x146Cleanup($session,$owned,$tokens,$removed);
$observations['schema_absence_after']=x146Absence($session);
} finally {$session->close();}
}
}
x146Report('mysql146-x-connection-checklist', 'Отличить рабочий X Protocol от отказа порта и проверить TLS', $rows, $checks, $observations, $runtime, $owned, $removed);
Ожидаемый результат
Первый контрольный SELECT — 1
X Protocol использует TLS — да
Версия TLS — TLSv1.3
Шифр X Protocol — TLS_AES_256_GCM_SHA384
Код отказа неслушающего порта — 2002
Рабочее соединение после отказа — 1
CLI-файл сохраняет result.html/result.json. Ничего не устанавливает и не создаёт объектов базы. Открывает собственную Session, проверяет чтение и закрывает её.
Таблица фактического запуска · Наблюдения и проверки JSON.
Что этот результат подтверждает
Нужны MySQL 8+ с включённым X Plugin, доступный X-порт, разрешённая учётная запись и совместимый TLS. Обычный порт 3306 предназначен для классического протокола; стандартный X-порт — 33060, в нашей отдельной лаборатории — 33140. ssl-mode=required требует шифрование, но не является проверкой личности сервера уровня verify_identity с доверенным CA и именем узла. Для X-соединения смотрим Mysqlx_ssl_cipher, а не классический Ssl_cipher. Таймаут подключения 1000 мс не ограничивает любой SQL-запрос; runner дополнительно ограничивает весь процесс. Код 2002 относится здесь к заведомо неслушающему порту; такой же код в другом окружении требует изучить адрес, маршрутизацию и сообщение. URI и пароли в отчёт не попадают.
Попробуйте другое условие
После переноса приложения SELECT не работает. В каком порядке проверять модуль, порт, TLS и права?
Разбор
Сначала отчёт текущего PHP. Затем проверьте, что адрес/порт относятся к X Protocol и сервер слушает его. При успешном соединении выполните простой SELECT и прочитайте X-specific TLS. Отказы аутентификации/прав разберите отдельно; не переключайте вслепую TLS в disabled и не выдавайте глобальные административные права.
Выбрать совместимый модуль и увидеть, готова ли среда сборки
Для существующего PHP нужен совместимый DLL/SO. Если подходящего готового файла нет, нужна сборочная среда. Программа показывает признаки текущего PHP и наличие строго перечисленных инструментов в PATH; ничего не устанавливает и не выдаёт наличие компилятора за готовое расширение.
- PHP 8.3, x64, NTS → критерии совместимости DLL
- PATH → наличие восьми известных инструментов
- Диагностический отчёт → выбрать DLL или подготовить SDK
- Отдельная настоящая сборка ниже → SO → загрузка новым PHP-процессом
Начните с одного действия
<?php
echo PHP_OS_FAMILY, "\n";
echo PHP_MAJOR_VERSION, '.', PHP_MINOR_VERSION, "\n";
echo PHP_INT_SIZE * 8, " bit ", PHP_ZTS ? 'TS' : 'NTS';Константы PHP описывают исполняющий программу интерпретатор. Ветка 8.3, архитектура x64 и NTS — три разных условия совместимости расширения. PATH — список каталогов поиска программ, а не доказательство, что они совместимы. В полной программе foreach перебирает фиксированные имена и пути, is_file проверяет файл, is_executable — возможность запуска в Unix. В Windows добавляются расширения PATHEXT. Команды из найденных файлов не выполняются, значения PATH и личные пути не публикуются.
Результат в указанном Windows-профиле
| Наблюдение | Результат |
|---|---|
| Операционная система | Windows |
| Ветка PHP | 8.3 |
| Разрядность | 64 |
| Сборка | NTS |
| mysql_xdevapi загружен | да |
| Инструментов из списка найдено | 0 |
| Подходящий маршрут | Совместимый DLL или Windows PHP SDK |
| Компиляция этим PHP-файлом запускалась | нет |
В другом PHP значения версии, SAPI, ini и инструментов могут отличаться: это и есть полезная часть диагностики. Сравнивайте наблюдения и причину, а не подгоняйте свой компьютер под текст таблицы.
Самостоятельная программа и архив
<?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 {
$tools=['phpize','php-config','g++','make','protoc','bison','cl','cmake'];
$paths=explode(PATH_SEPARATOR,getenv('PATH') ?: '');
$windows=PHP_OS_FAMILY==='Windows';
$extensions=$windows ? array_values(array_unique(array_merge([''],explode(';',getenv('PATHEXT') ?: '.EXE;.CMD;.BAT')))) : [''];
$found=[];
foreach ($tools as $tool) {
$found[$tool]=false;
foreach ($paths as $directory) {
$directory=trim($directory,'"');
if ($directory==='') {continue;}
foreach ($extensions as $suffix) {
$candidate=rtrim($directory,"\\/").DIRECTORY_SEPARATOR.$tool.$suffix;
if (is_file($candidate) && ($windows || is_executable($candidate))) {$found[$tool]=true; break 2;}
}
}
}
$linuxTools=['phpize','php-config','g++','make','protoc','bison'];
$missing=[];
foreach ($linuxTools as $tool) {if (!$found[$tool]) {$missing[]=$tool;}}
$route=$windows ? 'Совместимый DLL или Windows PHP SDK' : 'Совместимый SO или phpize и зависимости';
$rows=[['Операционная система',PHP_OS_FAMILY],['Ветка PHP',PHP_MAJOR_VERSION.'.'.PHP_MINOR_VERSION],
['Разрядность',PHP_INT_SIZE*8],['Сборка',PHP_ZTS ? 'TS' : 'NTS'],
['mysql_xdevapi загружен',extension_loaded('mysql_xdevapi')],
['Инструментов из списка найдено',count(array_filter($found))],
['Подходящий маршрут',$route],['Компиляция этим PHP-файлом запускалась',false]];
$checks=['fixed_tool_allowlist'=>count($found)===8 && count(array_diff(array_keys($found),$tools))===0,
'no_compilation_or_installation'=>true,'no_database_contact'=>$session===null,
'abi_fields_observed'=>PHP_INT_SIZE>0 && PHP_MAJOR_VERSION>0];
$observations+=['platform'=>PHP_OS_FAMILY,'php_branch'=>PHP_MAJOR_VERSION.'.'.PHP_MINOR_VERSION,
'bits'=>PHP_INT_SIZE*8,'zts'=>(bool)PHP_ZTS,'debug'=>(bool)PHP_DEBUG,
'fixed_tool_presence'=>$found,'missing_unix_tool_names'=>$missing,
'searched_path_values_omitted'=>true,'compiler_executed'=>false,'database_connection_attempted'=>false];
} finally {
if ($session instanceof mysql_xdevapi\Session) {
try {
$removed=x146Cleanup($session,$owned,$tokens,$removed);
$observations['schema_absence_after']=x146Absence($session);
} finally {$session->close();}
}
}
x146Report('mysql146-x-build-prerequisites', 'Выбрать совместимый модуль и увидеть, готова ли среда сборки', $rows, $checks, $observations, $runtime, $owned, $removed);
Ожидаемый результат
Операционная система — Windows
Ветка PHP — 8.3
Разрядность — 64
Сборка — NTS
mysql_xdevapi загружен — да
Инструментов из списка найдено — 0
Подходящий маршрут — Совместимый DLL или Windows PHP SDK
Компиляция этим PHP-файлом запускалась — нет
CLI-файл сохраняет result.html/result.json. Ничего не устанавливает и не создаёт объектов базы. Не открывает соединение с MySQL.
Таблица фактического запуска · Наблюдения и проверки JSON.
Что этот результат подтверждает
В нашем Windows-процессе не найдено ни одного из восьми инструментов, однако готовая DLL уже загружена: пользователю готового модуля компилятор не требуется. Отсутствие phpize в Windows нормально для SDK-маршрута. Найденный protoc сам по себе не доказывает наличие заголовков Protobuf и библиотеки, которые нужны configure. Отчёт не проверяет ABI произвольного найденного DLL и не подтверждает компиляцию. Ниже отдельно показан реально выполненный Linux-маршрут с конкретными версиями, флагом совместимости и проверкой загрузки.
Разработчик считал вывод protoc --version подтверждением всей зависимости. Наш разбор отдельно требует генератор, заголовки и библиотеку. Это задача из macOS; используемые здесь команды Ubuntu и Windows SDK описаны для своих платформ, а не перенесены из чужого окружения. protoc установлен, но configure не находит Protobuf.
Исторический сбой касался proto_gen и файлов сборки. Он мотивирует сохранять configure/make-журнал и явно проверять генерацию протоколов. Старые рецепты с PHP 7.2, сторонним PPA и сменой версии PHP здесь не копируются; маршрут сверяется с matching исходником и отдельным запуском. Сборка mysql_xdevapi прекращается на сгенерированных протоколах.
Попробуйте другое условие
protoc найден, а configure пишет «protobuf not found». Какое предположение надо проверить?
Разбор
Проверьте все три составляющие: компилятор protoc в PATH, заголовок google/protobuf/any.h в выбранном корне и библиотеку Protobuf. Укажите корень через --with-protobuf или MYSQL_XDEVAPI_PROTOBUF_ROOT. Не заменяйте это установкой случайной другой версии PHP и не считайте вывод protoc --version доказательством готовности всей зависимости.
Все настройки расширения и границы их действия
Для поддержки импорта нужно знать не только название опции, но и место, где её разрешено менять. Здесь сохранён весь перечень официальной страницы Runtime Configuration: шесть директив, типы, исходные значения и уровни изменения. Подробности, которых эта краткая страница не объясняет, отделены как наблюдения matching исходника 8.0.30.
| Директива | Тип | Значение по умолчанию | Где меняется |
|---|---|---|---|
| xmysqlnd.collect_memory_statistics | int | 0 | INI_SYSTEM (4) |
| xmysqlnd.collect_statistics | int | 1 | INI_ALL (7) |
| xmysqlnd.debug | string | пусто | INI_SYSTEM (4) |
| xmysqlnd.mempool_default_size | int | 16000 | INI_ALL (7) |
| xmysqlnd.net_read_timeout | int | 31536000 | INI_SYSTEM (4) |
| xmysqlnd.trace_alloc | string | пусто | INI_SYSTEM (4) |
INI_SYSTEM означает конфигурацию запуска/сервера, а не изменение произвольным ini_set в прикладном коде. INI_ALL допускает также локальную настройку из PHP. Числовые access 4 и 7 — битовая маска разрешённых уровней. В ini_get_all global_value — начальное значение конфигурации, local_value — действующее в текущем контексте. В PHP 8.3 не запрашиваем builtin_default_value: такое поле добавлено только в PHP 8.6; исходные значения здесь отдельно взяты из регистрации расширения и официальной таблицы.
xmysqlnd.collect_memory_statistics
Управляет сбором статистики памяти внутри расширения; это не memory_limit и не лимит размера выборки.
Тип: int; исходное значение: 0; уровень: INI_SYSTEM (4). В matching коде флаг участвует в статистических макросах. Изменение через ini_set запрещено; включение может добавить накладные расходы.
xmysqlnd.collect_statistics
Флаг сбора статистики внутренних операций; значение 0 отключает соответствующие накопления.
Тип: int; исходное значение: 1; уровень: INI_ALL (7). В примере временно меняется и восстанавливается. Сам факт изменения не доказывает ускорение конкретного запроса.
xmysqlnd.debug
Строка для внутреннего отладочного механизма; это не display_errors приложения.
Тип: string; исходное значение: пусто; уровень: INI_SYSTEM (4). Инициализация tracing в исходнике 8.0.30 зависит от PHP_DEBUG. У release-профиля PHP_DEBUG=0; не обещаем получение трассы одним ini-значением.
xmysqlnd.mempool_default_size
Документированная настройка исходного размера внутреннего пула памяти.
Тип: int; исходное значение: 16000; уровень: INI_ALL (7). В просмотренном matching исходнике найдено объявление/регистрация, но не рабочее чтение параметра аллокатором. Не предлагаем подбирать его как доказанный ускоритель.
xmysqlnd.net_read_timeout
Документированный параметр таймаута сетевого чтения; он не равен URI connect-timeout.
Тип: int; исходное значение: 31536000; уровень: INI_SYSTEM (4). В matching 8.0.30 найдены регистрация и отображение, но не использование поля в пути чтения. Поэтому этот урок не обещает, что настройка прервёт зависший запрос.
xmysqlnd.trace_alloc
Строка настройки внутренней трассировки выделений памяти.
Тип: string; исходное значение: пусто; уровень: INI_SYSTEM (4). Отладочная инициализация зависит от сборки; это не инструмент профилирования бизнес-кода и не путь лога, который можно обещать для любой DLL.
$settings = ini_get_all('mysql_xdevapi', true);
$memory = $settings['xmysqlnd.mempool_default_size'];
// Наш PHP 8.3: global_value='16000', local_value='16000', access=7.Второй аргумент true включает подробности. Функция возвращает массив; отсутствующее имя расширения нельзя опрашивать как установленное, поэтому полная программа сначала проверяет extension_loaded. Пустые строковые настройки debug и trace_alloc в наших данных представлены null, а не выдуманной пустой строкой; отчёт сохраняет фактические типы.
Два применения с контролем последствий
Для одного длительного CLI-задания можно временно отключить разрешённый сбор статистики, сохранить возвращённое прежнее значение и восстановить его в finally. Для расследования различий двух серверов полезнее сначала сохранить все шесть global/local/access, чем менять параметры наугад. Отчёт показывает, что collect_statistics прошёл 1 → 0 → 1, а попытка изменить collect_memory_statistics из PHP вернула false.
У этих параметров нет описанного changelog на официальной странице. Отсутствие подробного описания не даёт основания обещать несуществующий эффект. Название mempool_default_size не является ограничением памяти скрипта; net_read_timeout не заменяет ограничение времени всего процесса. Для надёжной проверки используем ограниченный тест подключения с отдельным connect-timeout и внешний лимит выполнения.
Полный пример чтения, отказа и восстановления · Все директивы в PHP Manual · Семантика ini_get_all.
Сборка из исходников: зависимости, команды и загруженный модуль
Сборка нужна, когда готового совместимого модуля нет или необходимо воспроизвести точный профиль. Полезный результат здесь — файл mysql_xdevapi.so, который выбранный PHP действительно загрузил. Наличие каталога исходников или удачный configure этого ещё не доказывают.
- Исходник 8.0.30 с проверенным SHA256
- phpize + configure → Makefile для конкретного PHP
- protoc + make → настоящий modules/mysql_xdevapi.so
- php -n -d extension=... → класс Session доступен
- Отдельный getSession + SELECT → проверка базы
Полный набор требований к сборке
| Часть | Зачем нужна | Как выбрать |
|---|---|---|
| PHP development files, phpize, php-config | Заголовки и параметры ABI целевого PHP | --with-php-config выбирает конкретную установку; не смешивать dev-файлы другой ветки |
| C/C++ toolchain и make | Компиляция и линковка расширения | Matching config.m4 8.0.30 включает C++17 |
| Boost | Заголовочные алгоритмы и структуры | Нужны headers, не бинарные библиотеки Boost; minimum 1.53 в config.m4 |
| OpenSSL | Криптографическая зависимость | Нужен соответствующий development-пакет и рабочие зависимости PHP/mysqlnd |
| Protobuf | Заголовки и библиотека протокольных сообщений | --with-protobuf=DIR или MYSQL_XDEVAPI_PROTOBUF_ROOT |
| protoc | Генерация .pb.cc/.h из .proto | Именно выбранный protoc должен находиться в PATH |
| Bison | Генерация парсеров в сборочной цепочке | Доступен в PATH; Windows — версия из выбранного PHP SDK |
| mysqlnd | Нативный драйвер, от которого зависит расширение | При изолированной загрузке динамического mysqlnd загрузить его раньше mysql_xdevapi |
Имя расширения — mysql_xdevapi, флаг configure — --enable-mysql-xdevapi. Корень Boost можно передать через --with-boost=DIR или MYSQL_XDEVAPI_BOOST_ROOT; для Protobuf есть аналогичные --with-protobuf и MYSQL_XDEVAPI_PROTOBUF_ROOT. Установка одного protoc не устанавливает автоматически все нужные заголовки и библиотеки. Проверяйте найденные configure пути, а не только список пакетов.
make protobufs создаёт C++-файлы сообщений; make clean-protobufs удаляет именно сгенерированные протокольные файлы в рабочем исходнике. Это не очистка базы. В нашем профиле они регенерированы protoc 3.21.12 под установленную библиотеку Protobuf. Исходный архив сохранён неизменным; рабочие .pb-файлы являются результатом сборки.
Реально выполненный профиль Linux
| Компонент | Фактический профиль |
|---|---|
| Система | Ubuntu 24.04 в WSL, x64 |
| PHP для сборки и загрузки | 8.3.6 NTS, PHP_DEBUG=0 |
| Компилятор | G++ 13.3.0; C++17 из config.m4 |
| Boost | 1.83.0 |
| Protobuf / protoc | 3.21.12 |
| Расширение | PECL mysql_xdevapi 8.0.30 |
| Генерация протоколов | make clean-protobufs, затем make protobufs |
| Дополнительный CXXFLAGS | -include boost/assert/source_location.hpp |
| Загрузка | php8.3 -n, сначала mysqlnd, затем абсолютный путь SO |
| Контакт с базой из этого Linux-процесса | Не выполнялся |
Первый make действительно завершился ошибкой: заголовок PHP определяет snprintf как макрос, а Boost 1.83 использует std::snprintf. После подстановки получалось несуществующее std::ap_php_snprintf. Единственная целевая поправка команды — предварительное подключение boost/assert/source_location.hpp до PHP-заголовков. Это адаптация порядка include для зафиксированного профиля, а не заявление, что исходник собирается с настройками по умолчанию в любой современной системе.
Основание можно проверить в Boost source_location.hpp и PHP snprintf.h. Не отключаем все предупреждения, не редактируем функции расширения и не заменяем исходник сторонним форком. Первый неуспешный журнал сохранён отдельно; последующая сборка использует тот же рабочий каталог и этот явный флаг.
Команды для такого же отдельного Linux-профиля
Сначала подготовьте зависимости и скачайте точный архив 8.0.30. Работайте в новой отдельной папке: mkdir source должен завершиться отказом, если она уже существует. Ниже показан маршрут, соответствующий выполненным этапам. Команды apt рассчитаны на Ubuntu 24.04; они устанавливают инструменты и могут обновить пакеты дистрибутива. Они не нужны для запуска уже установленной Windows DLL.
# Ubuntu 24.04; отдельная рабочая папка, не каталог работающего сайта.
# Администратор устанавливает инструменты для выбранного PHP:
sudo apt-get update
sudo apt-get install --no-install-recommends \
php8.3-dev php8.3-mysql build-essential autoconf pkg-config \
libboost-dev libprotobuf-dev protobuf-compiler libssl-dev bison
# Получите mysql_xdevapi-8.0.30.tgz с официального PECL.
# В каталоге архива проверяется конкретный исходник:
printf '%s %s\n' \
'4a1f7ac8fc59579194548365d3fcc380372749e9cc50895d8e065ea7f4717cfd' \
mysql_xdevapi-8.0.30.tgz | sha256sum --check
mkdir source
tar -xzf mysql_xdevapi-8.0.30.tgz -C source
cd source/mysql_xdevapi-8.0.30
phpize8.3
./configure --enable-mysql-xdevapi \
--with-php-config=/usr/bin/php-config8.3 \
--with-boost=/usr --with-protobuf=/usr
make clean-protobufs
make protobufs
# Адаптация для проверенного сочетания PHP 8.3.6 и Boost 1.83:
make -j2 CXXFLAGS='-g -O2 -include boost/assert/source_location.hpp'
test -f modules/mysql_xdevapi.so
sha256sum modules/mysql_xdevapi.so
ldd modules/mysql_xdevapi.so
php8.3 -n -d extension=mysqlnd \
-d "extension=$(pwd)/modules/mysql_xdevapi.so" \
-r 'echo PHP_VERSION, "\n", phpversion("mysql_xdevapi"), "\n"; var_dump(class_exists("mysql_xdevapi\\Session"));'
В нашей WSL-среде сетевой apt завершился отказом до установки. Зависимости были получены по официальным Ubuntu HTTPS-ссылкам через Windows, сверены по размеру и SHA256 из apt-метаданных и установлены apt-get --no-download. Для устаревших URL использован официальный snapshot по дате уже имеющихся метаданных. Это восстановление доставки пакетов, а не обход подписи или инструкция отключить проверку. Повторять такой путь при обычном работающем apt не требуется. Версия PHP осталась 8.3.6.
phpize готовит сборочную структуру под выбранный PHP. configure проверяет зависимости и создаёт Makefile. make компилирует модуль; -j2 ограничивает параллельность двумя заданиями. Предварительное -include касается C++-заголовка в этом окружении. ldd показывает динамические зависимости полученного SO. Команда загрузки использует -n, поэтому не меняет системный ini, а -d действует только на этот процесс. make install и глобальное включение mysql_xdevapi в этом опыте не выполнялись.
Какой результат реально получен
8.3.6
8.0.30
bool(true)Это значения PHP_VERSION, phpversion("mysql_xdevapi") и class_exists("mysql_xdevapi\\Session") из реально загруженного Linux-модуля. Полный проверочный файл также подтвердил mysqlnd, отсутствие прочитанных ini, наличие Session::createSchema и все шесть настроек. stderr при загрузке пуст. Сборка не объявлена свободной от предупреждений компилятора: сохранённый первый журнал содержит и предупреждение формата, и описанный фатальный конфликт include.
| Файл | SHA256 |
|---|---|
| Исходный mysql_xdevapi-8.0.30.tgz | 4a1f7ac8fc59579194548365d3fcc380372749e9cc50895d8e065ea7f4717cfd |
| Полученный модуль в проверенном окружении | 6dd33e35257426b7246b67e7834f688475c2e3d59556ca00b5075d1007395865 |
Хеш SO связывает именно наш результат, а не обещает побайтовую воспроизводимость в другом каталоге, с другой датой и набором пакетов. Исходный tarball проверяется по стабильному указанному хешу. Успешная загрузка подтверждает совместимость этого модуля с этим PHP; следующий шаг — отдельная проверка подключения и SELECT.
Windows SDK: что отличается от Linux
Windows собирает DLL в выбранном официальном PHP SDK, с компилятором и зависимостями соответствующей ветки PHP. Для PHP 8.3 официальный маршрут использует VS16 / Visual Studio 2019; для новых веток SDK и toolset надо сверять отдельно. Наличие g++ или Linux phpize в WSL не создаёт DLL для Windows. Сначала сопоставьте TS/NTS, x64/x86, debug/release и ветку PHP; затем запускайте консоль нужного SDK.
Protobuf на Windows может требовать статическую библиотеку с многопоточным DLL runtime. Официальные параметры CMake для такого варианта приведены ниже; исходный каталог, выбранную систему сборки CMake и архитектуру выбирают для своего SDK. В нашем выпуске Windows DLL использовалась готовая; её компиляция Visual Studio здесь не запускалась.
cmake -S C:\PATH\TO\protobuf\cmake -B C:\PATH\TO\protobuf\build -Dprotobuf_MSVC_STATIC_RUNTIME=OFF -Dprotobuf_BUILD_SHARED_LIBS=OFFЭто запись параметров CMake, а не строка Bash для вставки в cmd.exe: в Windows объедините её в одну команду либо используйте правила переноса своей оболочки. Используйте обратные слеши в путях, как рекомендует страница сборки. Скрипты старта PHP SDK могут изменить PATH; уже после запуска SDK ещё раз проверьте, какие protoc и bison найдены. Bison из несогласованного окружения может дать C2375 для zendparse; официальный Manual рекомендует Bison, поставляемый с выбранным SDK.
Два применения и следующий шаг
Для сопровождения локального сервиса сохраните исходный архив, версии зависимостей, команды и проверку загрузки: это позволяет отличить новый сбой сборки от изменения среды. Для расследования «protobuf not found» изучайте именно заголовки/библиотеку/генератор и configure-журнал, не меняя вслепую версию PHP. Короткий отчёт инструментов помогает выбрать путь до установки, но не заменяет перечисленные реальные этапы.
Полный официальный baseline сборки · phpize и выбор php-config.
Подготовить среду своего приложения
Сохраните отчёт целевого PHP, добейтесь загрузки совместимого модуля, затем подтвердите SELECT и TLS нужного X-соединения. Для нового учебного проекта переходите к созданию отдельной базы с проверкой владения, а для прикладных данных — к первой коллекции и таблицам с параметризованными запросами.
Сведения об окружении полезны до публикации приложения: технические пути и параметры сервера не являются его пользовательским интерфейсом. После расследования уберите временный веб-диагностический файл. Сборочную среду держите отдельно от работающего сайта.
Источники и обсуждения
- PHP Manual — mysql-xdevapi.setup
- PHP Manual — mysql-xdevapi.configuration
- PHP Manual — mysql-xdevapi.build
- PHP Manual — требования расширения
- PHP Manual — установка расширения
- PHP Manual — совместимость расширений Windows
- PHP Manual — сборка через phpize
- PHP Manual — выбор php-config
- PHP Manual — выбор и чтение ini
- PHP Manual — уровни изменения ini
- PHP Manual — ini_get_all
- PHP Manual — ini_set
- PHP Manual — ключи CLI
- PHP SDK для Windows
- Matching исходники PECL 8.0.30
- Boost 1.83: source_location и std::snprintf
- PHP 8.3.6: макрос snprintf
- Официальные снимки пакетов Ubuntu
Код и руководство с собой
Скачайте самостоятельные примеры с исходными данными, инструкциями и ожидаемыми результатами. Примеры HTTP и фрагменты для проекта помечены отдельно: им нужно указанное окружение.
Код не исполняется на сайте. Скачайте нужный пример и запускайте его в своём локальном окружении.
Материалы сверены 2026-10-12. Примеры и объяснения написаны для этого руководства; источники и обсуждения доступны в каждой главе.


