PHPから登録したCLOB型の文字化けについて
以前に、
PHPセッションをDBで管理 - OracleでのAutoIncrement用テーブルを作成編
PHPセッションをDBで管理 - PHPセッション管理のコールバック関数を設定編
というエントリを書きましたが、その時に使用したPHPから「OracleのCLOB型」にデータ登録をした場合に、「文字化け」が発生する現象があったので、補足として書いておきます。
PHPソース=SJIS + NLS_CHARACTERSET=JA16SJIS
今回の場合、「PHPソース=SJIS + NLS_CHARACTERSET=JA16SJIS」の環境で文字化けが発生しました。
Oracleの「NLS_CHARACTERSET」の設定値は以下のSQLで確認できます。
NLS_CHARACTERSETの確認(SQL)
SELECT * FROM NLS_DATABASE_PARAMETERS WHERE PARAMETER='NLS_CHARACTERSET'
今回の原因
今回、文字化けが発生した原因ですが、Oracleのサポートガイドに以下のような記述がありました。
注意:
Oracle Database グローバリゼーション・サポート・ガイド キャラクタ・セットの選択
データベース・キャラクタ・セットがマルチバイトの場合、CLOBデータはUCS-2との互換形式で格納されます。データベース・キャラクタ・セットがシングルバイトの場合、CLOBデータはデータベース・キャラクタ・セットで格納されます。
つまりは、CLOB型に全角等のマルチバイト文字を登録しようとした場合は、暗黙的に文字コードが「UCS-2」に変換されるみたいです。
今回はここの「SJIS→UCS-2」の変換で文字化けが発生しているようでした。
前回のサンプルコードの改良版(BLOB対応版)
PHPセッションをDBで管理 - PHPセッション管理のコールバック関数を設定編でCLOBを使用したサンプルソースを掲載しましたが、
「CLOBでは無く、BLOBを使用する」とした場合は、「セッションの書き込み」のソースの一部を変更する必要があります。
変更点は下記の2点です。
- empty_blob() → empty_blob() に変更
- oci_bind_by_name($stmt, ":lob", &$clob, -1, OCI_B_CLOB); → oci_bind_by_name($stmt, ":lob", &$blob, -1, OCI_B_BLOB); に変更
<?php //セッションの書き込み時(BLOB版) function sess_write($id, $data) { // セッションを一度削除 $conn = oci_connect("username", "password", "oracle_sid"); $sql = "DELETE FROM sessions WHERE sess_id = '" . $id . "'"; $stmt = oci_parse($conn, $sql); oci_execute($stmt, OCI_DEFAULT); // 新たなシーケンスでセッションを再度書き込み // シーケンスはOracleトリガーで自動インクリメントされる $sql = "INSERT INTO sessions (sess_id, data, update_time) VALUES ('$id', empty_blob(), sysdate) RETURNING data INTO :lob"; $stmt = oci_parse($conn, $sql); $blob = oci_new_descriptor($conn, OCI_D_LOB); oci_bind_by_name($stmt, ":lob", &$blob, -1, OCI_B_BLOB); oci_execute($stmt, OCI_DEFAULT); $blob->save($data); oci_commit($conn); oci_free_statement($stmt); oci_close($conn); $rtn = true; return $rtn; } ?>
今回参考にしたページ
Oracle Database グローバリゼーション・サポート・ガイド - キャラクタ・セットの選択
http://otndnld.oracle.co.jp/document/products/oracle10g/102/doc_cd/server.102/B19218-02/ch2charset.htm