我們與PHP的距離(二): 任性的大小寫

本系列專為從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\Boomclass會隨引入的命名而變。上例中,因為引入時是用nao\boom,接著Boom::classbOOm::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-4WordPress coding standards
  • 有盡可能設置linting rules甚至auto formatter來使語法一致。
  • code review是程式碼發布流程的一環。

這小小的任性就不太會造成實務上的麻煩,當作有趣的PHP豆知識來看就行了。

嗯,其實當程式碼的行為需要依賴reflection常常就是一個警訊;而大小寫都統一不了,更代表著實務規範上出了嚴重問題。想來PHP一定是想從設計上來警告我們吧?

對,一定是這樣,一定是這樣的吧

這樣一想,心,又溫暖了一點。

發表留言

Please log in using one of these methods to post your comment:

WordPress.com 標誌

您的留言將使用 WordPress.com 帳號。 登出 /  變更 )

Facebook照片

您的留言將使用 Facebook 帳號。 登出 /  變更 )

連結到 %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.