関数・クラス解説

oci_bind_by_name

version:PHP 5, PHP 7, PECL OCI8 >= 1.1.0 (公式)

Oracle プレースホルダに PHP 変数をバインドする

公式リファレンス

書式

oci_bind_by_name ( resource $statement , string $bv_name , mixed &$variable [, int $maxlength = -1 [, int $type = SQLT_CHR ]] ) : bool

説明

PHP の変数 variable を Oracle プレースホルダ bv_name にバインドします。 バインドは Oracle データベースのパフォーマンスに影響を及ぼす重要な仕組みで、 SQL インジェクション攻撃を防ぐための方法にもなります。

バインドを使うとデータベースでステートメントのコンテキストを (たとえ別のユーザーやプロセスが実行したものであっても) 再利用することができるようになります。 バインドによって SQL インジェクションの心配を軽減できる理由は、 バインド変数で代入したデータが決して SQL 文の一部と見なされることがないからです。 クォートやエスケープは不要です。

バインドする PHP 変数の中身を変更してステートメントを再実行するときにも、 ステートメントをパースしなおしたり変数をバインドしなおしたりする必要はありません。

Oracle のバインド変数は、データベースに渡す値に使う IN 変数と PHP に返される値に使う OUT 変数のふたつに大別できます。 バインド変数は IN あるいは OUT のどちらかの形式になります。バインド変数を入力用に使うか出力用に使うかは、 実行時に決まります。

OUT 変数を使う場合は maxlength を指定しなければなりません。 これを用いて、返される値を格納するのに必要なメモリを PHP が確保します。

IN 変数の場合も、もし PHP の変数にさまざまな値を格納してステートメントを何度も実行するのであれば maxlength を設定しておくことを推奨します。 そうしないと、最初に渡した PHP の変数の値にあわせて Oracle がデータの長さを切り詰めてしまう可能性があります。 最大長がどの程度になるかわからない場合は現在のデータサイズで oci_bind_by_name() を再コールしてから oci_execute() を実行するようにしましょう。 不必要に大きなサイズでバインドすると、 データベースのプロセスのメモリ使用量に影響を及ぼします。

バインドは、Oracle にデータをどのメモリアドレスから読み込むのかを指示します。 IN 変数の場合、oci_execute() がコールされたときにそのメモリアドレスに正しいデータがなければなりません。 つまり、バインドされる変数は実行時までスコープ内に残っていなければならないということです。 もしそうでなければ、 "ORA-01460: unimplemented or unreasonable conversion requested" のような予期せぬエラーが発生します。 OUT 変数の場合は、PHP の変数に何も値が格納されないといったことになるでしょう。

繰り返し実行されるステートメントで決して値が変わることのないバインド変数を使っていると、 Oracle のオプティマイザが最適な実行プランを選びにくくなる可能性があります。 実行に長い時間がかかり、再実行されることもめったにないようなステートメントは、 バインドの恩恵を受けられないでしょう。 しかし、どちらの場合であっても、文字列を連結して SQL 文を作るよりはバインドを使ったほうが安全です。 ユーザーの入力をフィルタリングせずに埋め込んでしまうというリスクをなくせるからです。

パラメータ

statement
有効な OCI8 ステートメント識別子。
bv_name
コロンを先頭につけたバインド変数プレースホルダをステートメント内で使います。 コロンは bv_name では必須ではありません。 Oracle はクエスチョンマークをプレースホルダとして使いません。
variable
bv_name に関連づける PHP の変数。
maxlength
バインド時の最大長。-1 に設定した場合、 variable の現在の長さを最大長として設定します。 この場合は、oci_bind_by_name() がコールされたときに variable が存在してデータが格納されている必要があります。
type
Oracle がデータを扱うときのデータ型。デフォルトの type は SQLT_CHR です。 Oracle は、可能な場合はこの型とデータベースのカラム (あるいは PL/SQL の変数) の型の間で変換を行います。 抽象データ型 (LOB/ROWID/BFILE) をバインドする必要がある場合、まず oci_new_descriptor() 関数を使用してこれを確保する必要があります。 length は抽象データ型用には 使用されず、-1 を設定する必要があります。 type に設定できる値は以下のとおりです。
  • SQLT_BFILEE or OCI_B_BFILE - BFILE 用
  • SQLT_CFILEE or OCI_B_CFILEE - CFILE 用
  • SQLT_CLOB or OCI_B_CLOB - CLOB 用
  • SQLT_BLOB or OCI_B_BLOB - BLOB 用
  • SQLT_RDD or OCI_B_ROWID - ROWID 用
  • SQLT_NTY or OCI_B_NTY - 名前付けされたデータ型用
  • SQLT_INT or OCI_B_INT - integer 用
  • SQLT_CHR - VARCHAR 用
  • SQLT_BIN or OCI_B_BIN - RAW カラム用
  • SQLT_LNG - LONG カラム用
  • SQLT_LBI - LONG RAW カラム用
  • SQLT_RSET - カーソル用。 oci_new_cursor() により、前もって生成されたもの。
  • SQLT_BOL あるいは OCI_B_BOL PL/SQL の BOOLEAN 用 (OCI8 2.0.7 以降と Oracle Database 12c 以降が必要)

返値

成功した場合に TRUE を、失敗した場合に FALSE を返します。

注意

警告 クォートは不要なので、magic_quotes_gpc や addslashes() を oci_bind_by_name() と同時に使わないでください。 自動的に追加されたクォートはデータベースの中にそのまま書き込まれてしまいます。 oci_bind_by_name() はデータをそのままの形で追加し、クォートやエスケープ文字の除去は行わないからです。

注意: WHERE 句の中の CHAR 型のカラムに文字列をバインドするときには、 Oracle における CHAR 型のカラムの比較が スペースで埋めた (固定長形式で) 行われることを覚えておきましょう。 WHERE 句を期待通りに動作させるには、 PHP の変数側でもスペースを追加してカラムの幅と同じにしておく必要があります。
注意: PHP の variable 引数は参照渡しです。 ループの形式によっては期待通りの動きをしないこともあります。 <?phpforeach ($myarray as $key => $value)  {    oci_bind_by_name($stid, $key, $value);}?> これは、それぞれのキーに対して $value の指す場所をバインドします。 つまり、すべてのバインド変数の値は ループの最後の処理で $value が指す値となります。 期待通りに動かすには、次のようにします。 <?phpforeach ($myarray as $key => $value) {    oci_bind_by_name($stid, $key, $myarray[$key]);}?>
警告 クォートは不要なので、magic_quotes_gpc や addslashes() を oci_bind_by_name() と同時に使わないでください。 自動的に追加されたクォートはデータベースの中にそのまま書き込まれてしまいます。 oci_bind_by_name() はデータをそのままの形で追加し、クォートやエスケープ文字の除去は行わないからです。

サンプル

例1 oci_bind_by_name() によるデータの挿入

// このようなテーブルを作ります //   CREATE TABLE mytab (id NUMBER, text VARCHAR2(40)); $conn = oci_connect('hr', 'welcome', 'localhost/XE'); if (!$conn) {     $m = oci_error();     trigger_error(htmlentities($m['message']), E_USER_ERROR); } $stid = oci_parse($conn,"INSERT INTO mytab (id, text) VALUES(:id_bv, :text_bv)"); $id = 1; $text = "Data to insert     "; oci_bind_by_name($stid, ":id_bv", $id); oci_bind_by_name($stid, ":text_bv", $text); oci_execute($stid); // テーブルに入る内容: 1, 'Data to insert     '

例2 一度組み立てたものを複数回実行

// このようなテーブルを作ります //   CREATE TABLE mytab (id NUMBER); $conn = oci_connect('hr', 'welcome', 'localhost/XE'); if (!$conn) {     $m = oci_error();     trigger_error(htmlentities($m['message']), E_USER_ERROR); } $a = array(1,3,5,7,11);  // 挿入するデータ $stid = oci_parse($conn, 'INSERT INTO mytab (id) VALUES (:bv)'); oci_bind_by_name($stid, ':bv', $v, 20); foreach ($a as $v) {     $r = oci_execute($stid, OCI_DEFAULT);  // 自動コミットはしません } oci_commit($conn); // すべてをここでコミットします // テーブルには次の 5 行が含まれます: 1, 3, 5, 7, 11 oci_free_statement($stid); oci_close($conn);

例3 foreach() ループでのバインド

$conn = oci_connect('hr', 'welcome', 'localhost/XE'); if (!$conn) {     $m = oci_error();     trigger_error(htmlentities($m['message']), E_USER_ERROR); } $sql = 'SELECT * FROM departments WHERE department_name = :dname AND location_id = :loc'; $stid = oci_parse($conn, $sql); $ba = array(':dname' => 'IT Support', ':loc' => 1700); foreach ($ba as $key => $val) {     // oci_bind_by_name($stid, $key, $val) ではうまくいきません。     // これは、すべてのプレースホルダを同じ内容にバインドしてしまうからです。     // $val ではなく、データの実際の位置を表す $ba[$key] を使いましょう。     oci_bind_by_name($stid, $key, $ba[$key]); } oci_execute($stid); $row = oci_fetch_array($stid, OCI_ASSOC+OCI_RETURN_NULLS); foreach ($row as $item) {     print $item." \n"; } oci_free_statement($stid); oci_close($conn);

例4 WHERE 句でのバインド

$conn = oci_connect("hr", "hrpwd", "localhost/XE"); if (!$conn) {     $m = oci_error();     trigger_error(htmlentities($m['message']), E_USER_ERROR); } $sql = 'SELECT last_name FROM employees WHERE department_id = :didbv ORDER BY last_name'; $stid = oci_parse($conn, $sql); $didbv = 60; oci_bind_by_name($stid, ':didbv', $didbv); oci_execute($stid); while (($row = oci_fetch_array($stid, OCI_ASSOC)) != false) {     echo $row['LAST_NAME'] ." \n"; } // 出力は //    Austin //    Ernst //    Hunold //    Lorentz //    Pataballa oci_free_statement($stid); oci_close($conn);

例5 LIKE 句でのバインド

$conn = oci_connect('hr', 'welcome', 'localhost/XE'); if (!$conn) {     $m = oci_error();     trigger_error(htmlentities($m['message']), E_USER_ERROR); } // 'South' からはじまるすべての都市を探します $stid = oci_parse($conn, "SELECT city FROM locations WHERE city LIKE :bv"); $city = 'South%';  // '%' は SQL でのワイルドカードです oci_bind_by_name($stid, ":bv", $city); oci_execute($stid); oci_fetch_all($stid, $res); foreach ($res['CITY'] as $c) {     print $c . " \n"; } // 出力は //   South Brunswick //   South San Francisco //   Southlake oci_free_statement($stid); oci_close($conn);

例6 REGEXP_LIKE でのバインド

$conn = oci_connect('hr', 'welcome', 'localhost/XE'); if (!$conn) {     $m = oci_error();     trigger_error(htmlentities($m['message']), E_USER_ERROR); } // 'ing' を含むすべての都市を探します $stid = oci_parse($conn, "SELECT city FROM locations WHERE REGEXP_LIKE(city, :bv)"); $city = '.*ing.*'; oci_bind_by_name($stid, ":bv", $city); oci_execute($stid); oci_fetch_all($stid, $res); foreach ($res['CITY'] as $c) {     print $c . " \n"; } // 出力は //   Beijing //   Singapore oci_free_statement($stid); oci_close($conn);

IN 句の中の条件数が少なくて固定なのであれば、個々にバインド変数を割り当てればいいでしょう。 実行時に不明な値は NULL が設定されます。 こうすればひとつのステートメントをすべてのユーザーで使い回すことができ、 Oracle の DB キャッシュの効率を最大化させることができます。

例7 IN 句での複数の値のバインド

$conn = oci_connect('hr', 'welcome', 'localhost/XE'); if (!$conn) {     $m = oci_error();     trigger_error(htmlentities($m['message']), E_USER_ERROR); } $sql = 'SELECT last_name FROM employees WHERE employee_id in (:e1, :e2, :e3)'; $stid = oci_parse($conn, $sql); $mye1 = 103; $mye2 = 104; $mye3 = NULL; // pretend we were not given this value oci_bind_by_name($stid, ':e1', $mye1); oci_bind_by_name($stid, ':e2', $mye2); oci_bind_by_name($stid, ':e3', $mye3); oci_execute($stid); oci_fetch_all($stid, $res); foreach ($res['LAST_NAME'] as $name) {     print $name ." \n"; } // 出力は //   Ernst //   Hunold oci_free_statement($stid); oci_close($conn);

例8 クエリが返す ROWID のバインド

// このようなテーブルを作ります //   CREATE TABLE mytab (id NUMBER, salary NUMBER, name VARCHAR2(40)); //   INSERT INTO mytab (id, salary, name) VALUES (1, 100, 'Chris'); //   COMMIT; $conn = oci_connect('hr', 'welcome', 'localhost/XE'); if (!$conn) {     $m = oci_error();     trigger_error(htmlentities($m['message']), E_USER_ERROR); } $stid = oci_parse($conn, 'SELECT ROWID, name FROM mytab WHERE id = :id_bv FOR UPDATE'); $id = 1; oci_bind_by_name($stid, ':id_bv', $id); oci_execute($stid); $row = oci_fetch_array($stid, OCI_ASSOC+OCI_RETURN_NULLS); $rid = $row['ROWID']; $name = $row['NAME']; // 名前を大文字に変換してそれを保存します $name = strtoupper($name); $stid = oci_parse($conn, 'UPDATE mytab SET name = :n_bv WHERE ROWID = :r_bv'); oci_bind_by_name($stid, ':n_bv', $name); oci_bind_by_name($stid, ':r_bv', $rid, -1, OCI_B_ROWID); oci_execute($stid); // テーブルに入る内容: 1, 100, CHRIS oci_free_statement($stid); oci_close($conn);

例9 INSERT での ROWID のバインド

// この例では id と name を挿入し、それから salary を更新します // このようなテーブルを作ります //   CREATE TABLE mytab (id NUMBER, salary NUMBER, name VARCHAR2(40)); // // Based on original ROWID example by thies at thieso dot net (980221) $conn = oci_connect('hr', 'welcome', 'localhost/XE'); if (!$conn) {     $m = oci_error();     trigger_error(htmlentities($m['message']), E_USER_ERROR); } $sql = "INSERT INTO mytab (id, name) VALUES(:id_bv, :name_bv)         RETURNING ROWID INTO :rid"; $ins_stid = oci_parse($conn, $sql); $rowid = oci_new_descriptor($conn, OCI_D_ROWID); oci_bind_by_name($ins_stid, ":id_bv",   $id,    10); oci_bind_by_name($ins_stid, ":name_bv", $name,  32); oci_bind_by_name($ins_stid, ":rid",     $rowid, -1, OCI_B_ROWID); $sql = "UPDATE mytab SET salary = :salary WHERE ROWID = :rid"; $upd_stid = oci_parse($conn, $sql); oci_bind_by_name($upd_stid, ":rid", $rowid, -1, OCI_B_ROWID); oci_bind_by_name($upd_stid, ":salary", $salary,   32); // 挿入する id と name $data = array(1111 => "Larry",               2222 => "Bill",               3333 => "Jim"); // それぞれの salary $salary = 10000; // 挿入し、その後すぐに各行を更新します foreach ($data as $id => $name) {     oci_execute($ins_stid);     oci_execute($upd_stid); } $rowid->free(); oci_free_statement($upd_stid); oci_free_statement($ins_stid); // 新しい行を表示します $stid = oci_parse($conn, "SELECT * FROM mytab"); oci_execute($stid); while ($row = oci_fetch_array($stid, OCI_ASSOC+OCI_RETURN_NULLS)) {     var_dump($row); } oci_free_statement($stid); oci_close($conn);

例10 PL/SQL ストアドファンクションでのバインド

//  PHP プログラムを実行する前に、ストアドファンクションを //  SQL*Plus あるいは SQL Developer で作ります // //  CREATE OR REPLACE FUNCTION myfunc(p IN NUMBER) RETURN NUMBER AS //  BEGIN //      RETURN p * 3; //  END; $conn = oci_connect('hr', 'welcome', 'localhost/XE'); if (!$conn) {     $e = oci_error();     trigger_error(htmlentities($e['message']), E_USER_ERROR); } $p = 8; $stid = oci_parse($conn, 'begin :r := myfunc(:p); end;'); oci_bind_by_name($stid, ':p', $p); // 返り値は OUT 変数に格納されます。デフォルトの型は文字列型なので、 // length が 40 ということは最大で 40 まで返される可能性があるということです oci_bind_by_name($stid, ':r', $r, 40); oci_execute($stid); print "$r\n";   // 24 と表示します oci_free_statement($stid); oci_close($conn);

例11 PL/SQL ストアドプロシージャでのパラメータのバインド

//  PHP プログラムを実行する前に、ストアドプロシージャを //  SQL*Plus あるいは SQL Developer で作ります // //  CREATE OR REPLACE PROCEDURE myproc(p1 IN NUMBER, p2 OUT NUMBER) AS //  BEGIN //      p2 := p1 * 2; //  END; $conn = oci_connect('hr', 'welcome', 'localhost/XE'); if (!$conn) {     $e = oci_error();     trigger_error(htmlentities($e['message']), E_USER_ERROR); } $p1 = 8; $stid = oci_parse($conn, 'begin myproc(:p1, :p2); end;'); oci_bind_by_name($stid, ':p1', $p1); // プロシージャの 2 番目のパラメータは OUT 変数です。デフォルトの型は文字列型なので、 // length が 40 ということは最大で 40 まで返される可能性があるということです oci_bind_by_name($stid, ':p2', $p2, 40); oci_execute($stid); print "$p2\n";   // 16 と表示します oci_free_statement($stid); oci_close($conn);

例12 CLOB 列のバインド

// 実行する前にテーブルを作成します。 //     CREATE TABLE mytab (mykey NUMBER, myclob CLOB); $conn = oci_connect('hr', 'welcome', 'localhost/XE'); if (!$conn) {     $e = oci_error();     trigger_error(htmlentities($e['message']), E_USER_ERROR); } $mykey = 12343;  // この例のための任意のキー $sql = "INSERT INTO mytab (mykey, myclob)         VALUES (:mykey, EMPTY_CLOB())         RETURNING myclob INTO :myclob"; $stid = oci_parse($conn, $sql); $clob = oci_new_descriptor($conn, OCI_D_LOB); oci_bind_by_name($stid, ":mykey", $mykey, 5); oci_bind_by_name($stid, ":myclob", $clob, -1, OCI_B_CLOB); oci_execute($stid, OCI_DEFAULT); $clob->save("A very long string"); oci_commit($conn); // CLOB データを取得します $query = 'SELECT myclob FROM mytab WHERE mykey = :mykey'; $stid = oci_parse ($conn, $query); oci_bind_by_name($stid, ":mykey", $mykey, 5); oci_execute($stid); print ''; while ($row = oci_fetch_array($stid, OCI_ASSOC+OCI_RETURN_LOBS)) {     print ''.$row['MYCLOB'].'';     // ループ内で、大きなサイズの変数を解放してから次のフェッチに進めます。     // これで、PHP のピークメモリ利用量を抑えます。     unset($row); } print '';

例13 PL/SQL の BOOLEAN のバインド

$conn = oci_connect('hr', 'welcome', 'localhost/XE'); if (!$conn) {     $e = oci_error();     trigger_error(htmlentities($e['message']), E_USER_ERROR); } $plsql =    "begin     :output1 := true;     :output2 := false;    end;"; $s = oci_parse($c, $plsql); oci_bind_by_name($s, ':output1', $output1, -1, OCI_B_BOL); oci_bind_by_name($s, ':output2', $output2, -1, OCI_B_BOL); oci_execute($s); var_dump($output1);  // true var_dump($output2);  // false

参考

  • oci_bind_array_by_name() - PHP の配列を Oracle PL/SQL の配列に名前でバインドする
  • oci_parse() - 実行のために Oracle の文をパースする
  • ワード検索


    ※入力キーワードが、関数名・説明文・タグに含まれるものを検索

    関数名アルファベット別

    A B C D E F G H I J
    K L M N O P Q R S T
    U V W X Y Z _

    最終更新一覧

    stristr
     大文字小文字を区別せず文字列を検索し、ヒット箇所以降(あるいは以前)の文字列を返却

    stripslashes
     バックスラッシュでエスケープされた文字列から、バックスラッシュを取り除く

    stripos
     大文字小文字を区別せずに文字列が最初に現れる位置を取得する

    stripcslashes
     addcslashes() でクォートされた文字列をアンクォートする

    strip_tags
     文字列から HTML と PHP のタグを除去して返却

    strcspn
     指定した文字が最初に現れる位置を調べる

    strcoll
     ロケールに基づいて2つの文字列を比較し同じか(あるいは大小)を判定する

    strcmp
     2つの文字列を比較し同じか(あるいは大小)を判定する

    strchr
     strstr() のエイリアス

    strcasecmp
     2つの文字列を比較(大文字小文字を区別せず同じとみなす)

    カテゴリー一覧

    PHP の振る舞いの変更
    音声フォーマットの操作
    認証サービス
    コマンドライン関連
    圧縮およびアーカイブ
    暗号
    データベース関連
    日付および時刻関連
    ファイルシステム
    自然言語および文字エンコーディング
    画像処理および作成
    メール関連
    数学
    テキスト以外の MIME 型
    プロセス制御
    その他の基本モジュール
    その他のサービス
    検索エンジン用の拡張モジュール
    サーバー固有のモジュール
    セッション関連
    テキスト処理
    変数・データ型関連
    ウェブサービス
    Windows 用のモジュール
    XML 操作
    GUI用の拡張モジュール