首页 > 解决方案 > 防止 PHP、PDO 和 MySQL/MariaDB 在不禁用 ATTR_EMULATE_PREPARES 的情况下将整数作为字符串返回

问题描述

我有以下架构:

+---------+--------------+------+-----+---------+----------------+
| 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_PREPARESFALSE,并获得我正在寻找的结果:

$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. 为什么这很重要?因为这样做显然会中断。我最初的问题如下,但我知道这不是一个真正的教义问题,而是一个 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 将整数作为字符串返回?

标签: phpmysqlpdodoctrine-ormdoctrine

解决方案


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


推荐阅读