1: <?php
2:
3: /**
4: * Created by vaibhav on 1/28/12 (3:34 AM).
5: *
6: * This file contains the QDbBackedSessionHandler class.
7: *
8: * Relies on a SQL database table with the following columns:
9: * id - STRING primary key
10: * last_access_time - INT
11: * data - BLOB or BINARY or VARBINARY. Must be a binary safe column, and capable of holding the maximum size of
12: * session data for your app, which depends on what you are putting in the $_SESSION variable.
13: *
14: * @package Sessions
15: */
16: class QDbBackedSessionHandler extends QBaseClass {
17:
18: /**
19: * @var int The index in the database array
20: */
21: protected static $intDbIndex;
22:
23: /**
24: * @var string The table name to be used for saving sessions.
25: */
26: protected static $strTableName;
27:
28: /**
29: * @var string The session name to be used for saving sessions.
30: */
31: protected static $strSessionName = '';
32:
33: /** @var bool Whether to encrypt the session data. Highly recommended whenever sessions can authenticate a user. */
34: public static $blnEncrypt = true;
35:
36: /** @var bool Whether to compress the session data. */
37: public static $blnCompress = true;
38:
39:
40: /**
41: * @static
42: *
43: * @param int $intDbIndex The index in the database array
44: * @param string $strTableName The table name to be used for saving sessions.
45: *
46: * @return bool
47: * @throws Exception|QCallerException|QInvalidCastException
48: */
49: public static function Initialize($intDbIndex = 1, $strTableName = "qc_session") {
50: self::$intDbIndex = QType::Cast($intDbIndex, QType::Integer);
51: self::$strTableName = QType::Cast($strTableName, QType::String);
52: // If the database index exists
53: if (!array_key_exists(self::$intDbIndex, QApplication::$Database)) {
54: throw new QCallerException('No database defined at DB_CONNECTION index ' . self::$intDbIndex . '. Correct your settings in configuration.inc.php.');
55: }
56: $objDatabase = QApplication::$Database[self::$intDbIndex];
57: // see if the database contains a table with desired name
58: if (!in_array(self::$strTableName, $objDatabase->GetTables())) {
59: throw new QCallerException('Table ' . self::$strTableName . ' not found in database at DB_CONNECTION index ' . self::$intDbIndex . '. Correct your settings in configuration.inc.php.');
60: }
61: // Set session handler functions
62: $session_ok = session_set_save_handler(
63: 'QDbBackedSessionHandler::SessionOpen',
64: 'QDbBackedSessionHandler::SessionClose',
65: 'QDbBackedSessionHandler::SessionRead',
66: 'QDbBackedSessionHandler::SessionWrite',
67: 'QDbBackedSessionHandler::SessionDestroy',
68: 'QDbBackedSessionHandler::SessionGarbageCollect'
69: );
70: // could not register the session handler functions
71: if (!$session_ok) {
72: throw new QCallerException("session_set_save_handler function failed");
73: }
74: // Will be called before session ends.
75: register_shutdown_function('session_write_close');
76: return $session_ok;
77: }
78:
79: /**
80: * Open the session (used by PHP when the session handler is active)
81: * @param string $save_path
82: * @param string $session_name
83: *
84: * @return bool
85: */
86: public static function SessionOpen($save_path, $session_name) {
87: self::$strSessionName = $session_name;
88: // Nothing to do
89: return true;
90: }
91:
92: /**
93: * Close the session (used by PHP when the session handler is active)
94: * @return bool
95: */
96: public static function SessionClose() {
97: // Nothing to do.
98: return true;
99: }
100:
101: /**
102: * Read the session data (used by PHP when the session handler is active)
103: * @param string $id
104: *
105: * @return string the session data, base64 decoded
106: */
107: public static function SessionRead($id) {
108: $id = self::$strSessionName . '.' . $id;
109: $objDatabase = QApplication::$Database[self::$intDbIndex];
110: $query = '
111: SELECT
112: ' . $objDatabase->EscapeIdentifier('data') . '
113: FROM
114: ' . $objDatabase->EscapeIdentifier(self::$strTableName) . '
115: WHERE
116: ' . $objDatabase->EscapeIdentifier('id') . ' = ' . $objDatabase->SqlVariable($id);
117:
118: $result = $objDatabase->Query($query);
119:
120: $result_row = $result->FetchArray();
121:
122:
123: if (!$result_row) // either the data was empty or the row was not found
124: return '';
125: $strData = $result_row['data'];
126: if (!$strData)
127: return '';
128:
129: // The session exists and was accessed. Return the data.
130: if (self::$blnEncrypt) {
131: try {
132: $crypt = new QCryptography();
133: $strData = $crypt->Decrypt($strData);
134: }
135: catch(Exception $e) {
136: // if mcyrpt not installed, skip this step
137: }
138: }
139:
140: if (self::$blnCompress) {
141: $strData = gzuncompress($strData);
142: }
143:
144: return $strData;
145: }
146:
147: /**
148: * Tells whether a session by given name exists or not (used by PHP when the session handler is active)
149: * @param string $id Session ID
150: *
151: * @return bool does the session exist or not
152: */
153: public static function SessionExists($id) {
154: $id = self::$strSessionName . '.' . $id;
155: $objDatabase = QApplication::$Database[self::$intDbIndex];
156: $query = '
157: SELECT 1
158: FROM
159: ' . $objDatabase->EscapeIdentifier(self::$strTableName) . '
160: WHERE
161: ' . $objDatabase->EscapeIdentifier('id') . ' = ' . $objDatabase->SqlVariable($id);
162:
163: $result = $objDatabase->Query($query);
164:
165: $result_row = $result->FetchArray();
166:
167: // either the data was empty or the row was not found
168: return !empty($result_row);
169: }
170:
171: /**
172: * Write data to the session
173: *
174: * @param string $id The session ID
175: * @param string $strSessionData Data to be written to the Session whose ID was supplied
176: *
177: * @return bool
178: */
179: public static function SessionWrite($id, $strSessionData) {
180: if (self::$blnCompress) {
181: $strEncoded = gzcompress($strSessionData);
182: }
183:
184: if (self::$blnEncrypt) {
185: try {
186: $crypt = new QCryptography();
187: $strEncoded = $crypt->Encrypt($strEncoded);
188: }
189: catch(Exception $e) {
190: // if mcyrpt not installed, skip this step
191: }
192: }
193:
194: assert (!empty($strEncoded));
195:
196: $id = self::$strSessionName . '.' . $id;
197: $objDatabase = QApplication::$Database[self::$intDbIndex];
198: $objDatabase->InsertOrUpdate(
199: self::$strTableName,
200: array(
201: 'data' => $strEncoded,
202: 'last_access_time' => time(),
203: 'id' => $id
204: ),
205: 'id');
206: return true;
207: }
208:
209: /**
210: * Destroy the session for a given session ID
211: *
212: * @param string $id The session ID
213: *
214: * @return bool
215: */
216: public static function SessionDestroy($id) {
217: $id = self::$strSessionName . '.' . $id;
218: $objDatabase = QApplication::$Database[self::$intDbIndex];
219: $query = '
220: DELETE FROM
221: ' . $objDatabase->EscapeIdentifier(self::$strTableName) . '
222: WHERE
223: ' . $objDatabase->EscapeIdentifier('id') . ' = ' . $objDatabase->SqlVariable($id);
224:
225: $objDatabase->NonQuery($query);
226: return true;
227: }
228:
229: /**
230: * Garbage collect session data (delete/destroy sessions which are older than the max allowed lifetime)
231: *
232: * @param int $intMaxSessionLifetime The max session lifetime (in seconds)
233: *
234: * @return bool
235: */
236: public static function SessionGarbageCollect($intMaxSessionLifetime) {
237: $objDatabase = QApplication::$Database[self::$intDbIndex];
238: $old = time() - $intMaxSessionLifetime;
239:
240: $query = '
241: DELETE FROM
242: ' . $objDatabase->EscapeIdentifier(self::$strTableName) . '
243: WHERE
244: ' . $objDatabase->EscapeIdentifier('last_access_time') . ' < ' . $objDatabase->SqlVariable($old);
245:
246: $objDatabase->NonQuery($query);
247: return true;
248: }
249: }