PHP8移行時にsprintfでエラー→Fatal error: Uncaught ValueError: Unknown format specifier “&” in 【path】

Fatal error: Uncaught ValueError: Unknown format specifier "&" in 【path】 PHP
※当サイトはアフィリエイト広告を掲載しています。

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が返されるようになったので、おそらく本件もそれが影響しているっぽいですね。

迅速な問題解決の参考になれば幸いです。

コメント

タイトルとURLをコピーしました