「CI 矩陣測過 PHP 7.1 到 8.3,代表這個套件正式支援這個範圍吧?」
打開 composer.json 你會發現一件有意思的事:這個套件從來沒有對外正式宣告過它支援哪個 PHP 版本範圍。今天來看這代表什麼。
composer.json 裡到底寫了什麼、沒寫什麼"php" 條件約束時,composer 依賴解析會怎麼行為composer.json 裡缺的那一行"require": {
"omnipay/common": "^3.0",
"ecpay/sdk": "^1.3"
}
require 區塊只列了兩個套件依賴,沒有 "php": ">=7.1" 這種版本約束。這不是打字漏掉,而是這個欄位本來就是選填——但選填不代表沒有影響。
CI 矩陣測過的範圍:.github/workflows/tests.yml 明確寫了 php: ['7.1','7.2','7.3','7.4','8.0','8.1','8.2','8.3'],每次 push 都會在這 8 個版本上各跑一次 phpunit。這是「開發者驗證過」的範圍。
套件對外承諾的範圍:composer.json 的 require.php 欄位,才是 Composer 在安裝時用來判斷「你的專案能不能裝這個套件」的依據。少了這行,Composer 不會在安裝階段擋下任何 PHP 版本——理論上就算你的專案跑在 PHP 5.6,Composer 也不會因為這個套件而拒絕安裝,它只會在你執行的時候才因為某個語法不相容而炸掉。
CI 矩陣證明的是「我們測過這個範圍」,composer.json 的版本約束才是「我們對外承諾這個範圍」——這兩件事沒有寫在同一個地方,就沒辦法互相檢查對方有沒有騙人。
❌ 現況:沒有 php 版本約束
"require": {
"omnipay/common": "^3.0",
"ecpay/sdk": "^1.3"
}
✅ 補上明確承諾,讓 Composer 在安裝階段就能把關
"require": {
"php": ">=7.1",
"omnipay/common": "^3.0",
"ecpay/sdk": "^1.3"
}
正例的差別不只是「多一行設定」。一旦寫了這行,Composer 在安裝依賴時就會主動檢查目前的 PHP 版本,版本不符會直接在 composer install 階段報錯,而不是等到程式跑到一半才因為語法不支援而中斷。把承諾寫進安裝流程能檢查的地方,比只寫在 CI 設定裡更早攔下問題——CI 矩陣只在「開發者自己的 push」上生效,別人的專案裝這個套件時,完全看不到你的 CI 設定跑過什麼。
明天會看:這個套件目前為什麼能撐住 7.1 到 8.3 這麼大的版本跨度——答案跟你可能猜的不太一樣,不是因為刻意做了版本分支處理,而是因為程式碼本身「沒去用到」會製造麻煩的新語法。後天則會看到另一種「承諾沒寫清楚」的樣子:README 上的徽章連結,跟現在真正在跑的 CI 工具鏈已經是兩回事。
你維護的專案,composer.json/package.json/等效設定檔裡的版本約束,跟 CI 矩陣測試的範圍是一致的嗎?如果不一致,是刻意的(例如故意保守一點)還是純粹沒人回頭檢查過?
composer.json 沒有 "php" 條目,代表這個套件沒有在安裝階段對外承諾支援的版本範圍明天看這個套件的程式碼為什麼天生就跨版本相容——順便看一個真實發生的案例:相容性不是只看自己的程式碼,依賴鏈裡別的套件先出狀況,一樣會拖累你。