Non-Thread-Safe PHP: администрирование сервера с NTS

Раздел: Администрирование сервера -> Настройка веб-сервера

Основные принципы работы с PHP Non-Thread-Safe

PHP Non-Thread-Safe (NTS) - сборка интерпретатора, которая не поддерживает одновременное выполнение в нескольких потоках одного процесса. Такая версия предназначена для веб-серверов, использующих многопроцессную модель (например, Apache с MPM prefork или Nginx в связке с PHP-FPM). В отличие от Thread-Safe (TS) сборки, NTS не содержит механизмов синхронизации доступа к глобальным данным, что даёт прирост производительности до 10-30% и снижает потребление памяти.

Основное решение: при настройке веб-сервера на базе Apache (prefork) или Nginx+PHP-FPM следует использовать именно NTS версию PHP. Это обеспечивает максимальную скорость обработки запросов и предотвращает неожиданные сбои, связанные с состоянием гонок. Для проверки текущей сборки выполняется команда:

php -i | grep "Thread Safety"

Mod rewrite index php (настройка mod_rewrite для index.php)

Если вывод содержит disabled - версия NTS. Если enabled - TS. Также можно воспользоваться PHP-скриптом:

<?php
echo PHP_ZTS ? 'Thread Safe' : 'Non-Thread Safe';
?>

Upstream php fpm (настройка upstream для php-fpm в nginx)

Результат для NTS: Non-Thread Safe.

Типичная ошибка: при попытке использовать NTS сборку с модулем mod_php для Apache в режиме worker (потоковый) возникает нестабильная работа и фатальные ошибки. Решение: переключить MPM на prefork или использовать FastCGI (например, php-fpm).

Как определить сборку PHP без запуска интерпретатора?

Если сам PHP не запускается, можно посмотреть на имя файла интерпретатора. В Windows: php-8.2.nts-Win32-vs16-x64.zip (NTS) против php-8.2.ts-Win32-vs16-x64.zip (TS). В Linux - по конфигурации сборки, например, в выводе phpinfo() или командой:

php -r "echo PHP_ZTS ? 'TS' : 'NTS';"

запустить локальный сервер php (запуск локального сервера php)

Проблема: в некоторых окружениях переменная PHP_ZTS не определена (если PHP собран без поддержки ZTS). Фактически её отсутствие означает NTS.

Как собрать PHP из исходников с отключённой потокобезопасностью?

Для компиляции используется флаг --disable-zts. Пример команды конфигурации:

./configure --prefix=/usr/local/php-nts --with-apxs2=/usr/bin/apxs --with-mysqli --enable-fpm --disable-zts

Php non thread safe (php non-thread-safe (не потокобезопасная версия))

После этого выполняются make и make install. Важно: если не указать --disable-zts, по умолчанию может быть включена ZTS (зависит от версии).

Ошибка: при сборке расширения для NTS вручную (например, через phpize) необходимо передать тот же флаг компиляции. Если этого не сделать, расширение загрузится с предупреждением "Unable to load dynamic library". Решение: при запуске phpize добавить флаг --disable-zts, либо пересобрать расширение сокета с соответствующими опциями.

Что делать, если расширение не загружается после перехода на NTS?

Расширения, собранные для TS, несовместимы с NTS. Лог ошибок может содержать строку:

PHP Warning:  PHP Startup: Unable to load dynamic library 'pdo_mysql' (tried: /usr/lib/php/20200930/pdo_mysql.so (/usr/lib/php/20200930/pdo_mysql.so: undefined symbol: compiler_globals))

Rewritecond request filename php (правило rewritecond для php в .htaccess)

Решение: загрузить версию расширения, собранную под NTS. Например, в репозиториях дистрибутивов пакеты PHP часто разделены: php (NTS) и php-zts (TS). Установка соответствующих пакетов:

# Ubuntu/Debian (NTS)
sudo apt install php-cli php-fpm php-mysql
# Для TS
sudo apt install php-zts php-zts-fpm php-zts-mysql

Apache 2.4 php 8.2 (настройка apache 2.4 с php 8.2)

Если расширение разрабатывается самостоятельно, его нужно перекомпилировать с теми же флагами, что и основной PHP. Пример:

cd /path/to/extension
phpize
./configure --with-php-config=/usr/local/php-nts/bin/php-config
make && sudo make install

Php index сервер (настройка индексного файла php на сервере)

Как настроить веб-сервер для работы с NTS?

Рекомендуется использовать FastCGI-прокси (PHP-FPM). Пример конфигурации Nginx для передачи запросов:

server {
    listen 80;
    server_name example.com;
    root /var/www/html;
    location ~ \.php$ {
        include fastcgi_params;
        fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
}

Var www html index php (размещение index.php в var/www/html)

Для Apache с mod_proxy_fcgi:

<VirtualHost *:80>
    ServerName example.com
    DocumentRoot /var/www/html
    <FilesMatch \.php$>
        SetHandler "proxy:unix:/var/run/php/php8.2-fpm.sock|fcgi://localhost"
    </FilesMatch>
</VirtualHost>

Возможная ошибка: если PHP-FPM слушает на TCP-порту, а не Unix-сокете, в конфигурации нужно указать соответствующий адрес. В логах Nginx может появиться "connect() failed (111: Connection refused)". Проверка: sudo service php8.2-fpm status.

Можно ли использовать NTS с многопоточным веб-сервером (Apache worker или event)?

Нет, это не рекомендуется. В многопоточном окружении один процесс PHP может выполняться одновременно в нескольких потоках, что приводит к повреждению данных. Необходимо применять Thread-Safe версию. Для NTS подходят только модели, где каждый запрос обрабатывается отдельным процессом (prefork, php-fpm).

Проблема: при попытке запустить NTS в многопоточном Apache возможны segmentation fault или необъяснимые глюки. Решение: переключить MPM или перейти на Nginx+PHP-FPM.

Расширенный пример проверки сборки PHP с использованием phpinfo():

Пример
<?php
phpinfo();
?>

В выводе, после открытия в браузере, ищем строку Thread Safety. Для NTS отображается "disabled".

Thread Safety => enabled (TS)
- или -
Thread Safety => disabled (NTS)

Пример компиляции PHP 8.2 с флагом --disable-zts и включением FPM:

Пример
wget https://www.php.net/distributions/php-8.2.12.tar.gz
tar -xzf php-8.2.12.tar.gz
cd php-8.2.12
./configure --prefix=/usr/local/php-nts \
    --enable-fpm \
    --with-mysqli \
    --with-pdo-mysql \
    --disable-zts \
    --with-openssl \
    --enable-bcmath
make -j$(nproc)
sudo make install

После установки проверка:

Пример
/usr/local/php-nts/bin/php -i | grep "Thread Safety"
Thread Safety => disabled

Демонстрация ошибки при загрузке расширения, собранного для TS, в NTS PHP:

Пример
php -m 2>&1 | grep -i error
PHP Warning:  PHP Startup: Unable to load dynamic library 'imagick' (tried: /usr/lib/php/20200930/imagick.so (/usr/lib/php/20200930/imagick.so: undefined symbol: zend_new_interned_string)) in Unknown on line 0

Решение: переустановить пакет imagick из репозитория NTS или собрать с той же конфигурацией.

Пример параллельного запуска двух версий PHP (TS и NTS) с разными сокетами PHP-FPM:

Пример
# Конфигурация пула для NTS /etc/php/8.2/fpm/pool.d/www.conf
listen = /run/php/php8.2-fpm.sock
# Для TS /etc/php/8.2-zts/fpm/pool.d/www.conf
listen = /run/php/php8.2-zts-fpm.sock

Nginx на один сайт использует NTS сокет, на другой - TS, что удобно для тестирования.

Пример скрипта, который выводит все загруженные расширения и их совместимость:

Пример
<?php
$zts = PHP_ZTS ? 'TS' : 'NTS';
echo "PHP сборка: $zts\n";
echo "Загруженные расширения:\n";
foreach (get_loaded_extensions() as $ext) {
    $info = new ReflectionExtension($ext);
    echo "  $ext (версия {$info->getVersion()})\n";
}
?>
PHP сборка: NTS
Загруженные расширения:
  Core (версия 8.2.12)
  date (версия 8.2.12)
  ...

PHP Non-Thread-Safe (не потокобезопасная версия) - comments

En
Php non thread safe (php)