|
eZ Publish
[trunk]
|
00001 <?php 00002 /** 00003 * File containing session interface 00004 * 00005 * @copyright Copyright (C) 1999-2012 eZ Systems AS. All rights reserved. 00006 * @license http://www.gnu.org/licenses/gpl-2.0.txt GNU General Public License v2 00007 * @version //autogentag// 00008 * @package lib 00009 */ 00010 00011 /** 00012 * eZ Publish Session interface class 00013 * 00014 * Session wrapper for session management, with support for handlers. 00015 * Handler is defined by site.ini\[Session]\Handler setting. 00016 * 00017 * The session system has a hook system which allows external code to perform 00018 * extra tasks before and after a certain action is executed. For instance to 00019 * cleanup a table when sessions are removed. 00020 * This can be used by adding a callback with the eZSession::addCallback function, 00021 * first param is type and second is callback (called with call_user_func_array) 00022 * 00023 * \code 00024 * function cleanupStuff( $db, $key, $escKey ) 00025 * { 00026 * // Do cleanup here 00027 * } 00028 * 00029 * eZSession::addCallback( 'destroy_pre', 'cleanupstuff'); 00030 * // Or if it was a class function: 00031 * // eZSession::addCallback( 'destroy_pre', array('myClass', 'myCleanupStuff') ); 00032 * \endcode 00033 * 00034 * When a specific session is destroyed in the database it will call the 00035 * \c destroy_pre and \c destroy_post hooks. The signature of the function is 00036 * function destroy( $db, $key, $escapedKey ) 00037 * 00038 * When a specific session is regenerated (login/logout) and kept it will call 00039 * \c regenerate_pre and \c regenerate_post hooks. The signature of the function is 00040 * function regenerate( $db, $escapedNewKey, $escapedOldKey, $escUserID ) 00041 * 00042 * When multiple sessions are expired (garbage collector) in the database it 00043 * will call the \c gc_pre and \c gc_post hooks. The signature of the function is 00044 * function gcollect( $db, $expiredTime ) 00045 * 00046 * When all sessions are removed from the database it will call the 00047 * \c cleanup_pre and \c cleanup_post hooks. The signature of the function is 00048 * function cleanup( $db ) 00049 * 00050 * \param $db The database object used by the session manager. 00051 * \param $key The session key which are being worked on, see also \a $escapedKey 00052 * \param $escapedKey The same key as \a $key but is escaped correctly for the database. 00053 * Use this to save a call to eZDBInterface::escapeString() 00054 * \param $expirationTime An integer specifying the timestamp of when the session 00055 * will expire. 00056 * \param $expiredTime Similar to \a $expirationTime but is the time used to figure 00057 * if a session is expired in the database. ie. all sessions with 00058 * lower expiration times will be removed. 00059 * 00060 * @package lib 00061 * @subpackage ezsession 00062 */ 00063 class eZSession 00064 { 00065 /** 00066 * User id, see {@link eZSession::userID()}. 00067 * 00068 * @var int 00069 */ 00070 static protected $userID = 0; 00071 00072 /** 00073 * Flag session has started, see {@link eZSession::start()}. 00074 * 00075 * @var bool 00076 */ 00077 static protected $hasStarted = false; 00078 00079 /** 00080 * Flag request contains session cookie, set in {@link eZSession::registerFunctions()}. 00081 * 00082 * @var bool|null 00083 */ 00084 static protected $hasSessionCookie = null; 00085 00086 /** 00087 * List of callback actions, see {@link eZSession::addCallback()}. 00088 * 00089 * @var array 00090 */ 00091 static protected $callbackFunctions = array(); 00092 00093 /** 00094 * Current session handler or false, see {@link eZSession::getHandlerInstance()}. 00095 * 00096 * @var ezpSessionHandler|null 00097 */ 00098 static protected $handlerInstance = null; 00099 00100 /** 00101 * Constructor (not used, this is an all static class) 00102 */ 00103 protected function __construct() 00104 { 00105 } 00106 00107 /** 00108 * Get session value (wrapper) 00109 * 00110 * @since 4.4 00111 * @param string|null $key Return the whole session array if null otherwise the value of $key 00112 * @param null|mixed $defaultValue Return this if not null and session has not started 00113 * @return mixed|null $defaultValue if key does not exist, otherwise session value depending on $key 00114 */ 00115 static public function &get( $key = null, $defaultValue = null ) 00116 { 00117 if ( self::$hasStarted === false ) 00118 { 00119 if ( $defaultValue !== null ) 00120 return $defaultValue; 00121 self::start(); 00122 } 00123 00124 if ( $key === null ) 00125 return $_SESSION; 00126 else if ( isset( $_SESSION[ $key ] ) ) 00127 return $_SESSION[ $key ]; 00128 return $defaultValue; 00129 } 00130 00131 /** 00132 * Set session value (wrapper) 00133 * 00134 * @since 4.4 00135 * @param string $key 00136 * @return bool 00137 */ 00138 static public function set( $key, $value ) 00139 { 00140 if ( self::$hasStarted === false ) 00141 { 00142 self::start(); 00143 } 00144 00145 $_SESSION[ $key ] = $value; 00146 return true; 00147 } 00148 00149 /** 00150 * Isset session value (wrapper) 00151 * 00152 * @since 4.4 00153 * @param string $key 00154 * @param bool $forceStart Force session start if true 00155 * @return bool|null Null if session has not started and $forceStart is false 00156 */ 00157 static public function issetkey( $key, $forceStart = true ) 00158 { 00159 if ( self::$hasStarted === false ) 00160 { 00161 if ( !$forceStart ) 00162 return null; 00163 self::start(); 00164 } 00165 00166 return isset( $_SESSION[ $key ] ); 00167 } 00168 00169 /** 00170 * unset session value (wrapper) 00171 * 00172 * @since 4.4 00173 * @param string $key 00174 * @param bool $forceStart Force session start if true 00175 * @return bool|null True if value was removed, false if it did not exist and 00176 * null if session is not started and $forceStart is false 00177 */ 00178 static public function unsetkey( $key, $forceStart = true ) 00179 { 00180 if ( self::$hasStarted === false ) 00181 { 00182 if ( !$forceStart ) 00183 return null; 00184 self::start(); 00185 } 00186 00187 if ( !isset( $_SESSION[ $key ] ) ) 00188 return false; 00189 00190 unset( $_SESSION[ $key ] ); 00191 return true; 00192 } 00193 00194 /** 00195 * Deletes all expired session data in the database, this function is not supported 00196 * by session handlers that don't have a session backend on their own. 00197 * 00198 * @since 4.1 00199 * @return bool 00200 */ 00201 static public function garbageCollector() 00202 { 00203 return self::getHandlerInstance()->gc( (int)$_SERVER['REQUEST_TIME'] ); 00204 } 00205 00206 /** 00207 * Truncates all session data in the database, this function is not supported 00208 * by session handlers that don't have a session backend on their own. 00209 * 00210 * @since 4.1 00211 * @return bool 00212 */ 00213 static public function cleanup() 00214 { 00215 return self::getHandlerInstance()->cleanup(); 00216 } 00217 00218 /** 00219 * Counts the number of active session and returns it, this function is not supported 00220 * by session handlers that don't have a session backend on their own. 00221 * 00222 * @since 4.1 00223 * @return string Number of sessions. 00224 */ 00225 static public function countActive() 00226 { 00227 return self::getHandlerInstance()->count(); 00228 } 00229 00230 /** 00231 * Register the needed session functions, this is called automatically by 00232 * {@link eZSession::start()}, so only call this if you don't start the session. 00233 * 00234 * @since 4.1 00235 * @return bool Depending on if eZSession is registrated as session handler. 00236 */ 00237 static protected function registerFunctions() 00238 { 00239 if ( self::$hasStarted || self::$handlerInstance !== null ) 00240 return false; 00241 00242 $ini = eZINI::instance(); 00243 if ( $ini->variable( 'Session', 'SessionNameHandler' ) === 'custom' ) 00244 { 00245 $sessionName = $ini->variable( 'Session', 'SessionNamePrefix' ); 00246 if ( $ini->variable( 'Session', 'SessionNamePerSiteAccess' ) === 'enabled' ) 00247 { 00248 $access = $GLOBALS['eZCurrentAccess']; 00249 // Use md5 to make sure name is only consistent of alphanumeric characters 00250 $sessionName .= md5( $access['name'] ); 00251 } 00252 session_name( $sessionName ); 00253 } 00254 else 00255 { 00256 $sessionName = session_name(); 00257 } 00258 00259 // See if user has session, used to avoid reading from db if no session. 00260 // Allow session bye post params for use by flash, but use $_POST directly 00261 // to avoid session double start issues ( #014686 ) caused by eZHTTPTool 00262 if ( isset( $_POST[ $sessionName ] ) ) 00263 { 00264 // First use session id from post params (for use in flash upload) 00265 session_id( $_POST[ $sessionName ] ); 00266 self::$hasSessionCookie = true; 00267 } 00268 else 00269 { 00270 // else check cookie as used by default 00271 self::$hasSessionCookie = isset( $_COOKIE[ $sessionName ] ); 00272 } 00273 00274 return self::getHandlerInstance()->setSaveHandler(); 00275 } 00276 00277 /** 00278 * Set default cookie parameters based on site.ini settings (fallback to php.ini settings) 00279 * Used by {@link eZSession::registerFunctions()} 00280 * Note: this will only have affect when session is created / re-created 00281 * 00282 * @since 4.4 00283 * @param int|false $lifetime Cookie timeout of session cookie, will read from ini if not set 00284 */ 00285 static public function setCookieParams( $lifetime = false ) 00286 { 00287 $ini = eZINI::instance(); 00288 $params = session_get_cookie_params(); 00289 if ( $lifetime === false ) 00290 { 00291 if ( $ini->hasVariable('Session', 'CookieTimeout') 00292 && $ini->variable('Session', 'CookieTimeout') ) 00293 $lifetime = (int) $ini->variable('Session', 'CookieTimeout'); 00294 else 00295 $lifetime = $params['lifetime']; 00296 } 00297 $path = $ini->hasVariable('Session', 'CookiePath') ? $ini->variable('Session', 'CookiePath') : $params['path']; 00298 $domain = $ini->hasVariable('Session', 'CookieDomain') ? $ini->variable('Session', 'CookieDomain') : $params['domain']; 00299 $secure = $ini->hasVariable('Session', 'CookieSecure') ? $ini->variable('Session', 'CookieSecure') : $params['secure']; 00300 if ( isset( $params['httponly'] ) ) // only available on PHP 5.2 and up 00301 { 00302 $httponly = $ini->hasVariable('Session', 'CookieHttponly') ? $ini->variable('Session', 'CookieHttponly') : $params['httponly']; 00303 session_set_cookie_params( $lifetime, $path, $domain, $secure, $httponly ); 00304 } 00305 else 00306 { 00307 session_set_cookie_params( $lifetime, $path, $domain, $secure ); 00308 } 00309 } 00310 00311 /** 00312 * Starts the session and sets the timeout of the session cookie. 00313 * Multiple calls will be ignored unless you call {@link eZSession::stop()} first. 00314 * 00315 * @since 4.1 00316 * @param int|false $cookieTimeout Use this to set custom cookie timeout. 00317 * @return bool Depending on if session was started. 00318 */ 00319 static public function start( $cookieTimeout = false ) 00320 { 00321 if ( self::lazyStart( false ) === false ) 00322 { 00323 return false; 00324 } 00325 self::setCookieParams( $cookieTimeout ); 00326 return self::forceStart(); 00327 } 00328 00329 /** 00330 * Inits eZSession and starts it if user has cookie and $startIfUserHasCookie is true. 00331 * 00332 * @since 4.4 00333 * @param bool $startIfUserHasCookie 00334 * @return bool|null 00335 */ 00336 static public function lazyStart( $startIfUserHasCookie = true ) 00337 { 00338 if ( self::$hasStarted || 00339 ( isset( $GLOBALS['eZSiteBasics']['session-required'] ) && 00340 !$GLOBALS['eZSiteBasics']['session-required'] ) ) 00341 { 00342 return false; 00343 } 00344 self::registerFunctions(); 00345 if ( $startIfUserHasCookie && self::$hasSessionCookie ) 00346 { 00347 self::setCookieParams(); 00348 return self::forceStart(); 00349 } 00350 return null; 00351 } 00352 00353 /** 00354 * See {@link eZSession::start()} 00355 * 00356 * @since 4.4 00357 * @return true 00358 */ 00359 static protected function forceStart() 00360 { 00361 session_start(); 00362 return self::$hasStarted = true; 00363 } 00364 00365 /** 00366 * Gets/generates the user hash for use in validating the session based on [Session] 00367 * SessionValidation* site.ini settings. The default hash is result of md5('empty'). 00368 * 00369 * @since 4.1 00370 * @deprecated as of 4.4, only returns default md5('empty') hash now for BC. 00371 * @return string MD5 hash based on parts of the user ip and agent string. 00372 */ 00373 static public function getUserSessionHash() 00374 { 00375 return 'a2e4822a98337283e39f7b60acf85ec9'; 00376 } 00377 00378 /** 00379 * Writes session data and stops the session, if not already stopped. 00380 * 00381 * @since 4.1 00382 * @return bool Depending on if session was stopped. 00383 */ 00384 static public function stop() 00385 { 00386 if ( !self::$hasStarted ) 00387 { 00388 return false; 00389 } 00390 session_write_close(); 00391 self::$hasStarted = false; 00392 self::$handlerInstance = null; 00393 return true; 00394 } 00395 00396 /** 00397 * Will make sure the user gets a new session ID while keepin the session data. 00398 * This is useful to call on logins, to avoid sessions theft from users. 00399 * NOTE: make sure you set new user id first using {@link eZSession::setUserID()} 00400 * 00401 * @since 4.1 00402 * @param bool $updateBackendData set to false to not update session backend with new session id and user id. 00403 * @return bool Depending on if session was regenerated. 00404 */ 00405 static public function regenerate( $updateBackendData = true ) 00406 { 00407 if ( !self::$hasStarted ) 00408 { 00409 return false; 00410 } 00411 if ( !function_exists( 'session_regenerate_id' ) ) 00412 { 00413 return false; 00414 } 00415 if ( headers_sent() ) 00416 { 00417 if ( PHP_SAPI !== 'cli' ) 00418 eZDebug::writeWarning( 'Could not regenerate session id, HTTP headers already sent.', __METHOD__ ); 00419 return false; 00420 } 00421 00422 return self::getHandlerInstance()->regenerate( ($updateBackendData && self::$hasSessionCookie) ); 00423 } 00424 00425 /** 00426 * Removes the current session and resets session variables. 00427 * Note: implicit stops session as well! 00428 * 00429 * @since 4.1 00430 * @return bool Depending on if session was removed. 00431 */ 00432 static public function remove() 00433 { 00434 if ( !self::$hasStarted ) 00435 { 00436 return false; 00437 } 00438 $_SESSION = array(); 00439 session_destroy(); 00440 self::$hasStarted = false; 00441 self::$handlerInstance = null; 00442 return true; 00443 } 00444 00445 /** 00446 * Sets the current userID used by ezpSessionHandlerDB::write() on shutdown. 00447 * 00448 * @since 4.1 00449 * @param int $userID to use in {@link ezpSessionHandlerDB::write()} 00450 */ 00451 static public function setUserID( $userID ) 00452 { 00453 self::$userID = $userID; 00454 } 00455 00456 /** 00457 * Gets the current user id. 00458 * 00459 * @since 4.1 00460 * @return int User id stored by {@link eZSession::setUserID()} 00461 */ 00462 static public function userID() 00463 { 00464 return self::$userID; 00465 } 00466 00467 /** 00468 * Returns if user had session cookie at start of request or not. 00469 * 00470 * @since 4.1 00471 * @return bool|null Null if session is not started yet. 00472 */ 00473 static public function userHasSessionCookie() 00474 { 00475 return self::$hasSessionCookie; 00476 } 00477 00478 /** 00479 * Returns if user session validated against stored data in db 00480 * or if it was invalidated during the current request. 00481 * 00482 * @since 4.1 00483 * @deprecated as of 4.4, only returns true for bc 00484 * @return bool|null Null if user is not validated yet (for instance a new session). 00485 */ 00486 static public function userSessionIsValid() 00487 { 00488 return true; 00489 } 00490 00491 /** 00492 * Return value to indicate if session has started or not 00493 * 00494 * @since 4.4 00495 * @return bool 00496 */ 00497 static public function hasStarted() 00498 { 00499 return self::$hasStarted; 00500 } 00501 00502 /** 00503 * Get curren session handler 00504 * 00505 * @since 4.4 00506 * @return ezpSessionHandler 00507 */ 00508 static public function getHandlerInstance() 00509 { 00510 if ( self::$handlerInstance === null ) 00511 { 00512 $ini = eZINI::instance(); 00513 if ( $ini->variable( 'Session', 'Handler' ) !== '' ) 00514 { 00515 $optionArray = array( 'iniFile' => 'site.ini', 00516 'iniSection' => 'Session', 00517 'iniVariable' => 'Handler', 00518 'handlerParams' => array( self::$hasSessionCookie ) ); 00519 00520 $options = new ezpExtensionOptions( $optionArray ); 00521 self::$handlerInstance = eZExtension::getHandlerClass( $options ); 00522 } 00523 if ( !self::$handlerInstance instanceof ezpSessionHandler ) 00524 { 00525 self::$handlerInstance = new ezpSessionHandlerPHP( self::$hasSessionCookie ); 00526 } 00527 } 00528 return self::$handlerInstance; 00529 } 00530 00531 /** 00532 * Adds a callback function, to be triggered by {@link eZSession::triggerCallback()} 00533 * when a certain session event occurs. 00534 * Use: eZSession::addCallback('gc_pre', myCustomGarabageFunction ); 00535 * 00536 * @since 4.1 00537 * @deprecated since 4.5, use {@link ezpEvent::getInstance()->attach()} with new events 00538 * @param string $type cleanup, gc, destroy, insert and update, pre and post types. 00539 * @param handler $callback a function to call. 00540 */ 00541 static public function addCallback( $type, $callback ) 00542 { 00543 if ( !isset( self::$callbackFunctions[$type] ) ) 00544 { 00545 self::$callbackFunctions[$type] = array(); 00546 } 00547 self::$callbackFunctions[$type][] = $callback; 00548 } 00549 00550 /** 00551 * Triggers callback functions by type, registrated by {@link eZSession::addCallback()} 00552 * Use: eZSession::triggerCallback('gc_pre', array( $db, $time ) ); 00553 * 00554 * @since 4.1 00555 * @deprecated since 4.5, use {@link ezpEvent::getInstance()->notify()} with new events 00556 * @param string $type cleanup, gc, destroy, insert and update, pre and post types. 00557 * @param array $params list of parameters to pass to the callback function. 00558 * @return bool 00559 */ 00560 static public function triggerCallback( $type, $params ) 00561 { 00562 if ( isset( self::$callbackFunctions[$type] ) ) 00563 { 00564 foreach( self::$callbackFunctions[$type] as $callback ) 00565 { 00566 call_user_func_array( $callback, $params ); 00567 } 00568 return true; 00569 } 00570 return false; 00571 } 00572 } 00573 00574 ?>