1: <?php
2: /**
3: * This file is part of the PHPLucidFrame library.
4: * Core utility for session handling and flash messaging
5: *
6: * @package PHPLucidFrame\Core
7: * @since PHPLucidFrame v 1.0.0
8: * @copyright Copyright (c), PHPLucidFrame.
9: * @link http://phplucidframe.com
10: * @license http://www.opensource.org/licenses/mit-license.php MIT License
11: *
12: * This source file is subject to the MIT license that is bundled
13: * with this source code in the file LICENSE
14: */
15:
16: /**
17: * @internal
18: * @ignore
19: *
20: * Initialize session.
21: * @see http://php.net/manual/en/session.configuration.php
22: *
23: * @return void
24: */
25: function __session_init()
26: {
27: $defaultTypes = array('default', 'database');
28: $options = array(
29: 'name' => 'LCSESSID', # The name of the session which is used as cookie name.
30: 'table' => 'lc_sessions', # The table name without prefix that stores the session data. It is only applicable to database session
31: 'gc_maxlifetime' => 240, # The number of minutes after which data will be seen as 'garbage' or the time an unused PHP session will be kept alive.
32: 'cookie_lifetime' => 180 # The number of minutes you want session cookies live for. The value 0 means "until the browser is closed."
33: );
34:
35: $userSettings = _cfg('session');
36: $type = (isset($userSettings['type']) && in_array($userSettings['type'], $defaultTypes))
37: ? $userSettings['type']
38: : 'default';
39:
40: if ($userSettings && isset($userSettings['options']) && is_array($userSettings['options'])) {
41: $options = array_merge($options, $userSettings['options']);
42: }
43:
44: # The table option must be given for database session
45: if ($type === 'database' && !$options['table']) {
46: $type = 'default';
47: }
48:
49: if ($type === 'database') {
50: define('LC_SESSION_TABLE', db_table($options['table']));
51: }
52:
53: if (isset($options['table'])) {
54: # no need this anymore later
55: unset($options['table']);
56: }
57:
58: # Force to cookie based session management
59: $options['use_cookies'] = true;
60: $options['use_only_cookies'] = true;
61: $options['use_trans_sid'] = false;
62: $options['cookie_httponly'] = true;
63:
64: foreach ($options as $key => $value) {
65: if ($key == 'gc_maxlifetime' || $key == 'cookie_lifetime') {
66: $options[$key] = $value * 60; # change to seconds
67: }
68: }
69:
70: if ($type === 'database') {
71: session_set_save_handler(
72: '__session_open',
73: '__session_close',
74: '__session_read',
75: '__session_write',
76: '__session_destroy',
77: '__session_clean'
78: );
79: register_shutdown_function('session_write_close');
80: }
81:
82: if (function_exists('session_beforeStart')) {
83: call_user_func('session_beforeStart');
84: }
85:
86: session_start($options);
87: }
88: /**
89: * @internal
90: * @ignore
91: *
92: * A callback for Database Session save handler
93: * The open callback executed when the session is being opened.
94: *
95: * @return boolean Success
96: */
97: function __session_open()
98: {
99: return true;
100: }
101: /**
102: * @internal
103: * @ignore
104: *
105: * A callback for database Session save handler
106: * The close callback executed when the session is being opened.
107: *
108: * @return boolean Success
109: */
110: function __session_close()
111: {
112: global $lc_session;
113:
114: $probability = mt_rand(1, 100);
115: if ($probability <= 10) {
116: $maxlifetime = $lc_session['options']['gc_maxlifetime'];
117: __session_clean($maxlifetime);
118: }
119:
120: return true;
121: }
122: /**
123: * @internal
124: * @ignore
125: *
126: * A callback for database Session save handler
127: * The read callback is executed when the session starts or when `session_start()` is called
128: * Used to read from a database session
129: *
130: * @param mixed $sessionId The ID that uniquely identifies session in database
131: * @return mixed The value of the key or false if it does not exist
132: */
133: function __session_read($sessionId)
134: {
135: if (!$sessionId) {
136: return false;
137: }
138:
139: $sql = 'SELECT session FROM '.LC_SESSION_TABLE.' WHERE sid = ":id"';
140: $data = db_fetch($sql, array('id' => $sessionId));
141:
142: return $data ?: false;
143: }
144: /**
145: * @internal
146: * @ignore
147: *
148: * A callback for database Session save handler
149: * The write callback is called when the session needs to be saved and closed.
150: * Helper function called on write for database sessions.
151: *
152: * @param integer $sessionId The ID that uniquely identifies session in database
153: * @param mixed $data The value of the data to be saved.
154: * @return boolean True for successful write, false otherwise.
155: */
156: function __session_write($sessionId, $data)
157: {
158: if (!$sessionId) {
159: return false;
160: }
161:
162: $record = array(
163: 'id' => $sessionId,
164: 'host' => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '',
165: 'timestamp' => time(),
166: 'session' => $data,
167: 'useragent' => isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : ''
168: );
169:
170: $sql = 'REPLACE INTO '.LC_SESSION_TABLE.' (sid, host, timestamp, session, useragent)
171: VALUES (":id", ":host", ":timestamp", ":session", ":useragent")';
172:
173: return db_query($sql, $record) ? true : false;
174: }
175: /**
176: * @internal
177: * @ignore
178: *
179: * A callback for database Session save handler
180: * This destroy callback is executed when a session is destroyed with `session_destroy()`
181: * It is called on the destruction of a database session.
182: *
183: * @param integer $sessionId The ID that uniquely identifies session in database
184: * @return boolean True for successful delete, false otherwise.
185: */
186: function __session_destroy($sessionId)
187: {
188: return db_delete(LC_SESSION_TABLE, array('sid' => $sessionId)) ? true : false;
189: }
190: /**
191: * @internal
192: * @ignore
193: *
194: * A callback for database Session save handler
195: * The garbage collector callback is invoked internally by PHP periodically in order to purge old database session data
196: *
197: * @param integer $maxlifetime The value of lifetime which is passed to this callback
198: * that can be set in `$lc_session['options']['gc_maxlifetime']` reflected to `session.gc_maxlifetime`
199: * @return boolean Success
200: */
201: function __session_clean($maxlifetime)
202: {
203: $backTime = time() - $maxlifetime;
204: $sql = 'DELETE FROM '.LC_SESSION_TABLE.' WHERE timestamp < :backTime';
205:
206: return db_query($sql, array('backTime' => $backTime)) ? true : false;
207: }
208: /**
209: * Set a message or value in Session using a name
210: *
211: * @param $name string The session variable name to store the value
212: * It can be a value separated by period, eg., user.name will be ['user']['name']
213: * @param mixed $value The value to be stored.
214: * @param boolean $serialize The value is to be serialized or not
215: *
216: * @return void
217: */
218: function session_set($name, $value = '', $serialize = false)
219: {
220: __dotNotationToArray($name, 'session', $value, $serialize);
221: }
222: /**
223: * Get a message or value of the given name from Session
224: *
225: * @param string $name The session variable name to retrieve its value
226: * It can be a value separated by period, e.g., user.name will be ['user']['name']
227: * @param boolean $unserialize The value is to be unserialized or not
228: *
229: * @return mixed The value from SESSION
230: */
231: function session_get($name, $unserialize = false)
232: {
233: $value = __dotNotationToArray($name, 'session');
234:
235: return ($unserialize && is_string($value)) ? unserialize($value) : $value;
236: }
237: /**
238: * Delete a message or value of the given name from Session
239: *
240: * @param string $name The session variable name to delete its value
241: * @return boolean
242: */
243: function session_delete($name)
244: {
245: $name = S_PREFIX . $name;
246: if (isset($_SESSION[$name])) {
247: unset($_SESSION[$name]);
248: return true;
249: }
250:
251: $keys = explode('.', $name);
252: $firstKey = array_shift($keys);
253:
254: if (count($keys)) {
255: if (!isset($_SESSION[$firstKey])) {
256: return false;
257: }
258:
259: $array = &$_SESSION[$firstKey];
260: $parent = &$_SESSION[$firstKey];
261: foreach ($keys as $k) {
262: if (isset($array[$k])) {
263: $parent = &$array;
264: $array = &$array[$k];
265: } else {
266: return false;
267: }
268: }
269: $array = null;
270: unset($array);
271: unset($parent[$k]);
272: }
273:
274: return true;
275: }
276:
277: if (!function_exists('flash_set')) {
278: /**
279: * Set the flash message in session
280: * This function is overridable from the custom helpers/session_helper.php
281: *
282: * @param mixed $msg The message or array of messages to be shown
283: * @param string $name The optional session name to store the message
284: * @param string $class The HTML class name; default is success
285: *
286: * @return void
287: */
288: function flash_set($msg, $name = '', $class = 'success')
289: {
290: $msgHTML = _msg($msg, $class, 'html');
291: $name = $name ?: 'general';
292: $_SESSION[S_PREFIX . 'flashMessage'][$name] = $msgHTML;
293: }
294: }
295:
296: if (!function_exists('flash_get')) {
297: /**
298: * Get the flash message from session and then delete it
299: * This function is overridable from the custom helpers/session_helper.php
300: *
301: * @param string $name The optional session name to retrieve the message from
302: * @param bool $html Return HTML or plain text
303: *
304: * @return string The HTML message
305: */
306: function flash_get($name = '', $html = true)
307: {
308: $name = $name ?: 'general';
309: $message = '';
310: if (isset($_SESSION[S_PREFIX.'flashMessage'][$name])) {
311: $message = $_SESSION[S_PREFIX.'flashMessage'][$name];
312: unset($_SESSION[S_PREFIX.'flashMessage'][$name]);
313: }
314:
315: return $html ? $message : strip_tags($message);
316: }
317: }
318:
319: /**
320: * Send a cookie
321: * Convenience method for setcookie()
322: *
323: * @param string $name The name of the cookie. 'cookiename' is called as cookie_get('cookiename') or $_COOKIE['cookiename']
324: * @param mixed $value The value of the cookie. This value is stored on the clients computer
325: * @param int $expiry The time the cookie expires. This is a Unix timestamp so is in number of seconds since the epoch.
326: * In other words, you'll most likely set this with the time() function plus the number of seconds before you want it to expire.
327: * If f set to 0, or omitted, the cookie will expire at the end of the session
328: * @param string $path The path on the server in which the cookie will be available on. The default path '/' will make it available to the entire domain.
329: * @param string $domain The domain that the cookie is available to. If it is not set, it depends on the configuration variable $lc_siteDomain.
330: * @param bool $secure Indicates that the cookie should only be transmitted over a secure HTTPS connection from the client
331: * @param bool $httpOnly When TRUE the cookie will be made accessible only through the HTTP protocol.
332: * This means that the cookie won't be accessible by scripting languages, such as JavaScript
333: *
334: * @see http://php.net/manual/en/function.setcookie.php
335: *
336: * @return void
337: */
338: function cookie_set($name, $value, $expiry = 0, $path = '/', $domain = '', $secure = false, $httpOnly = false)
339: {
340: if (!$domain) {
341: $domain = _cfg('siteDomain');
342: }
343:
344: $name = preg_replace('/^('.S_PREFIX.')/', '', $name);
345: $name = S_PREFIX . $name;
346: if ($expiry > 0) {
347: $expiry = time() + $expiry;
348: }
349:
350: setcookie($name, $value, $expiry, $path, $domain, $secure, $httpOnly);
351: }
352: /**
353: * Get a cookie
354: * Convenience method to access $_COOKIE[cookiename]
355: * @param string $name The name of the cookie to retrieve
356: *
357: * @return mixed
358: * The value of the cookie if found.
359: * NULL if not found.
360: * The entire $_COOKIE array if $name is not provided.
361: */
362: function cookie_get($name = '')
363: {
364: if (empty($name)) {
365: return $_COOKIE;
366: }
367:
368: $name = preg_replace('/^('.S_PREFIX.')/', '', $name);
369: $name = S_PREFIX . $name;
370:
371: return (isset($_COOKIE[$name])) ? $_COOKIE[$name] : null;
372: }
373: /**
374: * Delete a cookie
375: * Convenience method to delete $_COOKIE['cookiename']
376: * @param string $name The name of the cookie to delete
377: * @param string $path The path on the server in which the cookie will be available on.
378: * This would be the same value used for cookie_set().
379: *
380: * @return bool|array TRUE for the successful delete; FALSE for no delete.
381: */
382: function cookie_delete($name, $path = '/')
383: {
384: if (empty($name)) {
385: return $_COOKIE;
386: }
387:
388: $name = preg_replace('/^('.S_PREFIX.')/', '', $name);
389: $name = S_PREFIX . $name;
390:
391: if (isset($_COOKIE[$name])) {
392: unset($_COOKIE[$name]);
393: setcookie($name, null, -1, $path);
394: return true;
395: }
396:
397: return !isset($_COOKIE[$name]);
398: }
399: