Apache 2.4 + несколько PHP на Windows XP с одноядерным API

Извиняюсь за долгое чтение, но я думаю, что имеет смысл включать как можно больше подробностей, вместо того, чтобы ждать, пока люди попросят различные конфиги, логи и т. Д.

У меня старый ПК под управлением XP с установленным One Core API.

Я использую это для тестирования Apache 2.4.38 с несколькими версиями PHP. Все работает отлично, за исключением того, что PHP 7.2 и 7.3 возвращают статус 404 для всех файлов php с сообщением "Входной файл не указан". Журналы Apache, если я правильно их читаю, показывают, что php-cgi.exe вызывается правильно и отвечает за возвращение этого статуса, даже если файлы существуют.

SysInternals Process Monitor показывает, что Apache "httpd.exe" не находит php-файлы без проблем. После этого все версии PHP до 7.1 проверяют как "user.ini", так и запрашиваемый файл php. Версии 7.2 и 7.3, однако, проверяют только "user.ini" и вообще не пытаются получить доступ к запрошенному файлу php. Несмотря на это, они возвращают ошибку 404 с сообщением "Входной файл не указан".

Теперь я все уменьшил до рабочей конфигурации, которая показана ниже, если кто-то хочет попробовать это для себя. Apache и все сборки PHP установлены в папке, названной в соответствии с их 5-значным идентификатором версии. Есть один тестовый сайт "test2.local", который содержит один индексный файл.

  • Все 32-битные и все версии PHP не являются потокобезопасными (nts) версиями.
  • Apache - это текущая сборка из Apache Lounge (по состоянию на 8 февраля 2019 года).
  • PHP <= 5.5 - последние сборки для каждой версии.
  • PHP> = 5.6 - все текущие сборки (по состоянию на 8 февраля 2019 г.).
  • Все сборки PHP - это готовые двоичные файлы из php.net.
  • Все среды выполнения VC установлены и работают (2008, 2012, 2017).
  • Для простоты расширения PHP не загружаются.
  • Все версии PHP имеют собственный файл php.ini в своей папке.
  • Все двоичные файлы были проверены, и нет никаких отсутствующих зависимостей, за исключением обычных преступников (расширения PHP, которые полагаются на стороннее программное обеспечение, которое не установлено).
  • Все версии "php.exe" и "php-cgi.exe" отлично работают из командной строки и без ошибок регистрируются в журнале ошибок PHP.
  • Все версии "php-cgi.exe" до 7.1 отлично работают с Apache, также без каких-либо ошибок в журнале.
  • Версии 7.2 и 7.3 программы "php-cgi.exe" показывают состояние 404 для всех файлов php в журнале ошибок Apache, но опять же, ничего не регистрируйте в журнале ошибок PHP.

Структура папки:

Apache (Logs folder contains both Apache and PHP logs):
    2.4.38 ....... D:\www\APACHE\20438
    Logs ......... D:\www\APACHE\logs
PHP:
    5.2.17 ....... D:\www\PHP\50217
    5.3.29 ....... D:\www\PHP\50329
    5.4.45 ....... D:\www\PHP\50445
    5.5.17 ....... D:\www\PHP\50538
    5.6.40 ....... D:\www\PHP\50640
    7.0.33 ....... D:\www\PHP\70033
    7.1.26 ....... D:\www\PHP\70126
    7.2.15 ....... D:\www\PHP\70215
    7.3.02 ....... D:\www\PHP\70302
Virtual Hosts:
    test2.local .. D:\www\sites\test2  (contains 1 file: index.php)

Содержимое "D:\www\sites\test2\index.php":

<?php
echo <<<EOF
<!DOCTYPE html>
<html>
<head><meta charset="us-ascii"><title>test2.local</title></head>
<body><h1>test2.local</h1></body>
</html>
EOF;
?>

Конфигурация Apache настолько минимальна, насколько я думаю:

# Apache 2.4.38 Configuration

Define SRVROOT "D:\www\APACHE\20438"
Define LOGPATH "D:\www\APACHE\logs"
Define DOCROOT "D:\www\sites"

# Here I simply uncomment the PHP version I want to test and then restart Apache.
#Define PHPVER 50217
#Define PHPVER 50329
#Define PHPVER 50445
#Define PHPVER 50538
#Define PHPVER 50640
#Define PHPVER 70033
Define PHPVER 70126
#Define PHPVER 70215
#Define PHPVER 70302

ErrorLog "${LOGPATH}\apache_20438.txt"
LogLevel trace8

ServerRoot "${SRVROOT}"
DocumentRoot "${DOCROOT}"
ServerName apache.local
Listen 192.168.0.1:80

LoadModule access_compat_module     modules/mod_access_compat.so
LoadModule authz_core_module        modules/mod_authz_core.so
LoadModule dir_module               modules/mod_dir.so
LoadModule mime_module              modules/mod_mime.so
LoadModule fcgid_module             modules/mod_fcgid.so

<Directory />
    Options Indexes ExecCGI FollowSymLinks
    AllowOverride None
    Require all granted
</Directory>

TypesConfig conf/mime.types
AddType application/x-httpd-php .php
DirectoryIndex index.php index.htm

FcgidInitialEnv SystemRoot "C:\Windows"
FcgidInitialEnv SystemDrive "C:"
FcgidInitialEnv TEMP "D:\www\PHP\${PHPVER}\tmp"
FcgidInitialEnv TMP "D:\www\PHP\${PHPVER}\tmp"
FcgidInitialEnv windir "C:\WINDOWS"
FcgidInitialEnv PHPRC "D:\www\PHP\${PHPVER}"
FcgidInitialEnv PATH "D:\www\PHP\${PHPVER};D:\www\PHP\${PHPVER}\ext;${PATH}"
FcgidInitialEnv PHP_FCGI_MAX_REQUESTS 1000
FcgidMaxRequestsPerProcess 10
FcgidMaxProcesses 15
FcgidIOTimeout 50
FcgidIdleTimeout 50
FcgidFixPathinfo 0
FcgidWrapper "D:/www/PHP/${PHPVER}/php-cgi.exe" .php
AddHandler fcgid-script .php

<VirtualHost *:80>
    # This forces unknown addresses to the default site.
    DocumentRoot "${DOCROOT}"
</VirtualHost>

<VirtualHost *:80>
    DocumentRoot "${DOCROOT}\test2"
    ServerName test2.local
</VirtualHost>

Все конфигурации PHP одинаковы, за исключением того, что версии 7.1 и выше включают директиву default_charset, которая, если она отсутствует, вызывает 500 внутренних ошибок сервера. Я нашел это решение для 7.1 (которое также изначально не работало, но работает сейчас) благодаря страницам миграции на php.net. Я не могу найти ничего, что указывает на то, что необходимы дальнейшие изменения для 7.2 или 7.3.

Полное содержимое файлов "php.ini" выглядит следующим образом (XXXXX = идентификатор версии):

[PHP]
; Paths at the top for ease of copying configurations, which
; in this case you would only need to edit these two lines.
extension_dir="D:/www/PHP/XXXXX/ext"
error_log="D:/www/APACHE/logs/php_XXXXX.txt"

allow_url_fopen=On
allow_url_include=Off
asp_tags=Off
auto_globals_jit=On
cgi.fix_pathinfo=0
cgi.force_redirect=0

; default_charset must be set for PHP 7.1 onwards
default_charset="us-ascii"

default_mimetype="text/html"
default_socket_timeout=60
display_errors=On
display_startup_errors=On
engine=On
error_reporting=E_ALL
expose_php=On
html_errors=Off
ignore_repeated_errors=On
ignore_repeated_source=Off
implicit_flush=Off
log_errors=On
log_errors_max_len=1024
magic_quotes_gpc=Off
magic_quotes_runtime=Off
magic_quotes_sybase=Off
max_execution_time=0
max_input_time=60
memory_limit=128M
output_buffering=On
post_max_size=16M
precision=14
register_argc_argv=Off
register_globals=Off
register_long_arrays=Off
report_memleaks=On
request_order="GP"
serialize_precision=100
short_open_tag=On

; Default value for user_ini.filename is ".user.ini". I remove the first
; period here for ease. (In XP, creating files that begin with a period
; usually causes an error unless done from the command prompt.)
user_ini.filename="user.ini"

variables_order="EGPCS"
y2k_compliance=On
zlib.output_compression=Off

(Я читал во многих местах, что настройки информации о путях исправления должны быть "FcgidFixPathinfo=1" и "cgi.fix_pathinfo=1", но я пытался это сделать безуспешно. На самом деле, включение этих настроек имело только эффект обе переменные "SCRIPT_NAME" и "PHP_SELF" были установлены на "D:/index.php" вместо "/index.php" во всех рабочих версиях PHP. Другими словами, его включение сломало вещи вместо того, чтобы что-то исправлять.)

Когда все работает, тестирование так же просто, как запрос страницы по адресу " http://test2.local/index.php ". Результаты для PHP <=7.1 одинаковы, поэтому я буду использовать журналы 7.1. Аналогично, результаты для 7.2 и 7.3 одинаковы, поэтому я буду использовать журналы 7.2. Ни одна версия PHP не зарегистрировала никаких ошибок или не вылетела. Каждая версия была успешно загружена при первом запросе и продолжала работать до тех пор, пока Apache не был перезапущен.

Журнал запуска Apache (все версии PHP):

Setting LogLevel for all modules to trace8
Setting LogLevel for all modules to trace8
AH00455: Apache/2.4.38 (Win32) mod_fcgid/2.3.9 configured -- resuming normal operations
AH00456: Apache Lounge VC15 Server built: Jan 18 2019 12:31:19
AH00094: Command line: 'D:\\www\\APACHE\\20438\\bin\\httpd.exe -d D:/www/APACHE/20438 -E D:/www/APACHE/logs/apache_20438.txt'
AH02639: Using SO_REUSEPORT: no (0)
AH00418: Parent: Created child process 3428
AH00402: Parent: Sent the scoreboard to the child
Setting LogLevel for all modules to trace8
Setting LogLevel for all modules to trace8
AH00453: Child process is running
AH00391: Child: Retrieved our scoreboard from the parent.
AH00403: Child: Waiting for data for listening socket 192.168.0.1:80
AH00408: Parent: Duplicating socket 324 (192.168.0.1:80) and sending it to child process 3428
AH00411: Parent: Sent 1 listeners to child 3428
AH00407: Child: retrieved 1 listeners from parent
AH00352: Child: Acquired the start mutex.
AH00354: Child: Starting 64 worker threads.
mpm child 3428 (gen 0/slot 0) started
AH00334: Child: Accept thread listening on 192.168.0.1:80 using AcceptFilter connect

Журнал Apache для запроса " http://test2.local/index.php " (PHP 7.1):

Request received from client: GET /index.php HTTP/1.1
Headers received from client:
  Host: test2.local
  User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:52.0) Gecko/20100101 Firefox/52.0
  Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
  Accept-Language: en-GB,en;q=0.5
  Accept-Encoding: gzip, deflate
  DNT: 1
  Connection: keep-alive
  Upgrade-Insecure-Requests: 1
AH01626: authorization result of Require all granted: granted
AH01626: authorization result of <RequireAny>: granted
request authorized without authentication by access_checker_ex hook: /index.php
mod_fcgid: server test2.local:D:/www/PHP/70126/php-cgi.exe(532) started
Headers from script 'index.php':
  X-Powered-By: PHP/7.1.26
  Content-type: text/html; charset=us-ascii
Response sent with status 200, headers:
  Date: Mon, 18 Feb 2019 01:31:39 GMT
  Server: Apache/2.4.38 (Win32) mod_fcgid/2.3.9
  X-Powered-By: PHP/7.1.26
  Keep-Alive: timeout=5, max=100
  Connection: Keep-Alive
  Transfer-Encoding: chunked
  Content-Type: text/html; charset=us-ascii
brigade contains: bytes: 256, non-file bytes: 256, eor buckets: 0, morphing buckets: 0
brigade contains: bytes: 395, non-file bytes: 395, eor buckets: 0, morphing buckets: 0
brigade contains: bytes: 400, non-file bytes: 400, eor buckets: 0, morphing buckets: 0
brigade contains: bytes: 400, non-file bytes: 400, eor buckets: 1, morphing buckets: 0
will flush because of FLUSH bucket
seen in brigade so far: bytes: 400, non-file bytes: 400, eor buckets: 1, morphing buckets: 0
flushing now
total bytes written: 400
brigade contains: bytes: 0, non-file bytes: 0, eor buckets: 0, morphing buckets: 0
will flush because of FLUSH bucket
seen in brigade so far: bytes: 0, non-file bytes: 0, eor buckets: 0, morphing buckets: 0
flushing now
total bytes written: 400
brigade contains: bytes: 0, non-file bytes: 0, eor buckets: 0, morphing buckets: 0

Снимок экрана Process Monitor для " http://test2.local/index.php " (PHP 7.1):

Журнал Apache для запроса " http://test2.local/index.php " (PHP 7.2):

Request received from client: GET /index.php HTTP/1.1
Headers received from client:
  Host: test2.local
  User-Agent: Mozilla/5.0 (Windows NT 5.1; rv:52.0) Gecko/20100101 Firefox/52.0
  Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
  Accept-Language: en-GB,en;q=0.5
  Accept-Encoding: gzip, deflate
  DNT: 1
  Connection: keep-alive
  Upgrade-Insecure-Requests: 1
AH01626: authorization result of Require all granted: granted
AH01626: authorization result of <RequireAny>: granted
request authorized without authentication by access_checker_ex hook: /index.php
mod_fcgid: server test2.local:D:/www/PHP/70215/php-cgi.exe(808) started
Headers from script 'index.php':
  Status: 404 Not Found
Status line from script 'index.php': 404 Not Found
  X-Powered-By: PHP/7.2.15
  Content-type: text/html; charset=us-ascii
Response sent with status 404, headers:
  Date: Mon, 18 Feb 2019 01:34:54 GMT
  Server: Apache/2.4.38 (Win32) mod_fcgid/2.3.9
  X-Powered-By: PHP/7.2.15
  Keep-Alive: timeout=5, max=100
  Connection: Keep-Alive
  Transfer-Encoding: chunked
  Content-Type: text/html; charset=us-ascii
brigade contains: bytes: 263, non-file bytes: 263, eor buckets: 0, morphing buckets: 0
brigade contains: bytes: 294, non-file bytes: 294, eor buckets: 0, morphing buckets: 0
brigade contains: bytes: 299, non-file bytes: 299, eor buckets: 0, morphing buckets: 0
brigade contains: bytes: 299, non-file bytes: 299, eor buckets: 1, morphing buckets: 0
will flush because of FLUSH bucket
seen in brigade so far: bytes: 299, non-file bytes: 299, eor buckets: 1, morphing buckets: 0
flushing now
total bytes written: 299
brigade contains: bytes: 0, non-file bytes: 0, eor buckets: 0, morphing buckets: 0
will flush because of FLUSH bucket
seen in brigade so far: bytes: 0, non-file bytes: 0, eor buckets: 0, morphing buckets: 0
flushing now
total bytes written: 299
brigade contains: bytes: 0, non-file bytes: 0, eor buckets: 0, morphing buckets: 0

Снимок экрана Process Monitor для " http://test2.local/index.php " (PHP 7.2):

Любая помощь в разгадке этой тайны будет высоко оценена. Снова извиняюсь за длительное чтение.

1 ответ

Ну, для тех, кто заинтересован, я наконец-то разобрался с проблемой, и вы можете заставить ее работать, если вы захотите пересобрать PHP самостоятельно.

Виновником является вызов функции "FindFirstFileExW" в kernel32, которая завершается с ошибкой. Это один из файлов, который переименовывает One Core API и предоставляет оболочку, так что либо оболочка портит вызов (в чем я сомневаюсь), либо реальная функция просто возвращает INVALID_HANDLE_VALUE в XP по любой причине.

До PHP 7.2 функция "FindFirstFileW" использовалась вместо "FindFirstFileExW". Таким образом, исправление - это просто вопрос замены одной строки кода.

Итак, вот что вам нужно сделать, что в настоящее время применимо ко всем версиям PHP 7.2.x и 7.3.x.

Возьмите исходный код PHP и в папке Zend откройте файл "zend_virtual_cwd.c". Найдите текст "FindFirstFileExW". В файле будет только одно вхождение, которое для PHP 7.3.2 находится в строке 843.

837
838     if (save) {
839         pathw = php_win32_ioutil_any_to_w(path);
840         if (!pathw) {
841             return (size_t)-1;
842         }
843         hFind = FindFirstFileExW(pathw, FindExInfoBasic, &dataw, FindExSearchNameMatch, NULL, 0);
844         if (INVALID_HANDLE_VALUE == hFind) {
845             if (use_realpath == CWD_REALPATH) {
846                 /* file not found */
847                 FREE_PATHW()
848                 return (size_t)-1;
849             }
850             /* continue resolution anyway but don't save result in the cache */
851             save = 0;
852         } else {
853             FindClose(hFind);
854         }
855     }
856

Хорошей новостью является то, что обе функции используют структуру WIN32_FIND_DATA (параметр "dataw"). Таким образом, чтобы заставить это работать на XP, все, что нам нужно сделать, это вызвать "FindFirstFileW" вместо "FindFirstFileExW", помня, что "FindFirstFileW" принимает только 2 параметра, а не 6.

837
838     if (save) {
839         pathw = php_win32_ioutil_any_to_w(path);
840         if (!pathw) {
841             return (size_t)-1;
842         }
843     //  hFind = FindFirstFileExW(pathw, FindExInfoBasic, &dataw, FindExSearchNameMatch, NULL, 0);
844         hFind = FindFirstFileW(pathw, &dataw);
845         if (INVALID_HANDLE_VALUE == hFind) {
846             if (use_realpath == CWD_REALPATH) {
847                 /* file not found */
848                 FREE_PATHW()
849                 return (size_t)-1;
850             }
851             /* continue resolution anyway but don't save result in the cache */
852             save = 0;
853         } else {
854             FindClose(hFind);
855         }
856     }
856

Сохраните файл и пересоберите PHP. Если, как и я, вы используете предварительно скомпилированные двоичные файлы из php.net, затем выполните сборку, используя те же параметры конфигурации, что и в исходной сборке, а затем замените исходный файл "php7.dll" (или "php7ts.dll") новым только что построен Вам нужно будет отбросить некоторые параметры конфигурации, если у вас нет той же среды, что и у исходного компоновщика. Например, параметры конфигурации для исходных двоичных файлов 7.3.2 nts x86:

configure --enable-snapshot-build --enable-debug-pack --disable-zts 
--with-pdo-oci=c:\php-snap-build\deps_aux\oracle\x86\instantclient_12_1\sdk,shared 
--with-oci8-12c=c:\php-snap-build\deps_aux\oracle\x86\instantclient_12_1\sdk,shared 
--enable-object-out-dir=../obj/ --enable-com-dotnet=shared --without-analyzer --with-pgo

Поскольку у меня нет Oracle или PGO (что бы это ни было), я просто отбрасываю эти опции и собираю, используя

configure --enable-snapshot-build --enable-debug-pack --disable-zts
--enable-com-dotnet=shared --without-analyzer

Вот и все - PHP 7.2.x и 7.3.x работают на Windows XP с One Core API.

Другие вопросы по тегам