【问题标题】:Prevent PHP, PDO, and MySQL/MariaDB from returning integers as strings without disabling ATTR_EMULATE_PREPARES防止 PHP、PDO 和 MySQL/MariaDB 在不禁用 ATTR_EMULATE_PREPARES 的情况下将整数作为字符串返回
【发布时间】:2019-07-02 12:56:46
【问题描述】:

我有以下架构:

+---------+--------------+------+-----+---------+----------------+
| Field   | Type         | Null | Key | Default | Extra          |
+---------+--------------+------+-----+---------+----------------+
| id      | int(11)      | NO   | PRI | NULL    | auto_increment |
| name    | varchar(255) | NO   |     | NULL    |                |
| someInt | int(11)      | NO   |     | NULL    |                |
+---------+--------------+------+-----+---------+----------------+

当我运行以下脚本时,整数作为字符串返回。

$pdo = new \PDO("mysql:host={$db['host']};dbname={$db['dbname']};charset={$db['charset']}",$db['username'],$db['password'],array(\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY=>true,\PDO::ATTR_ERRMODE=>\PDO::ERRMODE_EXCEPTION,\PDO::ATTR_DEFAULT_FETCH_MODE=>\PDO::FETCH_ASSOC));
var_dump($pdo->query('SELECT * FROM integerTesting')->fetchAll());

array(3) {
  [0]=>
  array(3) {
    ["id"]=>
    string(1) "4"
    ["name"]=>
    string(6) "Name 1"
    ["someInt"]=>
    string(1) "1"
  }
  [1]=>
  array(3) {
    ["id"]=>
    string(1) "5"
    ["name"]=>
    string(6) "Name 2"
    ["someInt"]=>
    string(1) "2"
  }
  [2]=>
  array(3) {
    ["id"]=>
    string(1) "6"
    ["name"]=>
    string(6) "Name 3"
    ["someInt"]=>
    string(1) "3"
  }
}

所以,我将PDO::ATTR_EMULATE_PREPARES 更改为FALSE,并得到我正在寻找的结果:

$pdo = new \PDO("mysql:host={$db['host']};dbname={$db['dbname']};charset={$db['charset']}",$db['username'],$db['password'],array(\PDO::ATTR_EMULATE_PREPARES=>false,\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY=>true,\PDO::ATTR_ERRMODE=>\PDO::ERRMODE_EXCEPTION,\PDO::ATTR_DEFAULT_FETCH_MODE=>\PDO::FETCH_ASSOC));
var_dump($pdo->query('SELECT * FROM integerTesting')->fetchAll());

array(3) {
  [0]=>
  array(3) {
    ["id"]=>
    int(4)
    ["name"]=>
    string(6) "Name 1"
    ["someInt"]=>
    int(1)
  }
  [1]=>
  array(3) {
    ["id"]=>
    int(5)
    ["name"]=>
    string(6) "Name 2"
    ["someInt"]=>
    int(2)
  }
  [2]=>
  array(3) {
    ["id"]=>
    int(6)
    ["name"]=>
    string(6) "Name 3"
    ["someInt"]=>
    int(3)
  }
}

我是否回答了自己的问题?不,因为我的问题明确指出,没有禁用ATTR_EMULATE_PREPARES。为什么这很重要?因为这样做显然会中断。我最初的问题如下,但我知道这不是一个真正的 Doctrine 问题,而是一个 PDO/MySQL 问题。

我有以下 Doctrine 实体:

<?php
use Doctrine\ORM\Mapping as ORM;

/**
 * @Entity @Table(name="integerTesting")
 **/
class IntegerTesting
{
    /**
     * @var int
     *
     * @Column(type="integer")
     * @Id
     * @GeneratedValue(strategy="IDENTITY")
     */
    protected $id;

    /** @Column(type="string") */
    protected $name;

    /**
     * @var int
     *
     * @Column( type="integer")
     */
    protected $someInt;

    public function getId()
    {
        return $this->id;
    }

    public function getName()
    {
        return $this->name;
    }
    public function setName($name)
    {
        $this->name = $name;
        return $this;
    }

    public function getSomeInt()
    {
        return $this->someInt;
    }
    public function setSomeInt($someInt)
    {
        $this->someInt = $someInt;
        return $this;
    }
}

我添加一对或记录,然后读取一些记录,包括 Doctrine 的 findAll() 方法以及直接 PDO 查询:

<?php
require_once "bootstrap.php";

function addRow(int $i, $entityManager):void {
    $e = new IntegerTesting();
    $e->setName("Name $i");
    $e->setSomeInt($i);
    $entityManager->persist($e);
    $entityManager->flush();
}

for ($i = 1; $i <= 3; $i++) {
    addRow($i, $entityManager);
}

//Using Doctrine's findAll() method
var_dump($entityManager->getRepository('IntegerTesting')->findAll());

//Using direct PDO query
var_dump($entityManager->getConnection()->query('SELECT * FROM integerTesting')->fetchAll());

使用 Doctrine 的 findAll() 方法的输出返回整数列作为所需的整数类型。

array(3) {
  [0]=>
  object(IntegerTesting)#61 (3) {
    ["id":protected]=>
    int(4)
    ["name":protected]=>
    string(6) "Name 1"
    ["someInt":protected]=>
    int(1)
  }
  [1]=>
  object(IntegerTesting)#63 (3) {
    ["id":protected]=>
    int(5)
    ["name":protected]=>
    string(6) "Name 2"
    ["someInt":protected]=>
    int(2)
  }
  [2]=>
  object(IntegerTesting)#64 (3) {
    ["id":protected]=>
    int(6)
    ["name":protected]=>
    string(6) "Name 3"
    ["someInt":protected]=>
    int(3)
  }
}

但是,直接查询会将它们作为不需要的字符串返回:

array(3) {
  [0]=>
  array(3) {
    ["id"]=>
    string(1) "4"
    ["name"]=>
    string(6) "Name 1"
    ["someInt"]=>
    string(1) "1"
  }
  [1]=>
  array(3) {
    ["id"]=>
    string(1) "5"
    ["name"]=>
    string(6) "Name 2"
    ["someInt"]=>
    string(1) "2"
  }
  [2]=>
  array(3) {
    ["id"]=>
    string(1) "6"
    ["name"]=>
    string(6) "Name 3"
    ["someInt"]=>
    string(1) "3"
  }
}

我通过将PDO::ATTR_EMULATE_PREPARES 设置为FALSE 找到了解决方案,但是,经过艰苦的故障排除后,发现它破坏了 Doctrine 的某些其他方面(类类型继承,可能还有其他功能)。

如何防止 Doctrine 将整数作为字符串返回?

【问题讨论】:

  • 在 Symfony 示例中,对象被数据水合,这就是它具有正确数据类型的原因。在第二个示例中,您将 PDO 与 MySQL 一起使用。默认情况下,它将返回字符串或 null。您可以将 MySQL 驱动程序更改为 mysqlnd,使用 symfony 的内置方法,或者从 PDO 查询中转换结果。

标签: php mysql pdo doctrine-orm doctrine


【解决方案1】:

在 php-8.1.0RC1/ 中即使是 EMULATE_PREPARES 也是本机类型, https://github.com/php/php-src/blob/php-8.1.0RC1/UPGRADING#L130 所以将来应该可以通过将 PHP 升级到 8.1 来解决这个问题

【讨论】:

    【解决方案2】:

    好吧,我猜它在技术上并不能回答这个问题,因为我正在禁用 \PDO::ATTR_EMULATE_PREPARES。但是,它对我有用,因为我可以在每个查询的基础上这样做,因此不会扰乱 Doctrine。

        $pdo = $this->getEntityManager()->getConnection()->getWrappedConnection();
        $pdo->setAttribute(\PDO::ATTR_EMULATE_PREPARES,false);
    

    【讨论】:

      猜你喜欢
      • 2012-08-18
      • 2014-10-14
      • 2013-06-17
      • 2011-07-16
      • 2014-02-07
      • 1970-01-01
      • 2012-03-15
      • 2016-05-19
      • 2013-04-22
      相关资源
      最近更新 更多