記(Laravel)PDO 使用prepared statement 預處理LIMIT等欄位遇到的坑。
阿新 • • 發佈:2019-02-08
以下說明基於PHP7.1版本,Laravel 5.2版本。
直接貼程式碼:
$sql = " SELECT id
from my_table
LIMIT ?,?
";
$tests = DB::select($sql, [$offset, $limit]);
Laravel 5.2版本,預設 ATTR_EMULATE_PREPARES設定為true.
在檔案 Illuminate/Database/Connectors/Connector.php 24行.
class Connector
{
use DetectsLostConnections;
/**
* The default PDO connection options.
*
* @var array
*/
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 => true,
];
由於一些驅動不支援原生的預處理語句,PDO可以完全模擬預處理。
同時,PDO的模擬預處理是預設開啟的,並且,laravel對PDO的配置也是預設開啟的.
即便MYSQL驅動本身支援預處理,在預設開啟的狀態下,PDO是不會用到MYSQL本身提供的預處理功能。
坑在這裡:
PDO會把SQL語句進行模擬預處理之後會發送給MYSQL一個原始的SQL語句。
這種方式很詭異的是如果預處理的SQL語句中需要處理的欄位不是表中的欄位時,PDO會對繫結的引數
無腦新增單引號
無腦新增單引號
無腦新增單引號
因而導致了異常或查詢不到結果。
即:
limit '0','1'
解決這種問題的方法是設定PDO不去模擬預處理,而是交給MYSQL本身去做。方法是設定PDO的引數 ATTR_EMULATE_PREPARES 為 false
Laravel 中是在 config/database.php
'mysql' => [
'driver' => 'mysql',
'host' => env('DB_HOST', 'localhost'),
'database' => env('DB_DATABASE', 'forge'),
'username' => env('DB_USERNAME', 'forge'),
'password' => env('DB_PASSWORD', ''),
'charset' => 'utf8',
'collation' => 'utf8_unicode_ci',
'prefix' => '',
'strict' => false,
'engine' => null,
'options' =>[
PDO::ATTR_EMULATE_PREPARES => false,
],