Completed
Push — master ( 594ce5...8355c2 )
by Andrii
02:35
created

Connection::initConnection()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 3.072

Importance

Changes 0
Metric Value
dl 0
loc 8
ccs 4
cts 5
cp 0.8
rs 10
c 0
b 0
f 0
cc 3
nc 2
nop 0
crap 3.072
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\base\Component;
12
use yii\exceptions\InvalidConfigException;
13
use yii\exceptions\NotSupportedException;
14
use yii\caching\CacheInterface;
15
use yii\helpers\Yii;
16
17
/**
18
 * Connection represents a connection to a database via [PDO](http://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](http://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 [[masters]] and [[slaves]]. It will do load balancing and failover by choosing
26
 * appropriate servers. It will also automatically direct read operations to the slaves and write operations to
27
 * the masters.
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::class,
105
 *         'dsn' => 'mysql:host=127.0.0.1;dbname=demo;charset=utf8',
106
 *         'username' => 'root',
107
 *         'password' => '',
108
 *     ],
109
 * ],
110
 * ```
111
 *
112
 * The [[dsn]] property can be defined via configuration array:
113
 *
114
 * ```php
115
 * 'components' => [
116
 *     'db' => [
117
 *         '__class' => \yii\db\Connection::class,
118
 *         'dsn' => [
119
 *             'driver' => 'mysql',
120
 *             'host' => '127.0.0.1',
121
 *             'dbname' => 'demo',
122
 *             'charset' => 'utf8',
123
 *          ],
124
 *         'username' => 'root',
125
 *         'password' => '',
126
 *     ],
127
 * ],
128
 * ```
129
 *
130
 * @property string $driverName Name of the DB driver.
131
 * @property bool $isActive Whether the DB connection is established. This property is read-only.
132
 * @property string $lastInsertID The row ID of the last row inserted, or the last value retrieved from the
133
 * sequence object. This property is read-only.
134
 * @property Connection $master The currently active master connection. `null` is returned if there is no
135
 * master available. This property is read-only.
136
 * @property PDO $masterPdo The PDO instance for the currently active master connection. This property is
137
 * read-only.
138
 * @property QueryBuilder $queryBuilder The query builder for the current DB connection. Note that the type of
139
 * this property differs in getter and setter. See [[getQueryBuilder()]] and [[setQueryBuilder()]] for details.
140
 * @property Schema $schema The schema information for the database opened by this connection. This property
141
 * is read-only.
142
 * @property string $serverVersion Server version as a string. This property is read-only.
143
 * @property Connection $slave The currently active slave connection. `null` is returned if there is no slave
144
 * available and `$fallbackToMaster` is false. This property is read-only.
145
 * @property PDO $slavePdo The PDO instance for the currently active slave connection. `null` is returned if
146
 * no slave connection is available and `$fallbackToMaster` is false. This property is read-only.
147
 * @property Transaction|null $transaction The currently active transaction. Null if no active transaction.
148
 * This property is read-only.
149
 *
150
 * @author Qiang Xue <[email protected]>
151
 * @since 2.0
152
 */
153
class Connection extends Component implements ConnectionInterface
154
{
155
    /**
156
     * @event [[yii\base\Event|Event]] an event that is triggered after a DB connection is established
157
     */
158
    const EVENT_AFTER_OPEN = 'afterOpen';
159
    /**
160
     * @event [[yii\base\Event|Event]] an event that is triggered right before a top-level transaction is started
161
     */
162
    const EVENT_BEGIN_TRANSACTION = 'beginTransaction';
163
    /**
164
     * @event [[yii\base\Event|Event]] an event that is triggered right after a top-level transaction is committed
165
     */
166
    const EVENT_COMMIT_TRANSACTION = 'commitTransaction';
167
    /**
168
     * @event [[yii\base\Event|Event]] an event that is triggered right after a top-level transaction is rolled back
169
     */
170
    const EVENT_ROLLBACK_TRANSACTION = 'rollbackTransaction';
171
172
    /**
173
     * @var string|array the Data Source Name, or DSN, contains the information required to connect to the database.
174
     * Please refer to the [PHP manual](http://php.net/manual/en/pdo.construct.php) on
175
     * the format of the DSN string.
176
     *
177
     * For [SQLite](http://php.net/manual/en/ref.pdo-sqlite.connection.php) you may use a [path alias](guide:concept-aliases)
178
     * for specifying the database path, e.g. `sqlite:@app/data/db.sql`.
179
     *
180
     * Since version 3.0.0 an array can be passed to contruct a DSN string.
181
     * The `driver` array key is used as the driver prefix of the DSN,
182
     * all further key-value pairs are rendered as `key=value` and concatenated by `;`. For example:
183
     *
184
     * ```php
185
     * 'dsn' => [
186
     *     'driver' => 'mysql',
187
     *     'host' => '127.0.0.1',
188
     *     'dbname' => 'demo',
189
     *     'charset' => 'utf8',
190
     * ],
191
     * ```
192
     *
193
     * Will result in the DSN string `mysql:host=127.0.0.1;dbname=demo`.
194
     */
195
    public $dsn;
196
    /**
197
     * @var string the username for establishing DB connection. Defaults to `null` meaning no username to use.
198
     */
199
    public $username;
200
    /**
201
     * @var string the password for establishing DB connection. Defaults to `null` meaning no password to use.
202
     */
203
    public $password;
204
    /**
205
     * @var array PDO attributes (name => value) that should be set when calling [[open()]]
206
     * to establish a DB connection. Please refer to the
207
     * [PHP manual](http://php.net/manual/en/pdo.setattribute.php) for
208
     * details about available attributes.
209
     */
210
    public $attributes;
211
    /**
212
     * @var PDO the PHP PDO instance associated with this DB connection.
213
     * This property is mainly managed by [[open()]] and [[close()]] methods.
214
     * When a DB connection is active, this property will represent a PDO instance;
215
     * otherwise, it will be null.
216
     * @see pdoClass
217
     */
218
    public $pdo;
219
    /**
220
     * @var bool whether to enable schema caching.
221
     * Note that in order to enable truly schema caching, a valid cache component as specified
222
     * by [[schemaCache]] must be enabled and [[enableSchemaCache]] must be set true.
223
     * @see schemaCacheDuration
224
     * @see schemaCacheExclude
225
     * @see schemaCache
226
     */
227
    public $enableSchemaCache = false;
228
    /**
229
     * @var int number of seconds that table metadata can remain valid in cache.
230
     * Use 0 to indicate that the cached data will never expire.
231
     * @see enableSchemaCache
232
     */
233
    public $schemaCacheDuration = 3600;
234
    /**
235
     * @var array list of tables whose metadata should NOT be cached. Defaults to empty array.
236
     * The table names may contain schema prefix, if any. Do not quote the table names.
237
     * @see enableSchemaCache
238
     */
239
    public $schemaCacheExclude = [];
240
    /**
241
     * @var CacheInterface|string the cache object or the ID of the cache application component that
242
     * is used to cache the table metadata.
243
     * @see enableSchemaCache
244
     */
245
    public $schemaCache = 'cache';
246
    /**
247
     * @var bool whether to enable query caching.
248
     * Note that in order to enable query caching, a valid cache component as specified
249
     * by [[queryCache]] must be enabled and [[enableQueryCache]] must be set true.
250
     * Also, only the results of the queries enclosed within [[cache()]] will be cached.
251
     * @see queryCache
252
     * @see cache()
253
     * @see noCache()
254
     */
255
    public $enableQueryCache = true;
256
    /**
257
     * @var int the default number of seconds that query results can remain valid in cache.
258
     * Defaults to 3600, meaning 3600 seconds, or one hour. Use 0 to indicate that the cached data will never expire.
259
     * The value of this property will be used when [[cache()]] is called without a cache duration.
260
     * @see enableQueryCache
261
     * @see cache()
262
     */
263
    public $queryCacheDuration = 3600;
264
    /**
265
     * @var CacheInterface|string the cache object or the ID of the cache application component
266
     * that is used for query caching.
267
     * @see enableQueryCache
268
     */
269
    public $queryCache = 'cache';
270
271
    /**
272
     * @var bool whether to turn on prepare emulation. Defaults to false, meaning PDO
273
     * will use the native prepare support if available. For some databases (such as MySQL),
274
     * this may need to be set true so that PDO can emulate the prepare support to bypass
275
     * the buggy native prepare support.
276
     * The default value is null, which means the PDO ATTR_EMULATE_PREPARES value will not be changed.
277
     */
278
    public $emulatePrepare;
279
    /**
280
     * @var string the common prefix or suffix for table names. If a table name is given
281
     * as `{{%TableName}}`, then the percentage character `%` will be replaced with this
282
     * property value. For example, `{{%post}}` becomes `{{tbl_post}}`.
283
     */
284
    public $tablePrefix = '';
285
    /**
286
     * @var array mapping between PDO driver names and [[Schema]] classes.
287
     * The keys of the array are PDO driver names while the values are either the corresponding
288
     * schema class names or configurations. Please refer to [[Yii::createObject()]] for
289
     * details on how to specify a configuration.
290
     *
291
     * This property is mainly used by [[getSchema()]] when fetching the database schema information.
292
     * You normally do not need to set this property unless you want to use your own
293
     * [[Schema]] class to support DBMS that is not supported by Yii.
294
     */
295
    public $schemaMap = [
296
        'pgsql' => pgsql\Schema::class, // PostgreSQL
297
        'mysqli' => mysql\Schema::class, // MySQL
298
        'mysql' => mysql\Schema::class, // MySQL
299
        'sqlite' => sqlite\Schema::class, // sqlite 3
300
        'sqlite2' => sqlite\Schema::class, // sqlite 2
301
    ];
302
    /**
303
     * @var string Custom PDO wrapper class. If not set, it will use [[PDO]] or [[\yii\db\mssql\PDO]] when MSSQL is used.
304
     * @see pdo
305
     */
306
    public $pdoClass;
307
    /**
308
     * @var array mapping between PDO driver names and [[Command]] classes.
309
     * The keys of the array are PDO driver names while the values are either the corresponding
310
     * command class names or configurations. Please refer to [[Yii::createObject()]] for
311
     * details on how to specify a configuration.
312
     *
313
     * This property is mainly used by [[createCommand()]] to create new database [[Command]] objects.
314
     * You normally do not need to set this property unless you want to use your own
315
     * [[Command]] class or support DBMS that is not supported by Yii.
316
     * @since 2.0.14
317
     */
318
    public $commandMap = [
319
        'pgsql' => 'yii\db\Command', // PostgreSQL
320
        'mysqli' => 'yii\db\Command', // MySQL
321
        'mysql' => 'yii\db\Command', // MySQL
322
        'sqlite' => 'yii\db\sqlite\Command', // sqlite 3
323
        'sqlite2' => 'yii\db\sqlite\Command', // sqlite 2
324
        'sqlsrv' => 'yii\db\Command', // newer MSSQL driver on MS Windows hosts
325
        'oci' => 'yii\db\Command', // Oracle driver
326
        'mssql' => 'yii\db\Command', // older MSSQL driver on MS Windows hosts
327
        'dblib' => 'yii\db\Command', // dblib drivers on GNU/Linux (and maybe other OSes) hosts
328
    ];
329
    /**
330
     * @var bool whether to enable [savepoint](http://en.wikipedia.org/wiki/Savepoint).
331
     * Note that if the underlying DBMS does not support savepoint, setting this property to be true will have no effect.
332
     */
333
    public $enableSavepoint = true;
334
    /**
335
     * @var CacheInterface|string|false the cache object or the ID of the cache application component that is used to store
336
     * the health status of the DB servers specified in [[masters]] and [[slaves]].
337
     * This is used only when read/write splitting is enabled or [[masters]] is not empty.
338
     * Set boolean `false` to disabled server status caching.
339
     */
340
    public $serverStatusCache = 'cache';
341
    /**
342
     * @var int the retry interval in seconds for dead servers listed in [[masters]] and [[slaves]].
343
     * This is used together with [[serverStatusCache]].
344
     */
345
    public $serverRetryInterval = 600;
346
    /**
347
     * @var bool whether to enable read/write splitting by using [[slaves]] to read data.
348
     * Note that if [[slaves]] is empty, read/write splitting will NOT be enabled no matter what value this property takes.
349
     */
350
    public $enableSlaves = true;
351
    /**
352
     * @var array list of slave connection configurations. Each configuration is used to create a slave DB connection.
353
     * When [[enableSlaves]] is true, one of these configurations will be chosen and used to create a DB connection
354
     * for performing read queries only.
355
     * @see enableSlaves
356
     * @see slaveConfig
357
     */
358
    public $slaves = [];
359
    /**
360
     * @var array the configuration that should be merged with every slave configuration listed in [[slaves]].
361
     * For example,
362
     *
363
     * ```php
364
     * [
365
     *     'username' => 'slave',
366
     *     'password' => 'slave',
367
     *     'attributes' => [
368
     *         // use a smaller connection timeout
369
     *         PDO::ATTR_TIMEOUT => 10,
370
     *     ],
371
     * ]
372
     * ```
373
     */
374
    public $slaveConfig = [];
375
    /**
376
     * @var array list of master connection configurations. Each configuration is used to create a master DB connection.
377
     * When [[open()]] is called, one of these configurations will be chosen and used to create a DB connection
378
     * which will be used by this object.
379
     * Note that when this property is not empty, the connection setting (e.g. "dsn", "username") of this object will
380
     * be ignored.
381
     * @see masterConfig
382
     * @see shuffleMasters
383
     */
384
    public $masters = [];
385
    /**
386
     * @var array the configuration that should be merged with every master configuration listed in [[masters]].
387
     * For example,
388
     *
389
     * ```php
390
     * [
391
     *     'username' => 'master',
392
     *     'password' => 'master',
393
     *     'attributes' => [
394
     *         // use a smaller connection timeout
395
     *         PDO::ATTR_TIMEOUT => 10,
396
     *     ],
397
     * ]
398
     * ```
399
     */
400
    public $masterConfig = [];
401
    /**
402
     * @var bool whether to shuffle [[masters]] before getting one.
403
     * @since 2.0.11
404
     * @see masters
405
     */
406
    public $shuffleMasters = true;
407
    /**
408
     * @var bool whether to enable logging of database queries. Defaults to true.
409
     * You may want to disable this option in a production environment to gain performance
410
     * if you do not need the information being logged.
411
     * @since 2.0.12
412
     * @see enableProfiling
413
     */
414
    public $enableLogging = true;
415
    /**
416
     * @var bool whether to enable profiling of opening database connection and database queries. Defaults to true.
417
     * You may want to disable this option in a production environment to gain performance
418
     * if you do not need the information being logged.
419
     * @since 2.0.12
420
     * @see enableLogging
421
     */
422
    public $enableProfiling = true;
423
424
    /**
425
     * @var Transaction the currently active transaction
426
     */
427
    private $_transaction;
428
    /**
429
     * @var Schema the database schema
430
     */
431
    private $_schema;
432
    /**
433
     * @var string driver name
434
     */
435
    private $_driverName;
436
    /**
437
     * @var Connection|false the currently active master connection
438
     */
439
    private $_master = false;
440
    /**
441
     * @var Connection|false the currently active slave connection
442
     */
443
    private $_slave = false;
444
    /**
445
     * @var array query cache parameters for the [[cache()]] calls
446
     */
447
    private $_queryCacheInfo = [];
448
449
450
    /**
451
    * {@inheritdoc}
452
    */
453
    public function init()
454
    {
455
       if (is_array($this->dsn)) {
456
           $this->dsn = $this->buildDSN($this->dsn);
457
       }
458
       parent::init();
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class yii\base\Component as the method init() does only exist in the following sub-classes of yii\base\Component: yii\base\Application, yii\console\Application, yii\console\widgets\Table, yii\db\Connection, yii\db\Migration, yii\db\data\SqlDataProvider, yii\i18n\Translator, yii\mutex\DbMutex, yii\mutex\FileMutex, yii\mutex\Mutex, yii\mutex\MysqlMutex, yii\mutex\PgsqlMutex, yii\profile\Profiler, yii\test\ActiveFixture, yii\test\BaseActiveFixture, yii\test\DbFixture, yii\test\InitDbFixture, yii\tests\data\validators\TestValidator, yii\tests\framework\beha...RecordAttributeTypecast, yii\tests\framework\beha...s\ActiveRecordBlameable, yii\tests\framework\beha...WithDefaultValueClosure, yii\tests\framework\beha...ActiveRecordLockVersion, yii\tests\framework\behaviors\ActiveRecordRelated, yii\tests\framework\beha...s\ActiveRecordSluggable, yii\tests\framework\beha...veRecordSluggableUnique, yii\tests\framework\beha...s\ActiveRecordTimestamp, yii\tests\framework\beha...rdWithAttributeBehavior, yii\tests\framework\beha...dWithAttributesBehavior, yii\tests\framework\beha...tySluggableActiveRecord, yii\tests\framework\helpers\HtmlTestModel, yii\tests\framework\helpers\JsonModel, yii\tests\framework\helpers\TestConsoleModel, yii\tests\framework\test\AnimalFixture, yii\tests\framework\test\CustomDirectoryFixture, yii\tests\framework\test\CustomerFixture, yii\tests\framework\test\ProfileFixture, yii\validators\BooleanValidator, yii\validators\CompareValidator, yii\validators\DateValidator, yii\validators\DefaultValueValidator, yii\validators\EachValidator, yii\validators\EmailValidator, yii\validators\ExistValidator, yii\validators\FileValidator, yii\validators\FilterValidator, yii\validators\ImageValidator, yii\validators\InlineValidator, yii\validators\IpValidator, yii\validators\NumberValidator, yii\validators\RangeValidator, yii\validators\RegularExpressionValidator, yii\validators\RequiredValidator, yii\validators\SafeValidator, yii\validators\StringValidator, yii\validators\UniqueValidator, yii\validators\UrlValidator, yii\validators\Validator, yii\web\Application, yii\web\CacheSession, yii\web\DbSession, yii\web\UrlManager, yii\web\User, yii\web\View. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
459
    }
460
461
    /**
462
     * Returns a value indicating whether the DB connection is established.
463
     * @return bool whether the DB connection is established
464
     */
465
    public function getIsActive()
466
    {
467
        return $this->pdo !== null;
468
    }
469
470
    /**
471
     * Uses query cache for the queries performed with the callable.
472
     *
473
     * When query caching is enabled ([[enableQueryCache]] is true and [[queryCache]] refers to a valid cache),
474
     * queries performed within the callable will be cached and their results will be fetched from cache if available.
475
     * For example,
476
     *
477
     * ```php
478
     * // The customer will be fetched from cache if available.
479
     * // If not, the query will be made against DB and cached for use next time.
480
     * $customer = $db->cache(function (Connection $db) {
481
     *     return $db->createCommand('SELECT * FROM customer WHERE id=1')->queryOne();
482
     * });
483
     * ```
484
     *
485
     * Note that query cache is only meaningful for queries that return results. For queries performed with
486
     * [[Command::execute()]], query cache will not be used.
487
     *
488
     * @param callable $callable a PHP callable that contains DB queries which will make use of query cache.
489
     * The signature of the callable is `function (Connection $db)`.
490
     * @param int $duration the number of seconds that query results can remain valid in the cache. If this is
491
     * not set, the value of [[queryCacheDuration]] will be used instead.
492
     * Use 0 to indicate that the cached data will never expire.
493
     * @param \yii\caching\Dependency $dependency the cache dependency associated with the cached query results.
494
     * @return mixed the return result of the callable
495
     * @throws \Throwable if there is any exception during query
496
     * @see enableQueryCache
497
     * @see queryCache
498
     * @see noCache()
499
     */
500
    public function cache(callable $callable, $duration = null, $dependency = null)
501
    {
502
        $this->_queryCacheInfo[] = [$duration === null ? $this->queryCacheDuration : $duration, $dependency];
503
        try {
504
            $result = call_user_func($callable, $this);
505
            array_pop($this->_queryCacheInfo);
506
            return $result;
507
        } catch (\Throwable $e) {
0 ignored issues
show
Bug introduced by
The class Throwable does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
508
            array_pop($this->_queryCacheInfo);
509
            throw $e;
510
        }
511
    }
512
513
    /**
514
     * Disables query cache temporarily.
515
     *
516
     * Queries performed within the callable will not use query cache at all. For example,
517
     *
518
     * ```php
519
     * $db->cache(function (Connection $db) {
520
     *
521
     *     // ... queries that use query cache ...
522
     *
523
     *     return $db->noCache(function (Connection $db) {
524
     *         // this query will not use query cache
525
     *         return $db->createCommand('SELECT * FROM customer WHERE id=1')->queryOne();
526
     *     });
527
     * });
528
     * ```
529
     *
530
     * @param callable $callable a PHP callable that contains DB queries which should not use query cache.
531
     * The signature of the callable is `function (Connection $db)`.
532
     * @return mixed the return result of the callable
533
     * @throws \Throwable if there is any exception during query
534
     * @see enableQueryCache
535
     * @see queryCache
536
     * @see cache()
537
     */
538
    public function noCache(callable $callable)
539
    {
540
        $this->_queryCacheInfo[] = false;
541
        try {
542
            $result = call_user_func($callable, $this);
543
            array_pop($this->_queryCacheInfo);
544
            return $result;
545
        } catch (\Throwable $e) {
0 ignored issues
show
Bug introduced by
The class Throwable does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
546
            array_pop($this->_queryCacheInfo);
547
            throw $e;
548
        }
549
    }
550
551
    /**
552
     * Returns the current query cache information.
553
     * This method is used internally by [[Command]].
554
     * @param int $duration the preferred caching duration. If null, it will be ignored.
555
     * @param \yii\caching\Dependency $dependency the preferred caching dependency. If null, it will be ignored.
556
     * @return array the current query cache information, or null if query cache is not enabled.
557
     * @internal
558
     */
559 3
    public function getQueryCacheInfo($duration, $dependency)
560
    {
561 3
        if (!$this->enableQueryCache) {
562
            return null;
563
        }
564
565 3
        $info = end($this->_queryCacheInfo);
566 3
        if (is_array($info)) {
567
            if ($duration === null) {
568
                $duration = $info[0];
569
            }
570
            if ($dependency === null) {
571
                $dependency = $info[1];
572
            }
573
        }
574
575 3
        if ($duration === 0 || $duration > 0) {
576
            if (is_string($this->queryCache) && Yii::getApp()) {
0 ignored issues
show
Deprecated Code introduced by
The method yii\helpers\BaseYii::getApp() has been deprecated with message: 3.0.0 Use DI instead.

This method 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 method will be removed from the class and what other method or class to use instead.

Loading history...
577
                $cache = Yii::getApp()->get($this->queryCache, false);
0 ignored issues
show
Deprecated Code introduced by
The method yii\helpers\BaseYii::getApp() has been deprecated with message: 3.0.0 Use DI instead.

This method 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 method will be removed from the class and what other method or class to use instead.

Loading history...
578
            } else {
579
                $cache = $this->queryCache;
580
            }
581
            if ($cache instanceof CacheInterface) {
0 ignored issues
show
Bug introduced by
The class yii\caching\CacheInterface does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
582
                return [$cache, $duration, $dependency];
583
            }
584
        }
585
586 3
        return null;
587
    }
588
589
    /**
590
     * Establishes a DB connection.
591
     * It does nothing if a DB connection has already been established.
592
     * @throws Exception if connection fails
593
     */
594 3
    public function open()
595
    {
596 3
        if ($this->pdo !== null) {
597 3
            return;
598
        }
599
600 3
        if (!empty($this->masters)) {
601
            $db = $this->getMaster();
602
            if ($db !== null) {
603
                $this->pdo = $db->pdo;
604
                return;
605
            }
606
607
            throw new InvalidConfigException('None of the master DB servers is available.');
608
        }
609
610 3
        if (empty($this->dsn)) {
611
            throw new InvalidConfigException('Connection::dsn cannot be empty.');
612
        }
613
614 3
        $token = 'Opening DB connection: ' . $this->dsn;
615 3
        $enableProfiling = $this->enableProfiling;
616
        try {
617 3
            Yii::info($token, __METHOD__);
618 3
            if ($enableProfiling) {
619 3
                Yii::beginProfile($token, __METHOD__);
620
            }
621
622 3
            $this->pdo = $this->createPdoInstance();
623 3
            $this->initConnection();
624
625 3
            if ($enableProfiling) {
626 3
                Yii::endProfile($token, __METHOD__);
627
            }
628
        } catch (\PDOException $e) {
629
            if ($enableProfiling) {
630
                Yii::endProfile($token, __METHOD__);
631
            }
632
633
            throw new Exception($e->getMessage(), $e->errorInfo, (int) $e->getCode(), $e);
634
        }
635
    }
636
637
    /**
638
     * Closes the currently active DB connection.
639
     * It does nothing if the connection is already closed.
640
     */
641 3
    public function close()
642
    {
643 3
        if ($this->_master) {
644
            if ($this->pdo === $this->_master->pdo) {
645
                $this->pdo = null;
646
            }
647
648
            $this->_master->close();
649
            $this->_master = false;
650
        }
651
652 3
        if ($this->pdo !== null) {
653 3
            Yii::debug('Closing DB connection: ' . $this->dsn, __METHOD__);
654 3
            $this->pdo = null;
655 3
            $this->_schema = null;
656 3
            $this->_transaction = null;
657
        }
658
659 3
        if ($this->_slave) {
660
            $this->_slave->close();
661
            $this->_slave = false;
662
        }
663
    }
664
665
    /**
666
     * Creates the PDO instance.
667
     * This method is called by [[open]] to establish a DB connection.
668
     * The default implementation will create a PHP PDO instance.
669
     * You may override this method if the default PDO needs to be adapted for certain DBMS.
670
     * @return PDO the pdo instance
671
     */
672 3
    protected function createPdoInstance()
673
    {
674 3
        $pdoClass = $this->pdoClass;
675 3
        if ($pdoClass === null) {
676 3
            $pdoClass = 'PDO';
677 3
            if ($this->_driverName !== null) {
678
                $driver = $this->_driverName;
679 3
            } elseif (($pos = strpos($this->dsn, ':')) !== false) {
680 3
                $driver = strtolower(substr($this->dsn, 0, $pos));
681
            }
682 3
            if (isset($driver)) {
683 3
                if ($driver === 'mssql' || $driver === 'dblib') {
684
                    $pdoClass = mssql\PDO::class;
685 3
                } elseif ($driver === 'sqlsrv') {
686
                    $pdoClass = mssql\SqlsrvPDO::class;
687
                }
688
            }
689
        }
690
691 3
        $dsn = $this->dsn;
692 3
        if (strncmp('sqlite:@', $dsn, 8) === 0) {
693
            $dsn = 'sqlite:' . Yii::getAlias(substr($dsn, 7));
0 ignored issues
show
Deprecated Code introduced by
The method yii\helpers\BaseYii::getAlias() has been deprecated with message: 3.0.0 Use [[yii\base\Application::get()|Application::get()]] instead

This method 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 method will be removed from the class and what other method or class to use instead.

Loading history...
694
        }
695
696 3
        return new $pdoClass($dsn, $this->username, $this->password, $this->attributes);
697
    }
698
699
    /**
700
     * Initializes the DB connection.
701
     * This method is invoked right after the DB connection is established.
702
     * The default implementation turns on `PDO::ATTR_EMULATE_PREPARES`
703
     * if [[emulatePrepare]] is true.
704
     * It then triggers an [[EVENT_AFTER_OPEN]] event.
705
     */
706 3
    protected function initConnection()
707
    {
708 3
        $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
709 3
        if ($this->emulatePrepare !== null && constant('PDO::ATTR_EMULATE_PREPARES')) {
710
            $this->pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, $this->emulatePrepare);
711
        }
712 3
        $this->trigger(self::EVENT_AFTER_OPEN);
713
    }
714
715
    /**
716
     * Creates a command for execution.
717
     * @param string $sql the SQL statement to be executed
718
     * @param array $params the parameters to be bound to the SQL statement
719
     * @return Command the DB command
720
     */
721 3
    public function createCommand($sql = null, $params = [])
722
    {
723 3
        $driver = $this->getDriverName();
724 3
        $config = ['__class' => Command::class];
725 3
        if (isset($this->commandMap[$driver])) {
726 3
            $config = !is_array($this->commandMap[$driver]) ? ['__class' => $this->commandMap[$driver]] : $this->commandMap[$driver];
727
        }
728 3
        $config['db'] = $this;
729 3
        $config['sql'] = $sql;
730
        /** @var Command $command */
731 3
        $command = Yii::createObject($config);
732 3
        return $command->bindValues($params);
733
    }
734
735
    /**
736
     * Returns the currently active transaction.
737
     * @return Transaction|null the currently active transaction. Null if no active transaction.
738
     */
739 3
    public function getTransaction()
740
    {
741 3
        return $this->_transaction && $this->_transaction->getIsActive() ? $this->_transaction : null;
742
    }
743
744
    /**
745
     * Starts a transaction.
746
     * @param string|null $isolationLevel The isolation level to use for this transaction.
747
     * See [[Transaction::begin()]] for details.
748
     * @return Transaction the transaction initiated
749
     */
750
    public function beginTransaction($isolationLevel = null)
751
    {
752
        $this->open();
753
754
        if (($transaction = $this->getTransaction()) === null) {
755
            $transaction = $this->_transaction = new Transaction($this);
756
        }
757
        $transaction->begin($isolationLevel);
758
759
        return $transaction;
760
    }
761
762
    /**
763
     * Executes callback provided in a transaction.
764
     *
765
     * @param callable $callback a valid PHP callback that performs the job. Accepts connection instance as parameter.
766
     * @param string|null $isolationLevel The isolation level to use for this transaction.
767
     * See [[Transaction::begin()]] for details.
768
     * @throws \Throwable if there is any exception during query. In this case the transaction will be rolled back.
769
     * @return mixed result of callback function
770
     */
771
    public function transaction(callable $callback, $isolationLevel = null)
772
    {
773
        $transaction = $this->beginTransaction($isolationLevel);
774
        $level = $transaction->level;
775
776
        try {
777
            $result = call_user_func($callback, $this);
778
            if ($transaction->isActive && $transaction->level === $level) {
779
                $transaction->commit();
780
            }
781
        } catch (\Throwable $e) {
0 ignored issues
show
Bug introduced by
The class Throwable does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
782
            $this->rollbackTransactionOnLevel($transaction, $level);
783
            throw $e;
784
        }
785
786
        return $result;
787
    }
788
789
    /**
790
     * Rolls back given [[Transaction]] object if it's still active and level match.
791
     * In some cases rollback can fail, so this method is fail safe. Exception thrown
792
     * from rollback will be caught and just logged with [[\Yii::error()]].
793
     * @param Transaction $transaction Transaction object given from [[beginTransaction()]].
794
     * @param int $level Transaction level just after [[beginTransaction()]] call.
795
     */
796
    private function rollbackTransactionOnLevel($transaction, $level)
797
    {
798
        if ($transaction->isActive && $transaction->level === $level) {
799
            // https://github.com/yiisoft/yii2/pull/13347
800
            try {
801
                $transaction->rollBack();
802
            } catch (\Exception $e) {
803
                \Yii::error($e, __METHOD__);
804
                // hide this exception to be able to continue throwing original exception outside
805
            }
806
        }
807
    }
808
809
    /**
810
     * Returns the schema information for the database opened by this connection.
811
     * @return Schema the schema information for the database opened by this connection.
812
     * @throws NotSupportedException if there is no support for the current driver type
813
     */
814 3
    public function getSchema()
815
    {
816 3
        if ($this->_schema !== null) {
817 3
            return $this->_schema;
818
        }
819
820 3
        $driver = $this->getDriverName();
821 3
        if (isset($this->schemaMap[$driver])) {
822 3
            $config = !is_array($this->schemaMap[$driver]) ? ['__class' => $this->schemaMap[$driver]] : $this->schemaMap[$driver];
823 3
            $config['db'] = $this;
824
825 3
            return $this->_schema = Yii::createObject($config);
826
        }
827
828
        throw new NotSupportedException("Connection does not support reading schema information for '$driver' DBMS.");
829
    }
830
831
    /**
832
     * Returns the query builder for the current DB connection.
833
     * @return QueryBuilder the query builder for the current DB connection.
834
     */
835 3
    public function getQueryBuilder()
836
    {
837 3
        return $this->getSchema()->getQueryBuilder();
838
    }
839
840
    /**
841
     * Can be used to set [[QueryBuilder]] configuration via Connection configuration array.
842
     *
843
     * @param iterable $config the [[QueryBuilder]] properties to be configured.
844
     * @since 2.0.14
845
     */
846
    public function setQueryBuilder(iterable $config)
847
    {
848
        $builder = $this->getQueryBuilder();
849
        foreach ($config as $key => $value) {
850
            $builder->{$key} = $value;
851
        }
852
    }
853
854
    /**
855
     * Obtains the schema information for the named table.
856
     * @param string $name table name.
857
     * @param bool $refresh whether to reload the table schema even if it is found in the cache.
858
     * @return TableSchema table schema information. Null if the named table does not exist.
859
     */
860
    public function getTableSchema($name, $refresh = false)
861
    {
862
        return $this->getSchema()->getTableSchema($name, $refresh);
863
    }
864
865
    /**
866
     * Returns the ID of the last inserted row or sequence value.
867
     * @param string $sequenceName name of the sequence object (required by some DBMS)
868
     * @return string the row ID of the last row inserted, or the last value retrieved from the sequence object
869
     * @see http://php.net/manual/en/pdo.lastinsertid.php
870
     */
871
    public function getLastInsertID($sequenceName = '')
872
    {
873
        return $this->getSchema()->getLastInsertID($sequenceName);
874
    }
875
876
    /**
877
     * Quotes a string value for use in a query.
878
     * Note that if the parameter is not a string, it will be returned without change.
879
     * @param string $value string to be quoted
880
     * @return string the properly quoted string
881
     * @see http://php.net/manual/en/pdo.quote.php
882
     */
883
    public function quoteValue($value)
884
    {
885
        return $this->getSchema()->quoteValue($value);
886
    }
887
888
    /**
889
     * Quotes a table name for use in a query.
890
     * If the table name contains schema prefix, the prefix will also be properly quoted.
891
     * If the table name is already quoted or contains special characters including '(', '[[' and '{{',
892
     * then this method will do nothing.
893
     * @param string $name table name
894
     * @return string the properly quoted table name
895
     */
896 3
    public function quoteTableName($name)
897
    {
898 3
        return $this->getSchema()->quoteTableName($name);
899
    }
900
901
    /**
902
     * Quotes a column name for use in a query.
903
     * If the column name contains prefix, the prefix will also be properly quoted.
904
     * If the column name is already quoted or contains special characters including '(', '[[' and '{{',
905
     * then this method will do nothing.
906
     * @param string $name column name
907
     * @return string the properly quoted column name
908
     */
909
    public function quoteColumnName($name)
910
    {
911
        return $this->getSchema()->quoteColumnName($name);
912
    }
913
914
    /**
915
     * Processes a SQL statement by quoting table and column names that are enclosed within double brackets.
916
     * Tokens enclosed within double curly brackets are treated as table names, while
917
     * tokens enclosed within double square brackets are column names. They will be quoted accordingly.
918
     * Also, the percentage character "%" at the beginning or ending of a table name will be replaced
919
     * with [[tablePrefix]].
920
     * @param string $sql the SQL to be quoted
921
     * @return string the quoted SQL
922
     */
923 3
    public function quoteSql($sql)
924
    {
925 3
        return preg_replace_callback(
926 3
            '/(\\{\\{(%?[\w\-\. ]+%?)\\}\\}|\\[\\[([\w\-\. ]+)\\]\\])/',
927
            function ($matches) {
928
                if (isset($matches[3])) {
929
                    return $this->quoteColumnName($matches[3]);
930
                }
931
932
                return str_replace('%', $this->tablePrefix, $this->quoteTableName($matches[2]));
933 3
            },
934 3
            $sql
935
        );
936
    }
937
938
    /**
939
     * Returns the name of the DB driver. Based on the the current [[dsn]], in case it was not set explicitly
940
     * by an end user.
941
     * @return string name of the DB driver
942
     */
943 3
    public function getDriverName()
944
    {
945 3
        if ($this->_driverName === null) {
946 3
            if (($pos = strpos($this->dsn, ':')) !== false) {
947 3
                $this->_driverName = strtolower(substr($this->dsn, 0, $pos));
948
            } else {
949
                $this->_driverName = strtolower($this->getSlavePdo()->getAttribute(PDO::ATTR_DRIVER_NAME));
950
            }
951
        }
952
953 3
        return $this->_driverName;
954
    }
955
956
    /**
957
     * Changes the current driver name.
958
     * @param string $driverName name of the DB driver
959
     */
960
    public function setDriverName($driverName)
961
    {
962
        $this->_driverName = strtolower($driverName);
963
    }
964
965
    /**
966
     * Returns a server version as a string comparable by [[\version_compare()]].
967
     * @return string server version as a string.
968
     * @since 2.0.14
969
     */
970
    public function getServerVersion()
971
    {
972
        return $this->getSchema()->getServerVersion();
973
    }
974
975
    /**
976
     * Returns the PDO instance for the currently active slave connection.
977
     * When [[enableSlaves]] is true, one of the slaves will be used for read queries, and its PDO instance
978
     * will be returned by this method.
979
     * @param bool $fallbackToMaster whether to return a master PDO in case none of the slave connections is available.
980
     * @return PDO the PDO instance for the currently active slave connection. `null` is returned if no slave connection
981
     * is available and `$fallbackToMaster` is false.
982
     */
983 3
    public function getSlavePdo($fallbackToMaster = true)
984
    {
985 3
        $db = $this->getSlave(false);
986 3
        if ($db === null) {
987 3
            return $fallbackToMaster ? $this->getMasterPdo() : null;
988
        }
989
990
        return $db->pdo;
991
    }
992
993
    /**
994
     * Returns the PDO instance for the currently active master connection.
995
     * This method will open the master DB connection and then return [[pdo]].
996
     * @return PDO the PDO instance for the currently active master connection.
997
     */
998 3
    public function getMasterPdo()
999
    {
1000 3
        $this->open();
1001 3
        return $this->pdo;
1002
    }
1003
1004
    /**
1005
     * Returns the currently active slave connection.
1006
     * If this method is called for the first time, it will try to open a slave connection when [[enableSlaves]] is true.
1007
     * @param bool $fallbackToMaster whether to return a master connection in case there is no slave connection available.
1008
     * @return Connection the currently active slave connection. `null` is returned if there is no slave available and
1009
     * `$fallbackToMaster` is false.
1010
     */
1011 3
    public function getSlave($fallbackToMaster = true)
1012
    {
1013 3
        if (!$this->enableSlaves) {
1014
            return $fallbackToMaster ? $this : null;
1015
        }
1016
1017 3
        if ($this->_slave === false) {
1018 3
            $this->_slave = $this->openFromPool($this->slaves, $this->slaveConfig);
1019
        }
1020
1021 3
        return $this->_slave === null && $fallbackToMaster ? $this : $this->_slave;
1022
    }
1023
1024
    /**
1025
     * Returns the currently active master connection.
1026
     * If this method is called for the first time, it will try to open a master connection.
1027
     * @return Connection the currently active master connection. `null` is returned if there is no master available.
1028
     * @since 2.0.11
1029
     */
1030
    public function getMaster()
1031
    {
1032
        if ($this->_master === false) {
1033
            $this->_master = $this->shuffleMasters
1034
                ? $this->openFromPool($this->masters, $this->masterConfig)
1035
                : $this->openFromPoolSequentially($this->masters, $this->masterConfig);
1036
        }
1037
1038
        return $this->_master;
1039
    }
1040
1041
    /**
1042
     * Executes the provided callback by using the master connection.
1043
     *
1044
     * This method is provided so that you can temporarily force using the master connection to perform
1045
     * DB operations even if they are read queries. For example,
1046
     *
1047
     * ```php
1048
     * $result = $db->useMaster(function ($db) {
1049
     *     return $db->createCommand('SELECT * FROM user LIMIT 1')->queryOne();
1050
     * });
1051
     * ```
1052
     *
1053
     * @param callable $callback a PHP callable to be executed by this method. Its signature is
1054
     * `function (Connection $db)`. Its return value will be returned by this method.
1055
     * @return mixed the return value of the callback
1056
     * @throws \Throwable if there is any exception thrown from the callback
1057
     */
1058
    public function useMaster(callable $callback)
1059
    {
1060
        if ($this->enableSlaves) {
1061
            $this->enableSlaves = false;
1062
            try {
1063
                $result = call_user_func($callback, $this);
1064
            } catch (\Throwable $e) {
0 ignored issues
show
Bug introduced by
The class Throwable does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
1065
                $this->enableSlaves = true;
1066
                throw $e;
1067
            }
1068
            // TODO: use "finally" keyword when miminum required PHP version is >= 5.5
1069
            $this->enableSlaves = true;
1070
        } else {
1071
            $result = call_user_func($callback, $this);
1072
        }
1073
1074
        return $result;
1075
    }
1076
1077
    /**
1078
     * Opens the connection to a server in the pool.
1079
     * This method implements the load balancing among the given list of the servers.
1080
     * Connections will be tried in random order.
1081
     * @param array $pool the list of connection configurations in the server pool
1082
     * @param array $sharedConfig the configuration common to those given in `$pool`.
1083
     * @return Connection the opened DB connection, or `null` if no server is available
1084
     * @throws InvalidConfigException if a configuration does not specify "dsn"
1085
     */
1086 3
    protected function openFromPool(array $pool, array $sharedConfig)
1087
    {
1088 3
        shuffle($pool);
1089 3
        return $this->openFromPoolSequentially($pool, $sharedConfig);
1090
    }
1091
1092
    /**
1093
     * Opens the connection to a server in the pool.
1094
     * This method implements the load balancing among the given list of the servers.
1095
     * Connections will be tried in sequential order.
1096
     * @param array $pool the list of connection configurations in the server pool
1097
     * @param array $sharedConfig the configuration common to those given in `$pool`.
1098
     * @return Connection the opened DB connection, or `null` if no server is available
1099
     * @throws InvalidConfigException if a configuration does not specify "dsn"
1100
     * @since 2.0.11
1101
     */
1102 3
    protected function openFromPoolSequentially(array $pool, array $sharedConfig)
1103
    {
1104 3
        if (empty($pool)) {
1105 3
            return null;
1106
        }
1107
1108
        if (!isset($sharedConfig['__class'])) {
1109
            $sharedConfig['__class'] = get_class($this);
1110
        }
1111
1112
        $cache = is_string($this->serverStatusCache) ? Yii::getApp()->get($this->serverStatusCache, false) : $this->serverStatusCache;
0 ignored issues
show
Deprecated Code introduced by
The method yii\helpers\BaseYii::getApp() has been deprecated with message: 3.0.0 Use DI instead.

This method 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 method will be removed from the class and what other method or class to use instead.

Loading history...
1113
1114
        foreach ($pool as $config) {
1115
            $config = array_merge($sharedConfig, $config);
1116
            if (empty($config['dsn'])) {
1117
                throw new InvalidConfigException('The "dsn" option must be specified.');
1118
            }
1119
1120
            $key = [__METHOD__, $config['dsn']];
1121
            if ($cache instanceof CacheInterface && $cache->get($key)) {
0 ignored issues
show
Bug introduced by
The class yii\caching\CacheInterface does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
1122
                // should not try this dead server now
1123
                continue;
1124
            }
1125
1126
            /* @var $db Connection */
1127
            $db = Yii::createObject($config);
1128
1129
            try {
1130
                $db->open();
1131
                return $db;
1132
            } catch (\Exception $e) {
1133
                Yii::warning("Connection ({$config['dsn']}) failed: " . $e->getMessage(), __METHOD__);
1134
                if ($cache instanceof CacheInterface) {
0 ignored issues
show
Bug introduced by
The class yii\caching\CacheInterface does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
1135
                    // mark this server as dead and only retry it after the specified interval
1136
                    $cache->set($key, 1, $this->serverRetryInterval);
1137
                }
1138
            }
1139
        }
1140
1141
        return null;
1142
    }
1143
1144
    /**
1145
     * Build the Data Source Name or DSN
1146
     * @param array $config the DSN configurations
1147
     * @return string the formated DSN
1148
     * @throws InvalidConfigException if 'driver' key was not defined
1149
     */
1150
    private function buildDSN(array $config)
1151
    {
1152
        if (isset($config['driver'])) {
1153
            $driver = $config['driver'];
1154
            unset($config['driver']);
1155
1156
            $parts = [];
1157
            foreach ($config as $key => $value) {
1158
                $parts[] = "$key=$value";
1159
            }
1160
1161
            return "$driver:" . implode(';', $parts);
1162
        }
1163
        throw new InvalidConfigException("Connection DSN 'driver' must be set.");
1164
    }
1165
1166
    /**
1167
     * Close the connection before serializing.
1168
     * @return array
1169
     */
1170
    public function __sleep()
1171
    {
1172
        $fields = (array) $this;
1173
1174
        unset($fields['pdo']);
1175
        unset($fields["\000" . __CLASS__ . "\000" . '_master']);
1176
        unset($fields["\000" . __CLASS__ . "\000" . '_slave']);
1177
        unset($fields["\000" . __CLASS__ . "\000" . '_transaction']);
1178
        unset($fields["\000" . __CLASS__ . "\000" . '_schema']);
1179
1180
        return array_keys($fields);
1181
    }
1182
1183
    /**
1184
     * Reset the connection after cloning.
1185
     */
1186
    public function __clone()
1187
    {
1188
        parent::__clone();
1189
1190
        $this->_master = false;
1191
        $this->_slave = false;
1192
        $this->_schema = null;
1193
        $this->_transaction = null;
1194
        if (strncmp($this->dsn, 'sqlite::memory:', 15) !== 0) {
1195
            // reset PDO connection, unless its sqlite in-memory, which can only have one connection
1196
            $this->pdo = null;
1197
        }
1198
    }
1199
}
1200