0
0

プリペアドステートメントの出力(ログ,デバッグ用)

Posted at

悩み

ログ用に出すのもそうだし、SQLでエラーが出た際に
実際にDBに発行されているSQLの内容を調べる方法がわからなかった。
変数がバインドされた後に実際に投げられているSQLはどうやって調べるの、という疑問があった。

結論

私はこのように出しました。
私の環境(PHP8.2.9 mysql 5.7.36)では動きました。

    //バインドされた後のプリペアドステートメントを出力(ログ用)
    public function getPreparedStmtAfterBind($pdo_stmt) {
        
        ob_start();
        $pdo_stmt->debugDumpParams();
        $content = ob_get_contents();
        ob_end_clean();
        
        preg_match('/Sent\sSQL:\s\[\d+\]\s(.*?)\sParams/s', $content, $matches);

        return $matches[1];
    }
    
    使用方法
    引数にはPDOに対してprepare($sql)をして、executeした時の戻り値である変数を指定
    $stmt = $pdo->prepare($sql);
    $result = $stmt->execute($params);
    echo getPreparedStmtAfterBind($result);
    とすれば帰ってくるはずです。
    

やっていること

  1. ob_start()で出力バッファリング
  2. PDOStatementインスタンス(上の例だと$result)に対してdebugDumpParamsメソッドを実行
  3. 2の実行結果を変数contentに格納
  4. ob_end_clean()でバッファをクリアして終了
  5. contentの中からpreg_matchでほしいところだけ抽出
  6. その結果を出力

出力バッファリング周りについてはこの方を参考にしました。
https://servercan.net/blog/2021/03/pdo%E3%81%A7%E5%AE%9F%E8%A1%8C%E3%81%97%E3%81%9Fsql%E6%96%87%E3%82%92debugdumpparams%E3%81%8B%E3%82%89%E6%8A%BD%E5%87%BA%E8%A1%A8%E7%A4%BA%E3%81%99%E3%82%8B/

経緯

よくPHPでSQLを投げる際、
$stmt = $pdo->prepare($sql); した後に
$stmt->execute($params);
とかするのですが、
たまにエラーになることがよくあります。

SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens

与えた変数の数と、バインド変数の数があっていませんよ、というエラーです。

たいてい、

いやそんなことはない。あってるはずだ。

と思いながらデバッグを始めるんですけど。

実際にDBに発行されているSQL文って、ぱっとは出せないんですよね。
prepareメソッドを実行する際に引数として与える sql文を 出力して
埋め込む変数を1つずつechoなり var_dumpすれば見えなくもないですが、

見ずらい、めんどくさい

そこでなんとかして、出せないかといろいろ探していたところ
https://www.php.net/manual/ja/pdostatement.debugdumpparams.php
という求めていたものを見つけた!!
と思ったのですが、出力がまぁかゆいところに手が届かない、出力でした。

SQL: [322] UPDATE 
                    user 
                SET 
                    name = :name,
                    email = :email,
                    sex_type = :sex_type,
                    age_type = :age_type,
                    update_at = now()
                , icon_image_file = :icon_image_file WHERE id = :id
Sent SQL: [358] UPDATE 
                    user 
                SET 
                    name = 'あたし',
                    email = '**************************',
                    sex_type = '0',
                    age_type = '4',
                    update_at = now()
                , icon_image_file = '*************************' WHERE id = '**'
Params:  6
Key: Name: [5] :name
paramno=-1
name=[5] ":name"
is_param=1
param_type=2
Key: Name: [6] :email
paramno=-1
name=[6] ":email"
is_param=1
param_type=2
Key: Name: [9] :sex_type
paramno=-1
name=[9] ":sex_type"
is_param=1
param_type=2
Key: Name: [9] :age_type
paramno=-1
name=[9] ":age_type"
is_param=1
param_type=2
Key: Name: [16] :icon_image_file
paramno=-1
name=[16] ":icon_image_file"
is_param=1
param_type=2
Key: Name: [3] :id
paramno=-1
name=[3] ":id"
is_param=1
param_type=2

欲しいのは

Sent SQL: [358] UPDATE 
                    user 
                SET 
                    name = 'あたし',
                    email = '**************************',
                    sex_type = '0',
                    age_type = '4',
                    update_at = now()
                , icon_image_file = '*************************' WHERE id = '**'

この中の UPDATE から PARAMSの間なんですよね。
だから、preg_match()で抜き出しました。
正直、参考にした方の記事が無かったら思いつきませんでしたね。
ありがとうございます。

結果はこんな感じです。

UPDATE user SET name = 'あたし',email = '**************************', sex_type = '0',age_type = '4',update_at = now(), icon_image_file = '*************************' WHERE id = '**'

ちなみに、php自体は初心者なのでおかしな箇所があるかもしれません。
その時はご指摘ください。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0