本系列專為從PHP之外的語言開啟碼農人生、卻因命運安排整天與PHP為伍的人撰寫,收集一些從其他語言的角度看來不可思議的設計。如果能讓在讀這篇文章的你在實務上踩到而懷疑人生前就釋懷,就是我莫大的榮幸。
前言
繼前篇gethostbyname(),本篇討論少見於PHP之外的設計:varying case sensitivity,也就是某些情況下大小寫相關,某些情況下大小寫無關。總之,就是任性。
可能因為我個人有經年累月的大小寫相關的程式編寫習慣,再加上linter層層把關,我大概兩年多才發現有這個特性,當下簡直是晴天霹靂、茅廁頓開,在這非黑即白的大小寫相關性世界,PHP竟仍能為我們開一扇窗;彷彿在提醒我們,看待世間萬物切勿抱持成見,才能看見真實。
常有人說PHP是如詩般的語言,這,何嘗不正是其獨有的詩意呢?
嘴夠了,進入正題
更精確來講,PHP的大小寫相關性是:
- 函式名、命名空間名與類別名大小寫無關
- 變數名、常數名大小寫相關
…

大部分的時候這都只會造成一些小驚喜,像這樣:
class Foo {
const ABC = 'I am a constant';
static function bar() { ... }
}
$poetry = 'I am a variable';
Foo::bar(); // 可以
foo::Bar(); // 嗯,有何不可
print_r( $poEtry ); // 不行喔
print_r( Foo::aBC ); // 討厭,就說不行了,都欺負人家 ...
但一個沒站穩,玻璃心還是會摔碎的。
別名不只是別名
自從5.3加入命名空間後,為了使用上方便,PHP也同時加入了名稱引入的語法:
use XXX\yyy as zzz;
從此zzz
就成了XXX\yyy
的別名,我們就可以用zzz來取用XXX\yyy,省力又環保,可喜可樂,可喜可樂。
嗯,不過有件事要注意:類別名與命名空間名會隨著引入別名而變。
蝦?
來來,所謂千言萬語不如一段code,假設有下面兩個檔案:
// nao.php
<?php
namespace Nao;
class Boom {
static function cool() {
return 'My name is ' . self::class;
}
}
// whatever.php
<?php
require_once './namespace.php';
use nao\boom as Boom;
echo bOOm::class . ': ' . bOOm::cool() . "\n";
echo Boom::class . ': ' . Boom::cool() . "\n";
echo nAo\BoOm::class . ': ' . nAo\BoOm::cool() . "\n";
$boom = new Boom();
echo 'The instance class is: ' . get_class( $boom ) . "\n";
在俺的php7.0跑起來會是這樣:
nao\boom: My name is Nao\Boom
nao\boom: My name is Nao\Boom
nAo\BoOm: My name is Nao\Boom
The instance class is: Nao\Boom
從上面我們可以觀察到:
Nao\Boom
的class
會隨引入的命名而變。上例中,因為引入時是用nao\boom
,接著Boom::class
或bOOm::class
都變成nao\boom
了- 如果用完整名稱,
class
仍會因為我們寫的方式而變。例如第三行變成了nAo\BoOm
。 - 別名會影響的範圍僅限於別名所在的範圍。因此呼叫static function
cool()
時印出的類別名都仍然是原本的Nao\Boom
,用get_class()
來看Boom
物件的class也會是原本的Nao\Boom
而非nao\boom
。
…
寫到這裡,我心裡已經在高唱『蝦啦~嘿蝦啦~』
這就代表,像下面這些檢驗類別名稱做事的程式碼其實是很脆弱的:
if ( get_class( $object ) === Expected_Class_A::class ) {
// ... 做東做西 ...
}
$callbacks[ Some_Class::class ] = function( ... ) {
// 做東做西...
};
因為::class
的實際內容會因為引入的方法而變。想像一下,原本我們在寫這段程式碼的時候沒有依靠引入別名,一切都運作正常。有一天後續維護的人覺得老是寫完整名稱好麻煩,use
了一下,偏偏又不小心寫錯一個大小寫,一切突然就炸了。而且interpreter不會告訴你是因為大小寫寫錯,因為那就是合法的程式碼 …
重拾玻璃心
其實就像一開始說的,絕大多數的情況下這只會造成一些小驚喜。我個人僅在全職寫了兩年多後才偶然在內部工具的程式碼中踩到,代表這不太常造成什麼麻煩。更具體的說,只要我們的codebase有在進行正常的實務規範:
- 有統一的編寫標準,例如採用PSR-4與WordPress coding standards。
- 有盡可能設置linting rules甚至auto formatter來使語法一致。
- code review是程式碼發布流程的一環。
這小小的任性就不太會造成實務上的麻煩,當作有趣的PHP豆知識來看就行了。
嗯,其實當程式碼的行為需要依賴reflection常常就是一個警訊;而大小寫都統一不了,更代表著實務規範上出了嚴重問題。想來PHP一定是想從設計上來警告我們吧?
對,一定是這樣,一定是這樣的吧
這樣一想,心,又溫暖了一點。