1: <?php
2: /**
3: * Database Session save handler. Allows saving session information into a model.
4: *
5: * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
6: * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
7: *
8: * Licensed under The MIT License
9: * For full copyright and license information, please see the LICENSE.txt
10: * Redistributions of files must retain the above copyright notice.
11: *
12: * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
13: * @link https://cakephp.org CakePHP(tm) Project
14: * @package Cake.Model.Datasource.Session
15: * @since CakePHP(tm) v 2.0
16: * @license https://opensource.org/licenses/mit-license.php MIT License
17: */
18:
19: App::uses('CakeSessionHandlerInterface', 'Model/Datasource/Session');
20: App::uses('ClassRegistry', 'Utility');
21:
22: /**
23: * DatabaseSession provides methods to be used with CakeSession.
24: *
25: * @package Cake.Model.Datasource.Session
26: */
27: class DatabaseSession implements CakeSessionHandlerInterface {
28:
29: /**
30: * Reference to the model handling the session data
31: *
32: * @var Model
33: */
34: protected $_model;
35:
36: /**
37: * Number of seconds to mark the session as expired
38: *
39: * @var int
40: */
41: protected $_timeout;
42:
43: /**
44: * Constructor. Looks at Session configuration information and
45: * sets up the session model.
46: */
47: public function __construct() {
48: $modelName = Configure::read('Session.handler.model');
49:
50: if (empty($modelName)) {
51: $settings = array(
52: 'class' => 'Session',
53: 'alias' => 'Session',
54: 'table' => 'cake_sessions',
55: );
56: } else {
57: $settings = array(
58: 'class' => $modelName,
59: 'alias' => 'Session',
60: );
61: }
62: $this->_model = ClassRegistry::init($settings);
63: $this->_timeout = Configure::read('Session.timeout') * 60;
64: }
65:
66: /**
67: * Method called on open of a database session.
68: *
69: * @return bool Success
70: */
71: public function open() {
72: return true;
73: }
74:
75: /**
76: * Method called on close of a database session.
77: *
78: * @return bool Success
79: */
80: public function close() {
81: return true;
82: }
83:
84: /**
85: * Method used to read from a database session.
86: *
87: * @param int|string $id The key of the value to read
88: * @return mixed The value of the key or false if it does not exist
89: */
90: public function read($id) {
91: $row = $this->_model->find('first', array(
92: 'conditions' => array($this->_model->alias . '.' . $this->_model->primaryKey => $id)
93: ));
94:
95: if (empty($row[$this->_model->alias])) {
96: return '';
97: }
98:
99: if (!is_numeric($row[$this->_model->alias]['data']) && empty($row[$this->_model->alias]['data'])) {
100: return '';
101: }
102:
103: return (string)$row[$this->_model->alias]['data'];
104: }
105:
106: /**
107: * Helper function called on write for database sessions.
108: *
109: * Will retry, once, if the save triggers a PDOException which
110: * can happen if a race condition is encountered
111: *
112: * @param int $id ID that uniquely identifies session in database
113: * @param mixed $data The value of the data to be saved.
114: * @return bool True for successful write, false otherwise.
115: */
116: public function write($id, $data) {
117: if (!$id) {
118: return false;
119: }
120: $expires = time() + $this->_timeout;
121: $record = compact('id', 'data', 'expires');
122: $record[$this->_model->primaryKey] = $id;
123:
124: $options = array(
125: 'validate' => false,
126: 'callbacks' => false,
127: 'counterCache' => false
128: );
129: try {
130: return (bool)$this->_model->save($record, $options);
131: } catch (PDOException $e) {
132: return (bool)$this->_model->save($record, $options);
133: }
134: }
135:
136: /**
137: * Method called on the destruction of a database session.
138: *
139: * @param int $id ID that uniquely identifies session in database
140: * @return bool True for successful delete, false otherwise.
141: */
142: public function destroy($id) {
143: return (bool)$this->_model->delete($id);
144: }
145:
146: /**
147: * Helper function called on gc for database sessions.
148: *
149: * @param int $expires Timestamp (defaults to current time)
150: * @return bool Success
151: */
152: public function gc($expires = null) {
153: if (!$expires) {
154: $expires = time();
155: } else {
156: $expires = time() - $expires;
157: }
158: $this->_model->deleteAll(array($this->_model->alias . ".expires <" => $expires), false, false);
159: return true;
160: }
161:
162: }
163: