PageRenderTime 24ms CodeModel.GetById 16ms app.highlight 5ms RepoModel.GetById 1ms app.codeStats 0ms

/symphony/lib/core/class.session.php

https://bitbucket.org/bauhouse/symphony-2
PHP | 226 lines | 89 code | 27 blank | 110 comment | 15 complexity | db8eb91c0a092a367a338a9ababdf691 MD5 | raw file
  1<?php
  2
  3	/**
  4	 * @package core
  5	 */
  6
  7	 /**
  8	  * The Session class is a handler for all Session related logic in PHP. The functions
  9	  * map directly to all handler functions as defined by session_set_save_handler in
 10	  * PHP. In Symphony, this function is used in conjunction with the `Cookie` class.
 11	  * Based on: http://php.net/manual/en/function.session-set-save-handler.php#81761
 12	  * by klose at openriverbed dot de which was based on
 13	  * http://php.net/manual/en/function.session-set-save-handler.php#79706 by
 14	  * maria at junkies dot jp
 15	  *
 16	  * @link http://php.net/manual/en/function.session-set-save-handler.php
 17	  */
 18	require_once(CORE . '/class.cacheable.php');
 19
 20	Class Session{
 21
 22		/**
 23		 * If a Session has been created, this will be true, otherwise false
 24		 *
 25		 * @var boolean
 26		 */
 27		private static $_initialized = false;
 28
 29		/**
 30		 * False until a shutdown function is registered, true after that
 31		 *
 32		 * @var boolean
 33		 */
 34		private static $_registered = false;
 35
 36		/**
 37		 * Starts a Session object, only if one doesn't already exist. This function maps
 38		 * the Session Handler functions to this classes methods by reading the default
 39		 * information from the PHP ini file.
 40		 *
 41		 * @link http://php.net/manual/en/function.session-set-save-handler.php
 42		 * @link http://php.net/manual/en/function.session-set-cookie-params.php
 43		 * @param integer $lifetime
 44		 *  How long a Session is valid for, by default this is 0, which means it
 45		 *  never expires
 46		 * @param string $path
 47		 *  The path the cookie is valid for on the domain
 48		 * @param string $domain
 49		 *  The domain this cookie is valid for
 50		 * @param boolean $httpOnly
 51		 *  Whether this cookie can be read by Javascript. By default the cookie
 52		 *  can be read using Javascript and PHP
 53		 * @return string|boolean
 54		 *  Returns the Session ID on success, or false on error.
 55		 */
 56		public static function start($lifetime = 0, $path = '/', $domain = NULL, $httpOnly = false) {
 57
 58			if (!self::$_initialized) {
 59
 60				if(!is_object(Symphony::Database()) || !Symphony::Database()->isConnected()) return false;
 61
 62				if (session_id() == '') {
 63					ini_set('session.save_handler', 'user');
 64					ini_set('session.gc_maxlifetime', $lifetime);
 65					ini_set('session.gc_probability', '1');
 66					ini_set('session.gc_divisor', Symphony::Configuration()->get('session_gc_divisor', 'symphony'));
 67				}
 68
 69				session_set_save_handler(
 70					array('Session', 'open'),
 71					array('Session', 'close'),
 72					array('Session', 'read'),
 73					array('Session', 'write'),
 74					array('Session', 'destroy'),
 75					array('Session', 'gc')
 76				);
 77
 78				session_set_cookie_params($lifetime, $path, ($domain ? $domain : self::getDomain()), false, $httpOnly);
 79
 80				if(session_id() == ""){
 81					if(headers_sent()){
 82						throw new Exception('Headers already sent. Cannot start session.');
 83					}
 84					session_start();
 85				}
 86
 87				self::$_initialized = true;
 88			}
 89
 90			return session_id();
 91		}
 92
 93		/**
 94		 * Returns the current domain for the Session to be saved to, if the installation
 95		 * is on localhost, this returns null and just allows PHP to take care of setting
 96		 * the valid domain for the Session, otherwise it will return the non-www version
 97		 * of the domain host.
 98		 *
 99		 * @return string|null
100		 *  Null if on localhost, or HTTP_HOST is not set, a string of the domain name sans
101		 *  www otherwise
102		 */
103		public static function getDomain() {
104			if(isset($_SERVER['HTTP_HOST'])){
105
106				if(preg_match('/(localhost|127\.0\.0\.1)/', $_SERVER['HTTP_HOST']) || $_SERVER['SERVER_ADDR'] == '127.0.0.1'){
107					return null; // prevent problems on local setups
108				}
109
110				return preg_replace('/(^www.|:\d+$)/i', NULL, $_SERVER['HTTP_HOST']);
111
112			}
113
114			return null;
115		}
116
117		/**
118		 * Called when a Session is created, registers the close function
119		 *
120		 * @return boolean
121		 *  Always returns true
122		 */
123		public static function open() {
124			if (!self::$_registered) {
125				register_shutdown_function('session_write_close');
126				self::$_registered = true;
127			}
128
129			return self::$_registered;
130		}
131
132		/**
133		 * Allows the Session to close without any further logic. Acts as a
134		 * destructor function for the Session.
135		 *
136		 * @return boolean
137		 *  Always returns true
138		 */
139		public static function close() {
140			return true;
141		}
142
143		/**
144		 * Given an ID, and some data, save it into `tbl_sessions`. This uses
145		 * the ID as a unique key, and will override any existing data. If the
146		 * `$data` is deemed to be empty, no row will be saved in the database
147		 * unless there is an existing row.
148		 *
149		 * @param string $id
150		 *  The ID of the Session, usually a hash
151		 * @param string $data
152		 *  The Session information, usually a serialized object of
153		 * `$_SESSION[Cookie->_index]`
154		 * @return boolean
155		 *  True if the Session information was saved successfully, false otherwise
156		 */
157		public static function write($id, $data) {
158			// Only prevent this record from saving if there isn't already a record
159			// in the database. This prevents empty Sessions from being created, but
160			// allows them to be nulled.
161			if(is_null(Session::read($id))) {
162				if(preg_match('/^([^}]+\|a:0:{})+$/i', $data)) return true;
163			}
164
165			$fields = array(
166				'session' => $id,
167				'session_expires' => time(),
168				'session_data' => $data
169			);
170			return Symphony::Database()->insert($fields, 'tbl_sessions', true);
171		}
172
173		/**
174		 * Given a session's ID, return it's row from `tbl_sessions`
175		 *
176		 * @param string $id
177		 *  The identifier for the Session to fetch
178		 * @return string
179		 *  The serialised session data
180		 */
181		public static function read($id) {
182			return Symphony::Database()->fetchVar(
183				'session_data', 0,
184				sprintf(
185					"SELECT `session_data` FROM `tbl_sessions` WHERE `session` = '%s' LIMIT 1",
186					Symphony::Database()->cleanValue($id)
187				)
188			);
189		}
190
191		/**
192		 * Given a session's ID, remove it's row from `tbl_sessions`
193		 *
194		 * @param string $id
195		 *  The identifier for the Session to destroy
196		 * @return boolean
197		 *  True if the Session was deleted successfully, false otherwise
198		 */
199		public static function destroy($id) {
200			return Symphony::Database()->query(
201				sprintf(
202					"DELETE FROM `tbl_sessions` WHERE `session` = '%s'",
203					Symphony::Database()->cleanValue($id)
204				)
205			);
206		}
207
208		/**
209		 * The garbage collector, which removes all empty Sessions, or any
210		 * Sessions that have expired. This has a 10% chance of firing based
211		 * off the `gc_probability`/`gc_divisor`.
212		 *
213		 * @param integer $max
214		 *  The max session lifetime.
215		 * @return boolean
216		 *  True on Session deletion, false if an error occurs
217		 */
218		public static function gc($max) {
219			return Symphony::Database()->query(
220				sprintf(
221					"DELETE FROM `tbl_sessions` WHERE `session_expires` <= '%s'",
222					Symphony::Database()->cleanValue(time() - $max)
223				)
224			);
225		}
226	}