ConnectionFactory::createPdoResolver()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 4
nc 2
nop 1
1
<?php
2
namespace Childish\connection;
3
4
use PDO;
5
use Childish\support\Container;
6
use PDOException;
7
use Childish\support\Tools;
8
use InvalidArgumentException;
9
10
/**
11
 * ConnectionFactory
12
 *
13
 * @author    Pu ShaoWei <[email protected]>
14
 * @date      2017/12/7
15
 * @package   Childish\container
16
 * @version   1.0
17
 */
18
class ConnectionFactory
19
{
20
    /**
21
     * The IoC container instance.
22
     *
23
     * @var \Childish\support\Container
24
     */
25
    protected $container;
26
27
    /**
28
     * ConnectionFactory constructor.
29
     *
30
     * @param \Childish\support\Container $container
31
     */
32
    public function __construct(Container $container)
33
    {
34
        $this->container = $container;
35
    }
36
37
    /**
38
     * Establish a PDO connection based on the configuration.
39
     *
40
     * @param  array  $config
41
     * @param  string $name
42
     * @return \childish\connection\Connection
43
     */
44
    public function make(array $config, $name = null)
45
    {
46
        $config = $this->parseConfig($config, $name);
47
48
        if (isset($config['read'])) {
49
            return $this->createReadWriteConnection($config);
50
        }
51
52
        return $this->createSingleConnection($config);
53
    }
54
55
    /**
56
     * Parse and prepare the database configuration.
57
     *
58
     * @param  array  $config
59
     * @param  string $name
60
     * @return array
61
     */
62
    protected function parseConfig(array $config, $name)
63
    {
64
        return Tools::add(Tools::add($config, 'prefix', ''), 'name', $name);
65
    }
66
67
    /**
68
     * Create a single database connection instance.
69
     *
70
     * @param  array $config
71
     * @return \Childish\connection\Connection
72
     */
73
    protected function createSingleConnection(array $config)
74
    {
75
        $pdo = $this->createPdoResolver($config);
76
77
        return $this->createConnection(
78
            $config['driver'], $pdo, $config['database'], $config['prefix'], $config
79
        );
80
    }
81
82
    /**
83
     * Create a single database connection instance.
84
     *
85
     * @param  array $config
86
     * @return \Childish\connection\Connection
87
     */
88
    protected function createReadWriteConnection(array $config)
89
    {
90
        $connection = $this->createSingleConnection($this->getWriteConfig($config));
91
92
        return $connection->setReadPdo($this->createReadPdo($config));
93
    }
94
95
    /**
96
     * Create a new PDO instance for reading.
97
     *
98
     * @param  array $config
99
     * @return \Closure
100
     */
101
    protected function createReadPdo(array $config)
102
    {
103
        return $this->createPdoResolver($this->getReadConfig($config));
104
    }
105
106
    /**
107
     * Get the read configuration for a read / write connection.
108
     *
109
     * @param  array $config
110
     * @return array
111
     */
112
    protected function getReadConfig(array $config)
113
    {
114
        return $this->mergeReadWriteConfig(
115
            $config, $this->getReadWriteConfig($config, 'read')
116
        );
117
    }
118
119
    /**
120
     * Get the read configuration for a read / write connection.
121
     *
122
     * @param  array $config
123
     * @return array
124
     */
125
    protected function getWriteConfig(array $config)
126
    {
127
        return $this->mergeReadWriteConfig(
128
            $config, $this->getReadWriteConfig($config, 'write')
129
        );
130
    }
131
132
    /**
133
     * Get a read / write level configuration.
134
     *
135
     * @param  array  $config
136
     * @param  string $type
137
     * @return array
138
     */
139
    protected function getReadWriteConfig(array $config, $type)
140
    {
141
        return isset($config[$type][0])
142
            ? Tools::random($config[$type])
143
            : $config[$type];
144
    }
145
146
    /**
147
     * Merge a configuration for a read / write connection.
148
     *
149
     * @param  array $config
150
     * @param  array $merge
151
     * @return array
152
     */
153
    protected function mergeReadWriteConfig(array $config, array $merge)
154
    {
155
        return Tools::except(array_merge($config, $merge), ['read', 'write']);
156
    }
157
158
    /**
159
     * Create a new Closure that resolves to a PDO instance.
160
     *
161
     * @param  array $config
162
     * @return \Closure
163
     */
164
    protected function createPdoResolver(array $config)
165
    {
166
        return array_key_exists('host', $config)
167
            ? $this->createPdoResolverWithHosts($config)
168
            : $this->createPdoResolverWithoutHosts($config);
169
    }
170
171
    /**
172
     * Create a new Closure that resolves to a PDO instance with a specific host or an array of hosts.
173
     *
174
     * @param  array $config
175
     * @return \Closure
176
     */
177
    protected function createPdoResolverWithHosts(array $config)
178
    {
179
        return function () use ($config) {
180
            foreach (Tools::shuffle($hosts = $this->parseHosts($config)) as $key => $host) {
181
                $config['host'] = $host;
182
183
                try {
184
                    return $this->createConnector($config)->connect($config);
185
                } catch (PDOException $e) {
186
                    throw $e;
187
                }
188
            }
189
        };
190
    }
191
192
    /**
193
     * Parse the hosts configuration item into an array.
194
     *
195
     * @param  array $config
196
     * @return array
197
     */
198
    protected function parseHosts(array $config)
199
    {
200
        $hosts = Tools::wrap($config['host']);
201
202
        if (empty($hosts)) {
203
            throw new InvalidArgumentException('Database hosts array is empty.');
204
        }
205
206
        return $hosts;
207
    }
208
209
    /**
210
     * Create a new Closure that resolves to a PDO instance where there is no configured host.
211
     *
212
     * @param  array $config
213
     * @return \Closure
214
     */
215
    protected function createPdoResolverWithoutHosts(array $config)
216
    {
217
        return function () use ($config) {
218
            return $this->createConnector($config)->connect($config);
219
        };
220
    }
221
222
    /**
223
     * Create a connector instance based on the configuration.
224
     *
225
     * @param  array $config
226
     * @return \Childish\connection\MySqlConnector
227
     * @throws \InvalidArgumentException
228
     */
229
    public function createConnector(array $config)
230
    {
231
        if (!isset($config['driver'])) {
232
            throw new InvalidArgumentException('A driver must be specified.');
233
        }
234
        if ($this->container->bound($key = "db.connector.{$config['driver']}")) {
235
            return $this->container->make($key);
236
        }
237
        if ($config['driver'] !== 'mysql') {
238
            throw new InvalidArgumentException("Unsupported driver [{$config['driver']}]");
239
        }
240
        return new MySqlConnector;
241
    }
242
243
    /**
244
     * Create a new connection instance.
245
     *
246
     * @param  string        $driver
247
     * @param  \PDO|\Closure $connection
248
     * @param  string        $database
249
     * @param  string        $prefix
250
     * @param  array         $config
251
     * @return \Childish\connection\Connection
252
     * @throws \InvalidArgumentException
253
     */
254
    protected function createConnection($driver, $connection, $database, $prefix = '', array $config = [])
255
    {
256
        if ($resolver = Connection::getResolver($driver)) {
257
            return $resolver($connection, $database, $prefix, $config);
258
        }
259
        if ($driver !== 'mysql') {
260
            throw new InvalidArgumentException("Unsupported driver [$driver]");
261
        }
262
        return new MySqlConnection($connection, $database, $prefix, $config);
263
    }
264
}
265