10年以上前に自分で書いたコードをPHP7.4からPHP8.2へ変更中に、遭遇したエラーです。
そのままは載せられないので、ちょっと形を変えて載せます。こんな感じ。
<?php
echo sprintf('パスワードは半角英数字および !#$%&()\-?@{}で入力してください。', '文字列');
// エラー
// Fatal error: Uncaught ValueError: Unknown format specifier "&" in D:\example.com\file.php on line 148
少なくともPHP7系までは動作していたと思います。ググってみたところ、日本語による情報は執筆時点では見つかりませんでした。
英語圏のサイトでも主にWordPressのプラグインエラーとして発生しており、利用者目線の回避方法ばかり。技術者目線のめぼしいページが見つからず、解決まで少し時間を要したのでメモしておきます。
結論
sprintf
関数に与える第一引数が問題です。私の例では以下の部分。
'パスワードは半角英数字および !#$%&()\-?@{}で入力してください。'
より具体的には、エラーメッセージ内に指摘されている&
部分が問題ではなく、その1つ前の%
が問題です。
該当関数の第一引数内における%
は特別な意味合いを持っており、表示形式を指定する際に用いられます。例えば%s
は、文字列を置き換える際におなじみですね。
その%
の後に続く文字がここでは&
となっている。つまり「%&
なんて指定子ないよ!」というエラーです。
%
を%%
とすることでエスケープできるっぽいことを発見し、解決しました(後述しますが公式マニュアルでも確認できました)。
echo sprintf('パスワードは半角英数字および !#$%%&()\-?@{}で入力してください。', 'test');
本例のように、sprintf
を直接呼び出し、指定するなら、たしかにそりゃエスケープされていないだろうと直感的に分かるところです。
ですがあるクラスの変数としてエラーメッセージをまとめて定義していたので、すぐに気づきませんでした。。
公式マニュアルより
公式マニュアルを見ても書いてないなぁと思いつつ、自分でエスケープ方法を発見し、とりあえず解決した形となりました。
しかしながら改めて公式マニュアルの英語部分を見てみると……
With printf() and sprintf() functions, escape character is not backslash ‘\’ but rather ‘%’.
PHP: sprintf – Manual
printf()
とsprintf()
のエスケープは\
(バックスラッシュ)ではなく、%
だと書いてあるではありませんか!(しかも今から14年前……)
英語が得意ではない私としては、ついつい日本語を追ってしまう癖があるようです。
何度も書いている気がしますが、公式マニュアルは大事ですね。日本語訳されていないことも多々あるので、英語部分もマストです。
おまけ
今回元となったコードがPHP5~7当時くらいでしょうから、14年前からの仕様であれば、その時にエラーになっていたはずです。そうではなかったので、たぶんPHP8が原因と思われます。
公式マニュアルによれば、sprintf()
はPHP8.0から仕様が大幅に変わったようです。主にValueError
が返されるようになったので、おそらく本件もそれが影響しているっぽいですね。
迅速な問題解決の参考になれば幸いです。
コメント