Jelajahi Sumber

Extract connection options from query string

Andreas Braun 9 tahun lalu
induk
melakukan
490d6201df

+ 86 - 1
lib/Mongo/MongoClient.php

@@ -86,6 +86,8 @@ class MongoClient
             $server = 'mongodb://' . self::DEFAULT_HOST . ':' . self::DEFAULT_PORT;
         }
 
+        $this->applyConnectionOptions($server, $options);
+
         $this->server = $server;
         if (false === strpos($this->server, 'mongodb://')) {
             $this->server = 'mongodb://'.$this->server;
@@ -348,5 +350,88 @@ class MongoClient
             'connected', 'status', 'server', 'persistent'
         ];
     }
-}
 
+    /**
+     * @param $server
+     * @return array
+     */
+    private function extractUrlOptions($server)
+    {
+        $queryOptions = explode('&', parse_url($server, PHP_URL_QUERY));
+
+        $options = [];
+        foreach ($queryOptions as $option) {
+            if (strpos($option, '=') === false) {
+                continue;
+            }
+
+            $keyValue = explode('=', $option);
+            if ($keyValue[0] === 'readPreferenceTags') {
+                $options[$keyValue[0]][] = $this->getReadPreferenceTags($keyValue[1]);
+            } else {
+                $options[$keyValue[0]] = $keyValue[1];
+            }
+        }
+
+        return $options;
+    }
+
+    /**
+     * @param $readPreferenceTagString
+     * @return array
+     */
+    private function getReadPreferenceTags($readPreferenceTagString)
+    {
+        $tagSets = [];
+        foreach (explode(',', $readPreferenceTagString) as $index => $tagSet) {
+            $tags = explode(':', $tagSet);
+            $tagSets[$tags[0]] = $tags[1];
+        }
+
+        return $tagSets;
+    }
+
+    /**
+     * @param string $server
+     * @param array $options
+     */
+    private function applyConnectionOptions($server, array $options)
+    {
+        $urlOptions = $this->extractUrlOptions($server);
+
+        if (isset($urlOptions['wTimeout'])) {
+            $urlOptions['wTimeoutMS'] = $urlOptions['wTimeout'];
+            unset($urlOptions['wTimeout']);
+        }
+
+        if (isset($options['wTimeout'])) {
+            $options['wTimeoutMS'] = $options['wTimeout'];
+            unset($options['wTimeout']);
+        }
+
+        if (isset($options['readPreferenceTags'])) {
+            $options['readPreferenceTags'] = [$this->getReadPreferenceTags($options['readPreferenceTags'])];
+
+            // Special handling for readPreferenceTags which are merged
+            if (isset($urlOptions['readPreferenceTags'])) {
+                $options['readPreferenceTags'] = array_merge($urlOptions['readPreferenceTags'], $options['readPreferenceTags']);
+            }
+        }
+
+        $urlOptions = array_merge($urlOptions, $options);
+
+        if (isset($urlOptions['slaveOkay'])) {
+            $this->setReadPreferenceFromSlaveOkay($urlOptions['slaveOkay']);
+        } elseif (isset($urlOptions['readPreference']) || isset($urlOptions['readPreferenceTags'])) {
+            $readPreference = isset($urlOptions['readPreference']) ? $urlOptions['readPreference'] : null;
+            $tags = isset($urlOptions['readPreferenceTags']) ? $urlOptions['readPreferenceTags'] : null;
+            $this->setReadPreferenceFromParameters($readPreference, $tags);
+        }
+
+        if (isset($urlOptions['w']) || isset($urlOptions['wTimeoutMs'])) {
+            $writeConcern = (isset($urlOptions['w'])) ? $urlOptions['w'] : 1;
+            $wTimeout = (isset($urlOptions['wTimeoutMs'])) ? $urlOptions['wTimeoutMs'] : null;
+            $this->setWriteConcern($writeConcern, $wTimeout);
+        }
+    }
+}

+ 122 - 0
tests/Alcaeus/MongoDbAdapter/Mongo/MongoClientTest.php

@@ -115,4 +115,126 @@ class MongoClientTest extends TestCase
         $client = $this->getClient(null, 'localhost');
         $this->assertNotNull($client);
     }
+
+    /**
+     * @dataProvider dataReadPreferenceOptionsAreInherited
+     */
+    public function testReadPreferenceOptionsAreInherited($options, $uri, $expectedTagsets)
+    {
+        $client = $this->getClient($options, $uri);
+        $collection = $client->selectCollection('test', 'foo');
+
+        $this->assertSame(
+            [
+                'type' => \MongoClient::RP_SECONDARY_PREFERRED,
+                'tagsets' => $expectedTagsets
+            ],
+            $collection->getReadPreference()
+        );
+    }
+
+    public static function dataReadPreferenceOptionsAreInherited()
+    {
+        $options = [
+            'readPreference' => \MongoClient::RP_SECONDARY_PREFERRED,
+            'readPreferenceTags' => 'a:b',
+        ];
+
+        $overriddenOptions = [
+            'readPreference' => \MongoClient::RP_NEAREST,
+            'readPreferenceTags' => 'c:d',
+        ];
+
+        $multipleTagsets = [
+            'readPreference' => \MongoClient::RP_SECONDARY_PREFERRED,
+            'readPreferenceTags' => 'a:b,c:d',
+        ];
+
+        return [
+            'optionsArray' => [
+                'options' => $options,
+                'uri' => 'mongodb://localhost',
+                'expectedTagsets' => [['a' => 'b']],
+            ],
+            'queryString' => [
+                'options' => [],
+                'uri' => 'mongodb://localhost/?' . self::makeOptionString($options),
+                'expectedTagsets' => [['a' => 'b']],
+            ],
+            'multipleInQueryString' => [
+                'options' => [],
+                'uri' => 'mongodb://localhost/?' . self::makeOptionString($options) . '&readPreferenceTags=c:d',
+                'expectedTagsets' => [['a' => 'b'], ['c' => 'd']],
+            ],
+            'overridden' => [
+                'options' => $options,
+                'uri' => 'mongodb://localhost/?' . self::makeOptionString($overriddenOptions),
+                'expectedTagsets' => [['c' => 'd'], ['a' => 'b']],
+            ],
+            'multipleTagsetsOptions' => [
+                'options' => $multipleTagsets,
+                'uri' => 'mongodb://localhost',
+                'expectedTagsets' => [['a' => 'b', 'c' => 'd']],
+            ],
+            'multipleTagsetsQueryString' => [
+                'options' => null,
+                'uri' => 'mongodb://localhost/?' . self::makeOptionString($multipleTagsets),
+                'expectedTagsets' => [['a' => 'b', 'c' => 'd']],
+            ],
+        ];
+    }
+
+    /**
+     * @dataProvider dataWriteConcernOptionsAreInherited
+     */
+    public function testWriteConcernOptionsAreInherited($options, $uri)
+    {
+        $client = $this->getClient($options, $uri);
+        $collection = $client->selectCollection('test', 'foo');
+
+        $this->assertSame(['w' => 'majority', 'wtimeout' => 666], $collection->getWriteConcern());
+    }
+
+    public static function dataWriteConcernOptionsAreInherited()
+    {
+        $options = [
+            'w' => 'majority',
+            'wTimeoutMs' => 666,
+        ];
+
+        $overriddenOptions = [
+            'w' => '2',
+            'wTimeoutMs' => 333,
+        ];
+
+        return [
+            'optionsArray' => [
+                'options' => $options,
+                'uri' => 'mongodb://localhost',
+            ],
+            'queryString' => [
+                'options' => [],
+                'uri' => 'mongodb://localhost/?' . self::makeOptionString($options),
+            ],
+            'overridden' => [
+                'options' => $options,
+                'uri' => 'mongodb://localhost/?' . self::makeOptionString($overriddenOptions),
+            ]
+        ];
+    }
+
+    /**
+     * @param array $options
+     * @return string
+     */
+    private static function makeOptionString(array $options)
+    {
+        return implode('&', array_map(
+            function ($key, $value) {
+                return $key . '=' . $value;
+            },
+            array_keys($options),
+            array_values($options)
+        ));
+    }
 }