为长时间阅读道歉,但我认为尽可能详细地包含尽可能详细的内容,而不是等待人们要求各种配置,日志等。
我有一台旧PC运行XP并安装了One Core API。
我正在使用它来测试具有多个PHP版本的Apache 2.4.38。一切都很完美,除了PHP 7.2和7.3为所有php文件返回404状态,并显示消息“未指定输入文件”。 Apache日志,如果我正确读取它们,则表明正在正确调用php-cgi.exe,并且即使文件存在也负责返回此状态。
SysInternals Process Monitor显示Apache的“httpd.exe”发现php文件没问题。在此之后,所有高达7.1的PHP版本都会检查“user.ini”和请求的php文件。然而,版本7.2和7.3仅检查“user.ini”,并且根本不尝试访问所请求的php文件。尽管如此,它们返回404错误,并显示消息“未指定输入文件”。
我现在已经将所有内容扩展到了一个简单的工作配置,如果有人想自己尝试一下,这些配置如下所示。 Apache和所有PHP版本都安装在根据其5位版本ID命名的文件夹中。有一个测试站点“test2.local”,其中包含一个索引文件。
文件夹结构:
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 = Version ID):
[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”,但我试过这个没有成功。实际上,打开这些设置只会产生效果在所有正在运行的PHP版本中,“SCRIPT_NAME”和“PHP_SELF”变量都被设置为“D:/index.php”而不是“/index.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
用于请求“http://test2.local/index.php”的Apache日志(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
进程监视器“http://test2.local/index.php”的屏幕截图(PHP 7.1):
用于请求“http://test2.local/index.php”的Apache日志(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):
任何帮助搞清楚这个谜团的人都会非常感激。再次为冗长的阅读道歉。
好吧,对于任何有兴趣的人,我终于找到了问题,如果你愿意自己重新编译PHP,你可以让它工作。
罪魁祸首是调用kernel32中的“FindFirstFileExW”函数失败。这是One Core API重命名并提供包装的文件之一,因此要么包装器搞乱了调用(我怀疑),要么实际函数只是因为任何原因在XP上返回INVALID_HANDLE_VALUE。
在PHP 7.2之前,使用“FindFirstFileW”函数代替“FindFirstFileExW”。所以修复只是替换一行代码的问题。
所以这就是你需要做的,目前适用于所有7.2.x和7.3.x版本的PHP。
抓取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
就是这样 - 使用One Core API在Windows XP上运行的PHP 7.2.x和7.3.x。