Passed
Push — master ( 4c416c...eb956e )
by Alexander
09:35
created

Connection::getPrimaries()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 0
cts 2
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * @link http://www.yiiframework.com/
4
 * @copyright Copyright (c) 2008 Yii Software LLC
5
 * @license http://www.yiiframework.com/license/
6
 */
7
8
namespace yii\db;
9
10
use PDO;
11
use Yii;
12
use yii\base\Component;
13
use yii\base\InvalidConfigException;
14
use yii\base\NotSupportedException;
15
use yii\caching\CacheInterface;
16
17
/**
18
 * Connection represents a connection to a database via [PDO](https://secure.php.net/manual/en/book.pdo.php).
19
 *
20
 * Connection works together with [[Command]], [[DataReader]] and [[Transaction]]
21
 * to provide data access to various DBMS in a common set of APIs. They are a thin wrapper
22
 * of the [PDO PHP extension](https://secure.php.net/manual/en/book.pdo.php).
23
 *
24
 * Connection supports database replication and read-write splitting. In particular, a Connection component
25
 * can be configured with multiple [[primaries]] and [[replicas]]. It will do load balancing and failover by choosing
26
 * appropriate servers. It will also automatically direct read operations to the replicas and write operations to
27
 * the primary connections.
28
 *
29
 * To establish a DB connection, set [[dsn]], [[username]] and [[password]], and then
30
 * call [[open()]] to connect to the database server. The current state of the connection can be checked using [[$isActive]].
31
 *
32
 * The following example shows how to create a Connection instance and establish
33
 * the DB connection:
34
 *
35
 * ```php
36
 * $connection = new \yii\db\Connection([
37
 *     'dsn' => $dsn,
38
 *     'username' => $username,
39
 *     'password' => $password,
40
 * ]);
41
 * $connection->open();
42
 * ```
43
 *
44
 * After the DB connection is established, one can execute SQL statements like the following:
45
 *
46
 * ```php
47
 * $command = $connection->createCommand('SELECT * FROM post');
48
 * $posts = $command->queryAll();
49
 * $command = $connection->createCommand('UPDATE post SET status=1');
50
 * $command->execute();
51
 * ```
52
 *
53
 * One can also do prepared SQL execution and bind parameters to the prepared SQL.
54
 * When the parameters are coming from user input, you should use this approach
55
 * to prevent SQL injection attacks. The following is an example:
56
 *
57
 * ```php
58
 * $command = $connection->createCommand('SELECT * FROM post WHERE id=:id');
59
 * $command->bindValue(':id', $_GET['id']);
60
 * $post = $command->query();
61
 * ```
62
 *
63
 * For more information about how to perform various DB queries, please refer to [[Command]].
64
 *
65
 * If the underlying DBMS supports transactions, you can perform transactional SQL queries
66
 * like the following:
67
 *
68
 * ```php
69
 * $transaction = $connection->beginTransaction();
70
 * try {
71
 *     $connection->createCommand($sql1)->execute();
72
 *     $connection->createCommand($sql2)->execute();
73
 *     // ... executing other SQL statements ...
74
 *     $transaction->commit();
75
 * } catch (Exception $e) {
76
 *     $transaction->rollBack();
77
 * }
78
 * ```
79
 *
80
 * You also can use shortcut for the above like the following:
81
 *
82
 * ```php
83
 * $connection->transaction(function () {
84
 *     $order = new Order($customer);
85
 *     $order->save();
86
 *     $order->addItems($items);
87
 * });
88
 * ```
89
 *
90
 * If needed you can pass transaction isolation level as a second parameter:
91
 *
92
 * ```php
93
 * $connection->transaction(function (Connection $db) {
94
 *     //return $db->...
95
 * }, Transaction::READ_UNCOMMITTED);
96
 * ```
97
 *
98
 * Connection is often used as an application component and configured in the application
99
 * configuration like the following:
100
 *
101
 * ```php
102
 * 'components' => [
103
 *     'db' => [
104
 *         'class' => '\yii\db\Connection',
105
 *         'dsn' => 'mysql:host=127.0.0.1;dbname=demo',
106
 *         'username' => 'root',
107
 *         'password' => '',
108
 *         'charset' => 'utf8',
109
 *     ],
110
 * ],
111
 * ```
112
 *
113
 * @property string $driverName Name of the DB driver.
114
 * @property bool $isActive Whether the DB connection is established. This property is read-only.
115
 * @property string $lastInsertID The row ID of the last row inserted, or the last value retrieved from the
116
 * sequence object. This property is read-only.
117
 * @property bool $enableReplicas whether to enable read/write splitting by using [[replicas]] to read data.
118
 * Note that if [[replicas]] is empty, read/write splitting will NOT be enabled no matter what value this property takes.
119
 * @property array $replicas list of replica connection configurations. Each configuration is used to create a replica DB connection.
120
 * When [[enableReplicas]] is true, one of these configurations will be chosen and used to create a DB connection
121
 * for performing read queries only.
122
 * @property array $replicaConfig the configuration that should be merged with every replica configuration listed in [[replicas]].
123
 * For example,
124
 *
125
 * ```php
126
 * [
127
 *     'username' => 'replica',
128
 *     'password' => 'replica',
129
 *     'attributes' => [
130
 *         // use a smaller connection timeout
131
 *         PDO::ATTR_TIMEOUT => 10,
132
 *     ],
133
 * ]
134
 * ```
135
 * @property array $primaries list of primary connection configurations. Each configuration is used to create a primary DB connection.
136
 * When [[open()]] is called, one of these configurations will be chosen and used to create a DB connection
137
 * which will be used by this object.
138
 * Note that when this property is not empty, the connection setting (e.g. `dsn`, `username`) of this object will
139
 * be ignored.
140
 * @property array $primaryConfig the configuration that should be merged with every primary configuration listed in [[primaries]].
141
 * For example,
142
 *
143
 * ```php
144
 * [
145
 *     'username' => 'primary',
146
 *     'password' => 'primary',
147
 *     'attributes' => [
148
 *         // use a smaller connection timeout
149
 *         PDO::ATTR_TIMEOUT => 10,
150
 *     ],
151
 * ]
152
 * ```
153
 * @property bool $shufflePrimaries whether to shuffle [[primaries]] before getting one.
154
 * @property-read Connection|null $primary The currently active primary connection. `null` is returned if no primary
155
 * connection is available. This property is read-only.
156
 * @property-read PDO $primaryPdo The PDO instance for the currently active primary connection. This property is
157
 * read-only.
158
 * @property QueryBuilder $queryBuilder The query builder for the current DB connection. Note that the type of
159
 * this property differs in getter and setter. See [[getQueryBuilder()]] and [[setQueryBuilder()]] for details.
160
 * @property Schema $schema The schema information for the database opened by this connection. This property
161
 * is read-only.
162
 * @property string $serverVersion Server version as a string. This property is read-only.
163
 * @property-read Connection $replica The currently active replica connection. This property is read-only.
164
 * @property-read PDO $replicaPdo The PDO instance for the currently active replica connection. This property
165
 * is read-only.
166
 * @property Transaction|null $transaction The currently active transaction. Null if no active transaction.
167
 * This property is read-only.
168
 *
169
 * @author Qiang Xue <[email protected]>
170
 * @since 2.0
171
 */
172
class Connection extends Component
173
{
174
    /**
175
     * @event yii\base\Event an event that is triggered after a DB connection is established
176
     */
177
    const EVENT_AFTER_OPEN = 'afterOpen';
178
    /**
179
     * @event yii\base\Event an event that is triggered right before a top-level transaction is started
180
     */
181
    const EVENT_BEGIN_TRANSACTION = 'beginTransaction';
182
    /**
183
     * @event yii\base\Event an event that is triggered right after a top-level transaction is committed
184
     */
185
    const EVENT_COMMIT_TRANSACTION = 'commitTransaction';
186
    /**
187
     * @event yii\base\Event an event that is triggered right after a top-level transaction is rolled back
188
     */
189
    const EVENT_ROLLBACK_TRANSACTION = 'rollbackTransaction';
190
191
    /**
192
     * @var string the Data Source Name, or DSN, contains the information required to connect to the database.
193
     * Please refer to the [PHP manual](https://secure.php.net/manual/en/pdo.construct.php) on
194
     * the format of the DSN string.
195
     *
196
     * For [SQLite](https://secure.php.net/manual/en/ref.pdo-sqlite.connection.php) you may use a [path alias](guide:concept-aliases)
197
     * for specifying the database path, e.g. `sqlite:@app/data/db.sql`.
198
     *
199
     * @see charset
200
     */
201
    public $dsn;
202
    /**
203
     * @var string the username for establishing DB connection. Defaults to `null` meaning no username to use.
204
     */
205
    public $username;
206
    /**
207
     * @var string the password for establishing DB connection. Defaults to `null` meaning no password to use.
208
     */
209
    public $password;
210
    /**
211
     * @var array PDO attributes (name => value) that should be set when calling [[open()]]
212
     * to establish a DB connection. Please refer to the
213
     * [PHP manual](https://secure.php.net/manual/en/pdo.setattribute.php) for
214
     * details about available attributes.
215
     */
216
    public $attributes;
217
    /**
218
     * @var PDO the PHP PDO instance associated with this DB connection.
219
     * This property is mainly managed by [[open()]] and [[close()]] methods.
220
     * When a DB connection is active, this property will represent a PDO instance;
221
     * otherwise, it will be null.
222
     * @see pdoClass
223
     */
224
    public $pdo;
225
    /**
226
     * @var bool whether to enable schema caching.
227
     * Note that in order to enable truly schema caching, a valid cache component as specified
228
     * by [[schemaCache]] must be enabled and [[enableSchemaCache]] must be set true.
229
     * @see schemaCacheDuration
230
     * @see schemaCacheExclude
231
     * @see schemaCache
232
     */
233
    public $enableSchemaCache = false;
234
    /**
235
     * @var int number of seconds that table metadata can remain valid in cache.
236
     * Use 0 to indicate that the cached data will never expire.
237
     * @see enableSchemaCache
238
     */
239
    public $schemaCacheDuration = 3600;
240
    /**
241
     * @var array list of tables whose metadata should NOT be cached. Defaults to empty array.
242
     * The table names may contain schema prefix, if any. Do not quote the table names.
243
     * @see enableSchemaCache
244
     */
245
    public $schemaCacheExclude = [];
246
    /**
247
     * @var CacheInterface|string the cache object or the ID of the cache application component that
248
     * is used to cache the table metadata.
249
     * @see enableSchemaCache
250
     */
251
    public $schemaCache = 'cache';
252
    /**
253
     * @var bool whether to enable query caching.
254
     * Note that in order to enable query caching, a valid cache component as specified
255
     * by [[queryCache]] must be enabled and [[enableQueryCache]] must be set true.
256
     * Also, only the results of the queries enclosed within [[cache()]] will be cached.
257
     * @see queryCache
258
     * @see cache()
259
     * @see noCache()
260
     */
261
    public $enableQueryCache = true;
262
    /**
263
     * @var int the default number of seconds that query results can remain valid in cache.
264
     * Defaults to 3600, meaning 3600 seconds, or one hour. Use 0 to indicate that the cached data will never expire.
265
     * The value of this property will be used when [[cache()]] is called without a cache duration.
266
     * @see enableQueryCache
267
     * @see cache()
268
     */
269
    public $queryCacheDuration = 3600;
270
    /**
271
     * @var CacheInterface|string the cache object or the ID of the cache application component
272
     * that is used for query caching.
273
     * @see enableQueryCache
274
     */
275
    public $queryCache = 'cache';
276
    /**
277
     * @var string the charset used for database connection. The property is only used
278
     * for MySQL, PostgreSQL and CUBRID databases. Defaults to null, meaning using default charset
279
     * as configured by the database.
280
     *
281
     * For Oracle Database, the charset must be specified in the [[dsn]], for example for UTF-8 by appending `;charset=UTF-8`
282
     * to the DSN string.
283
     *
284
     * The same applies for if you're using GBK or BIG5 charset with MySQL, then it's highly recommended to
285
     * specify charset via [[dsn]] like `'mysql:dbname=mydatabase;host=127.0.0.1;charset=GBK;'`.
286
     */
287
    public $charset;
288
    /**
289
     * @var bool whether to turn on prepare emulation. Defaults to false, meaning PDO
290
     * will use the native prepare support if available. For some databases (such as MySQL),
291
     * this may need to be set true so that PDO can emulate the prepare support to bypass
292
     * the buggy native prepare support.
293
     * The default value is null, which means the PDO ATTR_EMULATE_PREPARES value will not be changed.
294
     */
295
    public $emulatePrepare;
296
    /**
297
     * @var string the common prefix or suffix for table names. If a table name is given
298
     * as `{{%TableName}}`, then the percentage character `%` will be replaced with this
299
     * property value. For example, `{{%post}}` becomes `{{tbl_post}}`.
300
     */
301
    public $tablePrefix = '';
302
    /**
303
     * @var array mapping between PDO driver names and [[Schema]] classes.
304
     * The keys of the array are PDO driver names while the values are either the corresponding
305
     * schema class names or configurations. Please refer to [[Yii::createObject()]] for
306
     * details on how to specify a configuration.
307
     *
308
     * This property is mainly used by [[getSchema()]] when fetching the database schema information.
309
     * You normally do not need to set this property unless you want to use your own
310
     * [[Schema]] class to support DBMS that is not supported by Yii.
311
     */
312
    public $schemaMap = [
313
        'pgsql' => 'yii\db\pgsql\Schema', // PostgreSQL
314
        'mysqli' => 'yii\db\mysql\Schema', // MySQL
315
        'mysql' => 'yii\db\mysql\Schema', // MySQL
316
        'sqlite' => 'yii\db\sqlite\Schema', // sqlite 3
317
        'sqlite2' => 'yii\db\sqlite\Schema', // sqlite 2
318
        'sqlsrv' => 'yii\db\mssql\Schema', // newer MSSQL driver on MS Windows hosts
319
        'oci' => 'yii\db\oci\Schema', // Oracle driver
320
        'mssql' => 'yii\db\mssql\Schema', // older MSSQL driver on MS Windows hosts
321
        'dblib' => 'yii\db\mssql\Schema', // dblib drivers on GNU/Linux (and maybe other OSes) hosts
322
        'cubrid' => 'yii\db\cubrid\Schema', // CUBRID
323
    ];
324
    /**
325
     * @var string Custom PDO wrapper class. If not set, it will use [[PDO]] or [[\yii\db\mssql\PDO]] when MSSQL is used.
326
     * @see pdo
327
     */
328
    public $pdoClass;
329
    /**
330
     * @var string the class used to create new database [[Command]] objects. If you want to extend the [[Command]] class,
331
     * you may configure this property to use your extended version of the class.
332
     * Since version 2.0.14 [[$commandMap]] is used if this property is set to its default value.
333
     * @see createCommand
334
     * @since 2.0.7
335
     * @deprecated since 2.0.14. Use [[$commandMap]] for precise configuration.
336
     */
337
    public $commandClass = 'yii\db\Command';
338
    /**
339
     * @var array mapping between PDO driver names and [[Command]] classes.
340
     * The keys of the array are PDO driver names while the values are either the corresponding
341
     * command class names or configurations. Please refer to [[Yii::createObject()]] for
342
     * details on how to specify a configuration.
343
     *
344
     * This property is mainly used by [[createCommand()]] to create new database [[Command]] objects.
345
     * You normally do not need to set this property unless you want to use your own
346
     * [[Command]] class or support DBMS that is not supported by Yii.
347
     * @since 2.0.14
348
     */
349
    public $commandMap = [
350
        'pgsql' => 'yii\db\Command', // PostgreSQL
351
        'mysqli' => 'yii\db\Command', // MySQL
352
        'mysql' => 'yii\db\Command', // MySQL
353
        'sqlite' => 'yii\db\sqlite\Command', // sqlite 3
354
        'sqlite2' => 'yii\db\sqlite\Command', // sqlite 2
355
        'sqlsrv' => 'yii\db\Command', // newer MSSQL driver on MS Windows hosts
356
        'oci' => 'yii\db\oci\Command', // Oracle driver
357
        'mssql' => 'yii\db\Command', // older MSSQL driver on MS Windows hosts
358
        'dblib' => 'yii\db\Command', // dblib drivers on GNU/Linux (and maybe other OSes) hosts
359
        'cubrid' => 'yii\db\Command', // CUBRID
360
    ];
361
    /**
362
     * @var bool whether to enable [savepoint](http://en.wikipedia.org/wiki/Savepoint).
363
     * Note that if the underlying DBMS does not support savepoint, setting this property to be true will have no effect.
364
     */
365
    public $enableSavepoint = true;
366
    /**
367
     * @var CacheInterface|string|false the cache object or the ID of the cache application component that is used to store
368
     * the health status of the DB servers specified in [[primaries]] and [[replicas]].
369
     * This is used only when read/write splitting is enabled or [[primaries]] is not empty.
370
     * Set boolean `false` to disabled server status caching.
371
     * @see openFromPoolSequentially() for details about the failover behavior.
372
     * @see serverRetryInterval
373
     */
374
    public $serverStatusCache = 'cache';
375
    /**
376
     * @var int the retry interval in seconds for dead servers listed in [[primaries]] and [[replicas]].
377
     * This is used together with [[serverStatusCache]].
378
     */
379
    public $serverRetryInterval = 600;
380
    /**
381
     * @var bool whether to enable read/write splitting by using [[replicas]] to read data.
382
     * Note that if [[replicas]] is empty, read/write splitting will NOT be enabled no matter what value this property takes.
383
     * @deprecated since 2.0.36. Use [[enableReplicas]] instead.
384
     */
385
    public $enableSlaves = true;
386
    /**
387
     * Returns the value of [[enableReplicas]].
388
     * @return bool
389
     * @since 2.0.36
390
     * @internal
391
     */
392
    public function getEnableReplicas()
393
    {
394
        return $this->enableSlaves;
0 ignored issues
show
Deprecated Code introduced by
The property yii\db\Connection::$enableSlaves has been deprecated: since 2.0.36. Use [[enableReplicas]] instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

394
        return /** @scrutinizer ignore-deprecated */ $this->enableSlaves;

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
395
    }
396
    /**
397
     * Sets the value of [[enableReplicas]].
398
     * @param bool $value
399
     * @since 2.0.36
400
     * @internal
401
     */
402
    public function setEnableReplicas($value)
403
    {
404
        $this->enableSlaves = $value;
0 ignored issues
show
Deprecated Code introduced by
The property yii\db\Connection::$enableSlaves has been deprecated: since 2.0.36. Use [[enableReplicas]] instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

404
        /** @scrutinizer ignore-deprecated */ $this->enableSlaves = $value;

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
405
    }
406
    /**
407
     * @var array list of replica connection configurations. Each configuration is used to create a replica DB connection.
408
     * When [[enableReplicas]] is true, one of these configurations will be chosen and used to create a DB connection
409
     * for performing read queries only.
410
     * @see enableSlaves
411
     * @see slaveConfig
412
     * @deprecated since 2.0.36. Use [[replicas]] instead.
413
     */
414
    public $slaves = [];
415
    /**
416
     * Returns the value of [[replicas]].
417
     * @return array
418
     * @since 2.0.36
419
     * @internal
420
     */
421
    public function getReplicas()
422
    {
423
        return $this->slaves;
0 ignored issues
show
Deprecated Code introduced by
The property yii\db\Connection::$slaves has been deprecated: since 2.0.36. Use [[replicas]] instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

423
        return /** @scrutinizer ignore-deprecated */ $this->slaves;

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
424
    }
425
    /**
426
     * Sets the value of [[replicas]].
427
     * @param array $value
428
     * @since 2.0.36
429
     * @internal
430
     */
431 10
    public function setReplicas($value)
432
    {
433 10
        $this->slaves = $value;
0 ignored issues
show
Deprecated Code introduced by
The property yii\db\Connection::$slaves has been deprecated: since 2.0.36. Use [[replicas]] instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

433
        /** @scrutinizer ignore-deprecated */ $this->slaves = $value;

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
434 10
    }
435
    /**
436
     * @var array the configuration that should be merged with every replica configuration listed in [[replicas]].
437
     * For example,
438
     *
439
     * ```php
440
     * [
441
     *     'username' => 'replica',
442
     *     'password' => 'replica',
443
     *     'attributes' => [
444
     *         // use a smaller connection timeout
445
     *         PDO::ATTR_TIMEOUT => 10,
446
     *     ],
447
     * ]
448
     * ```
449
     *
450
     * @deprecated since 2.0.36. Use [[replicaConfig]] instead.
451
     */
452
    public $slaveConfig = [];
453
    /**
454
     * Returns the value of [[replicaConfig]].
455
     * @return array
456
     * @since 2.0.36
457
     * @internal
458
     */
459
    public function getReplicaConfig()
460
    {
461
        return $this->slaveConfig;
0 ignored issues
show
Deprecated Code introduced by
The property yii\db\Connection::$slaveConfig has been deprecated: since 2.0.36. Use [[replicaConfig]] instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

461
        return /** @scrutinizer ignore-deprecated */ $this->slaveConfig;

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
462
    }
463
    /**
464
     * Sets the value of [[replicaConfig]].
465
     * @param array $value
466
     * @since 2.0.36
467
     * @internal
468
     */
469
    public function setReplicaConfig($value)
470
    {
471
        $this->slaveConfig = $value;
0 ignored issues
show
Deprecated Code introduced by
The property yii\db\Connection::$slaveConfig has been deprecated: since 2.0.36. Use [[replicaConfig]] instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

471
        /** @scrutinizer ignore-deprecated */ $this->slaveConfig = $value;

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
472
    }
473
    /**
474
     * @var array list of primary connection configurations. Each configuration is used to create a primary DB connection.
475
     * When [[open()]] is called, one of these configurations will be chosen and used to create a DB connection
476
     * which will be used by this object.
477
     * Note that when this property is not empty, the connection setting (e.g. `dsn`, `username`) of this object will
478
     * be ignored.
479
     * @see masterConfig
480
     * @see shuffleMasters
481
     * @deprecated since 2.0.36. Use [[primaries]] instead.
482
     */
483
    public $masters = [];
484
    /**
485
     * Returns the value of [[primaries]].
486
     * @return array
487
     * @since 2.0.36
488
     * @internal
489
     */
490
    public function getPrimaries()
491
    {
492
        return $this->masters;
0 ignored issues
show
Deprecated Code introduced by
The property yii\db\Connection::$masters has been deprecated: since 2.0.36. Use [[primaries]] instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

492
        return /** @scrutinizer ignore-deprecated */ $this->masters;

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
493
    }
494
    /**
495
     * Sets the value of [[primaries]].
496
     * @param array $value
497
     * @since 2.0.36
498
     * @internal
499
     */
500 4
    public function setPrimaries($value)
501
    {
502 4
        $this->masters = $value;
0 ignored issues
show
Deprecated Code introduced by
The property yii\db\Connection::$masters has been deprecated: since 2.0.36. Use [[primaries]] instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

502
        /** @scrutinizer ignore-deprecated */ $this->masters = $value;

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
503 4
    }
504
    /**
505
     * @var array the configuration that should be merged with every primary configuration listed in [[primaries]].
506
     * For example,
507
     *
508
     * ```php
509
     * [
510
     *     'username' => 'primary',
511
     *     'password' => 'primary',
512
     *     'attributes' => [
513
     *         // use a smaller connection timeout
514
     *         PDO::ATTR_TIMEOUT => 10,
515
     *     ],
516
     * ]
517
     * ```
518
     *
519
     * @deprecated since 2.0.36. Use [[primaryConfig]] instead.
520
     */
521
    public $masterConfig = [];
522
    /**
523
     * Returns the value of [[primaryConfig]].
524
     * @return array
525
     * @since 2.0.36
526
     * @internal
527
     */
528
    public function getPrimaryConfig()
529
    {
530
        return $this->masterConfig;
0 ignored issues
show
Deprecated Code introduced by
The property yii\db\Connection::$masterConfig has been deprecated: since 2.0.36. Use [[primaryConfig]] instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

530
        return /** @scrutinizer ignore-deprecated */ $this->masterConfig;

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
531
    }
532
    /**
533
     * Sets the value of [[primaryConfig]].
534
     * @param array $value
535
     * @since 2.0.36
536
     * @internal
537
     */
538
    public function setPrimaryConfig($value)
539
    {
540
        $this->masterConfig = $value;
0 ignored issues
show
Deprecated Code introduced by
The property yii\db\Connection::$masterConfig has been deprecated: since 2.0.36. Use [[primaryConfig]] instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

540
        /** @scrutinizer ignore-deprecated */ $this->masterConfig = $value;

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
541
    }
542
    /**
543
     * @var bool whether to shuffle [[primaries]] before getting one.
544
     * @since 2.0.11
545
     * @see masters
546
     * @deprecated since 2.0.36. Use [[shufflePrimaries]] instead.
547
     */
548
    public $shuffleMasters = true;
549
    /**
550
     * Returns the value of [[shufflePrimaries]].
551
     * @return bool
552
     * @since 2.0.36
553
     * @internal
554
     */
555
    public function getShufflePrimaries()
556
    {
557
        return $this->shuffleMasters;
0 ignored issues
show
Deprecated Code introduced by
The property yii\db\Connection::$shuffleMasters has been deprecated: since 2.0.36. Use [[shufflePrimaries]] instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

557
        return /** @scrutinizer ignore-deprecated */ $this->shuffleMasters;

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
558
    }
559
    /**
560
     * Sets the value of [[shufflePrimaries]].
561
     * @param bool $value
562
     * @since 2.0.36
563
     * @internal
564
     */
565
    public function setShufflePrimaries($value)
566
    {
567
        $this->shuffleMasters = $value;
0 ignored issues
show
Deprecated Code introduced by
The property yii\db\Connection::$shuffleMasters has been deprecated: since 2.0.36. Use [[shufflePrimaries]] instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

567
        /** @scrutinizer ignore-deprecated */ $this->shuffleMasters = $value;

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
568
    }
569
    /**
570
     * @var bool whether to enable logging of database queries. Defaults to true.
571
     * You may want to disable this option in a production environment to gain performance
572
     * if you do not need the information being logged.
573
     * @since 2.0.12
574
     * @see enableProfiling
575
     */
576
    public $enableLogging = true;
577
    /**
578
     * @var bool whether to enable profiling of opening database connection and database queries. Defaults to true.
579
     * You may want to disable this option in a production environment to gain performance
580
     * if you do not need the information being logged.
581
     * @since 2.0.12
582
     * @see enableLogging
583
     */
584
    public $enableProfiling = true;
585
586
    /**
587
     * @var Transaction the currently active transaction
588
     */
589
    private $_transaction;
590
    /**
591
     * @var Schema the database schema
592
     */
593
    private $_schema;
594
    /**
595
     * @var string driver name
596
     */
597
    private $_driverName;
598
    /**
599
     * @var Connection|false the currently active primary connection
600
     */
601
    private $_primary = false;
602
    /**
603
     * @var Connection|false the currently active replica connection
604
     */
605
    private $_replica = false;
606
    /**
607
     * @var array query cache parameters for the [[cache()]] calls
608
     */
609
    private $_queryCacheInfo = [];
610
    /**
611
     * @var string[] quoted table name cache for [[quoteTableName()]] calls
612
     */
613
    private $_quotedTableNames;
614
    /**
615
     * @var string[] quoted column name cache for [[quoteColumnName()]] calls
616
     */
617
    private $_quotedColumnNames;
618
619
620
    /**
621
     * Returns a value indicating whether the DB connection is established.
622
     * @return bool whether the DB connection is established
623
     */
624 332
    public function getIsActive()
625
    {
626 332
        return $this->pdo !== null;
627
    }
628
629
    /**
630
     * Uses query cache for the queries performed with the callable.
631
     *
632
     * When query caching is enabled ([[enableQueryCache]] is true and [[queryCache]] refers to a valid cache),
633
     * queries performed within the callable will be cached and their results will be fetched from cache if available.
634
     * For example,
635
     *
636
     * ```php
637
     * // The customer will be fetched from cache if available.
638
     * // If not, the query will be made against DB and cached for use next time.
639
     * $customer = $db->cache(function (Connection $db) {
640
     *     return $db->createCommand('SELECT * FROM customer WHERE id=1')->queryOne();
641
     * });
642
     * ```
643
     *
644
     * Note that query cache is only meaningful for queries that return results. For queries performed with
645
     * [[Command::execute()]], query cache will not be used.
646
     *
647
     * @param callable $callable a PHP callable that contains DB queries which will make use of query cache.
648
     * The signature of the callable is `function (Connection $db)`.
649
     * @param int $duration the number of seconds that query results can remain valid in the cache. If this is
650
     * not set, the value of [[queryCacheDuration]] will be used instead.
651
     * Use 0 to indicate that the cached data will never expire.
652
     * @param \yii\caching\Dependency $dependency the cache dependency associated with the cached query results.
653
     * @return mixed the return result of the callable
654
     * @throws \Exception|\Throwable if there is any exception during query
655
     * @see enableQueryCache
656
     * @see queryCache
657
     * @see noCache()
658
     */
659 6
    public function cache(callable $callable, $duration = null, $dependency = null)
660
    {
661 6
        $this->_queryCacheInfo[] = [$duration === null ? $this->queryCacheDuration : $duration, $dependency];
662
        try {
663 6
            $result = call_user_func($callable, $this);
664 6
            array_pop($this->_queryCacheInfo);
665 6
            return $result;
666
        } catch (\Exception $e) {
667
            array_pop($this->_queryCacheInfo);
668
            throw $e;
669
        } catch (\Throwable $e) {
670
            array_pop($this->_queryCacheInfo);
671
            throw $e;
672
        }
673
    }
674
675
    /**
676
     * Disables query cache temporarily.
677
     *
678
     * Queries performed within the callable will not use query cache at all. For example,
679
     *
680
     * ```php
681
     * $db->cache(function (Connection $db) {
682
     *
683
     *     // ... queries that use query cache ...
684
     *
685
     *     return $db->noCache(function (Connection $db) {
686
     *         // this query will not use query cache
687
     *         return $db->createCommand('SELECT * FROM customer WHERE id=1')->queryOne();
688
     *     });
689
     * });
690
     * ```
691
     *
692
     * @param callable $callable a PHP callable that contains DB queries which should not use query cache.
693
     * The signature of the callable is `function (Connection $db)`.
694
     * @return mixed the return result of the callable
695
     * @throws \Exception|\Throwable if there is any exception during query
696
     * @see enableQueryCache
697
     * @see queryCache
698
     * @see cache()
699
     */
700 40
    public function noCache(callable $callable)
701
    {
702 40
        $this->_queryCacheInfo[] = false;
703
        try {
704 40
            $result = call_user_func($callable, $this);
705 40
            array_pop($this->_queryCacheInfo);
706 40
            return $result;
707 4
        } catch (\Exception $e) {
708 4
            array_pop($this->_queryCacheInfo);
709 4
            throw $e;
710
        } catch (\Throwable $e) {
711
            array_pop($this->_queryCacheInfo);
712
            throw $e;
713
        }
714
    }
715
716
    /**
717
     * Returns the current query cache information.
718
     * This method is used internally by [[Command]].
719
     * @param int $duration the preferred caching duration. If null, it will be ignored.
720
     * @param \yii\caching\Dependency $dependency the preferred caching dependency. If null, it will be ignored.
721
     * @return array the current query cache information, or null if query cache is not enabled.
722
     * @internal
723
     */
724 1505
    public function getQueryCacheInfo($duration, $dependency)
725
    {
726 1505
        if (!$this->enableQueryCache) {
727 44
            return null;
728
        }
729
730 1503
        $info = end($this->_queryCacheInfo);
731 1503
        if (is_array($info)) {
732 6
            if ($duration === null) {
0 ignored issues
show
introduced by
The condition $duration === null is always false.
Loading history...
733 6
                $duration = $info[0];
734
            }
735 6
            if ($dependency === null) {
736 6
                $dependency = $info[1];
737
            }
738
        }
739
740 1503
        if ($duration === 0 || $duration > 0) {
741 6
            if (is_string($this->queryCache) && Yii::$app) {
742
                $cache = Yii::$app->get($this->queryCache, false);
743
            } else {
744 6
                $cache = $this->queryCache;
745
            }
746 6
            if ($cache instanceof CacheInterface) {
747 6
                return [$cache, $duration, $dependency];
748
            }
749
        }
750
751 1503
        return null;
752
    }
753
754
    /**
755
     * Establishes a DB connection.
756
     * It does nothing if a DB connection has already been established.
757
     * @throws Exception if connection fails
758
     */
759 2034
    public function open()
760
    {
761 2034
        if ($this->pdo !== null) {
762 1615
            return;
763
        }
764
765 1979
        if (!empty($this->masters)) {
0 ignored issues
show
Deprecated Code introduced by
The property yii\db\Connection::$masters has been deprecated: since 2.0.36. Use [[primaries]] instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

765
        if (!empty(/** @scrutinizer ignore-deprecated */ $this->masters)) {

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
766 9
            $db = $this->getMaster();
0 ignored issues
show
Deprecated Code introduced by
The function yii\db\Connection::getMaster() has been deprecated: since 2.0.36. Use [[getPrimary()]] instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

766
            $db = /** @scrutinizer ignore-deprecated */ $this->getMaster();

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
767 9
            if ($db !== null) {
768 9
                $this->pdo = $db->pdo;
769 9
                return;
770
            }
771
772 8
            throw new InvalidConfigException('None of the primary DB servers are available.');
773
        }
774
775 1979
        if (empty($this->dsn)) {
776
            throw new InvalidConfigException('Connection::dsn cannot be empty.');
777
        }
778
779 1979
        $token = 'Opening DB connection: ' . $this->dsn;
780 1979
        $enableProfiling = $this->enableProfiling;
781
        try {
782 1979
            if ($this->enableLogging) {
783 1979
                Yii::info($token, __METHOD__);
784
            }
785
786 1979
            if ($enableProfiling) {
787 1979
                Yii::beginProfile($token, __METHOD__);
788
            }
789
790 1979
            $this->pdo = $this->createPdoInstance();
791 1979
            $this->initConnection();
792
793 1979
            if ($enableProfiling) {
794 1979
                Yii::endProfile($token, __METHOD__);
795
            }
796 12
        } catch (\PDOException $e) {
797 12
            if ($enableProfiling) {
798 12
                Yii::endProfile($token, __METHOD__);
799
            }
800
801 12
            throw new Exception($e->getMessage(), $e->errorInfo, (int) $e->getCode(), $e);
802
        }
803 1979
    }
804
805
    /**
806
     * Closes the currently active DB connection.
807
     * It does nothing if the connection is already closed.
808
     */
809 2131
    public function close()
810
    {
811 2131
        if ($this->_primary) {
812 8
            if ($this->pdo === $this->_primary->pdo) {
813 8
                $this->pdo = null;
814
            }
815
816 8
            $this->_primary->close();
817 8
            $this->_primary = false;
818
        }
819
820 2131
        if ($this->pdo !== null) {
821 1784
            Yii::debug('Closing DB connection: ' . $this->dsn, __METHOD__);
822 1784
            $this->pdo = null;
823
        }
824
825 2131
        if ($this->_replica) {
826 4
            $this->_replica->close();
827 4
            $this->_replica = false;
828
        }
829
830 2131
        $this->_schema = null;
831 2131
        $this->_transaction = null;
832 2131
        $this->_driverName = null;
833 2131
        $this->_queryCacheInfo = [];
834 2131
        $this->_quotedTableNames = null;
835 2131
        $this->_quotedColumnNames = null;
836 2131
    }
837
838
    /**
839
     * Creates the PDO instance.
840
     * This method is called by [[open]] to establish a DB connection.
841
     * The default implementation will create a PHP PDO instance.
842
     * You may override this method if the default PDO needs to be adapted for certain DBMS.
843
     * @return PDO the pdo instance
844
     */
845 1979
    protected function createPdoInstance()
846
    {
847 1979
        $pdoClass = $this->pdoClass;
848 1979
        if ($pdoClass === null) {
0 ignored issues
show
introduced by
The condition $pdoClass === null is always false.
Loading history...
849 1979
            $pdoClass = 'PDO';
850 1979
            if ($this->_driverName !== null) {
851 239
                $driver = $this->_driverName;
852 1745
            } elseif (($pos = strpos($this->dsn, ':')) !== false) {
853 1745
                $driver = strtolower(substr($this->dsn, 0, $pos));
854
            }
855 1979
            if (isset($driver)) {
856 1979
                if ($driver === 'mssql' || $driver === 'dblib') {
857
                    $pdoClass = 'yii\db\mssql\PDO';
858 1979
                } elseif ($driver === 'sqlsrv') {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $driver does not seem to be defined for all execution paths leading up to this point.
Loading history...
859
                    $pdoClass = 'yii\db\mssql\SqlsrvPDO';
860
                }
861
            }
862
        }
863
864 1979
        $dsn = $this->dsn;
865 1979
        if (strncmp('sqlite:@', $dsn, 8) === 0) {
866 1
            $dsn = 'sqlite:' . Yii::getAlias(substr($dsn, 7));
867
        }
868
869 1979
        return new $pdoClass($dsn, $this->username, $this->password, $this->attributes);
870
    }
871
872
    /**
873
     * Initializes the DB connection.
874
     * This method is invoked right after the DB connection is established.
875
     * The default implementation turns on `PDO::ATTR_EMULATE_PREPARES`
876
     * if [[emulatePrepare]] is true, and sets the database [[charset]] if it is not empty.
877
     * It then triggers an [[EVENT_AFTER_OPEN]] event.
878
     */
879 1979
    protected function initConnection()
880
    {
881 1979
        $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
882 1979
        if ($this->emulatePrepare !== null && constant('PDO::ATTR_EMULATE_PREPARES')) {
883
            if ($this->driverName !== 'sqlsrv') {
884
                $this->pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, $this->emulatePrepare);
885
            }
886
        }
887 1979
        if (in_array($this->getDriverName(), ['mssql', 'dblib'], true)) {
888
            $this->pdo->exec('SET ANSI_NULL_DFLT_ON ON');
889
        }
890 1979
        if ($this->charset !== null && in_array($this->getDriverName(), ['pgsql', 'mysql', 'mysqli', 'cubrid'], true)) {
891
            $this->pdo->exec('SET NAMES ' . $this->pdo->quote($this->charset));
892
        }
893 1979
        $this->trigger(self::EVENT_AFTER_OPEN);
894 1979
    }
895
896
    /**
897
     * Creates a command for execution.
898
     * @param string $sql the SQL statement to be executed
899
     * @param array $params the parameters to be bound to the SQL statement
900
     * @return Command the DB command
901
     */
902 1596
    public function createCommand($sql = null, $params = [])
903
    {
904 1596
        $driver = $this->getDriverName();
905 1596
        $config = ['class' => 'yii\db\Command'];
906 1596
        if ($this->commandClass !== $config['class']) {
0 ignored issues
show
Deprecated Code introduced by
The property yii\db\Connection::$commandClass has been deprecated: since 2.0.14. Use [[$commandMap]] for precise configuration. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

906
        if (/** @scrutinizer ignore-deprecated */ $this->commandClass !== $config['class']) {

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
907
            $config['class'] = $this->commandClass;
0 ignored issues
show
Deprecated Code introduced by
The property yii\db\Connection::$commandClass has been deprecated: since 2.0.14. Use [[$commandMap]] for precise configuration. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

907
            $config['class'] = /** @scrutinizer ignore-deprecated */ $this->commandClass;

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
908 1596
        } elseif (isset($this->commandMap[$driver])) {
909 1596
            $config = !is_array($this->commandMap[$driver]) ? ['class' => $this->commandMap[$driver]] : $this->commandMap[$driver];
910
        }
911 1596
        $config['db'] = $this;
912 1596
        $config['sql'] = $sql;
913
        /** @var Command $command */
914 1596
        $command = Yii::createObject($config);
915 1596
        return $command->bindValues($params);
916
    }
917
918
    /**
919
     * Returns the currently active transaction.
920
     * @return Transaction|null the currently active transaction. Null if no active transaction.
921
     */
922 1565
    public function getTransaction()
923
    {
924 1565
        return $this->_transaction && $this->_transaction->getIsActive() ? $this->_transaction : null;
925
    }
926
927
    /**
928
     * Starts a transaction.
929
     * @param string|null $isolationLevel The isolation level to use for this transaction.
930
     * See [[Transaction::begin()]] for details.
931
     * @return Transaction the transaction initiated
932
     */
933 39
    public function beginTransaction($isolationLevel = null)
934
    {
935 39
        $this->open();
936
937 39
        if (($transaction = $this->getTransaction()) === null) {
938 39
            $transaction = $this->_transaction = new Transaction(['db' => $this]);
939
        }
940 39
        $transaction->begin($isolationLevel);
941
942 39
        return $transaction;
943
    }
944
945
    /**
946
     * Executes callback provided in a transaction.
947
     *
948
     * @param callable $callback a valid PHP callback that performs the job. Accepts connection instance as parameter.
949
     * @param string|null $isolationLevel The isolation level to use for this transaction.
950
     * See [[Transaction::begin()]] for details.
951
     * @throws \Exception|\Throwable if there is any exception during query. In this case the transaction will be rolled back.
952
     * @return mixed result of callback function
953
     */
954 23
    public function transaction(callable $callback, $isolationLevel = null)
955
    {
956 23
        $transaction = $this->beginTransaction($isolationLevel);
957 23
        $level = $transaction->level;
958
959
        try {
960 23
            $result = call_user_func($callback, $this);
961 15
            if ($transaction->isActive && $transaction->level === $level) {
962 15
                $transaction->commit();
963
            }
964 8
        } catch (\Exception $e) {
965 8
            $this->rollbackTransactionOnLevel($transaction, $level);
966 8
            throw $e;
967
        } catch (\Throwable $e) {
968
            $this->rollbackTransactionOnLevel($transaction, $level);
969
            throw $e;
970
        }
971
972 15
        return $result;
973
    }
974
975
    /**
976
     * Rolls back given [[Transaction]] object if it's still active and level match.
977
     * In some cases rollback can fail, so this method is fail safe. Exception thrown
978
     * from rollback will be caught and just logged with [[\Yii::error()]].
979
     * @param Transaction $transaction Transaction object given from [[beginTransaction()]].
980
     * @param int $level Transaction level just after [[beginTransaction()]] call.
981
     */
982 8
    private function rollbackTransactionOnLevel($transaction, $level)
983
    {
984 8
        if ($transaction->isActive && $transaction->level === $level) {
985
            // https://github.com/yiisoft/yii2/pull/13347
986
            try {
987 8
                $transaction->rollBack();
988
            } catch (\Exception $e) {
989
                \Yii::error($e, __METHOD__);
990
                // hide this exception to be able to continue throwing original exception outside
991
            }
992
        }
993 8
    }
994
995
    /**
996
     * Returns the schema information for the database opened by this connection.
997
     * @return Schema the schema information for the database opened by this connection.
998
     * @throws NotSupportedException if there is no support for the current driver type
999
     */
1000 2065
    public function getSchema()
1001
    {
1002 2065
        if ($this->_schema !== null) {
1003 1687
            return $this->_schema;
1004
        }
1005
1006 2010
        $driver = $this->getDriverName();
1007 2010
        if (isset($this->schemaMap[$driver])) {
1008 2010
            $config = !is_array($this->schemaMap[$driver]) ? ['class' => $this->schemaMap[$driver]] : $this->schemaMap[$driver];
1009 2010
            $config['db'] = $this;
1010
1011 2010
            return $this->_schema = Yii::createObject($config);
1012
        }
1013
1014
        throw new NotSupportedException("Connection does not support reading schema information for '$driver' DBMS.");
1015
    }
1016
1017
    /**
1018
     * Returns the query builder for the current DB connection.
1019
     * @return QueryBuilder the query builder for the current DB connection.
1020
     */
1021 1100
    public function getQueryBuilder()
1022
    {
1023 1100
        return $this->getSchema()->getQueryBuilder();
1024
    }
1025
1026
    /**
1027
     * Can be used to set [[QueryBuilder]] configuration via Connection configuration array.
1028
     *
1029
     * @param array $value the [[QueryBuilder]] properties to be configured.
1030
     * @since 2.0.14
1031
     */
1032
    public function setQueryBuilder($value)
1033
    {
1034
        Yii::configure($this->getQueryBuilder(), $value);
1035
    }
1036
1037
    /**
1038
     * Obtains the schema information for the named table.
1039
     * @param string $name table name.
1040
     * @param bool $refresh whether to reload the table schema even if it is found in the cache.
1041
     * @return TableSchema table schema information. Null if the named table does not exist.
1042
     */
1043 242
    public function getTableSchema($name, $refresh = false)
1044
    {
1045 242
        return $this->getSchema()->getTableSchema($name, $refresh);
1046
    }
1047
1048
    /**
1049
     * Returns the ID of the last inserted row or sequence value.
1050
     * @param string $sequenceName name of the sequence object (required by some DBMS)
1051
     * @return string the row ID of the last row inserted, or the last value retrieved from the sequence object
1052
     * @see https://secure.php.net/manual/en/pdo.lastinsertid.php
1053
     */
1054 6
    public function getLastInsertID($sequenceName = '')
1055
    {
1056 6
        return $this->getSchema()->getLastInsertID($sequenceName);
1057
    }
1058
1059
    /**
1060
     * Quotes a string value for use in a query.
1061
     * Note that if the parameter is not a string, it will be returned without change.
1062
     * @param string $value string to be quoted
1063
     * @return string the properly quoted string
1064
     * @see https://secure.php.net/manual/en/pdo.quote.php
1065
     */
1066 1075
    public function quoteValue($value)
1067
    {
1068 1075
        return $this->getSchema()->quoteValue($value);
1069
    }
1070
1071
    /**
1072
     * Quotes a table name for use in a query.
1073
     * If the table name contains schema prefix, the prefix will also be properly quoted.
1074
     * If the table name is already quoted or contains special characters including '(', '[[' and '{{',
1075
     * then this method will do nothing.
1076
     * @param string $name table name
1077
     * @return string the properly quoted table name
1078
     */
1079 1361
    public function quoteTableName($name)
1080
    {
1081 1361
        if (isset($this->_quotedTableNames[$name])) {
1082 1002
            return $this->_quotedTableNames[$name];
1083
        }
1084 1295
        return $this->_quotedTableNames[$name] = $this->getSchema()->quoteTableName($name);
1085
    }
1086
1087
    /**
1088
     * Quotes a column name for use in a query.
1089
     * If the column name contains prefix, the prefix will also be properly quoted.
1090
     * If the column name is already quoted or contains special characters including '(', '[[' and '{{',
1091
     * then this method will do nothing.
1092
     * @param string $name column name
1093
     * @return string the properly quoted column name
1094
     */
1095 1416
    public function quoteColumnName($name)
1096
    {
1097 1416
        if (isset($this->_quotedColumnNames[$name])) {
1098 875
            return $this->_quotedColumnNames[$name];
1099
        }
1100 1361
        return $this->_quotedColumnNames[$name] = $this->getSchema()->quoteColumnName($name);
1101
    }
1102
1103
    /**
1104
     * Processes a SQL statement by quoting table and column names that are enclosed within double brackets.
1105
     * Tokens enclosed within double curly brackets are treated as table names, while
1106
     * tokens enclosed within double square brackets are column names. They will be quoted accordingly.
1107
     * Also, the percentage character "%" at the beginning or ending of a table name will be replaced
1108
     * with [[tablePrefix]].
1109
     * @param string $sql the SQL to be quoted
1110
     * @return string the quoted SQL
1111
     */
1112 1640
    public function quoteSql($sql)
1113
    {
1114 1640
        return preg_replace_callback(
1115 1640
            '/(\\{\\{(%?[\w\-\. ]+%?)\\}\\}|\\[\\[([\w\-\. ]+)\\]\\])/',
1116 1640
            function ($matches) {
1117 817
                if (isset($matches[3])) {
1118 601
                    return $this->quoteColumnName($matches[3]);
1119
                }
1120
1121 713
                return str_replace('%', $this->tablePrefix, $this->quoteTableName($matches[2]));
1122 1640
            },
1123 1640
            $sql
1124
        );
1125
    }
1126
1127
    /**
1128
     * Returns the name of the DB driver. Based on the the current [[dsn]], in case it was not set explicitly
1129
     * by an end user.
1130
     * @return string name of the DB driver
1131
     */
1132 2298
    public function getDriverName()
1133
    {
1134 2298
        if ($this->_driverName === null) {
1135 2229
            if (($pos = strpos($this->dsn, ':')) !== false) {
1136 2229
                $this->_driverName = strtolower(substr($this->dsn, 0, $pos));
1137
            } else {
1138
                $this->_driverName = strtolower($this->getSlavePdo()->getAttribute(PDO::ATTR_DRIVER_NAME));
0 ignored issues
show
Deprecated Code introduced by
The function yii\db\Connection::getSlavePdo() has been deprecated: since 2.0.36. Use [[getReplicaPdo()]] instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1138
                $this->_driverName = strtolower(/** @scrutinizer ignore-deprecated */ $this->getSlavePdo()->getAttribute(PDO::ATTR_DRIVER_NAME));

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
1139
            }
1140
        }
1141
1142 2298
        return $this->_driverName;
1143
    }
1144
1145
    /**
1146
     * Changes the current driver name.
1147
     * @param string $driverName name of the DB driver
1148
     */
1149
    public function setDriverName($driverName)
1150
    {
1151
        $this->_driverName = strtolower($driverName);
1152
    }
1153
1154
    /**
1155
     * Returns a server version as a string comparable by [[\version_compare()]].
1156
     * @return string server version as a string.
1157
     * @since 2.0.14
1158
     */
1159 411
    public function getServerVersion()
1160
    {
1161 411
        return $this->getSchema()->getServerVersion();
1162
    }
1163
1164
    /**
1165
     * Returns the PDO instance for the currently active replica connection.
1166
     * When [[enableReplicas]] is true, one of the replicas will be used for read queries, and its PDO instance
1167
     * will be returned by this method.
1168
     * @param bool $fallbackToPrimary whether to return the primary PDO if no replica connections are available.
1169
     * @return PDO|null the PDO instance for the currently active replica connection. `null` is returned if no
1170
     * replica connections are available and `$fallbackToPrimary` is false.
1171
     * @since 2.0.36
1172
     */
1173 1748
    public function getReplicaPdo($fallbackToPrimary = true)
1174
    {
1175 1748
        $db = $this->getSlave(false);
0 ignored issues
show
Deprecated Code introduced by
The function yii\db\Connection::getSlave() has been deprecated: since 2.0.36. Use [[getReplica()]] instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1175
        $db = /** @scrutinizer ignore-deprecated */ $this->getSlave(false);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
1176 1748
        if ($db === null) {
1177 1744
            return $fallbackToPrimary ? $this->getMasterPdo() : null;
0 ignored issues
show
Deprecated Code introduced by
The function yii\db\Connection::getMasterPdo() has been deprecated: since 2.0.36. Use [[getPrimaryPdo()]] instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1177
            return $fallbackToPrimary ? /** @scrutinizer ignore-deprecated */ $this->getMasterPdo() : null;

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
1178
        }
1179
1180 5
        return $db->pdo;
1181
    }
1182
1183
    /**
1184
     * Returns the PDO instance for the currently active replica connection.
1185
     * When [[enableReplicas]] is true, one of the replicas will be used for read queries, and its PDO instance
1186
     * will be returned by this method.
1187
     * @param bool $fallbackToPrimary whether to return the primary PDO if no replica connections are available.
1188
     * @return PDO|null the PDO instance for the currently active replica connection. `null` is returned if no
1189
     * replica connections are available and `$fallbackToPrimary` is false.
1190
     * @deprecated since 2.0.36. Use [[getReplicaPdo()]] instead.
1191
     */
1192
    public function getSlavePdo($fallbackToPrimary = true)
1193
    {
1194
        return $this->getReplicaPdo($fallbackToPrimary);
1195
    }
1196
1197
    /**
1198
     * Returns the PDO instance for the currently active primary connection.
1199
     * This method will open the primary DB connection and then return [[pdo]].
1200
     * @return PDO the PDO instance for the currently active primary connection.
1201
     * @since 2.0.36
1202
     */
1203 1778
    public function getPrimaryPdo()
1204
    {
1205 1778
        $this->open();
1206 1778
        return $this->pdo;
1207
    }
1208
1209
    /**
1210
     * Returns the PDO instance for the currently active primary connection.
1211
     * This method will open the primary DB connection and then return [[pdo]].
1212
     * @return PDO the PDO instance for the currently active primary connection.
1213
     * @deprecated since 2.0.36. Use [[getPrimaryPdo()]] instead.
1214
     */
1215
    public function getMasterPdo()
1216
    {
1217
        return $this->getPrimaryPdo();
1218
    }
1219
1220
    /**
1221
     * Returns the currently active replica connection.
1222
     * If this method is called for the first time, it will try to open a replica connection when [[enableReplicas]]
1223
     * is true.
1224
     * @param bool $fallbackToPrimary whether to return the primary connection if no replica connections are
1225
     * available.
1226
     * @return Connection|null the currently active replica connection. `null` is returned if no replica connections
1227
     * are available and `$fallbackToPrimary` is false.
1228
     * @since 2.0.36
1229
     */
1230 1750
    public function getReplica($fallbackToPrimary = true)
1231
    {
1232 1750
        if (!$this->enableSlaves) {
0 ignored issues
show
Deprecated Code introduced by
The property yii\db\Connection::$enableSlaves has been deprecated: since 2.0.36. Use [[enableReplicas]] instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1232
        if (!/** @scrutinizer ignore-deprecated */ $this->enableSlaves) {

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1233 209
            return $fallbackToPrimary ? $this : null;
1234
        }
1235
1236 1651
        if ($this->_replica === false) {
1237 1651
            $this->_replica = $this->openFromPool($this->slaves, $this->slaveConfig);
0 ignored issues
show
Deprecated Code introduced by
The property yii\db\Connection::$slaves has been deprecated: since 2.0.36. Use [[replicas]] instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1237
            $this->_replica = $this->openFromPool(/** @scrutinizer ignore-deprecated */ $this->slaves, $this->slaveConfig);

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
Deprecated Code introduced by
The property yii\db\Connection::$slaveConfig has been deprecated: since 2.0.36. Use [[replicaConfig]] instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1237
            $this->_replica = $this->openFromPool($this->slaves, /** @scrutinizer ignore-deprecated */ $this->slaveConfig);

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1238
        }
1239
1240 1645
        return $this->_replica === null && $fallbackToPrimary ? $this : $this->_replica;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->_replica =...$this : $this->_replica also could return the type true which is incompatible with the documented return type null|yii\db\Connection.
Loading history...
1241
    }
1242
1243
    /**
1244
     * Returns the currently active replica connection.
1245
     * If this method is called for the first time, it will try to open a replica connection when [[enableReplicas]]
1246
     * is true.
1247
     * @param bool $fallbackToPrimary whether to return the primary connection if no replica connections are
1248
     * available.
1249
     * @return Connection|null the currently active replica connection. `null` is returned if no replica connections
1250
     * are available and `$fallbackToPrimary` is false.
1251
     * @deprecated since 2.0.36. Use [[getReplica()]] instead.
1252
     */
1253
    public function getSlave($fallbackToPrimary = true)
1254
    {
1255
        return $this->getReplica($fallbackToPrimary);
1256
    }
1257
1258
1259
    /**
1260
     * Returns the currently active primary connection.
1261
     * If this method is called for the first time, it will try to open a primary connection.
1262
     * @return Connection|null the currently active primary connection. `null` is returned if no primary connection
1263
     * is available.
1264
     * @since 2.0.36
1265
     */
1266 11
    public function getPrimary()
1267
    {
1268 11
        if ($this->_primary === false) {
1269 11
            $this->_primary = $this->shuffleMasters
0 ignored issues
show
Deprecated Code introduced by
The property yii\db\Connection::$shuffleMasters has been deprecated: since 2.0.36. Use [[shufflePrimaries]] instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1269
            $this->_primary = /** @scrutinizer ignore-deprecated */ $this->shuffleMasters

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1270 2
                ? $this->openFromPool($this->masters, $this->masterConfig)
0 ignored issues
show
Deprecated Code introduced by
The property yii\db\Connection::$masters has been deprecated: since 2.0.36. Use [[primaries]] instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1270
                ? $this->openFromPool(/** @scrutinizer ignore-deprecated */ $this->masters, $this->masterConfig)

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
Deprecated Code introduced by
The property yii\db\Connection::$masterConfig has been deprecated: since 2.0.36. Use [[primaryConfig]] instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1270
                ? $this->openFromPool($this->masters, /** @scrutinizer ignore-deprecated */ $this->masterConfig)

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1271 9
                : $this->openFromPoolSequentially($this->masters, $this->masterConfig);
0 ignored issues
show
Deprecated Code introduced by
The property yii\db\Connection::$masterConfig has been deprecated: since 2.0.36. Use [[primaryConfig]] instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1271
                : $this->openFromPoolSequentially($this->masters, /** @scrutinizer ignore-deprecated */ $this->masterConfig);

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
Deprecated Code introduced by
The property yii\db\Connection::$masters has been deprecated: since 2.0.36. Use [[primaries]] instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1271
                : $this->openFromPoolSequentially(/** @scrutinizer ignore-deprecated */ $this->masters, $this->masterConfig);

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1272
        }
1273
1274 11
        return $this->_primary;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->_primary also could return the type true which is incompatible with the documented return type null|yii\db\Connection.
Loading history...
1275
    }
1276
1277
    /**
1278
     * Returns the currently active primary connection.
1279
     * If this method is called for the first time, it will try to open a primary connection.
1280
     * @return Connection|null the currently active primary connection. `null` is returned if no primary connection
1281
     * is available.
1282
     * @since 2.0.11
1283
     * @deprecated since 2.0.36. Use [[getPrimary()]] instead.
1284
     */
1285
    public function getMaster()
1286
    {
1287
        return $this->getPrimary();
1288
    }
1289
1290
    /**
1291
     * Executes the provided callback by using the primary connection.
1292
     *
1293
     * This method is provided so that you can temporarily force using the primary connection to perform
1294
     * DB operations even if they are read queries. For example,
1295
     *
1296
     * ```php
1297
     * $result = $db->usePrimary(function ($db) {
1298
     *     return $db->createCommand('SELECT * FROM user LIMIT 1')->queryOne();
1299
     * });
1300
     * ```
1301
     *
1302
     * @param callable $callback a PHP callable to be executed by this method. Its signature is
1303
     * `function (Connection $db)`. Its return value will be returned by this method.
1304
     * @return mixed the return value of the callback
1305
     * @throws \Exception|\Throwable if there is any exception thrown from the callback
1306
     * @since 2.0.36
1307
     */
1308 100
    public function usePrimary(callable $callback)
1309
    {
1310 100
        if ($this->enableSlaves) {
0 ignored issues
show
Deprecated Code introduced by
The property yii\db\Connection::$enableSlaves has been deprecated: since 2.0.36. Use [[enableReplicas]] instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1310
        if (/** @scrutinizer ignore-deprecated */ $this->enableSlaves) {

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1311 100
            $this->enableSlaves = false;
0 ignored issues
show
Deprecated Code introduced by
The property yii\db\Connection::$enableSlaves has been deprecated: since 2.0.36. Use [[enableReplicas]] instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1311
            /** @scrutinizer ignore-deprecated */ $this->enableSlaves = false;

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1312
            try {
1313 100
                $result = call_user_func($callback, $this);
1314 4
            } catch (\Exception $e) {
1315 4
                $this->enableSlaves = true;
0 ignored issues
show
Deprecated Code introduced by
The property yii\db\Connection::$enableSlaves has been deprecated: since 2.0.36. Use [[enableReplicas]] instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1315
                /** @scrutinizer ignore-deprecated */ $this->enableSlaves = true;

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1316 4
                throw $e;
1317
            } catch (\Throwable $e) {
1318
                $this->enableSlaves = true;
0 ignored issues
show
Deprecated Code introduced by
The property yii\db\Connection::$enableSlaves has been deprecated: since 2.0.36. Use [[enableReplicas]] instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1318
                /** @scrutinizer ignore-deprecated */ $this->enableSlaves = true;

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1319
                throw $e;
1320
            }
1321
            // TODO: use "finally" keyword when miminum required PHP version is >= 5.5
1322 96
            $this->enableSlaves = true;
0 ignored issues
show
Deprecated Code introduced by
The property yii\db\Connection::$enableSlaves has been deprecated: since 2.0.36. Use [[enableReplicas]] instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

1322
            /** @scrutinizer ignore-deprecated */ $this->enableSlaves = true;

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
1323
        } else {
1324
            $result = call_user_func($callback, $this);
1325
        }
1326
1327 96
        return $result;
1328
    }
1329
1330
    /**
1331
     * Executes the provided callback by using the primary connection.
1332
     *
1333
     * This method is provided so that you can temporarily force using the primary connection to perform
1334
     * DB operations even if they are read queries. For example,
1335
     *
1336
     * ```php
1337
     * $result = $db->usePrimary(function ($db) {
1338
     *     return $db->createCommand('SELECT * FROM user LIMIT 1')->queryOne();
1339
     * });
1340
     * ```
1341
     *
1342
     * @param callable $callback a PHP callable to be executed by this method. Its signature is
1343
     * `function (Connection $db)`. Its return value will be returned by this method.
1344
     * @return mixed the return value of the callback
1345
     * @throws \Exception|\Throwable if there is any exception thrown from the callback
1346
     * @deprecated since 2.0.36. Use [[usePrimary()]] instead.
1347
     */
1348
    public function useMaster(callable $callback)
1349
    {
1350
        return $this->usePrimary($callback);
1351
    }
1352
1353
    /**
1354
     * Opens the connection to a server in the pool.
1355
     *
1356
     * This method implements load balancing and failover among the given list of the servers.
1357
     * Connections will be tried in random order.
1358
     * For details about the failover behavior, see [[openFromPoolSequentially]].
1359
     *
1360
     * @param array $pool the list of connection configurations in the server pool
1361
     * @param array $sharedConfig the configuration common to those given in `$pool`.
1362
     * @return Connection the opened DB connection, or `null` if no server is available
1363
     * @throws InvalidConfigException if a configuration does not specify "dsn"
1364
     * @see openFromPoolSequentially
1365
     */
1366 1651
    protected function openFromPool(array $pool, array $sharedConfig)
1367
    {
1368 1651
        shuffle($pool);
1369 1651
        return $this->openFromPoolSequentially($pool, $sharedConfig);
1370
    }
1371
1372
    /**
1373
     * Opens the connection to a server in the pool.
1374
     *
1375
     * This method implements failover among the given list of servers.
1376
     * Connections will be tried in sequential order. The first successful connection will return.
1377
     *
1378
     * If [[serverStatusCache]] is configured, this method will cache information about
1379
     * unreachable servers and does not try to connect to these for the time configured in [[serverRetryInterval]].
1380
     * This helps to keep the application stable when some servers are unavailable. Avoiding
1381
     * connection attempts to unavailable servers saves time when the connection attempts fail due to timeout.
1382
     *
1383
     * If none of the servers are available the status cache is ignored and connection attempts are made to all
1384
     * servers (Since version 2.0.35). This is to avoid downtime when all servers are unavailable for a short time.
1385
     * After a successful connection attempt the server is marked as avaiable again.
1386
     *
1387
     * @param array $pool the list of connection configurations in the server pool
1388
     * @param array $sharedConfig the configuration common to those given in `$pool`.
1389
     * @return Connection the opened DB connection, or `null` if no server is available
1390
     * @throws InvalidConfigException if a configuration does not specify "dsn"
1391
     * @since 2.0.11
1392
     * @see openFromPool
1393
     * @see serverStatusCache
1394
     */
1395 1659
    protected function openFromPoolSequentially(array $pool, array $sharedConfig)
1396
    {
1397 1659
        if (empty($pool)) {
1398 1639
            return null;
1399
        }
1400
1401 21
        if (!isset($sharedConfig['class'])) {
1402 21
            $sharedConfig['class'] = get_class($this);
1403
        }
1404
1405 21
        $cache = is_string($this->serverStatusCache) ? Yii::$app->get($this->serverStatusCache, false) : $this->serverStatusCache;
1406
1407 21
        foreach ($pool as $i => $config) {
1408 21
            $pool[$i] = $config = array_merge($sharedConfig, $config);
1409 21
            if (empty($config['dsn'])) {
1410 6
                throw new InvalidConfigException('The "dsn" option must be specified.');
1411
            }
1412
1413 15
            $key = [__METHOD__, $config['dsn']];
1414 15
            if ($cache instanceof CacheInterface && $cache->get($key)) {
1415
                // should not try this dead server now
1416
                continue;
1417
            }
1418
1419
            /* @var $db Connection */
1420 15
            $db = Yii::createObject($config);
1421
1422
            try {
1423 15
                $db->open();
1424 15
                return $db;
1425 8
            } catch (\Exception $e) {
1426 8
                Yii::warning("Connection ({$config['dsn']}) failed: " . $e->getMessage(), __METHOD__);
1427 8
                if ($cache instanceof CacheInterface) {
1428
                    // mark this server as dead and only retry it after the specified interval
1429 4
                    $cache->set($key, 1, $this->serverRetryInterval);
1430
                }
1431
                // exclude server from retry below
1432 8
                unset($pool[$i]);
1433
            }
1434
        }
1435
1436 8
        if ($cache instanceof CacheInterface) {
1437
            // if server status cache is enabled and no server is available
1438
            // ignore the cache and try to connect anyway
1439
            // $pool now only contains servers we did not already try in the loop above
1440 4
            foreach ($pool as $config) {
1441
1442
                /* @var $db Connection */
1443
                $db = Yii::createObject($config);
1444
                try {
1445
                    $db->open();
1446
                } catch (\Exception $e) {
1447
                    Yii::warning("Connection ({$config['dsn']}) failed: " . $e->getMessage(), __METHOD__);
1448
                    continue;
1449
                }
1450
1451
                // mark this server as available again after successful connection
1452
                $cache->delete([__METHOD__, $config['dsn']]);
1453
1454
                return $db;
1455
            }
1456
        }
1457
1458 8
        return null;
1459
    }
1460
1461
    /**
1462
     * Close the connection before serializing.
1463
     * @return array
1464
     */
1465 19
    public function __sleep()
1466
    {
1467 19
        $fields = (array) $this;
1468
1469 19
        unset($fields['pdo']);
1470 19
        unset($fields["\000" . __CLASS__ . "\000" . '_primary']);
1471 19
        unset($fields["\000" . __CLASS__ . "\000" . '_replica']);
1472 19
        unset($fields["\000" . __CLASS__ . "\000" . '_transaction']);
1473 19
        unset($fields["\000" . __CLASS__ . "\000" . '_schema']);
1474
1475 19
        return array_keys($fields);
1476
    }
1477
1478
    /**
1479
     * Reset the connection after cloning.
1480
     */
1481 7
    public function __clone()
1482
    {
1483 7
        parent::__clone();
1484
1485 7
        $this->_primary = false;
1486 7
        $this->_replica = false;
1487 7
        $this->_schema = null;
1488 7
        $this->_transaction = null;
1489 7
        if (strncmp($this->dsn, 'sqlite::memory:', 15) !== 0) {
1490
            // reset PDO connection, unless its sqlite in-memory, which can only have one connection
1491 6
            $this->pdo = null;
1492
        }
1493 7
    }
1494
}
1495