2012-03-08 121 views
0

最近我鼓励PHP代码中的奇怪错误。这就像使用foreach(){}使用PDOStatement对象时,内部的块将被忽略。这里是一个触发这个行为我的测试代码:尝试catch块不能与pdo语句和foreach一起使用

<?php 
/* 
// THIS WORKS AS EXPECTED, JUST UNCOMMENT 
try{ 
    for($i = 0; $i < 3; $i++){ 
     try{ 
      echo "\nTHROWING EXCEPTION\n\n"; 
      throw new Exception("AAAAAAAAAAAAAAAAAAAAAAAAA"); 
      echo "EXCETION THROWN (SHOULD NOT SHOW)\n"; 
     }catch(Exception $err){ 
      echo "========= INNER CATCH =========\n"; 
      echo $err->getMessage() . "\n"; 
     } 
    } 
}catch(Exception $e){ 
    echo "=========== OUTER CATCH ===========\n"; 
    die($e->getMessage() . "\n"); 
} 
die("\nEVERYTHING OK\n"); 
*/ 
$dsn = "mysql:dbname=test_pdo;host=localhost"; 
try { 
    $dbh = new PDO($dsn, 'root', "", array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8")); 
    $dbh->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC); 
    $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); 

    $dbh->exec(" 
    CREATE TABLE IF NOT EXISTS `test` (
    `id` int(10) unsigned NOT NULL, 
    `name` varchar(255) NOT NULL, 
    PRIMARY KEY (`id`), 
    UNIQUE KEY `name` (`name`) 
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 
    "); 
    $dbh->exec("TRUNCATE test"); 
    $dbh->exec(" 
    INSERT INTO test (id, name) VALUES 
    (1, 'JOHN DOE 1'), (2, 'JOHN DOE 2'), 
    (3, 'JOHN DOE 3'), (4, 'JOHN DOE 4') 
    "); 
    echo "\nRECORDS CREATED\n"; 
    $sth = $dbh->prepare("SELECT id FROM test WHERE id = ?"); 
    foreach(array(2, 3, 4) as $id){ 
     $sth->execute(array($id)); 
     $i = 0; 
     foreach($sth as $row){ 
      $num = $row['id'] - 1; 
      // this sql should throw 2300 error duplicate key 
      $sql = "UPDATE test SET name = 'JOHN DOE $num' WHERE id = {$row['id']}"; 
      try{ 
       echo "\nSENDING QUERY: $sql\n\n"; 
       $dbh->exec($sql); 
      }catch(Exception $err){ 
       $code = $err->getCode(); 
       $msg = $err->getMessage(); 
       //duplicate key error? 
       if($err->getCode() != "23000"){ 
        echo "THROWING EXCEPTION FROM INNER CATCH.\n ERROR CODE: $code\n"; 
        throw $err; 
       } 
       echo <<<TXT 
============ GOOD CATCH ============ 
ERROR CODE: $code 
ERROR MSG: $msg 
SQL: $sql 


TXT; 
      } 
     } 

    } 
}catch(Exception $e){ 
    $code = $e->getCode(); 
    $msg = $e->getMessage(); 
    echo <<<TXT 
============ WRONG CATCH =========== 
ERROR CODE: $code 
ERROR MSG: $msg 


TXT; 
} 

此输出这样的事情在我的本地(PHP 5.3.6-13ubuntu3.6用了Suhosin贴片(CLI))和服务器(PHP 5.2.17 ):

RECORDS CREATED 

SENDING QUERY: UPDATE test SET name = 'JOHN DOE 1' WHERE id = 2 

============ GOOD CATCH ============ 
ERROR CODE: 23000 
ERROR MSG: SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'JOHN DOE 1' for key 'name' 
SQL: UPDATE test SET name = 'JOHN DOE 1' WHERE id = 2 

============ WRONG CATCH =========== 
ERROR CODE: 23000 
ERROR MSG: SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'JOHN DOE 1' for key 'name' 

不知道为什么,但是当我改变foreach($sth as $row){foreach($sth->fetchAll() as $row){万物按预期工作:

RECORDS CREATED 

SENDING QUERY: UPDATE test SET name = 'JOHN DOE 1' WHERE id = 2 

============ GOOD CATCH ============ 
ERROR CODE: 23000 
ERROR MSG: SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'JOHN DOE 1' for key 'name' 
SQL: UPDATE test SET name = 'JOHN DOE 1' WHERE id = 2 


SENDING QUERY: UPDATE test SET name = 'JOHN DOE 2' WHERE id = 3 

============ GOOD CATCH ============ 
ERROR CODE: 23000 
ERROR MSG: SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'JOHN DOE 2' for key 'name' 
SQL: UPDATE test SET name = 'JOHN DOE 2' WHERE id = 3 


SENDING QUERY: UPDATE test SET name = 'JOHN DOE 3' WHERE id = 4 

============ GOOD CATCH ============ 
ERROR CODE: 23000 
ERROR MSG: SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'JOHN DOE 3' for key 'name' 
SQL: UPDATE test SET name = 'JOHN DOE 3' WHERE id = 4 

难道我发现了一个错误还是我做错了什么?有人可以确认这种行为吗? THX

// 编辑

升级PHP版本5.3.10似乎解决了这个问题。 Thx寻求帮助...

回答

0

不,从PDO返回的结果不是您可以直接迭代的结果。这不是一个PDO异常,当你尝试这样做时它是一个PHP--它不是一个数组或者具有迭代器的对象。

当您获取它时,会创建一个在foreach循环中有效的数组。

+0

'PDOStatement'实现了'Traversable'(http://www.php.net/manual/pl/class.traversable.php),所以它可以与foreach一起使用,还是我在这里错了? – piotrekkr 2012-03-08 22:50:05