WordPress 要放掉 PHP 7.0 與 PHP 7.1 的支援了

WordPress 說要放掉舊版的 PHP,本來看到標題在想是 PHP 8.0 與 PHP 8.1,仔細看才發現是 PHP 7.0 與 PHP 7.1:「Dropping support for PHP 7.0 and 7.1」。

從「PHP 7 ChangeLog」這邊可以看到 PHP 7.0.0 與 7.1.0 分別是 2015 年十二月與 2016 年十二月的事情了... 印象中這是 PHP 效能飛越性提升的年代,從 7.0、7.1、7.2、7.3 到 7.4 都有顯著的改善:「PHP Benchmarks 7.4 vs 7.3 vs 7.2 vs 7.1 vs 7.0 (php-fpm)」。

Internet Archive 上面的「Supported Versions」可以看到 7.0 與 7.1 分別在 2019 年初與 2019 年年底終止維護,離現在差不多是 5 年與 4 年了。

沒注意到 WordPress 還有支援這麼舊的版本,大概是為了一些八百年沒更新的 PHP hosting...

PHP 8.3 相比於 PHP 8.2 的效能提升

找資料的時候意外發現 PHP 8.3 相對於 PHP 8.2 的效能提升好像不算小?目前看到這兩個地方有提到:

前面那篇的 benchmark 數據可以看出來愈大愈複雜的框架,提升的效能就愈多:

  • 乾淨的 WordPress 從 158 rps 成長到 169 rps,大約 7% 的增加。
  • 如果是 WooCommerce 的話從 49 rps 到 58 rps,大約是 18.4%。
  • 接著 Laravel 則是從 670 rps 到 925 rps,提升了 38.1%。
  • Drupal 則是 941 rps 到 1432 rps,提升了 52.2%。

在「Make your app faster with PHP 8.3」這邊提到了 PHP 8.3 改善了很多關於效能的項目。

首先提到的是 JIT 的改善:

The Just-In-Time (JIT) compiler has been further optimized for better efficiency. The execution of scripts is faster and consumes less CPU time. This is especially beneficial for resource-intensive tasks.

然後是 opcode 這邊的改善:

PHP has refined how it handles opcodes (the instructions in the PHP bytecode). Version 8.3 uses more efficient ways to interpret and execute these opcodes. This reduces the execution time of scripts.

然後 GC 機制也改善了:

PHP 8.3 enhances the garbage collection mechanism, which is responsible for freeing memory occupied by unused objects. This results in more efficient memory usage and can significantly improve performance for memory-intensive applications.

array 的改善:

Other improvements include optimizations for handling arrays and an enhanced type system.

對於複雜的應用就很容易都受惠,然後就有頗大的提升...

換成 t4g.small 後的一些整理

昨天在這邊提到因為 Amazon EC2t4g.small 提供了 free tier 方案 (到今年年底),blog 主機剛好從 t4g.micro 改成用 t4g.small,到年底前可以看看有沒有 t5g 或是類似的主機出來:「往上升級或是用 Unlimited mode 撐」。

除了換完後 CPU credit 給的量上升減緩了情況以外,我在檢查時才發現 PHPopcache 的 cache 使用量也超過預設值 128MB 了,改成 192MB 後看起來 CPU usage 也有下降一些:

這點算是先前沒注意到的,上面 PHP 跑兩個 WordPress 以及一個 MediaWiki (都掛了各式各樣的 plugin & extension),還有一個自己寫的小東西,這樣會超過 opcache 的 cache 大小...

現在換到 t4g.small 後總算又開始養的起 CPU credit 了:

另外也補上幾個 CloudWatch Alarms (看起來 free tier 是十個) 監控主機的 CPUCreditBalance,然後透過 AWS Chatbot 接到自己的 Slack 上,至少之後有狀況的時候會主動通知。

t4g 的 CPU credit 被吃完了

這個站 blog.gslin.org 掛了三個多小時:

先連機器 SSH 看起來是正常的,但習慣性的 w 看一下情況發現 CPU load 有 6.x,用 top 看一下就看到幾隻 php82-fpm 跑滿 CPU,心裡大概有底是被砍站了...

先把 nginx 停下來,瞄了一下 /var/log/nginx 下面的 log 就知道是 ClaudeBot 造成的,看起來都是從 AWSus-east-1 機器打過來的。

然後翻一下 log 看看什麼時候開始打的,先看 log 已經被 gzip 起來的這些:

$ echo /var/log/nginx/blog.gslin.org_ssl-access.log.{?,??}.gz | xargs -n1 | xargs -n1 -I% sh -c "echo %; zgrep ClaudeBot % | wc"
/var/log/nginx/blog.gslin.org_ssl-access.log.2.gz
  13031  169403 1986719
/var/log/nginx/blog.gslin.org_ssl-access.log.3.gz
    459    5967   85350
/var/log/nginx/blog.gslin.org_ssl-access.log.4.gz
  14533  188929 2219819
/var/log/nginx/blog.gslin.org_ssl-access.log.5.gz
   6502   84526 1026178
/var/log/nginx/blog.gslin.org_ssl-access.log.6.gz
  32483  422279 4905919
/var/log/nginx/blog.gslin.org_ssl-access.log.7.gz
  21304  276952 3221877
/var/log/nginx/blog.gslin.org_ssl-access.log.8.gz
   7921  102973 1199356
/var/log/nginx/blog.gslin.org_ssl-access.log.9.gz
      0       0       0
/var/log/nginx/blog.gslin.org_ssl-access.log.10.gz
      0       0       0
/var/log/nginx/blog.gslin.org_ssl-access.log.11.gz
      0       0       0
/var/log/nginx/blog.gslin.org_ssl-access.log.12.gz
      0       0       0
/var/log/nginx/blog.gslin.org_ssl-access.log.13.gz
      0       0       0
/var/log/nginx/blog.gslin.org_ssl-access.log.14.gz
      0       0       0

看起來是從 blog.gslin.org_ssl-access.log.8.gz 這邊開始的,大概是 1/25 開始 (機器上面是 UTC 時間):

-rw-r----- 1 www-data adm   1894325 Jan 26 00:00 /var/log/nginx/blog.gslin.org_ssl-access.log.8.gz

然後再來看一下最近的 log,看起來是這兩天打的特別重,到五萬多:

$ echo /var/log/nginx/blog.gslin.org_ssl-access.log{,.?} | xargs -n1 | xargs -n1 -I% sh -c "echo %; grep ClaudeBot % | wc"
/var/log/nginx/blog.gslin.org_ssl-access.log
  29436  382668 4387703
/var/log/nginx/blog.gslin.org_ssl-access.log.1
  51712  672256 7852345

拉了 AWS 的圖來看跟預期的差不多:

機器是 t4g.micro 而且沒開 burstable,先前差不多都是略低於 10% 的線在跑,剛好利用 CPU credit 的概念,這幾天看起來就是被打而跑上去。

好像該補一下 alarm,丟到我自己的 Slack 以及 Pushover...

升級到 WordPress 6.3 遇到的問題

WordPress 6.3 出了:「WordPress 6.3 “Lionel”」,順手按了升級就爛掉了,發現是 admin 介面爛掉,網站本身還能瀏覽,先翻出 PHP 的錯誤訊息:

2023/08/09 04:52:32 [error] 845702#845702: *1350433 FastCGI sent in stderr: "PHP message: PHP Fatal error:  Uncaught ArgumentCountError: Too few arguments to function W3TC\Util_Environment::is_dbcluster(), 0 passed in /srv/blog.gslin.org/public/wp-content/db.php on line 56 and exactly 1 expected in /srv/blog.gslin.org/public/wp-content/plugins/w3-total-cache/Util_Environment.php:176

順著這個訊息找到這個算新的討論:「Fatal error in Util_Environment」,看起來跟 W3 Total Cache 有關。

(話說這個錯誤訊息還比較新,Kagi 上面找不到,後來是在 DuckDuckGo 上找到)

解法就比較粗暴一點,先到 wp-content/plugins 下讓 W3 Total Cache 失效:

sudo chmod 000 w3-total-cache

跑完升級後會出現錯誤訊息提示 W3 Total Cache 沒有啟用,這時候再開回來:

sudo chmod 755 w3-total-cache

後續把 cache 都清掉就正常了。

用 Fly.io 跑 RSS-Bridge,再把現有的 twitter2facebook 與 twitter2plurk 改寫

Twitter 把我本來 read-only 的兩個應用程式停用掉了,加上這陣子的新聞,就改用其他方式來處理。

用的是先前在「用 RSS-Bridge 接服務」提到的 RSS-Bridge,可以將 Twitter 的資料轉成 JSON Feed

其中 RSS-Bridge 是 PHP 寫的,剛好就拿先前在「在 Fly.io 上面跑 PHP」這邊提到的方法丟上 Fly.io,不需要自己架主機跑了。

然後把 twitter2facebooktwitter2plurk 這兩個專案裡面本來抓 Twitter API 的程式碼改成抓 JSON Feed。

先這樣子弄,之後再看看要不要搬...

在 Fly.io 上面跑 PHP

Heroku 把 free tier 拔的差不多後 (「Heroku 公佈了廢止免費方案的時間表」、「Heroku 的替代方案」),大家手上的小專案都往其他的服務跑,目前看起來做的比較有規模的就是 Fly.io 了,一個人可以建很多個 organization,而每個 organization 都有 free quota 可以用...

Fly.io 官方的文件 FLy.io Docs 裡面可以看到說明,介紹怎麼把 Laravel 站台跑起來,不過沒介紹怎麼跑純 PHP 站台,所以就看了文件研究看看,發現可以用 Docker container 跑,那就簡單了。

專案放在 GitHubgslin/fly-vanilla-php 上面。

一開始我的 Fly App 用的是 V1 的版本,是跑起來了,但後來還是換到 V2 跑,雖然兩者用起來沒有太大區別 (參考「Fly Apps」這邊的說明),但畢竟官方打算都把 V1 掛上 legacy 了,新的專案就儘量別用了...

另外一開始用 buildpacks 編 Docker image,但發現太慢了,就還是去 Docker Hub 上找個大戶人家包好的 image 來用。

首先是 fly.toml 這個檔案,這邊就直接指定用 Dockerfile 來編。

然後是 Dockerfile 這個檔案,這邊用的 image 是 richarvey/nginx-php-fpm 這包,預設會開在 port 80,所以 fly.toml 裡面就把 internal_port 指定在 80

另外就是指定了 WEBROOT,我把 root 放在 public/ 下面。

跑起來以後就可以用他提供的網址測試了,我這個專案在 https://hidden-river-325.fly.dev/ 這邊可以看到,另外我有設定自己的 domain,在 https://test-fly.gslin.com/ 也可以看到一樣的 phpinfo(); 資訊。

這邊有個小插曲,我想要掛自己的 domain 上去跑 HTTPS,但如果機器沒有掛 IPv6 address 的話,Fly.io 的系統不會認定設定已經完成,也就不會往下去申請 Let's Encrypt 的憑證,我放了一天覺得奇怪,摸了辦天才發現這個 "feature"。

用 RSS-Bridge 接服務

查資料的時候發現 RSS-Bridge 這個用 PHP 寫的專案,直接找個 PHP hosting 架起來就可以用了,沒有什麼其他的需求。

簡單架起來測了一輪,看起來不賴啊,如果一般人要用的話可以考慮就用這個專案就好,量很少的人可以用官方列出來的 Public instances 玩一下,量多的人可以自己架,PHP hosting 還蠻好找的,官方要求要 7.4+,注意一下 PHP hosting 提供的版本應該不會有太多問題。

自己寫的 feedgen 比較偏順便練 Python,不過當時的確是還不知道有這樣的專案,看了一下 GitHub 上的 tag 記錄,2013 就有的專案...

Laravel 將不會有 LTS 版本

查資料的時候發現,在 Laravel 9 剛發佈的時候是有掛 LTS 版本的資訊 (從「Laravel 9 (LTS) 出了」這邊的截圖可以看到),但在發佈後沒多就就被拿掉了,在 Taylor OtwellTwitter 上有提到這件事情:

從幾個 forum 討論的態度上看起來以後不會出新的 LTS 版本了,之後的版本都是提供一年的 bug fix + security fix,再加上另外一年的 security fix,基本上有兩年的 support,算是半強迫開發者時間到了就要升級版本...

另外一個有看到的問題是,現在的 Laravel 9 支援的 PHP 版本因為底層 Symfony 要 PHP 8.0+ 關係也一起被拉上來,連 PHP 7.4 都不支援了:

這個靠「***** The main PPA for supported PHP versions with many PECL extensions *****」這類 3rd-party repository 來補是還能解,但感覺 Symfony 對這些問題的態度...

PHP 8.2 預計要將一些字串內指定變數的方法標為 Deprecated,在 9.0 移除

Twitter 上看到這個蠻大的改變:

裡面的連結是「PHP RFC: Deprecate ${} string interpolation」,在文件中提到了 PHP 語言支援的四種字串內指定變數的方式:

  1. Directly embedding variables (“$foo”)
  2. Braces outside the variable (“{$foo}”)
  3. Braces after the dollar sign (“${foo}”)
  4. Variable variables (“${expr}”, equivalent to (string) ${expr})

提案在 PHP 8.2 裡將 3 與 4 兩種方式標為 deprecated,並且在 PHP 9.0 移除,目前看起來是 31:1 通過了...