Completed
Push — master ( 46d273...229a5a )
by Mathieu
02:04
created

MasterSlavesConnection   A

Complexity

Total Complexity 31

Size/Duplication

Total Lines 216
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Importance

Changes 6
Bugs 1 Features 1
Metric Value
wmc 31
c 6
b 1
f 1
lcom 1
cbo 3
dl 0
loc 216
rs 9.8

21 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 1
A checkSlaves() 0 8 3
A connectToMaster() 0 6 1
A connectToSlave() 0 6 1
A isConnectedToMaster() 0 4 2
A getCurrentConnection() 0 4 1
A wrap() 0 10 3
A chooseASlave() 0 14 4
A totalSlavesWeight() 0 8 2
A disableCurrentSlave() 0 9 2
A slaves() 0 4 1
A prepare() 0 5 1
A query() 0 4 1
A quote() 0 4 1
A exec() 0 5 1
A lastInsertId() 0 4 1
A beginTransaction() 0 5 1
A commit() 0 5 1
A rollBack() 0 5 1
A errorCode() 0 4 1
A errorInfo() 0 4 1
1
<?php
2
3
namespace Ez\DbLinker\Driver\Connection;
4
5
use Exception;
6
use Doctrine\DBAL\DriverManager;
7
use Doctrine\DBAL\Driver\Connection;
8
9
class MasterSlavesConnection implements Connection, ConnectionWrapper
10
{
11
    use ConnectionWrapperTrait;
12
13
    private $master;
14
    private $slaves;
15
    private $currentConnectionParams;
16
    private $currentSlave;
17
18
    public function __construct(array $master, array $slaves)
19
    {
20
        $this->master = $master;
21
        $this->checkSlaves($slaves);
22
        $this->slaves = $slaves;
23
    }
24
25
    private function checkSlaves(array $slaves)
26
    {
27
        foreach ($slaves as $slave) {
28
            if ((int)$slave['weight'] < 0) {
29
                throw new Exception('Slave weight must be >= 0');
30
            }
31
        }
32
    }
33
34
    public function connectToMaster()
35
    {
36
        $this->currentConnectionParams = $this->master;
37
        $this->currentSlave = null;
38
        $this->wrappedConnection = null;
39
    }
40
41
    public function connectToSlave()
42
    {
43
        $this->currentConnectionParams = null;
44
        $this->currentSlave = null;
45
        $this->wrappedConnection = null;
46
    }
47
48
    public function isConnectedToMaster()
49
    {
50
        return $this->currentSlave === null && $this->currentConnectionParams !== null;
51
    }
52
53
    /**
54
     * @inherit
55
     */
56
    public function getCurrentConnection()
57
    {
58
        return $this->wrappedConnection();
59
    }
60
61
    protected function wrap()
62
    {
63
        if ($this->currentConnectionParams === null) {
64
            $this->currentSlave = $this->chooseASlave();
65
            $this->currentConnectionParams = $this->currentSlave ? $this->slaves[$this->currentSlave] : $this->master;
66
        }
67
        $connection = DriverManager::getConnection($this->currentConnectionParams);
68
        $this->wrappedConnection = $connection->getWrappedConnection();
69
        $this->wrappedDriver = $connection->getDriver();
70
    }
71
72
    private function chooseASlave()
73
    {
74
        $totalSlavesWeight = $this->totalSlavesWeight();
75
        if ($totalSlavesWeight < 1) {
76
            return null;
77
        }
78
        $weightTarget = mt_rand(1, $totalSlavesWeight);
79
        foreach ($this->slaves as $n => $slave) {
80
            $weightTarget -= $slave['weight'];
81
            if ($weightTarget <= 0) {
82
                return $n;
83
            }
84
        }
85
    }
86
87
    private function totalSlavesWeight()
88
    {
89
        $weight = 0;
90
        foreach ($this->slaves as $slave) {
91
            $weight += $slave['weight'];
92
        }
93
        return $weight;
94
    }
95
96
    public function disableCurrentSlave()
97
    {
98
        if ($this->currentSlave !== null) {
99
            array_splice($this->slaves, $this->currentSlave, 1);
100
            $this->currentSlave = null;
101
        }
102
        $this->currentConnectionParams = null;
103
        $this->wrappedConnection = null;
104
    }
105
106
    public function slaves()
107
    {
108
        return $this->slaves;
109
    }
110
111
    /**
112
     * Prepares a statement for execution and returns a Statement object.
113
     *
114
     * @param string $prepareString
115
     *
116
     * @return \Doctrine\DBAL\Driver\Statement
117
     */
118
    public function prepare($prepareString)
119
    {
120
        $this->connectToMaster();
121
        return $this->wrappedConnection()->prepare($prepareString);
122
    }
123
124
    /**
125
     * Executes an SQL statement, returning a result set as a Statement object.
126
     *
127
     * @return \Doctrine\DBAL\Driver\Statement
128
     */
129
    public function query()
130
    {
131
        return call_user_func_array([$this->wrappedConnection(), __FUNCTION__], func_get_args());
132
    }
133
134
    /**
135
     * Quotes a string for use in a query.
136
     *
137
     * @param string  $input
138
     * @param integer $type
139
     *
140
     * @return string
141
     */
142
    public function quote($input, $type = \PDO::PARAM_STR)
143
    {
144
        return $this->wrappedConnection()->quote($input, $type);
145
    }
146
147
    /**
148
     * Executes an SQL statement and return the number of affected rows.
149
     *
150
     * @param string $statement
151
     *
152
     * @return integer
153
     */
154
    public function exec($statement)
155
    {
156
        $this->connectToMaster();
157
        return $this->wrappedConnection()->exec($statement);
158
    }
159
160
    /**
161
     * Returns the ID of the last inserted row or sequence value.
162
     *
163
     * @param string|null $name
164
     *
165
     * @return string
166
     */
167
    public function lastInsertId($name = null)
168
    {
169
        return $this->wrappedConnection()->lastInsertId($name);
170
    }
171
172
    /**
173
     * Initiates a transaction.
174
     *
175
     * @return boolean TRUE on success or FALSE on failure.
176
     */
177
    public function beginTransaction()
178
    {
179
        $this->connectToMaster();
180
        return $this->wrappedConnection()->beginTransaction();
181
    }
182
183
    /**
184
     * Commits a transaction.
185
     *
186
     * @return boolean TRUE on success or FALSE on failure.
187
     */
188
    public function commit()
189
    {
190
        $this->connectToMaster();
191
        return $this->wrappedConnection()->commit();
192
    }
193
194
    /**
195
     * Rolls back the current transaction, as initiated by beginTransaction().
196
     *
197
     * @return boolean TRUE on success or FALSE on failure.
198
     */
199
    public function rollBack()
200
    {
201
        $this->connectToMaster();
202
        return $this->wrappedConnection()->rollBack();
203
    }
204
205
    /**
206
     * Returns the error code associated with the last operation on the database handle.
207
     *
208
     * @return string|null The error code, or null if no operation has been run on the database handle.
209
     */
210
    public function errorCode()
211
    {
212
        return $this->wrappedConnection()->errorCode();
213
    }
214
215
    /**
216
     * Returns extended error information associated with the last operation on the database handle.
217
     *
218
     * @return array
219
     */
220
    public function errorInfo()
221
    {
222
        return $this->wrappedConnection()->errorInfo();
223
    }
224
}
225