Failed Conditions
Push — master ( ba421d...be3478 )
by Marco
14s
created

PoolingShardConnection   A

Complexity

Total Complexity 33

Size/Duplication

Total Lines 208
Duplicated Lines 8.17 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 91.18%

Importance

Changes 0
Metric Value
wmc 33
lcom 1
cbo 5
dl 17
loc 208
ccs 62
cts 68
cp 0.9118
rs 9.3999
c 0
b 0
f 0

11 Methods

Rating   Name   Duplication   Size   Complexity  
C __construct() 0 38 11
A getActiveShardId() 0 4 1
A getParams() 0 4 2
C connect() 4 31 8
A connectTo() 13 13 4
A isConnected() 0 8 2
A close() 0 6 1
A getHost() 0 6 1
A getPort() 0 6 1
A getUsername() 0 6 1
A getPassword() 0 6 1

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace Doctrine\DBAL\Sharding;
21
22
use Doctrine\Common\EventManager;
23
use Doctrine\DBAL\Configuration;
24
use Doctrine\DBAL\Connection;
25
use Doctrine\DBAL\Driver;
26
use Doctrine\DBAL\Event\ConnectionEventArgs;
27
use Doctrine\DBAL\Events;
28
use Doctrine\DBAL\Sharding\ShardChoser\ShardChoser;
29
30
/**
31
 * Sharding implementation that pools many different connections
32
 * internally and serves data from the currently active connection.
33
 *
34
 * The internals of this class are:
35
 *
36
 * - All sharding clients are specified and given a shard-id during
37
 *   configuration.
38
 * - By default, the global shard is selected. If no global shard is configured
39
 *   an exception is thrown on access.
40
 * - Selecting a shard by distribution value delegates the mapping
41
 *   "distributionValue" => "client" to the ShardChooser interface.
42
 * - An exception is thrown if trying to switch shards during an open
43
 *   transaction.
44
 *
45
 * Instantiation through the DriverManager looks like:
46
 *
47
 * @example
48
 *
49
 * $conn = DriverManager::getConnection(array(
50
 *    'wrapperClass' => 'Doctrine\DBAL\Sharding\PoolingShardConnection',
51
 *    'driver' => 'pdo_mysql',
52
 *    'global' => array('user' => '', 'password' => '', 'host' => '', 'dbname' => ''),
53
 *    'shards' => array(
54
 *        array('id' => 1, 'user' => 'slave1', 'password', 'host' => '', 'dbname' => ''),
55
 *        array('id' => 2, 'user' => 'slave2', 'password', 'host' => '', 'dbname' => ''),
56
 *    ),
57
 *    'shardChoser' => 'Doctrine\DBAL\Sharding\ShardChoser\MultiTenantShardChoser',
58
 * ));
59
 * $shardManager = $conn->getShardManager();
60
 * $shardManager->selectGlobal();
61
 * $shardManager->selectShard($value);
62
 *
63
 * @author Benjamin Eberlei <[email protected]>
64
 */
65
class PoolingShardConnection extends Connection
66
{
67
    /**
68
     * @var array
69
     */
70
    private $activeConnections;
71
72
    /**
73
     * @var integer
74
     */
75
    private $activeShardId;
76
77
    /**
78
     * @var array
79
     */
80
    private $connections;
81
82
    /**
83
     * @param array                         $params
84
     * @param \Doctrine\DBAL\Driver         $driver
85
     * @param \Doctrine\DBAL\Configuration  $config
86
     * @param \Doctrine\Common\EventManager $eventManager
87
     *
88
     * @throws \InvalidArgumentException
89
     */
90 15
    public function __construct(array $params, Driver $driver, Configuration $config = null, EventManager $eventManager = null)
91
    {
92 15
        if ( !isset($params['global']) || !isset($params['shards'])) {
93 2
            throw new \InvalidArgumentException("Connection Parameters require 'global' and 'shards' configurations.");
94
        }
95
96 13
        if ( !isset($params['shardChoser'])) {
97 1
            throw new \InvalidArgumentException("Missing Shard Choser configuration 'shardChoser'");
98
        }
99
100 12
        if (is_string($params['shardChoser'])) {
101 11
            $params['shardChoser'] = new $params['shardChoser'];
102
        }
103
104 12
        if ( ! ($params['shardChoser'] instanceof ShardChoser)) {
105 1
            throw new \InvalidArgumentException("The 'shardChoser' configuration is not a valid instance of Doctrine\DBAL\Sharding\ShardChoser\ShardChoser");
106
        }
107
108 11
        $this->connections[0] = array_merge($params, $params['global']);
109
110 11
        foreach ($params['shards'] as $shard) {
111 11
            if ( ! isset($shard['id'])) {
112 1
                throw new \InvalidArgumentException("Missing 'id' for one configured shard. Please specify a unique shard-id.");
113
            }
114
115 10
            if ( !is_numeric($shard['id']) || $shard['id'] < 1) {
116 1
                throw new \InvalidArgumentException("Shard Id has to be a non-negative number.");
117
            }
118
119 9
            if (isset($this->connections[$shard['id']])) {
120 1
                throw new \InvalidArgumentException("Shard " . $shard['id'] . " is duplicated in the configuration.");
121
            }
122
123 9
            $this->connections[$shard['id']] = array_merge($params, $shard);
124
        }
125
126 8
        parent::__construct($params, $driver, $config, $eventManager);
127 8
    }
128
129
    /**
130
     * Get active shard id.
131
     * 
132
     * @return integer
133
     */
134 1
    public function getActiveShardId()
135
    {
136 1
        return $this->activeShardId;
137
    }
138
139
    /**
140
     * {@inheritdoc}
141
     */
142 8
    public function getParams()
143
    {
144 8
        return $this->activeShardId ? $this->connections[$this->activeShardId] : $this->connections[0];
145
    }
146
147
    /**
148
     * {@inheritdoc}
149
     */
150 1
    public function getHost()
151
    {
152 1
        $params = $this->getParams();
153
154 1
        return $params['host'] ?? parent::getHost();
155
    }
156
157
    /**
158
     * {@inheritdoc}
159
     */
160 1
    public function getPort()
161
    {
162 1
        $params = $this->getParams();
163
164 1
        return $params['port'] ?? parent::getPort();
165
    }
166
167
    /**
168
     * {@inheritdoc}
169
     */
170 1
    public function getUsername()
171
    {
172 1
        $params = $this->getParams();
173
174 1
        return $params['user'] ?? parent::getUsername();
175
    }
176
177
    /**
178
     * {@inheritdoc}
179
     */
180 1
    public function getPassword()
181
    {
182 1
        $params = $this->getParams();
183
184 1
        return $params['password'] ?? parent::getPassword();
185
    }
186
187
    /**
188
     * Connects to a given shard.
189
     *
190
     * @param mixed $shardId
191
     *
192
     * @return boolean
193
     *
194
     * @throws \Doctrine\DBAL\Sharding\ShardingException
195
     */
196 8
    public function connect($shardId = null)
197
    {
198 8
        if ($shardId === null && $this->_conn) {
199 1
            return false;
200
        }
201
202 8
        if ($shardId !== null && $shardId === $this->activeShardId) {
203
            return false;
204
        }
205
206 8
        if ($this->getTransactionNestingLevel() > 0) {
207 1
            throw new ShardingException("Cannot switch shard when transaction is active.");
208
        }
209
210 8
        $this->activeShardId = (int)$shardId;
211
212 8
        if (isset($this->activeConnections[$this->activeShardId])) {
213
            $this->_conn = $this->activeConnections[$this->activeShardId];
214
215
            return false;
216
        }
217
218 8
        $this->_conn = $this->activeConnections[$this->activeShardId] = $this->connectTo($this->activeShardId);
219
220 8 View Code Duplication
        if ($this->_eventManager->hasListeners(Events::postConnect)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
221
            $eventArgs = new ConnectionEventArgs($this);
222
            $this->_eventManager->dispatchEvent(Events::postConnect, $eventArgs);
223
        }
224
225 8
        return true;
226
    }
227
228
    /**
229
     * Connects to a specific connection.
230
     *
231
     * @param string $shardId
232
     *
233
     * @return \Doctrine\DBAL\Driver\Connection
234
     */
235 8 View Code Duplication
    protected function connectTo($shardId)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
236
    {
237 8
        $params = $this->getParams();
238
239 8
        $driverOptions = isset($params['driverOptions']) ? $params['driverOptions'] : [];
240
241 8
        $connectionParams = $this->connections[$shardId];
242
243 8
        $user = isset($connectionParams['user']) ? $connectionParams['user'] : null;
244 8
        $password = isset($connectionParams['password']) ? $connectionParams['password'] : null;
245
246 8
        return $this->_driver->connect($connectionParams, $user, $password, $driverOptions);
247
    }
248
249
    /**
250
     * @param string|null $shardId
251
     *
252
     * @return boolean
253
     */
254 1
    public function isConnected($shardId = null)
255
    {
256 1
        if ($shardId === null) {
257
            return $this->_conn !== null;
258
        }
259
260 1
        return isset($this->activeConnections[$shardId]);
261
    }
262
263
    /**
264
     * @return void
265
     */
266 2
    public function close()
267
    {
268 2
        $this->_conn             = null;
269 2
        $this->activeConnections = null;
0 ignored issues
show
Documentation Bug introduced by
It seems like null of type null is incompatible with the declared type array of property $activeConnections.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
270 2
        $this->activeShardId     = null;
271 2
    }
272
}
273