Completed
Pull Request — master (#35)
by Mathieu
02:03
created

MasterSlavesConnection::wrappedConnection()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

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