Failed Conditions
Pull Request — master (#35)
by Mathieu
01:58
created

MasterSlavesConnection::checkSlavesWeights()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

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