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: * @since 2.0.0
15: * @license https://opensource.org/licenses/mit-license.php MIT License
16: */
17: namespace Cake\Http\Session;
18:
19: use Cake\ORM\Entity;
20: use Cake\ORM\Locator\LocatorAwareTrait;
21: use SessionHandlerInterface;
22:
23: /**
24: * DatabaseSession provides methods to be used with Session.
25: */
26: class DatabaseSession implements SessionHandlerInterface
27: {
28: use LocatorAwareTrait;
29:
30: /**
31: * Reference to the table handling the session data
32: *
33: * @var \Cake\ORM\Table
34: */
35: protected $_table;
36:
37: /**
38: * Number of seconds to mark the session as expired
39: *
40: * @var int
41: */
42: protected $_timeout;
43:
44: /**
45: * Constructor. Looks at Session configuration information and
46: * sets up the session model.
47: *
48: * @param array $config The configuration for this engine. It requires the 'model'
49: * key to be present corresponding to the Table to use for managing the sessions.
50: */
51: public function __construct(array $config = [])
52: {
53: if (isset($config['tableLocator'])) {
54: $this->setTableLocator($config['tableLocator']);
55: }
56: $tableLocator = $this->getTableLocator();
57:
58: if (empty($config['model'])) {
59: $config = $tableLocator->exists('Sessions') ? [] : ['table' => 'sessions'];
60: $this->_table = $tableLocator->get('Sessions', $config);
61: } else {
62: $this->_table = $tableLocator->get($config['model']);
63: }
64:
65: $this->_timeout = (int)ini_get('session.gc_maxlifetime');
66: }
67:
68: /**
69: * Set the timeout value for sessions.
70: *
71: * Primarily used in testing.
72: *
73: * @param int $timeout The timeout duration.
74: * @return $this
75: */
76: public function setTimeout($timeout)
77: {
78: $this->_timeout = $timeout;
79:
80: return $this;
81: }
82:
83: /**
84: * Method called on open of a database session.
85: *
86: * @param string $savePath The path where to store/retrieve the session.
87: * @param string $name The session name.
88: * @return bool Success
89: */
90: public function open($savePath, $name)
91: {
92: return true;
93: }
94:
95: /**
96: * Method called on close of a database session.
97: *
98: * @return bool Success
99: */
100: public function close()
101: {
102: return true;
103: }
104:
105: /**
106: * Method used to read from a database session.
107: *
108: * @param string|int $id ID that uniquely identifies session in database.
109: * @return string Session data or empty string if it does not exist.
110: */
111: public function read($id)
112: {
113: $result = $this->_table
114: ->find('all')
115: ->select(['data'])
116: ->where([$this->_table->getPrimaryKey() => $id])
117: ->disableHydration()
118: ->first();
119:
120: if (empty($result)) {
121: return '';
122: }
123:
124: if (is_string($result['data'])) {
125: return $result['data'];
126: }
127:
128: $session = stream_get_contents($result['data']);
129:
130: if ($session === false) {
131: return '';
132: }
133:
134: return $session;
135: }
136:
137: /**
138: * Helper function called on write for database sessions.
139: *
140: * @param string|int $id ID that uniquely identifies session in database.
141: * @param mixed $data The data to be saved.
142: * @return bool True for successful write, false otherwise.
143: */
144: public function write($id, $data)
145: {
146: if (!$id) {
147: return false;
148: }
149: $expires = time() + $this->_timeout;
150: $record = compact('data', 'expires');
151: $record[$this->_table->getPrimaryKey()] = $id;
152: $result = $this->_table->save(new Entity($record));
153:
154: return (bool)$result;
155: }
156:
157: /**
158: * Method called on the destruction of a database session.
159: *
160: * @param string|int $id ID that uniquely identifies session in database.
161: * @return bool True for successful delete, false otherwise.
162: */
163: public function destroy($id)
164: {
165: $this->_table->delete(new Entity(
166: [$this->_table->getPrimaryKey() => $id],
167: ['markNew' => false]
168: ));
169:
170: return true;
171: }
172:
173: /**
174: * Helper function called on gc for database sessions.
175: *
176: * @param int $maxlifetime Sessions that have not updated for the last maxlifetime seconds will be removed.
177: * @return bool True on success, false on failure.
178: */
179: public function gc($maxlifetime)
180: {
181: $this->_table->deleteAll(['expires <' => time()]);
182:
183: return true;
184: }
185: }
186: