カテゴリー
SugiBlog Webエンジニアのためのお役立ちTips

PDOで取得したデータが、数値型なのに文字列型になってしまう

PHPのバージョンを7.4系から8.x系にバージョンアップする事案があり、変更点やシステムの動作に問題がないか等の調査をしていました。
その中で公式ページにも見当たらないことがあったので、実際にバージョンを切り替えながら検証して分かったことを共有させていただければと思います。

今回発見した問題点はPDOのプリペアドステートメントです。
セキュリティ上、脆弱性のないように開発する中で必ず使われているかと思います。

ただ、このプリペアドステートメント、数値型のデータを文字列型で返してきます。
困りますね。
しかしながらそういう仕様なので、それに合わせてコーディングするしかないわけなんですが、
これがなんと、8.1以降だと数値型で返ってるのです。

なんということでしょう・・・。

厳密な比較を行っている箇所はすべて修正しないといけません。
例えば以下のような場合です。

if($col["COLUMN_NAME_INDEX"] === "1") { ... }

このCOLUMN_NAME_INDEXのデータ型は数値型(Integer)ですが、PDOのプリペアドステートメントで返ってくるのは文字列型のため、このような比較をしています。
これが8.1以降だとCOLUMN_NAME_INDEXのデータが数値型のため、(1 === "1")という式となり判定はfalseとなります。

この問題については、コーディング自体に間違いがあるわけではありませんので、コード解析ツール等にかけても出てこないと思います。
原因の究明に時間がかかったので、ここに記しておきます。

更に調査したところ、PDOの設定でエミュレート・モードというものがあり、有効/無効(true/false)で設定します。
実際には今回の件に関する具体的な設定ではないのですが、この設定を無効(false)に設定してみると8.0でも数値型で返ってきました。

PDO::setAttribute(PDO::ATTR_EMULATE_PREPARES, false)

検証したバージョンは次の通りです。
7.4.29
8.0.28
8.1.17
8.2.4

また、逆に従来の文字列に合わせる場合だと確実に動作させることができます。
PDO::ATTR_STRINGIFY_FETCHESオプションをtrueにすることで全て文字列で返すように設定することが可能です。
公式リファレンスにも「全てのフェッチする値を強制的に文字列として扱います。」と書かれています。

PDO::setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false)

公式:PHP 8.0.x から PHP 8.1.x への移行

1,154 views