Просмотр исходного кода

Add exceptions handling to MongoDB.

Olivier Lechevalier 10 лет назад
Родитель
Сommit
ee7a5016ec

+ 1 - 0
lib/Mongo/MongoClient.php

@@ -91,6 +91,7 @@ class MongoClient
         }
     }
 
+
     /**
      * Closes this database connection
      *

+ 10 - 0
lib/Mongo/MongoCollection.php

@@ -55,6 +55,7 @@ class MongoCollection
      */
     public function __construct(MongoDB $db, $name)
     {
+        $this->checkCollectionName($name);
         $this->db = $db;
         $this->name = $name;
 
@@ -772,5 +773,14 @@ class MongoCollection
 
         return null;
     }
+
+    private function checkCollectionName($name)
+    {
+        if (empty($name)) {
+            throw new Exception('Collection name cannot be empty');
+        } elseif (strpos($name, chr(0)) !== false) {
+            throw new Exception('Collection name cannot contain null bytes');
+        }
+    }
 }
 

+ 21 - 3
lib/Mongo/MongoDB.php

@@ -15,6 +15,7 @@
 
 use Alcaeus\MongoDbAdapter\Helper;
 use Alcaeus\MongoDbAdapter\TypeConverter;
+use Alcaeus\MongoDbAdapter\ExceptionConverter;
 use MongoDB\Model\CollectionInfo;
 
 /**
@@ -130,7 +131,11 @@ class MongoDB
             unset($options['includeSystemCollections']);
         }
 
-        $collections = $this->db->listCollections($options);
+        try {
+            $collections = $this->db->listCollections($options);
+        } catch (\MongoDB\Driver\Exception\Exception $e) {
+            ExceptionConverter::toLegacy($e);
+        }
 
         $getCollectionInfo = function (CollectionInfo $collectionInfo) {
             return [
@@ -156,7 +161,11 @@ class MongoDB
             unset($options['includeSystemCollections']);
         }
 
-        $collections = $this->db->listCollections($options);
+        try {
+            $collections = $this->db->listCollections($options);
+        } catch (\MongoDB\Driver\Exception\Exception $e) {
+            ExceptionConverter::toLegacy($e);
+        }
 
         $getCollectionName = function (CollectionInfo $collectionInfo) {
             return $collectionInfo->getName();
@@ -260,7 +269,12 @@ class MongoDB
      */
     public function createCollection($name, $options)
     {
-        $this->db->createCollection($name, $options);
+        try {
+            $this->db->createCollection($name, $options);
+        } catch (\MongoDB\Driver\Exception\Exception $e) {
+            return false;
+        }
+
         return $this->selectCollection($name);
     }
 
@@ -366,12 +380,16 @@ class MongoDB
             $cursor->setReadPreference($this->getReadPreference());
 
             return iterator_to_array($cursor)[0];
+        } catch (\MongoDB\Driver\Exception\ExecutionTimeoutException $e) {
+            throw new MongoCursorTimeoutException($e->getMessage(), $e->getCode(), $e);
         } catch (\MongoDB\Driver\Exception\RuntimeException $e) {
             return [
                 'ok' => 0,
                 'errmsg' => $e->getMessage(),
                 'code' => $e->getCode(),
             ];
+        } catch (\MongoDB\Driver\Exception\Excepiton $e) {
+            ExceptionConverter::toLegacy($e);
         }
     }
 

+ 95 - 0
tests/Alcaeus/MongoDbAdapter/MongoDBTest.php

@@ -16,6 +16,50 @@ class MongoDBTest extends TestCase
         $this->assertSame('mongo-php-adapter.test', (string) $collection);
     }
 
+    public function testSelectCollectionEmptyName()
+    {
+        $database = $this->getDatabase();
+
+        $this->setExpectedException('Exception', 'Collection name cannot be empty');
+
+        $database->selectCollection('');
+    }
+
+    public function testSelectCollectionWithNullBytes()
+    {
+        $database = $this->getDatabase();
+
+        $this->setExpectedException('Exception', 'Collection name cannot contain null bytes');
+
+        $database->selectCollection('foo' . chr(0));
+    }
+
+    public function testCreateCollection()
+    {
+        $database = $this->getDatabase();
+
+        $collection = $database->createCollection('test', ['capped' => true, 'size' => 100]);
+        $this->assertInstanceOf('MongoCollection', $collection);
+
+        $document = ['foo' => 'bar'];
+        $collection->insert($document);
+
+        $checkDatabase = $this->getCheckDatabase();
+        foreach ($checkDatabase->listCollections() as $collectionInfo) {
+            if ($collectionInfo->getName() === 'test') {
+                $this->assertTrue($collectionInfo->isCapped());
+                return;
+            }
+        }
+    }
+
+    public function testCreateCollectionInvalidParameters()
+    {
+        $database = $this->getDatabase();
+
+        $this->assertFalse($database->createCollection('test', ['capped' => 2, 'size' => 100]));
+    }
+
     public function testGetCollectionProperty()
     {
         $db = $this->getDatabase();
@@ -42,6 +86,21 @@ class MongoDBTest extends TestCase
         $this->assertEquals($expected, $db->command(['listDatabases' => 1]));
     }
 
+    public function testCommandCursorTimeout()
+    {
+        $database = $this->getDatabase();
+
+        $this->failMaxTimeMS();
+
+        $this->setExpectedException('MongoCursorTimeoutException');
+
+        $database->command([
+            "count" => "test",
+            "query" => array("a" => 1),
+            "maxTimeMS" => 100,
+        ]);
+    }
+
     public function testReadPreference()
     {
         $database = $this->getDatabase();
@@ -137,6 +196,19 @@ class MongoDBTest extends TestCase
         $this->assertContains('test', $this->getDatabase()->getCollectionNames());
     }
 
+    public function testGetCollectionNamesExecutionTimeoutException()
+    {
+        $document = ['foo' => 'bar'];
+        $this->getCollection()->insert($document);
+        $database = $this->getDatabase();
+
+        $this->failMaxTimeMS();
+
+        $this->setExpectedException('MongoExecutionTimeoutException');
+
+        $database->getCollectionNames(['maxTimeMS' => 1]);
+    }
+
     public function testGetCollectionInfo()
     {
         $document = ['foo' => 'bar'];
@@ -152,6 +224,20 @@ class MongoDBTest extends TestCase
         $this->fail('The test collection was not found');
     }
 
+    public function testGetCollectionInfoExecutionTimeoutException()
+    {
+        $document = ['foo' => 'bar'];
+        $this->getCollection()->insert($document);
+
+        $database = $this->getDatabase();
+
+        $this->failMaxTimeMS();
+
+        $this->setExpectedException('MongoExecutionTimeoutException');
+
+        $database->getCollectionInfo(['maxTimeMS' => 1]);
+    }
+
     public function testListCollections()
     {
         $document = ['foo' => 'bar'];
@@ -167,6 +253,15 @@ class MongoDBTest extends TestCase
         $this->fail('The test collection was not found');
     }
 
+    public function testListCollectionsExecutionTimeoutException()
+    {
+        $this->failMaxTimeMS();
+
+        $this->setExpectedException('MongoExecutionTimeoutException');
+
+        $this->getDatabase()->listCollections(['maxTimeMS' => 1]);
+    }
+
     public function testDrop()
     {
         $document = ['foo' => 'bar'];

+ 51 - 1
tests/Alcaeus/MongoDbAdapter/TestCase.php

@@ -12,11 +12,19 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase
     }
 
     /**
+     * @return \MongoDB\Client
+     */
+    protected function getCheckClient()
+    {
+        return new Client('mongodb://localhost', ['connect' => true]);
+    }
+
+    /**
      * @return \MongoDB\Database
      */
     protected function getCheckDatabase()
     {
-        $client = new Client('mongodb://localhost', ['connect' => true]);
+        $client = $this->getCheckClient();
         return $client->selectDatabase('mongo-php-adapter');
     }
 
@@ -95,4 +103,46 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase
 
         return $collection;
     }
+
+    protected function configureFailPoint($failPoint, $mode, $data = [])
+    {
+        $this->checkFailPoint();
+
+        $doc = array(
+            "configureFailPoint" => $failPoint,
+            "mode"               => $mode,
+        );
+        if ($data) {
+            $doc["data"] = $data;
+        }
+
+        $adminDb = $this->getCheckClient()->selectDatabase('admin');
+        $result = $adminDb->command($doc);
+        $arr = current($result->toArray());
+        if (empty($arr->ok)) {
+            throw new RuntimeException("Failpoint failed");
+        }
+
+        return true;
+    }
+
+    protected function checkFailPoint()
+    {
+        $database = $this->getCheckClient()->selectDatabase('test');
+        try {
+            $database->command(['configureFailPoint' => 1]);
+        } catch (\MongoDB\Driver\Exception\Exception $e) {
+            /* command not found */
+            if ($e->getCode() == 59) {
+                $this->markTestSkipped(
+                  'This test require the mongo daemon to be started with the test flag: --setParameter enableTestCommands=1'
+                );
+            }
+        }
+    }
+
+    protected function failMaxTimeMS()
+    {
+        return $this->configureFailPoint("maxTimeAlwaysTimeOut", array("times" => 1));
+    }
 }