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