1: <?php
2: /**
3: * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
4: * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
5: *
6: * Licensed under The MIT License
7: * For full copyright and license information, please see the LICENSE.txt
8: * Redistributions of files must retain the above copyright notice.
9: *
10: * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
11: * @link https://cakephp.org CakePHP(tm) Project
12: * @since 3.0.0
13: * @license https://opensource.org/licenses/mit-license.php MIT License
14: */
15: namespace Cake\Database;
16:
17: use Cake\Core\App;
18: use Cake\Core\Retry\CommandRetry;
19: use Cake\Database\Exception\MissingConnectionException;
20: use Cake\Database\Exception\MissingDriverException;
21: use Cake\Database\Exception\MissingExtensionException;
22: use Cake\Database\Exception\NestedTransactionRollbackException;
23: use Cake\Database\Log\LoggedQuery;
24: use Cake\Database\Log\LoggingStatement;
25: use Cake\Database\Log\QueryLogger;
26: use Cake\Database\Retry\ReconnectStrategy;
27: use Cake\Database\Schema\CachedCollection;
28: use Cake\Database\Schema\Collection as SchemaCollection;
29: use Cake\Datasource\ConnectionInterface;
30: use Cake\Log\Log;
31: use Exception;
32:
33: /**
34: * Represents a connection with a database server.
35: */
36: class Connection implements ConnectionInterface
37: {
38: use TypeConverterTrait;
39:
40: /**
41: * Contains the configuration params for this connection.
42: *
43: * @var array
44: */
45: protected $_config;
46:
47: /**
48: * Driver object, responsible for creating the real connection
49: * and provide specific SQL dialect.
50: *
51: * @var \Cake\Database\Driver
52: */
53: protected $_driver;
54:
55: /**
56: * Contains how many nested transactions have been started.
57: *
58: * @var int
59: */
60: protected $_transactionLevel = 0;
61:
62: /**
63: * Whether a transaction is active in this connection.
64: *
65: * @var bool
66: */
67: protected $_transactionStarted = false;
68:
69: /**
70: * Whether this connection can and should use savepoints for nested
71: * transactions.
72: *
73: * @var bool
74: */
75: protected $_useSavePoints = false;
76:
77: /**
78: * Whether to log queries generated during this connection.
79: *
80: * @var bool
81: */
82: protected $_logQueries = false;
83:
84: /**
85: * Logger object instance.
86: *
87: * @var \Cake\Database\Log\QueryLogger|null
88: */
89: protected $_logger;
90:
91: /**
92: * The schema collection object
93: *
94: * @var \Cake\Database\Schema\Collection|null
95: */
96: protected $_schemaCollection;
97:
98: /**
99: * NestedTransactionRollbackException object instance, will be stored if
100: * the rollback method is called in some nested transaction.
101: *
102: * @var \Cake\Database\Exception\NestedTransactionRollbackException|null
103: */
104: protected $nestedTransactionRollbackException;
105:
106: /**
107: * Constructor.
108: *
109: * @param array $config configuration for connecting to database
110: */
111: public function __construct($config)
112: {
113: $this->_config = $config;
114:
115: $driver = '';
116: if (!empty($config['driver'])) {
117: $driver = $config['driver'];
118: }
119: $this->setDriver($driver, $config);
120:
121: if (!empty($config['log'])) {
122: $this->enableQueryLogging($config['log']);
123: }
124: }
125:
126: /**
127: * Destructor
128: *
129: * Disconnects the driver to release the connection.
130: */
131: public function __destruct()
132: {
133: if ($this->_transactionStarted && class_exists('Cake\Log\Log')) {
134: Log::warning('The connection is going to be closed but there is an active transaction.');
135: }
136: }
137:
138: /**
139: * {@inheritDoc}
140: */
141: public function config()
142: {
143: return $this->_config;
144: }
145:
146: /**
147: * {@inheritDoc}
148: */
149: public function configName()
150: {
151: if (empty($this->_config['name'])) {
152: return '';
153: }
154:
155: return $this->_config['name'];
156: }
157:
158: /**
159: * Sets the driver instance. If a string is passed it will be treated
160: * as a class name and will be instantiated.
161: *
162: * @param \Cake\Database\Driver|string $driver The driver instance to use.
163: * @param array $config Config for a new driver.
164: * @throws \Cake\Database\Exception\MissingDriverException When a driver class is missing.
165: * @throws \Cake\Database\Exception\MissingExtensionException When a driver's PHP extension is missing.
166: * @return $this
167: */
168: public function setDriver($driver, $config = [])
169: {
170: if (is_string($driver)) {
171: $className = App::className($driver, 'Database/Driver');
172: if (!$className || !class_exists($className)) {
173: throw new MissingDriverException(['driver' => $driver]);
174: }
175: $driver = new $className($config);
176: }
177: if (!$driver->enabled()) {
178: throw new MissingExtensionException(['driver' => get_class($driver)]);
179: }
180:
181: $this->_driver = $driver;
182:
183: return $this;
184: }
185:
186: /**
187: * Get the retry wrapper object that is allows recovery from server disconnects
188: * while performing certain database actions, such as executing a query.
189: *
190: * @return \Cake\Core\Retry\CommandRetry The retry wrapper
191: */
192: public function getDisconnectRetry()
193: {
194: return new CommandRetry(new ReconnectStrategy($this));
195: }
196:
197: /**
198: * Gets the driver instance.
199: *
200: * @return \Cake\Database\Driver
201: */
202: public function getDriver()
203: {
204: return $this->_driver;
205: }
206:
207: /**
208: * Sets the driver instance. If a string is passed it will be treated
209: * as a class name and will be instantiated.
210: *
211: * If no params are passed it will return the current driver instance.
212: *
213: * @deprecated 3.4.0 Use setDriver()/getDriver() instead.
214: * @param \Cake\Database\Driver|string|null $driver The driver instance to use.
215: * @param array $config Either config for a new driver or null.
216: * @throws \Cake\Database\Exception\MissingDriverException When a driver class is missing.
217: * @throws \Cake\Database\Exception\MissingExtensionException When a driver's PHP extension is missing.
218: * @return \Cake\Database\Driver
219: */
220: public function driver($driver = null, $config = [])
221: {
222: deprecationWarning('Connection::driver() is deprecated. Use Connection::setDriver()/getDriver() instead.');
223: if ($driver !== null) {
224: $this->setDriver($driver, $config);
225: }
226:
227: return $this->getDriver();
228: }
229:
230: /**
231: * Connects to the configured database.
232: *
233: * @throws \Cake\Database\Exception\MissingConnectionException if credentials are invalid.
234: * @return bool true, if the connection was already established or the attempt was successful.
235: */
236: public function connect()
237: {
238: try {
239: return $this->_driver->connect();
240: } catch (Exception $e) {
241: throw new MissingConnectionException(['reason' => $e->getMessage()], null, $e);
242: }
243: }
244:
245: /**
246: * Disconnects from database server.
247: *
248: * @return void
249: */
250: public function disconnect()
251: {
252: $this->_driver->disconnect();
253: }
254:
255: /**
256: * Returns whether connection to database server was already established.
257: *
258: * @return bool
259: */
260: public function isConnected()
261: {
262: return $this->_driver->isConnected();
263: }
264:
265: /**
266: * Prepares a SQL statement to be executed.
267: *
268: * @param string|\Cake\Database\Query $sql The SQL to convert into a prepared statement.
269: * @return \Cake\Database\StatementInterface
270: */
271: public function prepare($sql)
272: {
273: return $this->getDisconnectRetry()->run(function () use ($sql) {
274: $statement = $this->_driver->prepare($sql);
275:
276: if ($this->_logQueries) {
277: $statement = $this->_newLogger($statement);
278: }
279:
280: return $statement;
281: });
282: }
283:
284: /**
285: * Executes a query using $params for interpolating values and $types as a hint for each
286: * those params.
287: *
288: * @param string $query SQL to be executed and interpolated with $params
289: * @param array $params list or associative array of params to be interpolated in $query as values
290: * @param array $types list or associative array of types to be used for casting values in query
291: * @return \Cake\Database\StatementInterface executed statement
292: */
293: public function execute($query, array $params = [], array $types = [])
294: {
295: return $this->getDisconnectRetry()->run(function () use ($query, $params, $types) {
296: if (!empty($params)) {
297: $statement = $this->prepare($query);
298: $statement->bind($params, $types);
299: $statement->execute();
300: } else {
301: $statement = $this->query($query);
302: }
303:
304: return $statement;
305: });
306: }
307:
308: /**
309: * Compiles a Query object into a SQL string according to the dialect for this
310: * connection's driver
311: *
312: * @param \Cake\Database\Query $query The query to be compiled
313: * @param \Cake\Database\ValueBinder $generator The placeholder generator to use
314: * @return string
315: */
316: public function compileQuery(Query $query, ValueBinder $generator)
317: {
318: return $this->getDriver()->compileQuery($query, $generator)[1];
319: }
320:
321: /**
322: * Executes the provided query after compiling it for the specific driver
323: * dialect and returns the executed Statement object.
324: *
325: * @param \Cake\Database\Query $query The query to be executed
326: * @return \Cake\Database\StatementInterface executed statement
327: */
328: public function run(Query $query)
329: {
330: return $this->getDisconnectRetry()->run(function () use ($query) {
331: $statement = $this->prepare($query);
332: $query->getValueBinder()->attachTo($statement);
333: $statement->execute();
334:
335: return $statement;
336: });
337: }
338:
339: /**
340: * Executes a SQL statement and returns the Statement object as result.
341: *
342: * @param string $sql The SQL query to execute.
343: * @return \Cake\Database\StatementInterface
344: */
345: public function query($sql)
346: {
347: return $this->getDisconnectRetry()->run(function () use ($sql) {
348: $statement = $this->prepare($sql);
349: $statement->execute();
350:
351: return $statement;
352: });
353: }
354:
355: /**
356: * Create a new Query instance for this connection.
357: *
358: * @return \Cake\Database\Query
359: */
360: public function newQuery()
361: {
362: return new Query($this);
363: }
364:
365: /**
366: * Sets a Schema\Collection object for this connection.
367: *
368: * @param \Cake\Database\Schema\Collection $collection The schema collection object
369: * @return $this
370: */
371: public function setSchemaCollection(SchemaCollection $collection)
372: {
373: $this->_schemaCollection = $collection;
374:
375: return $this;
376: }
377:
378: /**
379: * Gets a Schema\Collection object for this connection.
380: *
381: * @return \Cake\Database\Schema\Collection
382: */
383: public function getSchemaCollection()
384: {
385: if ($this->_schemaCollection !== null) {
386: return $this->_schemaCollection;
387: }
388:
389: if (!empty($this->_config['cacheMetadata'])) {
390: return $this->_schemaCollection = new CachedCollection($this, $this->_config['cacheMetadata']);
391: }
392:
393: return $this->_schemaCollection = new SchemaCollection($this);
394: }
395:
396: /**
397: * Gets or sets a Schema\Collection object for this connection.
398: *
399: * @deprecated 3.4.0 Use setSchemaCollection()/getSchemaCollection()
400: * @param \Cake\Database\Schema\Collection|null $collection The schema collection object
401: * @return \Cake\Database\Schema\Collection
402: */
403: public function schemaCollection(SchemaCollection $collection = null)
404: {
405: deprecationWarning(
406: 'Connection::schemaCollection() is deprecated. ' .
407: 'Use Connection::setSchemaCollection()/getSchemaCollection() instead.'
408: );
409: if ($collection !== null) {
410: $this->setSchemaCollection($collection);
411: }
412:
413: return $this->getSchemaCollection();
414: }
415:
416: /**
417: * Executes an INSERT query on the specified table.
418: *
419: * @param string $table the table to insert values in
420: * @param array $data values to be inserted
421: * @param array $types list of associative array containing the types to be used for casting
422: * @return \Cake\Database\StatementInterface
423: */
424: public function insert($table, array $data, array $types = [])
425: {
426: return $this->getDisconnectRetry()->run(function () use ($table, $data, $types) {
427: $columns = array_keys($data);
428:
429: return $this->newQuery()->insert($columns, $types)
430: ->into($table)
431: ->values($data)
432: ->execute();
433: });
434: }
435:
436: /**
437: * Executes an UPDATE statement on the specified table.
438: *
439: * @param string $table the table to update rows from
440: * @param array $data values to be updated
441: * @param array $conditions conditions to be set for update statement
442: * @param array $types list of associative array containing the types to be used for casting
443: * @return \Cake\Database\StatementInterface
444: */
445: public function update($table, array $data, array $conditions = [], $types = [])
446: {
447: return $this->getDisconnectRetry()->run(function () use ($table, $data, $conditions, $types) {
448: return $this->newQuery()->update($table)
449: ->set($data, $types)
450: ->where($conditions, $types)
451: ->execute();
452: });
453: }
454:
455: /**
456: * Executes a DELETE statement on the specified table.
457: *
458: * @param string $table the table to delete rows from
459: * @param array $conditions conditions to be set for delete statement
460: * @param array $types list of associative array containing the types to be used for casting
461: * @return \Cake\Database\StatementInterface
462: */
463: public function delete($table, $conditions = [], $types = [])
464: {
465: return $this->getDisconnectRetry()->run(function () use ($table, $conditions, $types) {
466: return $this->newQuery()->delete($table)
467: ->where($conditions, $types)
468: ->execute();
469: });
470: }
471:
472: /**
473: * Starts a new transaction.
474: *
475: * @return void
476: */
477: public function begin()
478: {
479: if (!$this->_transactionStarted) {
480: if ($this->_logQueries) {
481: $this->log('BEGIN');
482: }
483:
484: $this->getDisconnectRetry()->run(function () {
485: $this->_driver->beginTransaction();
486: });
487:
488: $this->_transactionLevel = 0;
489: $this->_transactionStarted = true;
490: $this->nestedTransactionRollbackException = null;
491:
492: return;
493: }
494:
495: $this->_transactionLevel++;
496: if ($this->isSavePointsEnabled()) {
497: $this->createSavePoint((string)$this->_transactionLevel);
498: }
499: }
500:
501: /**
502: * Commits current transaction.
503: *
504: * @return bool true on success, false otherwise
505: */
506: public function commit()
507: {
508: if (!$this->_transactionStarted) {
509: return false;
510: }
511:
512: if ($this->_transactionLevel === 0) {
513: if ($this->wasNestedTransactionRolledback()) {
514: $e = $this->nestedTransactionRollbackException;
515: $this->nestedTransactionRollbackException = null;
516: throw $e;
517: }
518:
519: $this->_transactionStarted = false;
520: $this->nestedTransactionRollbackException = null;
521: if ($this->_logQueries) {
522: $this->log('COMMIT');
523: }
524:
525: return $this->_driver->commitTransaction();
526: }
527: if ($this->isSavePointsEnabled()) {
528: $this->releaseSavePoint((string)$this->_transactionLevel);
529: }
530:
531: $this->_transactionLevel--;
532:
533: return true;
534: }
535:
536: /**
537: * Rollback current transaction.
538: *
539: * @param bool|null $toBeginning Whether or not the transaction should be rolled back to the
540: * beginning of it. Defaults to false if using savepoints, or true if not.
541: * @return bool
542: */
543: public function rollback($toBeginning = null)
544: {
545: if (!$this->_transactionStarted) {
546: return false;
547: }
548:
549: $useSavePoint = $this->isSavePointsEnabled();
550: if ($toBeginning === null) {
551: $toBeginning = !$useSavePoint;
552: }
553: if ($this->_transactionLevel === 0 || $toBeginning) {
554: $this->_transactionLevel = 0;
555: $this->_transactionStarted = false;
556: $this->nestedTransactionRollbackException = null;
557: if ($this->_logQueries) {
558: $this->log('ROLLBACK');
559: }
560: $this->_driver->rollbackTransaction();
561:
562: return true;
563: }
564:
565: $savePoint = $this->_transactionLevel--;
566: if ($useSavePoint) {
567: $this->rollbackSavepoint($savePoint);
568: } elseif ($this->nestedTransactionRollbackException === null) {
569: $this->nestedTransactionRollbackException = new NestedTransactionRollbackException();
570: }
571:
572: return true;
573: }
574:
575: /**
576: * Enables/disables the usage of savepoints, enables only if driver the allows it.
577: *
578: * If you are trying to enable this feature, make sure you check the return value of this
579: * function to verify it was enabled successfully.
580: *
581: * ### Example:
582: *
583: * `$connection->enableSavePoints(true)` Returns true if drivers supports save points, false otherwise
584: * `$connection->enableSavePoints(false)` Disables usage of savepoints and returns false
585: *
586: * @param bool $enable Whether or not save points should be used.
587: * @return $this
588: */
589: public function enableSavePoints($enable)
590: {
591: if ($enable === false) {
592: $this->_useSavePoints = false;
593: } else {
594: $this->_useSavePoints = $this->_driver->supportsSavePoints();
595: }
596:
597: return $this;
598: }
599:
600: /**
601: * Disables the usage of savepoints.
602: *
603: * @return $this
604: */
605: public function disableSavePoints()
606: {
607: $this->_useSavePoints = false;
608:
609: return $this;
610: }
611:
612: /**
613: * Returns whether this connection is using savepoints for nested transactions
614: *
615: * @return bool true if enabled, false otherwise
616: */
617: public function isSavePointsEnabled()
618: {
619: return $this->_useSavePoints;
620: }
621:
622: /**
623: * Returns whether this connection is using savepoints for nested transactions
624: * If a boolean is passed as argument it will enable/disable the usage of savepoints
625: * only if driver the allows it.
626: *
627: * If you are trying to enable this feature, make sure you check the return value of this
628: * function to verify it was enabled successfully.
629: *
630: * ### Example:
631: *
632: * `$connection->useSavePoints(true)` Returns true if drivers supports save points, false otherwise
633: * `$connection->useSavePoints(false)` Disables usage of savepoints and returns false
634: * `$connection->useSavePoints()` Returns current status
635: *
636: * @deprecated 3.4.0 Use enableSavePoints()/isSavePointsEnabled() instead.
637: * @param bool|null $enable Whether or not save points should be used.
638: * @return bool true if enabled, false otherwise
639: */
640: public function useSavePoints($enable = null)
641: {
642: deprecationWarning(
643: 'Connection::useSavePoints() is deprecated. ' .
644: 'Use Connection::enableSavePoints()/isSavePointsEnabled() instead.'
645: );
646: if ($enable !== null) {
647: $this->enableSavePoints($enable);
648: }
649:
650: return $this->isSavePointsEnabled();
651: }
652:
653: /**
654: * Creates a new save point for nested transactions.
655: *
656: * @param string $name The save point name.
657: * @return void
658: */
659: public function createSavePoint($name)
660: {
661: $this->execute($this->_driver->savePointSQL($name))->closeCursor();
662: }
663:
664: /**
665: * Releases a save point by its name.
666: *
667: * @param string $name The save point name.
668: * @return void
669: */
670: public function releaseSavePoint($name)
671: {
672: $this->execute($this->_driver->releaseSavePointSQL($name))->closeCursor();
673: }
674:
675: /**
676: * Rollback a save point by its name.
677: *
678: * @param string $name The save point name.
679: * @return void
680: */
681: public function rollbackSavepoint($name)
682: {
683: $this->execute($this->_driver->rollbackSavePointSQL($name))->closeCursor();
684: }
685:
686: /**
687: * Run driver specific SQL to disable foreign key checks.
688: *
689: * @return void
690: */
691: public function disableForeignKeys()
692: {
693: $this->getDisconnectRetry()->run(function () {
694: $this->execute($this->_driver->disableForeignKeySQL())->closeCursor();
695: });
696: }
697:
698: /**
699: * Run driver specific SQL to enable foreign key checks.
700: *
701: * @return void
702: */
703: public function enableForeignKeys()
704: {
705: $this->getDisconnectRetry()->run(function () {
706: $this->execute($this->_driver->enableForeignKeySQL())->closeCursor();
707: });
708: }
709:
710: /**
711: * Returns whether the driver supports adding or dropping constraints
712: * to already created tables.
713: *
714: * @return bool true if driver supports dynamic constraints
715: */
716: public function supportsDynamicConstraints()
717: {
718: return $this->_driver->supportsDynamicConstraints();
719: }
720:
721: /**
722: * {@inheritDoc}
723: *
724: * ### Example:
725: *
726: * ```
727: * $connection->transactional(function ($connection) {
728: * $connection->newQuery()->delete('users')->execute();
729: * });
730: * ```
731: */
732: public function transactional(callable $callback)
733: {
734: $this->begin();
735:
736: try {
737: $result = $callback($this);
738: } catch (Exception $e) {
739: $this->rollback(false);
740: throw $e;
741: }
742:
743: if ($result === false) {
744: $this->rollback(false);
745:
746: return false;
747: }
748:
749: try {
750: $this->commit();
751: } catch (NestedTransactionRollbackException $e) {
752: $this->rollback(false);
753: throw $e;
754: }
755:
756: return $result;
757: }
758:
759: /**
760: * Returns whether some nested transaction has been already rolled back.
761: *
762: * @return bool
763: */
764: protected function wasNestedTransactionRolledback()
765: {
766: return $this->nestedTransactionRollbackException instanceof NestedTransactionRollbackException;
767: }
768:
769: /**
770: * {@inheritDoc}
771: *
772: * ### Example:
773: *
774: * ```
775: * $connection->disableConstraints(function ($connection) {
776: * $connection->newQuery()->delete('users')->execute();
777: * });
778: * ```
779: */
780: public function disableConstraints(callable $callback)
781: {
782: return $this->getDisconnectRetry()->run(function () use ($callback) {
783: $this->disableForeignKeys();
784:
785: try {
786: $result = $callback($this);
787: } catch (Exception $e) {
788: $this->enableForeignKeys();
789: throw $e;
790: }
791:
792: $this->enableForeignKeys();
793:
794: return $result;
795: });
796: }
797:
798: /**
799: * Checks if a transaction is running.
800: *
801: * @return bool True if a transaction is running else false.
802: */
803: public function inTransaction()
804: {
805: return $this->_transactionStarted;
806: }
807:
808: /**
809: * Quotes value to be used safely in database query.
810: *
811: * @param mixed $value The value to quote.
812: * @param string|null $type Type to be used for determining kind of quoting to perform
813: * @return string Quoted value
814: */
815: public function quote($value, $type = null)
816: {
817: list($value, $type) = $this->cast($value, $type);
818:
819: return $this->_driver->quote($value, $type);
820: }
821:
822: /**
823: * Checks if the driver supports quoting.
824: *
825: * @return bool
826: */
827: public function supportsQuoting()
828: {
829: return $this->_driver->supportsQuoting();
830: }
831:
832: /**
833: * Quotes a database identifier (a column name, table name, etc..) to
834: * be used safely in queries without the risk of using reserved words.
835: *
836: * @param string $identifier The identifier to quote.
837: * @return string
838: */
839: public function quoteIdentifier($identifier)
840: {
841: return $this->_driver->quoteIdentifier($identifier);
842: }
843:
844: /**
845: * Enables or disables metadata caching for this connection
846: *
847: * Changing this setting will not modify existing schema collections objects.
848: *
849: * @param bool|string $cache Either boolean false to disable metadata caching, or
850: * true to use `_cake_model_` or the name of the cache config to use.
851: * @return void
852: */
853: public function cacheMetadata($cache)
854: {
855: $this->_schemaCollection = null;
856: $this->_config['cacheMetadata'] = $cache;
857: }
858:
859: /**
860: * {@inheritDoc}
861: *
862: * @deprecated 3.7.0 Use enableQueryLogging() and isQueryLoggingEnabled() instead.
863: */
864: public function logQueries($enable = null)
865: {
866: deprecationWarning(
867: 'Connection::logQueries() is deprecated. ' .
868: 'Use enableQueryLogging() and isQueryLoggingEnabled() instead.'
869: );
870: if ($enable === null) {
871: return $this->_logQueries;
872: }
873: $this->_logQueries = $enable;
874: }
875:
876: /**
877: * Enable/disable query logging
878: *
879: * @param bool $value Enable/disable query logging
880: * @return $this
881: */
882: public function enableQueryLogging($value)
883: {
884: $this->_logQueries = (bool)$value;
885:
886: return $this;
887: }
888:
889: /**
890: * Disable query logging
891: *
892: * @return $this
893: */
894: public function disableQueryLogging()
895: {
896: $this->_logQueries = false;
897:
898: return $this;
899: }
900:
901: /**
902: * Check if query logging is enabled.
903: *
904: * @return bool
905: */
906: public function isQueryLoggingEnabled()
907: {
908: return $this->_logQueries;
909: }
910:
911: /**
912: * {@inheritDoc}
913: *
914: * @deprecated 3.5.0 Use getLogger() and setLogger() instead.
915: */
916: public function logger($instance = null)
917: {
918: deprecationWarning(
919: 'Connection::logger() is deprecated. ' .
920: 'Use Connection::setLogger()/getLogger() instead.'
921: );
922: if ($instance === null) {
923: return $this->getLogger();
924: }
925:
926: $this->setLogger($instance);
927: }
928:
929: /**
930: * Sets a logger
931: *
932: * @param \Cake\Database\Log\QueryLogger $logger Logger object
933: * @return $this
934: */
935: public function setLogger($logger)
936: {
937: $this->_logger = $logger;
938:
939: return $this;
940: }
941:
942: /**
943: * Gets the logger object
944: *
945: * @return \Cake\Database\Log\QueryLogger logger instance
946: */
947: public function getLogger()
948: {
949: if ($this->_logger === null) {
950: $this->_logger = new QueryLogger();
951: }
952:
953: return $this->_logger;
954: }
955:
956: /**
957: * Logs a Query string using the configured logger object.
958: *
959: * @param string $sql string to be logged
960: * @return void
961: */
962: public function log($sql)
963: {
964: $query = new LoggedQuery();
965: $query->query = $sql;
966: $this->getLogger()->log($query);
967: }
968:
969: /**
970: * Returns a new statement object that will log the activity
971: * for the passed original statement instance.
972: *
973: * @param \Cake\Database\StatementInterface $statement the instance to be decorated
974: * @return \Cake\Database\Log\LoggingStatement
975: */
976: protected function _newLogger(StatementInterface $statement)
977: {
978: $log = new LoggingStatement($statement, $this->_driver);
979: $log->setLogger($this->getLogger());
980:
981: return $log;
982: }
983:
984: /**
985: * Returns an array that can be used to describe the internal state of this
986: * object.
987: *
988: * @return array
989: */
990: public function __debugInfo()
991: {
992: $secrets = [
993: 'password' => '*****',
994: 'username' => '*****',
995: 'host' => '*****',
996: 'database' => '*****',
997: 'port' => '*****'
998: ];
999: $replace = array_intersect_key($secrets, $this->_config);
1000: $config = $replace + $this->_config;
1001:
1002: return [
1003: 'config' => $config,
1004: 'driver' => $this->_driver,
1005: 'transactionLevel' => $this->_transactionLevel,
1006: 'transactionStarted' => $this->_transactionStarted,
1007: 'useSavePoints' => $this->_useSavePoints,
1008: 'logQueries' => $this->_logQueries,
1009: 'logger' => $this->_logger
1010: ];
1011: }
1012: }
1013: