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