ソースを参照

Merge pull request #14721 from notpron/3.x-rollback-on-throwable

Rollback transaction on any Throwable
Mark Story 5 年 前
コミット
2157bcaccd
2 ファイル変更31 行追加0 行削除
  1. 4 0
      src/Database/Connection.php
  2. 27 0
      tests/TestCase/Database/ConnectionTest.php

+ 4 - 0
src/Database/Connection.php

@@ -29,6 +29,7 @@ use Cake\Database\Schema\Collection as SchemaCollection;
 use Cake\Datasource\ConnectionInterface;
 use Cake\Log\Log;
 use Exception;
+use Throwable;
 
 /**
  * Represents a connection with a database server.
@@ -743,6 +744,9 @@ class Connection implements ConnectionInterface
 
         try {
             $result = $transaction($this);
+        } catch (Throwable $e) {
+            $this->rollback(false);
+            throw $e;
         } catch (Exception $e) {
             $this->rollback(false);
             throw $e;

+ 27 - 0
tests/TestCase/Database/ConnectionTest.php

@@ -27,6 +27,7 @@ use Cake\Database\Statement\BufferedStatement;
 use Cake\Datasource\ConnectionManager;
 use Cake\Log\Log;
 use Cake\TestSuite\TestCase;
+use Error;
 use Exception;
 use ReflectionMethod;
 use ReflectionProperty;
@@ -1120,6 +1121,32 @@ class ConnectionTest extends TestCase
     }
 
     /**
+     * Tests that the transactional method will rollback the transaction
+     * and throw the same error if the callback raises one
+     *
+     * @return void
+     * @throws \Error
+     */
+    public function testTransactionalWithPHP7Error()
+    {
+        $this->skipIf(version_compare(PHP_VERSION, '7.0.0', '<'), 'Error class only exists since PHP 7.');
+
+        $this->expectException(\Error::class);
+        $driver = $this->getMockFormDriver();
+        $connection = $this->getMockBuilder(Connection::class)
+            ->setMethods(['connect', 'commit', 'begin', 'rollback'])
+            ->setConstructorArgs([['driver' => $driver]])
+            ->getMock();
+        $connection->expects($this->at(0))->method('begin');
+        $connection->expects($this->at(1))->method('rollback');
+        $connection->expects($this->never())->method('commit');
+        $connection->transactional(function ($conn) use ($connection) {
+            $this->assertSame($connection, $conn);
+            throw new \Error();
+        });
+    }
+
+    /**
      * Tests it is possible to set a schema collection object
      *
      * @return void