记(Laravel)PDO 使用prepared statement 预处理LIMIT等字段遇到的坑。

来源:互联网 发布:pycharm和python 编辑:程序博客网 时间:2024/06/05 16:11

以下说明基于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,            ],
阅读全文
0 0