嘗試在 laravel 中引用一個過去寫的 class
該 class 是由 constructor 的參數綁定 pdo 物件
用來封裝一些對資料庫的操作
在除錯過程中,發現是 laravel 由 \DB::connection()->getPdo()
取得的 PDO 物件,只要遇到 $stmt->bindParam(略, 略, \PDO::PARAM_BOOL)
就會出錯,第三個參數改成 \PDO::PARAM_INT
就可以運作。(資料庫欄位以及PHP的變數確認都是 bool)
於是我做了下面的測試,但不知道是什麼原因導致這樣的結果:
資料庫直接用命令列建立
CREATE TABLE test_table(bool_col BOOLEAN NOT NULL)
controller 裡面的內容這樣寫
public function test()
{
$boolVal=rand(0,1)?false:true;
$pdo=\DB::connection()->getPdo();
$stmt=$pdo->prepare('INSERT INTO test_table(bool_col) VALUES(:bool_val)');
$stmt->bindParam(':bool_val', $boolVal, \PDO::PARAM_BOOL);
if($stmt->execute()!==false) {
echo 'success.';
} else {
var_dump($stmt->errorInfo());
}
}
array(3) { [0]=> string(5) "00000" [1]=> NULL [2]=> NULL }
,但如果把 \PDO::PARAM_BOOL
改成 \PDO::PARAM_INT
則可正確執行。\PDO::PARAM_BOOL
不會發生錯誤。$stmt->errorInfo()
無法得知錯誤發生在哪邊,是不是有其他方式可以得到錯誤訊息?雖然解決問題很重要
但你都用laravel了
除了一些特殊需求的sql
你要不要改用Eloquent來做做看
因為是引用舊的函式庫才發現這問題
如果是新寫的就會考慮直接用 Laravel 裡面的物件處理
https://bugs.php.net/bug.php?id=38546
php 歷史錯誤
所以....就這樣吧xd
因為直接自己用 new 建立 PDO 物件的話
並不會發生這樣的錯誤(可以使用 \PDO::PARAM_BOOL
)
所以想知道差別在哪
我去找 laravel 內部的程式碼
在 Illuminate/Database/Connectors/Connector.php 裡面找到他 new PDO 時有給一個 $options 參數
return new PDO($dsn, $username, $password, $options);
而那個 $options
的內容為
protected $options = [
PDO::ATTR_CASE => PDO::CASE_NATURAL,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_ORACLE_NULLS => PDO::NULL_NATURAL,
PDO::ATTR_STRINGIFY_FETCHES => false,
PDO::ATTR_EMULATE_PREPARES => false,
];
把 PDO::ATTR_EMULATE_PREPARES => false
改成 true
後就可以正常使用 \PDO::PARAM_BOOL
了
這個設定的解釋可參考:https://www.php.net/manual/en/pdo.setattribute.php
主要是跟當 DB 不支援 prepare 某些方法時是否讓 driver 去模擬
PS. 在實際應用時不會直接改 laravel 原始碼,而是去設定 database/config.php 的 options,例如
'mysql' => [
//略
'options' => [
PDO::ATTR_EMULATE_PREPARES => true,
]
];
感謝補充
總感覺跟強型態有些關係
true之後就要小心sql injection了
https://www.cnblogs.com/alazalazalaz/p/6056393.html
謝謝提供資料,這塊我之前的確不知道。
剛剛另外找了一篇:
Are PDO prepared statements sufficient to prevent SQL injection?
根據這篇的敘述,主要是因為有些字串編碼會有問題
(utf8 是安全的,如果有正確設定連線的話)
laravel 連線時會根據 config/database.php 檔案的設定去指定 charset,如果是 utf8_mb4 的話應該可以使用 PDO::ATTR_EMULATE_PREPARES
當然可以的話直接不使用 PDO::ATTR_EMULATE_PREPARES 應該是最好的,以上是針對盡量不動到舊有程式碼的狀況下。
如果有錯誤之處再麻煩指正,感謝。