准备好的语句和错误的lastInsertId

时间:2012-12-01 15:31:00

标签: php pdo

我对PDO有一种非常奇怪的行为。我不会详细介绍,因为它会花费太多时间,但基本上我观察到的是,当我重新使用一个执行简单INSERT的\ PDOStatement时,我在调用PDO :: lastInsertId时错误地获取了一个错误的值( )。

我第一次执行该语句时工作正常,我找回了正确的ID。相反,后续执行将始终返回“0”。这更奇怪,因为它只发生在测试之间(PHPUnit)。所以说我在test1(工作)中使用预处理语句执行插入,在test2中它将失败。

当在非单元测试环境中执行多次预处理语句时(在实例中的简单php文件中),它一切正常,最后插入的id总是准确的。确实非常奇怪。

这是测试(请注意,PersistencyManagerInstance只是PersistencyManager的简单内容):

<?php

class PersistencyManagerTest extends PHPUnit_Framework_TestCase {

   const DELETE_ALL = "TRUNCATE user";
   const ADD_USER = "INSERT INTO user values(null, :username, :password)";
   const CHECK_USER_EXISTENCE = "SELECT * FROM user WHERE username = :username AND password = :password";
   const DELETE_USER_BY_ID = "DELETE FROM user WHERE id = ?";

   protected $manager = null;

   public function __construct() {
      $this->manager = new PersistencyManagerInstance(PDOFactory::build());
   }

   public function setUp() {
      $this->manager->exec(self::DELETE_ALL);
   }

   public function tearDown() {
      $this->manager->exec(self::DELETE_ALL);
   }

   public function testInsert() {
      $user = new User("laurent", "password");
      $id = $this->manager->insert(self::ADD_USER, $user->export());
      $this->assertEquals("1", $id);
   }

   public function testInsertAgain() {
      $user1 = new User("laurent1", "password1");
      $id = $this->manager->insert(self::ADD_USER, $user1->export());
      $this->assertEquals("1", $id);
   }

   public function testQuery() {
      $user = new User("laurent", "password");
      $this->manager->insert(self::ADD_USER, $user->export());
      $results = $this->manager->query(self::CHECK_USER_EXISTENCE, $user->export());
      $this->assertEquals(1, count($results));
   }

   public function testExec() {
      $user = new User("laurent", "password---");
      $id = $this->manager->insert(self::ADD_USER, $user->export());
      $affected = $this->manager->exec(self::DELETE_USER_BY_ID, array($id));
      $this->assertEquals(1, $affected);
   }
}

testInsert在testInsertAgain没有的情况下有效。

这是班级:

<?php

namespace memory\manager;

use \PDO;

abstract class PersistencyManager {

   /**
    * @var array An array of \PDOStatement objects
    */
   protected static $ps = array();

   /**
    * @var \PDO 
    */
   protected $connection = null;

   protected function prepareStmt($sql) {
      // return $this->connection->prepare($sql);
      $key = md5($sql);
      if (!isset(self::$ps[$key])) {
         self::$ps[$key] = $this->connection->prepare($sql);
      }
      return self::$ps[$key];
   }

   public function __construct(PDO $connection) {
      $this->connection = $connection;
      $this->connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
   }

   public function __destruct() {
      $this->connection = null;
   }

   /**
    * Good for SELECT operations. By default it fetches using arrays.
    * @param string $sql
    * @param array $values
    * @param integer $fetchStyle
    * @return array A list of matching elements (The elements' type depends on $fetchStyle)
    */
   public function query($sql, array $values = array(), $fetchStyle = PDO::FETCH_ASSOC) {
      $prepared = $this->prepareStmt($sql);
      $prepared->execute($values);
      $prepared->setFetchMode($fetchStyle);
      $all = $prepared->fetchAll();
      $prepared->closeCursor();
      return $all;
   }

   /**
    * Good for INSERT operations.
    * @param string $sql
    * @param array $values
    * @return string Last inserted element's id in string format
    */
   public function insert($sql, array $values = array()) {
      $prepared = $this->prepareStmt($sql);
      $prepared->execute($values);
      $prepared->closeCursor();
      return $this->connection->lastInsertId();
   }

   /**
    * Good for all the remaining routines.
    * @param string $sql
    * @param array $values
    * @return integer The number of effected rows
    */
   public function exec($sql, array $values = array()) {
      $prepared = $this->prepareStmt($sql);
      $prepared->execute($values);
      $count = $prepared->rowCount();
      $prepared->closeCursor();
      return $count;
   }
}

有什么想法吗?

干杯

1 个答案:

答案 0 :(得分:0)

我在每次测试时都开始建立新连接。这就是原因。