あの PDO::PARAM_INT の挙動が変わった!


(2020/3/27 追記)徳丸さんが記事書かれたので、リンクしときます!

PHP 7.2以降におけるPDO::PARAM_INTの仕様変更
さすが、検証の方法が、私のよりちゃんとしてますね^^;
float に関しての言及がないので、一応、以下は役に立つはず!

以下、元記事

あの何もしないことで有名な PDO::PARAM_INT の仕様がいつのまにか変わっていました。
が、解説記事が見当たらなかったので、検証とともに記事にしときます。
多分、アップグレードガイドにも載ってない秘密の情報ですw1

何もしない PDO::PARAM_INT とは?

PDO 使用している人には結構有名な話ですが、こいつ、ほとんど働きませんでしたw
参考記事:PDOでの数値列の扱いにはワナがいっぱい(2)

PDO::PARAM_INTはboolをintに変換(他は何もしない)

つまり、int へキャストされることを期待して
$stmt->bindValue(':test', 1.1, PDO::PARAM_INT);
なんてぶっこんでも、普通に1.1が投入されます
サボりすぎだろw

何が変わったのか?

プリペアドステートメントのエミュレーションが ON な時、ちゃんと働くようになりました!
正しく、int へキャストされます

本件は以下の変更で対応されたようです。
Emulated statements let value dictate parameter type

ただし、エミュレーションが OFF だと、まだサボりやがりますw

検証

以下のサンプルをいくつかのバージョンで動かしてみました。


<?php
try {
    $pdo = new PDO(
        'mysql:dbname=hoge;host=localhost;charset=utf8',
        '',
        '',
        [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            // PDO::ATTR_EMULATE_PREPARES => false,
            PDO::ATTR_EMULATE_PREPARES => true,
        ]
    );
    $stmt = $pdo->prepare('INSERT INTO test (php_v,test) VALUES (:php,:test)');
    $stmt->bindValue(':php', phpversion(), PDO::PARAM_STR);
    $stmt->bindValue(':test', 1.1, PDO::PARAM_INT);
    $stmt->execute();
} catch (PDOException $e) {
    exit($e->getMessage());
}
php のバージョン エミュレーション
7.1.33 OFF 1.1
7.1.33 ON 1.1
7.2.26 OFF 1.1
7.2.26 ON 1
7.3.13 OFF 1.1
7.3.13 ON 1
7.4.1 OFF 1.1
7.4.1 ON 1

影響

いくつかのトラブルを見ると、float にデータを突っ込む時にPDO::PARAM_STRを使うのを気持ち悪がって、PDO::PARAM_INTを採用したケースで被害が出ているようです。
まぁ、2択で INT を選ぶ気持ちはわかる!

これ、結構でかい問題になると思うので、バージョンアップ対応時には注意してください。


  1. Binding value as PDO::PARAM_INT has different behaviour in PHP 7.2 で、なぜかアップグレードガイドに載せることがスルーされてます