PHPのtrimの挙動を勘違いしていた

こんにちは、エンジニアのたべたつです。

先日、とある実装を行っていたときに文字列を固定で削除したかったのでtrim関数を使ったのですが、思っていた挙動と違った結果になったためドキュメントを改めて読んでみると新しい発見がたくさんありました。

突然ですがクイズです

以下の結果はどのようになるでしょうか。

echo ltrim('PHPengineer', 'PHP');

echo ltrim('PHPengineer', 'PH');

echo rtrim('category_tag/age', '/age');

答え

第1問

echo ltrim('PHPengineer', 'PHP');
// engineer

第2問

echo ltrim('PHPengineer', 'PH');
// engineer

第3問

echo rtrim('category_tag/age', '/age');
// category_t

知っている人からしたらとても簡単な問題でしたね。

PHPのtrimの挙動

上記のクイズの通りですが、PHPのtrim(ltrim, rtrim)の第2引数は、trimしたい文字列(string)ではなく文字(character)の群(characters)なんですね。

www.php.net

なので、この仕様を知らないと、実行しているファイルが置かれてるディレクトリを取得したい、などの場合に以下のようなプログラムを書き、 /root/sandbox/index.php としてサーバに配置すると、ローカルでは正常に動作するが、リリースすると意図せずバグる、ということが発生します。

echo rtrim(__FILE__, '/index.php');
// /root/sandbo

PHPで(先頭|末尾)に含まれる特定の文字列だけを取り除きたい

上記のtrimの挙動を踏まえて、文字列の先頭 or 末尾の特定の文字列を削除するにはどうしたらいいのか。

str_replace

1つ目は str_replace で空文字に置換してしまう方法です。

$needle = 'PHP';
$replace = '';
$subject = 'PHPengineer';

$result = str_replace($needle, $replace, $subject);

ただし、検索対象の文字列で始まるか、先頭以外に含まれている場合、複数含まれる可能性もある。そのため、 str_starts_with や 第4引数を駆使して置換結果が1つであることを確認する必要がある。

substr

2つ目は substr を駆使して文字列を切り取ってしまう方法です。

$subject = 'PHPengineer';
$needle = 'PHP';

substr($subject, count($needle), count($subject));
// 末尾を消す場合、以下のように - をつければ消すことができる。
substr($subject, 0, -count($needle));

この場合は1と違い検索対象の文字列で始まるか、だけを確認すれば良いが、countを2回使用していて、ぱっと見で処理がわかりにくい印象を受けます。

状況によってどちらがいいのかは変わりそうでした。 調べた限りではより良い方法がなさそうでした。(そもそも先頭、末尾の固定文字を削除したい、ということがあまりさなそう)

まとめ

これまで勘違いしていたtrimの挙動ですが、公式ドキュメントを読むとちゃんと書かれていて、色々な知らない情報があるな、と改めて思いました。

公式ドキュメント、大事ですね。

おまけ

trim、範囲指定できる。知らなかった。

echo ltrim('abcdefgh', 'a..d');
// efgh

echo ltrim('1234567891011', '1..5');
// 67891011