在 Win11 安裝 Apache 2.4 和 PHP 8.1 或 PHP 8.2

緣由

早年是用 AppServ ,近年習慣用 XAMPP ,但最近 MariaDB 一直壞掉,查了後發現的確在 Windows 上會有這樣的問題maria_communitygithub

this is a data corruption bug.
Restoring from backup and running on a MariaDB version at or above 10.6.9, 10.7.5, 10.8.4, 10.9.2, 10.10.1.

但是 XAMPP 在今天(2022-11-30)的版本裡包的是 MariaDB 10.4.27 ,並不在該 bug maria_jira的解決版本 (Fix Version/s 欄)之列。所以我就想說要換其他的版本。

那要換裝哪個呢? Apache 官網並沒有提供 Windows 的安裝檔,但是有推薦幾個第三方軟體apache,不過我逐個逛過後覺得都有一些問題:

  • ApacheHaus: 只會裝 Apache 。頁面上幾乎沒有甚麼關於日期的資訊,我都不知道這些東西是何時釋出的。
  • Apache Lounge: 只會裝 Apache 。一打開就看到錯別字:”The binaries, are build with …” 這句應該是過去分詞 built 才對吧!有明顯錯字的我都不太放心…
  • Bitnami WAMP Stack: 有 PHP 和 MySQL ,但就這麼剛好也公布了不會再弄這類軟體。 “Bitnami will discontinue the support for all native installers by November 30, 2022.”bitnami
  • WampServer: 有 PHP 、 MySQL 、 MariaDB 。但安裝需求很高,要 VC9~VS16 全部都有(Visual C++ 2008~2019 Redistributable),且會裝至少三個版本的 PHP 、兩個版本的 phpMyAdmin ,根本浪費資源。
  • XAMPP: 有 PHP 、 MariaDB ,但就一開始的原因, MariaDB 會當。

合輯包都陣亡了,只好從前兩個純 Apache 軟體選一個。
除了 ApacheHaus 的資料太少我實在覺得怪怪的之外,最後的臨門一腳是 PHP 官網php_windows寫著:

推荐 ApacheLounge 编译的 Apache

而且後來發現 PHP 的 Windows 版本下載頁面php_download也寫著:

Please use the Apache builds provided by Apache Lounge.
They provide VC15 and VS16 builds of Apache for x86 and x64.
We use their binaries to build the Apache SAPIs.

安裝 Apache

其實沒甚麼好裝的,就是下載壓縮檔然後解壓縮。但是「要下載哪個」可能會是問題。
先到電腦的「程式和功能」確認自己有哪些 Microsoft Visual C++ Redistributable 的版本,然後在 ApacheLounge 選擇有支援的。

撰文時有提供的是 VS17 (2022) 的 64 位元版本,及 VS16 (2019) 的 32 和 64 位元版本。
若是 PHP 8.1 的話建議 VS16 的版本,若是 PHP 8.2 的話則建議選 VS17 的版本。理由請見文末 [[#OpenSSL 版本問題]] 。

若電腦上沒有要求的版本,記得「先」去微軟官網下載安裝。(或是 ApacheLounge 頁面也有連往微軟載點的連結)

然後找個地方放解壓縮後的檔案。一般建議路徑上不要有空格,也就是不建議放在 C:\Program Files\ 裡。
由於不涉及系統註冊碼,因此不一定要放在系統槽,這樣若因故要重灌作業系統的話,也不用特別備份這邊的設定。

安裝 PHP

一樣是下載和解壓縮,一樣是要確認好要下載哪個。
PHP 有一個專為 Windows 而弄的網域和下載頁面php_download,裡頭會有不同的分類方式:

  • Visual Studio 版本
  • 位元數
  • 多執行緒安全

前兩個好判斷,就看自己作業系統的狀況,最後一個的解答則寫在下載頁面的左側邊欄:

With Apache, using the apache2handler SAPI, you have to use the Thread Safe (TS) versions of PHP.

下載後同樣找個地方解壓縮放著,同樣建議路徑上不要有空格,同樣不一定要在系統槽。

設定 Apache

讓 Apache 載入 PHP

PHP 官網提供三種方式php_apache2,我習慣是第一種。
編輯 Apache 資料夾裡的 conf/httpd.conf ,加入:

1
2
3
4
5
LoadModule php_module "c:/php/php8apache2_4.dll"
<FilesMatch \.php$>
SetHandler application/x-httpd-php
</FilesMatch>
PHPIniDir "C:/php"

幾個小設定

  • DocumentRoot 和其後的第一個 <Directory> 改成自己文件放置的位置。
  • <IfModule dir_module> 裡面的 DirectoryIndex 加上 index.php 。(空格分隔,順序有差)
  • 如果需要虛擬主機,記得啟用 Include conf/extra/httpd-vhosts.conf ,當然也要自己再去編輯該檔案。
  • 如果會有 .htaccess 檔案的配置,記得要在需要的 <Directory> 區塊裡編輯 AllowOverride

httpS

這裡說的是允許瀏覽器對你的伺服器進行 https 連線。
若是要讓伺服器對其他電腦進行 https 連線,則是 PHP 那邊的設定。

要支援 https 的知識門檻比較高,沒有接觸過的話會很花時間,趕時間的話建議先跳過。
要讓 Apache 支援 https ,有四個部分要弄:

生出一組憑證。

Apache Lounge 以外的 Apache 系列軟體,大多有附上一個自簽憑證,但我遇過的其實都過期了。是不至於不能用,但就是每次連向頁面就會被瀏覽器再警告一次。

「如何取得憑證」就跟 Apache 沒甚麼關係了,另參考:

編輯 conf/httpd.conf

  • 啟用這兩個(預設這兩個是被註解掉的):
    1
    2
    LoadModule socache_shmcb_module modules/mod_socache_shmcb.so
    LoadModule ssl_module modules/mod_ssl.so
  • 加上 Include conf/extra/httpd-ssl.conf 。(建議用 <IfModule ssl_module /> 包起來)
  • 如果要強制轉到 https (也就是使用者連向 http ,就自動被轉去 https )的話,那就在 <Directory /> 裡加上 Redirect "/" "https://localhost/" apache_redirect

編輯 conf/extra/httpd-ssl.conf

要增/修的是 <VirtualHost /> ,基本上跟 httpd-vhost.conf 邏輯類似,但要注意:

  • SSLEngine on :這個沒開的話, PHP 的 $_SERVER['HPPTS'] 會不存在,而且可能會讀錯張憑證。
    沒開的話 error.log 會有 You configured HTTP(80) on the standard HTTPS(443) port!
  • ServerName 的值後面要補上 :443 (或其他埠號),不然 PHP 的 $_SERVER['SERVER_PORT'] 會是 80 (即使順利用 https 連線)。
    但是 ServerAlias 只能有主機名,「不能」加埠號!
  • 修改 SSLCertificateFileSSLCertificateKeyFile ,分別指向對應的檔案。
  • 網路上有些範例會有 SSLCertificateChainFileSSLCACertificateFile ,但我試的時候都不用。(注意 Apache 版本,同為 2.4.x 的運作也可能不同,例如前者是在 2.4.8 版被廢棄 apache_ssl。)

多個域名時,其一的設定會像下面這樣:

1
2
3
4
5
6
7
8
<VirtualHost *:443>
DocumentRoot "D:/xxxxx"
ServerName a.localhost:443
ServerAlias another.localhost
SSLEngine on
SSLCertificateFile "D:/xxxxxx/a.localhost.crt"
SSLCertificateKeyFile "D:/xxxxxxx/a.localhost.key"
</VirtualHost>

附論: SNI

以前 https 是沒法支援多張憑證的,這是因為「驗證證書」是在「確認域名」之前。(依照協定,必須先建立 TCP 連線,然後 TLS 握手,接著才是 HTTP 協定)
在此技術之前,應對的做法是在一份證書裡面塞好幾個域名(最多20個gca)。

但後來發展出 Server Name Indication 技術,瀏覽器可以在驗證證書的階段就講清楚自己是要連哪個域名,這樣伺服器就也知道要回覆哪一張證書。
不過,此技術也有缺點lichi_chen

TLS handshake 做完之後才會加密,這等於說你要訪問哪個站其實是可以被中間的網路節點看到的。
因為 SNI 露在外面,有些國家網路政策可以利用這個資訊,在網路中間網路節點作怪,故意不給你訪問某些網站。

設定 PHP

進到 PHP 的資料夾,把 php.ini-development 複製成 php.ini 然後開啟之。

extension_dir

Windows 下可以設定 extension_dir = "C:/<php_dir>/ext"
雖然沒有一定要設定,但不開的話後面會比較麻煩。

Dynamic Extensions

常用的有 mbstring, openssl, curl, mysqli

mbstring 為例
如果上面的 extension_dir 有設定正確,那麼這邊只要 extension=mbstring 就可以了;

但如果沒有設定 extension_dir,那這邊就要設定完整路徑和副檔名,例如:

1
extension="C:/<php_dir>/ext/php_mbstring.dll"

才有用。(注意檔名前面有 php_ ,後面有 .dll 。)

可以用 php.exe -m 確認哪些有被啟用,
但實際是否有載入成功,要重啟 Apache 看 <apache_dir>/logs/error.log 有沒有錯誤訊息。
opensslcurl 有可能沒法這樣就順利運作,詳見文末 [[#OpenSSL 版本問題]] 。

如果 cURL 和 OpenSSL 持續沒能運作,可以看看這篇

設定環境變數 (PATH)

Windows 10 和 11 的話是在
設定→系統→系統資訊→進階系統設定→環境變數…

如果權限夠的話就編輯下面區塊;不夠的話就編輯上面區塊的「PATH」,「新增」一筆 PHP 的根目錄。

啟動 Apache

關鍵程式是 Apache 資料夾裡的 bin/httpd.exe ,以下指令假設是在該目錄,或是有把該目錄加進 PATH

無須特權的做法:

  • 啟動: httpd
  • 關閉: Ctrl+C

需特權的做法:

  • 註冊為「服務」:httpd -k install
  • 啟動服務: httpd -k start
  • 關閉服務: httpd -k stop

詳參官網 httpd - Apache Hypertext Transfer Protocol Server - Apache HTTP Server Version 2.4

如果有什麼不對勁的,請開啟 <apache_dir>/logs/error.log 查看。

OpenSSL 版本問題

最前面提到 PHP 8.1 請搭配用 VS16 編譯的 Apache ; PHP 8.2 的話才可能可已用 VS17 編譯的版本。
其實關鍵似乎不是 VS 的版本,而是 OpenSSL 的版本。

總之,如果版本不對的話, Apache 是可以啟動,但並不會成功載入 OpenSSL 及 cURL 。在 <apache_dir>/logs/error.log 會出現:

1
2
PHP Warning:  PHP Startup: Unable to load dynamic library 'curl' in Unknown on line 0
PHP Warning:  PHP Startup: Unable to load dynamic library 'openssl' in Unknown on line 0

折騰兩天後似乎是找到原因了。mtudor 於2008年時寫道

When any application attempts to use a dll file in windows, the system searches for this file using the following order:
1. The directory from which the application loaded.
2. The windows\system32 directory.
3. The windows\system directory.
4. The windows directory.
5. The current directory.
6. The directories that are listed in the PATH environment variable.

For PHP running under Apache, the application directory is <apache dir>\bin and NOT <php dir>.

epos_jk 則在 2017 年時提到

Beginning with version 1.1.0 OpenSSL did change their libary names!
libeay32.dll is now libcrypto-*.dll (e.g. libcrypto-1_1-x64.dll for OpenSSL 1.1.x on 64bit windows)
ssleay32.dll is now libssl-*.dll (e.g. libssl-1_1-x64.dll for OpenSSL 1.1.x on 64bit windows)

然後比對一下這幾個來源的檔案:

PHP 8.1.13 built with VS16:

  • libcrypto-1_1-x64.dll
  • libssl-1_1-x64.dll

PHP 8.2.5 built with VS16:

  • libcrypto-3-x64.dll
  • libssl-3-x64.dll

Apache 2.4.54 built with VS16:

  • libcrypto-1_1-x64.dll
  • libssl-1_1-x64.dll

Apache 2.4.54 built with VS17:

  • libcrypto-3-x64.dll
  • libssl-3-x64.dll

Apache-VS17 和 PHP-8.2 都是 libcrypto-3-x64.dlllibssl-3-x64.dll
Apache-VS16 和 PHP-8.1 都是 libcrypto-1_1-x64.dlllibssl-1_1-x64.dll

注意這裡的「版本差別」意義不太一樣,
Apache 都是 2.4.54 版,是編譯環境不同造成了附帶的函示庫版本不同;
PHP 則是原始碼的版本就不同。

總之,要使用能互相搭配的版本。

我這次嘗試時是先裝了 VS17 編譯的 Apache ,看來是 PHP 嘗試載入 libcrypto-*.dlllibssl-*.dll* 時,載到了自己看不懂的更新版本。

雖然 PHP 官網說:

注意: Win32 用户注意
要在 Windows 环境下使用这个模块,libeay32.dllssleay32.dll 库文件必须放到 PATH 环境变量包含的目录下。
OpenSSL 1.1 下则为 libcrypto-*.dlllibssl-*.dll 文件。
同样的,libssh2.dll 文件也需要放到 PATH 环境变量包含的目录下。
不需要用 cURL 网站上的 libcurl.dll 库。

但依照前面 mtudor 的說法,就算那樣也還是會優先載入 <apache dir>\bin 裡面的,那麼問題(大概)仍會發生。
而且,我改用 VS16 編譯的 Apache 之後,沒有複製東西到系統資料夾,也沒有修改 PATH ,就順利解決問題了。

另一個方式是,把 PHP 資料夾裡的那兩個 DLL 檔複製到 <apache dir>\bin 。但我覺得這個解法會讓 Apache 資料夾裡有著不屬於 Apache 的東西,比較不推薦。下面的方法比較適合

cURL 和 sodium

要啟用 PHP 的 curl ,需要 Apache 載入 PHP 資料夾裡的 libssh2.dll
要啟用 PHP 的 sodium ,需要 Apache 載入 PHP 資料夾裡的 libsodium.dll

請在 Apache 的 conf/httpd.conf 裡面加上:

1
2
LoadFile "D:/php-8.2.6/libssh2.dll"
LoadFile "D:/php-8.2.6/libsodium.dll"

https://blog.csdn.net/qq_38702465/article/details/87574161

前述的 OpenSSL 版本問題 據說 可以用此法解。

更新:
Apache Lounge 公告說 Apache 2.4.57 是最後一個有用 VS16 編譯的版本,之後就只會有 VS17 編譯的版本。
因此,若要在 Apache 2.4.58 (或更新)以後使用 PHP 8.1 (或更舊),應該就會需要上述的引入方法。

參考資料