PHP Conference Japan 2024

pg_query_params

(PHP 5 >= 5.1.0, PHP 7, PHP 8)

pg_query_params將命令提交到伺服器並等待結果,可以將參數與 SQL 命令文字分開傳遞

說明

pg_query_params(PgSql\Connection $connection = ?, 字串 $query, 陣列 $params): PgSql\Result|false

提交一個命令到伺服器並等待結果,並能夠將參數與 SQL 命令文字分開傳遞。

pg_query_params() 類似於 pg_query(),但提供了額外的功能:參數值可以與命令字串本身分開指定。pg_query_params() 僅支援 PostgreSQL 7.4 或更高版本的連線;使用早期版本時將會失敗。

如果使用參數,則在 query 字串中以 $1、$2 等方式引用它們。相同的參數可能會在 query 中出現多次;在這種情況下將使用相同的值。params 指定參數的實際值。此陣列中的 null 值表示對應的參數是 SQL NULL

pg_query_params() 相較於 pg_query() 的主要優點是參數值可以與 query 字串分開,從而避免了繁瑣且容易出錯的引號和跳脫字元處理。與 pg_query() 不同,pg_query_params() 在給定字串中最多允許一個 SQL 命令。(其中可以有分號,但不能有多個非空命令。)

參數

connection

一個 PgSql\Connection 實例。當未指定 connection 時,將使用預設連線。預設連線是 pg_connect()pg_pconnect() 建立的最後一個連線。

警告

從 PHP 8.1.0 開始,不建議使用預設連線。

query

參數化的 SQL 陳述式。必須只包含單個陳述式。(不允許以分號分隔的多個陳述式。)如果使用任何參數,則將它們稱為 $1、$2 等。

使用者提供的數值應始終以參數形式傳遞,而不是插入到查詢字串中,因為它們可能形成 SQL 注入 攻擊向量,並且在處理包含引號的數據時會引入錯誤。如果由於某些原因您無法使用參數,請確保插入的數值已 正確跳脫

params

一個參數值陣列,用於替換原始預備查詢字串中的 $1、$2 等佔位符。陣列中的元素數量必須與佔位符的數量相符。

不支援將 bytea 欄位的值作為參數。請改用 pg_escape_bytea(),或使用大型物件函式。

傳回值

成功時傳回 PgSql\Result 實例,失敗時傳回 false

更新日誌

版本 說明
8.1.0 現在傳回 PgSql\Result 實例;先前傳回的是 資源
8.1.0 connection 參數現在需要一個 PgSql\Connection 實例;先前需要的是 資源

範例

範例 #1 使用 pg_query_params()

<?php
// 連線到名為 "mary" 的資料庫
$dbconn = pg_connect("dbname=mary");

// 尋找所有名為 Joe's Widgets 的商店。請注意,不需要
// 跳脫 "Joe's Widgets"
$result = pg_query_params($dbconn, 'SELECT * FROM shops WHERE name = $1', array("Joe's Widgets"));

// 與僅使用 pg_query 的比較
$str = pg_escape_string("Joe's Widgets");
$result = pg_query($dbconn, "SELECT * FROM shops WHERE name = '{$str}'");

?>

另請參閱

新增註釋

使用者貢獻的註釋 14 則註釋

victor dot engmark at terreactive dot ch
13 年前
您無法使用 pg_query_params 執行多個語句,但您仍然可以在不使用 pg_query 的情況下獲得交易支援

<?php
$connection
= pg_connect("host=127.0.0.1 port=5432 dbname=foo user=bar password=baz");
pg_query($connection, 'DROP TABLE IF EXISTS example');
pg_query($connection, 'CREATE TABLE example (col char(1))');
pg_query($connection, 'INSERT INTO example (col) VALUES (\'a\')');
// 在另一個連線執行 'SELECT col FROM example' 會回傳 "a"
pg_query($connection, 'BEGIN');
pg_query_params($connection, 'UPDATE example SET col = $1', array('b'));
// 在另一個連線執行 'SELECT col FROM example' 仍然回傳 "a"
pg_query_params($connection, 'UPDATE example SET col = $1', array('c'));
// 在另一個連線執行 'SELECT col FROM example' 仍然回傳 "a"
pg_query($connection, 'COMMIT');
// 在另一個連線執行 'SELECT col FROM example' 會回傳 "c"
?>
php at richardneill dot org
10 年前
如果您想將查詢直接貼到 PSQL 中,除錯參數化查詢可能會很繁瑣。這裡有一個小技巧可以幫助您

<?php
$sql
= "SELECT * from table WHERE col_a = $1 and col_b=$2 and col_c=$3";
$params = array (42, "a string", NULL);

$debug = preg_replace_callback(
'/\$(\d+)\b/',
function(
$match) use ($params) {
$key=($match[1]-1); return ( is_null($params[$key])?'NULL':pg_escape_literal($params[$key]) );
},
$sql);

echo
"$debug";
//印出: SELECT * from table WHERE col_a = '42' and col_b='a string' and col_c=NULL
?>

這個方法可以正常運作,除非在(不常見的)情況下,我們有一個字面上的 $N;正則表達式會在不應該取代的地方取代它。例如
<?php
// ' ... $1 ... ' 和 $1 都會被取代;前者是錯誤的,後者是正確的。
$sql = "SELECT 'Your bill is for $1' AS invoice WHERE 7 = $1";
$params = array(7);
//$debug: SELECT 'Your bill is for $7' AS invoice WHERE 7 = '7'"
?>
ac at esilo dot com
14 年前
`pg_query` 和 `pg_query_params` 可以合併成單一函式。這也免除了為 `pg_query_params` 建構參數陣列的需要。

<?php
function my_query($conn, $query)
{
if(
func_num_args() == 2)
return
pg_query($conn, $query);

$args = func_get_args();
$params = array_splice($args, 2);
return
pg_query_params($conn, $query, $params);
}
?>

用法

<?php
/* 非參數化範例 */
my_query($conn, "SELECT $val1 + $val2");

/* 參數化範例 */
my_query($conn, "SELECT $1 + $2", $val1, $val2);
?>
dt309 at f2s dot com
17 年前
如果您需要在 select 查詢中為一個欄位提供多個可能的值,那麼以下內容將會有所幫助。

<?php
// 假設 $values[] 是一個包含您感興趣值的陣列。
$values = array(1, 4, 5, 8);

// 要使用 pg_query() 選取不定數量的參數,您可以使用:
$valuelist = implode(', ', $values);
$query = "SELECT * FROM table1 WHERE col1 IN ($valuelist)";
$result = pg_query($query)
or die(
pg_last_error());

// 因此您可以假設以下程式碼可以運作。
$query = 'SELECT * FROM table1 WHERE col1 IN ($1)';
$result = pg_query_params($query, array($valuelist))
or die(
pg_last_error());
// 產生錯誤訊息:'ERROR: invalid input syntax for integer'
// 它只在指定單一值時有效。

// 您必須使用以下方法:
$valuelist = '{' . implode(', ', $values ). '}';
$query = 'SELECT * FROM table1 WHERE col1 = ANY ($1)';
$result = pg_query_params($query, array($valuelist));
?>

此範例中產生的錯誤是由 PostGreSQL 產生的。

最後一個方法是透過建立一個包含所需值的 SQL 陣列來運作。「IN (...)」和「= ANY (...)」是等效的,但 ANY 用於處理陣列,而 IN 用於處理簡單列表。
jsnell at e-normous dot com
17 年前
當插入到 pg 資料表的布林值欄位時,您不能提供 PHP 布林值類型。您必須改用字串「t」或「f」。PHP 會嘗試將作為參數提供的布林值更改為字串,然後嘗試使用空字串表示 false。

失敗範例
pg_query_params('insert into table1 (bool_column) values ($1)', array(false));

可行的範例
pg_query_params('insert into lookup_permissions (system) values ($1)', array(false ? 't' : 'f'));
peter dot kehl+nospam at gmail dot com
12 年前
pg_query_params() 的第三個參數 $params 會忽略零位元組字元(PHP 中的 "\0" 或 chr(0))之後的任何字串值部分。這可能是 serialize() 的結果。

請參閱 https://bugs.php.net/bug.php?id=63344
alec at smecher dot bc dot ca
12 年前
請注意,由於您地區設定的數字格式設定,您可能無法以參數形式傳遞數值,並使其在 PostgreSQL 中仍然是數字。

如果您的系統地區設定使用「,」作為小數點分隔符號,則以下程式碼將導致資料庫錯誤

pg_query_params($conn, 'SELECT $1::numeric', array(3.5));

要使其運作,需要使用例如 number_format 手動將 3.5 轉換為字串。

(我已將此問題提交為錯誤 #46408,但顯然這是預期行為。)
strata_ranger at hotmail dot com
15 年前
關於布林值,在查詢中傳遞它們時,只需將它們強制轉換為 (整數) 類型即可 -- '0' 和 '1' 是 SQL 布林輸入完全可接受的字面值。

- https://postgresql.dev.org.tw/docs/8.2/interactive/datatype-boolean.html

使用雙引號編寫參數化查詢也是安全的,這允許您在查詢中混合常數值和佔位符,而不必擔心 PHP 是否會嘗試替換參數化字串中的任何變數。

當然,這也意味著與 PHP 的雙引號字串語法不同,您可以在 SQL 字串中包含字面上的 $1、$2 等,例如:

<?php
// 可以正常運作($1 是佔位符,$2 是字面意思)
pg_query_params("INSERT INTO foo (col1, col2) VALUES ($1, 'costs $2')", Array($data1));

// 會拋出 E_WARNING(傳遞過多參數)
pg_query_params("INSERT INTO foo (col1, col2) VALUES ($1, 'costs $2')", Array($data1, $data2));
?>
mledford
18 年前
如果您嘗試複製 pg_query_params 函式,您可能還需要支援 NULL 值。雖然 is_int 會針對 NULL 值返回 true,但 SQL 的格式…

function pg_query_params( $db, $query, $parameters ) {
// 依需要跳脫參數並為回呼函式建置參數
global $pg_query_params__parameters;
foreach( $parameters as $k=>$v ) {
if ( is_null($v) ) {
$parameters[$k] = 'NULL';
} else {
$parameters[$k] = ( is_int( $v ) ? $v : "'".pg_escape_string( $v )."'" );
}
}
$pg_query_params__parameters = $parameters;

// 使用 pg_query 呼叫
return pg_query( $db, preg_replace_callback( '/\$([0-9]+)/', 'pg_query_params__callback', $query));
}
cc+php at c2se dot com
18 年前
這是一個用於防止 SQL 注入攻擊的有用函式,因此,對於那些還無法升級到 PHP5.1 的人來說,這裡有一個可在舊版 PHP 上類似運作的替代函式…

<?php # 適用於 Postgresql 和舊版 PHP 的參數化查詢實作

if( !function_exists( 'pg_query_params' ) ) {

function
pg_query_params__callback( $at ) {
global
$pg_query_params__parameters;
return
$pg_query_params__parameters[ $at[1]-1 ];
}

function
pg_query_params( $db, $query, $parameters ) {

// 依需要跳脫參數並為回呼函式建構參數
global $pg_query_params__parameters;
foreach(
$parameters as $k=>$v )
$parameters[$k] = ( is_int( $v ) ? $v : "'".pg_escape_string( $v )."'" );
$pg_query_params__parameters = $parameters;

// 使用 pg_query 呼叫
return pg_query( $db, preg_replace_callback( '/\$([0-9]+)/', 'pg_query_params__callback', $query ) );

}
}

// 範例:pg_query_params( $db_resource, "SELECT * FROM table WHERE col1=$1 AND col2=$2", array( 42, "It's ok" ) );
?>
匿名
7 年前
如果其中一個參數是陣列(例如,傳遞給預存程序的整數陣列),則它必須在陣列內表示為集合,而不是 PHP 陣列表示法。

例如:兩個參數的 var_dump 輸出,一個整數和一個整數陣列
aaa 是:陣列
(
[0] => 1
[1] => {2,3}
)

你不想要

bbb 是:陣列
(
[0] => 1
[1] => 陣列
(
[0] => 2
[1] => 3
)

)
php at richardneill dot org
10 年前
pg_query_params() *確實* 接受 NULL 值。它們將會自動正確地轉換為 SQL NULL。因此,例如

<?php
$sql
= "UPDATE tbl_example SET column_a = $1, column_b=$2";
$params = array(NULL, 42);
$result = pg_params ($sql, $params);

//等同於:
$result = pg_query ("UPDATE tbl_example SET column_a = NULL, column_b = '42')";

//而不是,如同人們可能擔心的那樣,以下任何一種(不正確的)情況:
// ... column_a = '' ...
// ... column_a = 'NULL' ...
?>

請注意,您可以在 UPDATE 或 INSERT 陳述式中以這種方式使用 NULL,但不能在 WHERE 子句中使用。這不是 pg_query_params() 的限制,而是 SQL 語言的結果。
因此,如果您想要以下類型的查詢

<?php
//根據資料,where 測試的參數可能是 NULL 也可能不是
//以下的寫法對於 $1 是錯誤的。
$sql = "SELECT * from tbl_example WHERE column_a = $1 and column_b = $2";
$params = array(NULL, 42);
$result = pg_params ($sql, $params);
?>

這會失敗,因為 SQL 語法無效:您應該使用「IS NULL」而不是「= 42」。解決方案是使用 SQL 的「IS [NOT] DISTINCT FROM」語法。

<?php
$sql
= "SELECT ... WHERE column IS NOT DISTINCT FROM $1"
$params = array (42); //這樣可以正常運作,等同於 "where column = 42"
$params = array (NULL); //這樣也可以正常運作,等同於 "where column is null"
?>

(附註:雖然這很煩人,但這樣的行為是正確的。PostgreSQL 有一個相容性選項「transform_null_equals」,但即使您可能期望它能解決問題,它在這裡也幫不上忙。)
php at richardneill dot org
10 年前
關於布林值類型轉換的注意事項
pg_query_params() 和相關函式會在適當的情況下,在 PHP 的 NULL 和 SQL 的 NULL 之間進行無縫的自動轉換。
然而,其他所有東西都會以字串的形式傳入(和傳出)。
以下方法在處理布林值欄位時可能會有幫助

<?php
$sql
= " ... ";
$params = array (1, 2, 3, true, false);

//將布林值轉換為 'true' 和 'false'。[NULL 值已經處理]。
foreach ($params as &$value){
if (
is_bool($value)){
$value = ($value) ? 'true':'false';
}
}

//現在執行查詢:
$result = pg_query_params ($sql, $params);
$row = pg_fetch_assoc ($result,0) //第一列

//對於布林值,將 't' 和 'f' 轉換回 true 和 false。檢查欄位類型,以免意外轉換錯誤的資料。
foreach ($row as $key => &$value){
$type = pg_field_type($result,pg_field_num($result, $key));
if (
$type == 'bool'){
$value = ($value == 't');
}
}

//$row[] 現在包含布林值、NULL 值和字串。
?>
php at richardneill dot org
10 年前
對於參數化日期,不允許使用 NOW() 值(它會被轉換為字面量字串,並使 Postgres 出錯),但是 'now'
作為參數是允許的,並且具有相同的效果。
To Top