Subversion-Projekte lars-tiefland.laravel_shop

Revision

Zur aktuellen Revision | Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php
2
 
3
namespace Illuminate\Database;
4
 
5
use Doctrine\DBAL\Types\Type;
6
use Illuminate\Database\Connectors\ConnectionFactory;
7
use Illuminate\Support\Arr;
8
use Illuminate\Support\ConfigurationUrlParser;
9
use Illuminate\Support\Str;
10
use Illuminate\Support\Traits\Macroable;
11
use InvalidArgumentException;
12
use PDO;
13
use RuntimeException;
14
 
15
/**
16
 * @mixin \Illuminate\Database\Connection
17
 */
18
class DatabaseManager implements ConnectionResolverInterface
19
{
20
    use Macroable {
21
        __call as macroCall;
22
    }
23
 
24
    /**
25
     * The application instance.
26
     *
27
     * @var \Illuminate\Contracts\Foundation\Application
28
     */
29
    protected $app;
30
 
31
    /**
32
     * The database connection factory instance.
33
     *
34
     * @var \Illuminate\Database\Connectors\ConnectionFactory
35
     */
36
    protected $factory;
37
 
38
    /**
39
     * The active connection instances.
40
     *
41
     * @var array<string, \Illuminate\Database\Connection>
42
     */
43
    protected $connections = [];
44
 
45
    /**
46
     * The custom connection resolvers.
47
     *
48
     * @var array<string, callable>
49
     */
50
    protected $extensions = [];
51
 
52
    /**
53
     * The callback to be executed to reconnect to a database.
54
     *
55
     * @var callable
56
     */
57
    protected $reconnector;
58
 
59
    /**
60
     * The custom Doctrine column types.
61
     *
62
     * @var array<string, array>
63
     */
64
    protected $doctrineTypes = [];
65
 
66
    /**
67
     * Create a new database manager instance.
68
     *
69
     * @param  \Illuminate\Contracts\Foundation\Application  $app
70
     * @param  \Illuminate\Database\Connectors\ConnectionFactory  $factory
71
     * @return void
72
     */
73
    public function __construct($app, ConnectionFactory $factory)
74
    {
75
        $this->app = $app;
76
        $this->factory = $factory;
77
 
78
        $this->reconnector = function ($connection) {
79
            $this->reconnect($connection->getNameWithReadWriteType());
80
        };
81
    }
82
 
83
    /**
84
     * Get a database connection instance.
85
     *
86
     * @param  string|null  $name
87
     * @return \Illuminate\Database\Connection
88
     */
89
    public function connection($name = null)
90
    {
91
        [$database, $type] = $this->parseConnectionName($name);
92
 
93
        $name = $name ?: $database;
94
 
95
        // If we haven't created this connection, we'll create it based on the config
96
        // provided in the application. Once we've created the connections we will
97
        // set the "fetch mode" for PDO which determines the query return types.
98
        if (! isset($this->connections[$name])) {
99
            $this->connections[$name] = $this->configure(
100
                $this->makeConnection($database), $type
101
            );
102
        }
103
 
104
        return $this->connections[$name];
105
    }
106
 
107
    /**
108
     * Parse the connection into an array of the name and read / write type.
109
     *
110
     * @param  string  $name
111
     * @return array
112
     */
113
    protected function parseConnectionName($name)
114
    {
115
        $name = $name ?: $this->getDefaultConnection();
116
 
117
        return Str::endsWith($name, ['::read', '::write'])
118
                            ? explode('::', $name, 2) : [$name, null];
119
    }
120
 
121
    /**
122
     * Make the database connection instance.
123
     *
124
     * @param  string  $name
125
     * @return \Illuminate\Database\Connection
126
     */
127
    protected function makeConnection($name)
128
    {
129
        $config = $this->configuration($name);
130
 
131
        // First we will check by the connection name to see if an extension has been
132
        // registered specifically for that connection. If it has we will call the
133
        // Closure and pass it the config allowing it to resolve the connection.
134
        if (isset($this->extensions[$name])) {
135
            return call_user_func($this->extensions[$name], $config, $name);
136
        }
137
 
138
        // Next we will check to see if an extension has been registered for a driver
139
        // and will call the Closure if so, which allows us to have a more generic
140
        // resolver for the drivers themselves which applies to all connections.
141
        if (isset($this->extensions[$driver = $config['driver']])) {
142
            return call_user_func($this->extensions[$driver], $config, $name);
143
        }
144
 
145
        return $this->factory->make($config, $name);
146
    }
147
 
148
    /**
149
     * Get the configuration for a connection.
150
     *
151
     * @param  string  $name
152
     * @return array
153
     *
154
     * @throws \InvalidArgumentException
155
     */
156
    protected function configuration($name)
157
    {
158
        $name = $name ?: $this->getDefaultConnection();
159
 
160
        // To get the database connection configuration, we will just pull each of the
161
        // connection configurations and get the configurations for the given name.
162
        // If the configuration doesn't exist, we'll throw an exception and bail.
163
        $connections = $this->app['config']['database.connections'];
164
 
165
        if (is_null($config = Arr::get($connections, $name))) {
166
            throw new InvalidArgumentException("Database connection [{$name}] not configured.");
167
        }
168
 
169
        return (new ConfigurationUrlParser)
170
                    ->parseConfiguration($config);
171
    }
172
 
173
    /**
174
     * Prepare the database connection instance.
175
     *
176
     * @param  \Illuminate\Database\Connection  $connection
177
     * @param  string  $type
178
     * @return \Illuminate\Database\Connection
179
     */
180
    protected function configure(Connection $connection, $type)
181
    {
182
        $connection = $this->setPdoForType($connection, $type)->setReadWriteType($type);
183
 
184
        // First we'll set the fetch mode and a few other dependencies of the database
185
        // connection. This method basically just configures and prepares it to get
186
        // used by the application. Once we're finished we'll return it back out.
187
        if ($this->app->bound('events')) {
188
            $connection->setEventDispatcher($this->app['events']);
189
        }
190
 
191
        if ($this->app->bound('db.transactions')) {
192
            $connection->setTransactionManager($this->app['db.transactions']);
193
        }
194
 
195
        // Here we'll set a reconnector callback. This reconnector can be any callable
196
        // so we will set a Closure to reconnect from this manager with the name of
197
        // the connection, which will allow us to reconnect from the connections.
198
        $connection->setReconnector($this->reconnector);
199
 
200
        $this->registerConfiguredDoctrineTypes($connection);
201
 
202
        return $connection;
203
    }
204
 
205
    /**
206
     * Prepare the read / write mode for database connection instance.
207
     *
208
     * @param  \Illuminate\Database\Connection  $connection
209
     * @param  string|null  $type
210
     * @return \Illuminate\Database\Connection
211
     */
212
    protected function setPdoForType(Connection $connection, $type = null)
213
    {
214
        if ($type === 'read') {
215
            $connection->setPdo($connection->getReadPdo());
216
        } elseif ($type === 'write') {
217
            $connection->setReadPdo($connection->getPdo());
218
        }
219
 
220
        return $connection;
221
    }
222
 
223
    /**
224
     * Register custom Doctrine types with the connection.
225
     *
226
     * @param  \Illuminate\Database\Connection  $connection
227
     * @return void
228
     */
229
    protected function registerConfiguredDoctrineTypes(Connection $connection): void
230
    {
231
        foreach ($this->app['config']->get('database.dbal.types', []) as $name => $class) {
232
            $this->registerDoctrineType($class, $name, $name);
233
        }
234
 
235
        foreach ($this->doctrineTypes as $name => [$type, $class]) {
236
            $connection->registerDoctrineType($class, $name, $type);
237
        }
238
    }
239
 
240
    /**
241
     * Register a custom Doctrine type.
242
     *
243
     * @param  string  $class
244
     * @param  string  $name
245
     * @param  string  $type
246
     * @return void
247
     *
248
     * @throws \Doctrine\DBAL\DBALException
249
     * @throws \RuntimeException
250
     */
251
    public function registerDoctrineType(string $class, string $name, string $type): void
252
    {
253
        if (! class_exists('Doctrine\DBAL\Connection')) {
254
            throw new RuntimeException(
255
                'Registering a custom Doctrine type requires Doctrine DBAL (doctrine/dbal).'
256
            );
257
        }
258
 
259
        if (! Type::hasType($name)) {
260
            Type::addType($name, $class);
261
        }
262
 
263
        $this->doctrineTypes[$name] = [$type, $class];
264
    }
265
 
266
    /**
267
     * Disconnect from the given database and remove from local cache.
268
     *
269
     * @param  string|null  $name
270
     * @return void
271
     */
272
    public function purge($name = null)
273
    {
274
        $name = $name ?: $this->getDefaultConnection();
275
 
276
        $this->disconnect($name);
277
 
278
        unset($this->connections[$name]);
279
    }
280
 
281
    /**
282
     * Disconnect from the given database.
283
     *
284
     * @param  string|null  $name
285
     * @return void
286
     */
287
    public function disconnect($name = null)
288
    {
289
        if (isset($this->connections[$name = $name ?: $this->getDefaultConnection()])) {
290
            $this->connections[$name]->disconnect();
291
        }
292
    }
293
 
294
    /**
295
     * Reconnect to the given database.
296
     *
297
     * @param  string|null  $name
298
     * @return \Illuminate\Database\Connection
299
     */
300
    public function reconnect($name = null)
301
    {
302
        $this->disconnect($name = $name ?: $this->getDefaultConnection());
303
 
304
        if (! isset($this->connections[$name])) {
305
            return $this->connection($name);
306
        }
307
 
308
        return $this->refreshPdoConnections($name);
309
    }
310
 
311
    /**
312
     * Set the default database connection for the callback execution.
313
     *
314
     * @param  string  $name
315
     * @param  callable  $callback
316
     * @return mixed
317
     */
318
    public function usingConnection($name, callable $callback)
319
    {
320
        $previousName = $this->getDefaultConnection();
321
 
322
        $this->setDefaultConnection($name);
323
 
324
        return tap($callback(), function () use ($previousName) {
325
            $this->setDefaultConnection($previousName);
326
        });
327
    }
328
 
329
    /**
330
     * Refresh the PDO connections on a given connection.
331
     *
332
     * @param  string  $name
333
     * @return \Illuminate\Database\Connection
334
     */
335
    protected function refreshPdoConnections($name)
336
    {
337
        [$database, $type] = $this->parseConnectionName($name);
338
 
339
        $fresh = $this->configure(
340
            $this->makeConnection($database), $type
341
        );
342
 
343
        return $this->connections[$name]
344
                    ->setPdo($fresh->getRawPdo())
345
                    ->setReadPdo($fresh->getRawReadPdo());
346
    }
347
 
348
    /**
349
     * Get the default connection name.
350
     *
351
     * @return string
352
     */
353
    public function getDefaultConnection()
354
    {
355
        return $this->app['config']['database.default'];
356
    }
357
 
358
    /**
359
     * Set the default connection name.
360
     *
361
     * @param  string  $name
362
     * @return void
363
     */
364
    public function setDefaultConnection($name)
365
    {
366
        $this->app['config']['database.default'] = $name;
367
    }
368
 
369
    /**
370
     * Get all of the support drivers.
371
     *
372
     * @return string[]
373
     */
374
    public function supportedDrivers()
375
    {
376
        return ['mysql', 'pgsql', 'sqlite', 'sqlsrv'];
377
    }
378
 
379
    /**
380
     * Get all of the drivers that are actually available.
381
     *
382
     * @return string[]
383
     */
384
    public function availableDrivers()
385
    {
386
        return array_intersect(
387
            $this->supportedDrivers(),
388
            str_replace('dblib', 'sqlsrv', PDO::getAvailableDrivers())
389
        );
390
    }
391
 
392
    /**
393
     * Register an extension connection resolver.
394
     *
395
     * @param  string  $name
396
     * @param  callable  $resolver
397
     * @return void
398
     */
399
    public function extend($name, callable $resolver)
400
    {
401
        $this->extensions[$name] = $resolver;
402
    }
403
 
404
    /**
405
     * Remove an extension connection resolver.
406
     *
407
     * @param  string  $name
408
     * @return void
409
     */
410
    public function forgetExtension($name)
411
    {
412
        unset($this->extensions[$name]);
413
    }
414
 
415
    /**
416
     * Return all of the created connections.
417
     *
418
     * @return array<string, \Illuminate\Database\Connection>
419
     */
420
    public function getConnections()
421
    {
422
        return $this->connections;
423
    }
424
 
425
    /**
426
     * Set the database reconnector callback.
427
     *
428
     * @param  callable  $reconnector
429
     * @return void
430
     */
431
    public function setReconnector(callable $reconnector)
432
    {
433
        $this->reconnector = $reconnector;
434
    }
435
 
436
    /**
437
     * Set the application instance used by the manager.
438
     *
439
     * @param  \Illuminate\Contracts\Foundation\Application  $app
440
     * @return $this
441
     */
442
    public function setApplication($app)
443
    {
444
        $this->app = $app;
445
 
446
        return $this;
447
    }
448
 
449
    /**
450
     * Dynamically pass methods to the default connection.
451
     *
452
     * @param  string  $method
453
     * @param  array  $parameters
454
     * @return mixed
455
     */
456
    public function __call($method, $parameters)
457
    {
458
        if (static::hasMacro($method)) {
459
            return $this->macroCall($method, $parameters);
460
        }
461
 
462
        return $this->connection()->$method(...$parameters);
463
    }
464
}