【PHP】pg_query_params関数を使ったクエリ実行方法を解説

pg_query_params関数の使い方、理解していますか?

pg_query_params関数は、PHPからPostgreSQLに対してSQLコマンドを実行する際に利用する関数です。しかし、どのように関数を利用すればよいのか、他の関数と機能がどのように違うのか理解できていない人もいることでしょう。

そこで今回は、

・pg_query_params関数の機能と使い方
・pg_query関数との違い

について解説します。

pg_query_params関数の基本

pg_query_params関数は、PostgreSQLに対してSQLコマンドを送信するための関数です。引数と戻り値は以下の通りです。

引数 第1引数 コネクションリソース
第2引数 SQL
第3引数 パラメータ
戻り値 SQL実行結果

PHPのバージョン5.1.0で実装された関数で、バージョン7.4以降のPostgreSQLに対して動作します。

第1引数:コネクションリソース

第1引数のコネクションリソースは、pg_connect関数などによってPostgreSQLに接続したときに返されるリソース情報のことです。SQLを実行する際は、どのデータベースに対して実行するかをコネクションリソースを用いて設定します。

例えば、2つのデータベースが存在し、各データベースに対してSQLを実行する際は、以下のサンプルコードのように記述します。

コネクションリソースは、省略することも可能です。省略した際は、直近で作成されたコネクションリソースをデフォルトで指定するように動作します。

例えば、以下のサンプルコードの場合、SQLはデータベース「phpdb」に対して実行するように動作します。開発するアプリケーション全体に対して、1つのデータベースしか利用しない場合は、コードの記述量を減らすために第1引数を省略するのも一手です。

第2引数:SQL

第2引数は、パラメータ化されたSQLを設定します。パラメータ化とは、SQL文の中で「リテラル」として記述される部分を、プレースホルダに置き換えることです。例えば、以下のSQL文は、VALUESキーワードの後に記述されている「1」「’apple’」「130」がリテラルにあたります。

パラメータ化する際は以下のサンプルコードのように、リテラルの部分を$1、$2……と設定します。

パラメータ化することによって、第3引数で設定した値が後々挿入されるように動作します。

第3引数:パラメータ

第3引数は、パラメータ化した箇所($1、$2、…にあたる部分)に実際に当てはめる値を配列形式で設定します。先程示した、productテーブルにデータを挿入するSQLを例にした場合、以下のサンプルコードのように記述します。

以下のサンプルコードのように、パラメータの個数と配列の要素数を合わせていない場合、SQLを正常に実行できないので注意してください。

pg_query_params関数の使用例

ここからは、いくつか例を挙げながらpg_query_params関数の使い方を詳しく解説します。なお、これから示すサンプルコードは、データベースに接続する処理を省略しています。実行する際には、データベースへの接続処理を実装することを忘れずに行いましょう。

データの挿入

商品ID、商品名、価格の3つのカラムから構成されるテーブル「products」を考えてみます。

カラム NULL許容
商品ID(id) serial ×
商品名(name) char
価格(price) int

上記のテーブルに対してデータを複数個挿入するサンプルコードを、以下に示します。サンプルコードでは、挿入対象のデータ配列をforeachによって順に抽出し、商品名と価格を各々のパラメータに当てはめて1つずつSQLを実行しています。ちなみに、idは設定しなくても自動的に採番されるため、サンプルコードでは特に記述していません。

上記サンプルコードを実行し、SQL Shellでデータが挿入されているか確認した結果は、以下の通りです。

SQL Shell確認結果

データの検索

以下のようなテーブル「tasks」を考えてみます。

カラム NULL許容
タスクID(id) serial ×
開始日(start_date) date
完了日(due_date) date
タスク内容(task_details) char
完了状態(status) int

また、上記テーブルに対して下記のようにデータが保存されているとします。なお、完了状態の値の指す意味は「0 = 未着手」「1 = 完了」「2 = 中止」です。

挿入されているデータ

テーブル「tasks」に対して、開始日で検索し結果を表示するサンプルコードを、以下に示します。サンプルコードでは、SQLのWHERE句で指定する開始日をパラメータ化しています。SQL実行後は、pg_fetch_all関数で取得データを取り出し、リストタグを用いて結果を表示します。

ブラウザで確認した結果は、以下の通りです。

ブラウザ確認結果

データの更新

引き続き「tasks」テーブルについて考えてみます。

テーブルに保存されているデータの内、開始日が3月のデータ対して、完了状態を「0=未着手」から「1=完了」に更新するサンプルコードを、以下に示します。データの更新は、更新する値と更新対象のデータの条件をパラメータ化します。

SQL実行前の更新対象データは、以下の通りです。

更新前のデータ

SQL実行後は、完了状態(status)の値が1に更新されていることが分かります。

更新後のデータ

pg_query関数との違い

pg_query_params関数と機能が似ている関数として、pg_query関数が存在します。ここでは、pg_query関数とpg_query_params関数の違いについて解説しましょう。

pg_query関数とは?

pg_query関数も、SQLを実行できる関数ですが、引数は2つしか取りません。また、第2引数に設定するSQL文もパラメータ化する必要はありません。

2つの関数の違いを比較するため、従業員の情報を保存するテーブル「employees」から、名前が「Sato Taro」のデータを検索する処理を考えてみます。以下2つのサンプルコードは、pg_query関数を利用した検索処理とpg_query_params関数を利用した検索処理の両方を並べています。いずれの処理を実行しても、同じ結果が返ってきます。

2つの関数の違いを理解するためには、SQLインジェクションと事象について理解する必要があります。

SQLインジェクションはプログラムの脆弱性の一種

SQLインジェクションは、意図しないSQLを実行することでデータベースに対して不正な操作を行うことです。SQLインジェクションによって、外部に漏らしてはならないデータが抜き取られたり、データが改ざんされたりする危険があり、対策を講じなければなりません。

pg_query関数は、使い方を誤るとSQLインジェクションによってデータベースが不正に操作されるおそれがあります。先程示したpg_query関数のサンプルコードについて考えてみます。

上記サンプルコードは、WHERE句によって条件指定する際に、画面から入力された文字列「Sato Taro」を連結することで、「Sato Taro」に該当するデータを検索するSQL文を作成しています。

ここで、画面から以下の文字列が入力されたとしましょう。

すると、以下のSQL文が作成されます。

入力された文字列は、employeesテーブルに保存されているデータを削除するSQL文です。本来、名前を入力値として想定しているのにも関わらず、SQL文が入力されることにより、意図しない操作が実行されデータがすべて削除されてしまいます。これがSQLインジェクションです。

sqlインジェクションと対策方法のサニタイジングとは?詳しく解説します

SQLインジェクションを防げるpg_query_params関数

pg_query_params関数は、pg_query関数が持つ脆弱性を克服し、SQLインジェクションを防ぐことができます。SQL文をパラメータ化することによって、入力されたSQL文を通常の文字列として解釈するからです。

例えば以下のサンプルコードのように、パラメータ化SQL文とpg_query_params関数で処理を実装した場合は、名前が「’; DELETE FROM employees –」のデータを検索するSQL文として解釈されます。

データベースを操作する際に、画面から入力された値を基にSQL文を作成する必要がある場合は、pg_query_params関数を利用することが推奨されています。

まとめ

いかがでしたか?

今回は、

・pg_query_params関数の機能と使い方
・pg_query関数との違い

について解説しました。

pg_query_paramsは、SQLコマンドとパラメータを分離でき、直接パラメータを書き込むよりも扱いやすいのが特徴です。他の関数との違いを理解しつつ、積極的に利用しましょう。

データベースとは一体何なのか?初心者の疑問をまとめて解決