1<?php 2/** 3 * +-----------------------------------------------------------------------+ 4 * | Copyright (c) 2004, Tony Bibbs | 5 * | All rights reserved. | 6 * | | 7 * | Redistribution and use in source and binary forms, with or without | 8 * | modification, are permitted provided that the following conditions | 9 * | are met: | 10 * | | 11 * | o Redistributions of source code must retain the above copyright | 12 * | notice, this list of conditions and the following disclaimer. | 13 * | o Redistributions in binary form must reproduce the above copyright | 14 * | notice, this list of conditions and the following disclaimer in the | 15 * | documentation and/or other materials provided with the distribution.| 16 * | o The names of the authors may not be used to endorse or promote | 17 * | products derived from this software without specific prior written | 18 * | permission. | 19 * | | 20 * | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 21 * | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 22 * | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 23 * | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 24 * | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 25 * | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 26 * | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 27 * | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 28 * | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 29 * | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 30 * | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 31 * | | 32 * +-----------------------------------------------------------------------+ 33 * | Author: Tony Bibbs <tony@geeklog.net> | 34 * +-----------------------------------------------------------------------+ 35 * 36 * PHP version 5 37 * 38 * @category HTTP 39 * @package HTTP_Session2 40 * @author Alexander Radivaniovich <info@wwwlab.net> 41 * @author Tony Bibbs <tony@geeklog.net> 42 * @license http://www.opensource.org/licenses/bsd-license.php The BSD License 43 * @version CVS: $Id: Session2.php 267739 2008-10-25 16:54:23Z till $ 44 * @link http://pear.php.net/package/HTTP_Session2 45 */ 46 47/** 48 * HTTP_Session2_Exception 49 */ 50require_once 'HTTP/Session2/Exception.php'; 51 52/** 53 * Class for managing HTTP sessions 54 * 55 * Provides access to session-state values as well as session-level 56 * settings and lifetime management methods. 57 * Based on the standart PHP session handling mechanism 58 * it provides for you more advanced features such as 59 * database container, idle and expire timeouts, etc. 60 * 61 * Expample 1: 62 * 63 * <code> 64 * // Setting some options and detecting of a new session 65 * HTTP_Session2::useCookies(false); 66 * HTTP_Session2::start('MySessionID'); 67 * HTTP_Session2::set('variable', 'The string'); 68 * if (HTTP_Session2::isNew()) { 69 * echo 'new session was created with the current request'; 70 * $visitors++; // Increase visitors count 71 * } 72 * 73 * //HTTP_Session2::regenerateId(); 74 * </code> 75 * 76 * Example 2: 77 * 78 * <code> 79 * // Using database container 80 * HTTP_Session2::setContainer('DB'); 81 * HTTP_Session2::start(); 82 * </code> 83 * 84 * Example 3: 85 * 86 * <code> 87 * // Setting timeouts 88 * HTTP_Session2::start(); 89 * HTTP_Session2::setExpire(time() + 60 * 60); // expires in one hour 90 * HTTP_Session2::setIdle(10 * 60); // idles in ten minutes 91 * if (HTTP_Session2::isExpired()) { 92 * // expired 93 * echo('Your session is expired!'); 94 * HTTP_Session2::destroy(); 95 * } 96 * if (HTTP_Session2::isIdle()) { 97 * // idle 98 * echo('You've been idle for too long!'); 99 * HTTP_Session2::destroy(); 100 * } 101 * HTTP_Session2::updateIdle(); 102 * </code> 103 * 104 * @category HTTP 105 * @package HTTP_Session2 106 * @author Alexander Radivaniovich <info@wwwlab.net> 107 * @author Tony Bibbs <tony@geeklog.net> 108 * @license http://www.opensource.org/licenses/bsd-license.php The BSD License 109 * @version Release: @package_version@ 110 * @link http://pear.php.net/package/HTTP_Session2 111 */ 112class HTTP_Session2 113{ 114 /** 115 * @const STARTED - The session was started with the current request 116 */ 117 const STARTED = 1; 118 119 /** 120 * @const CONTINUE - No new session was started with the current request 121 */ 122 const CONTINUED = 2; 123 124 /** 125 * @const ERR_UNKNOWN_CONTAINER - Container not found. 126 */ 127 const ERR_UNKNOWN_CONTAINER = 667; 128 129 /** 130 * @const ERR_SYSTEM_PERM - System permissions not sufficient. 131 * E.g. Not enough permissions to override ini-settings. 132 */ 133 const ERR_SYSTEM_PERM = 668; 134 135 /** 136 * @const ERR_SYSTEM_PRECONDITION - Precondition failed. E.g. error occured and 137 * HTTP_Session2 can't start up, etc.. 138 */ 139 const ERR_SYSTEM_PRECONDITION = 669; 140 141 /** 142 * @const ERR_NOT_IMPLEMENTED Feature is not yet Implement in the container. 143 */ 144 const ERR_NOT_IMPLEMENTED = 670; 145 146 /** 147 * Container instance 148 */ 149 public static $container; 150 151 /** 152 * Sets user-defined session storage functions 153 * 154 * Sets the user-defined session storage functions which are used 155 * for storing and retrieving data associated with a session. 156 * This is most useful when a storage method other than 157 * those supplied by PHP sessions is preferred. 158 * i.e. Storing the session data in a local database. 159 * 160 * @param string $container Name of the container (e.g. DB, MDB, ...). 161 * @param array $container_options Options, most likely an array. 162 * 163 * @return void 164 * @see session_set_save_handler() 165 */ 166 static function setContainer($container, $container_options = null) 167 { 168 $container_class = 'HTTP_Session2_Container_' . $container; 169 $container_classfile = 'HTTP/Session2/Container/' . $container . '.php'; 170 171 if (!class_exists($container_class)) { 172 include_once $container_classfile; 173 } 174 if (!class_exists($container_class)) { 175 throw new HTTP_Session2_Exception( 176 "Container class, $container_class, does not exist", 177 self::ERR_UNKNOWN_CONTAINER); 178 } 179 self::$container = new $container_class($container_options); 180 181 self::$container->set(); 182 } 183 184 /** 185 * Initializes session data 186 * 187 * Creates a session (or resumes the current one 188 * based on the session id being passed 189 * via a GET variable or a cookie). 190 * You can provide your own name and/or id for a session. 191 * 192 * @param string $name Name of a session, default is 'SessionID' 193 * @param string $id Id of a session which will be used 194 * only when the session is new 195 * 196 * @return void 197 * @see session_name() 198 * @see session_id() 199 * @see session_start() 200 */ 201 public static function start($name = 'SessionID', $id = null) 202 { 203 self::name($name); 204 if (is_null(self::detectID())) { 205 if ($id) { 206 self::id($id); 207 } else { 208 self::id(uniqid(dechex(rand()))); 209 } 210 } 211 session_start(); 212 if (!isset($_SESSION['__HTTP_Session2_Info'])) { 213 $_SESSION['__HTTP_Session2_Info'] = self::STARTED; 214 } else { 215 $_SESSION['__HTTP_Session2_Info'] = self::CONTINUED; 216 } 217 } 218 219 /** 220 * Writes session data and ends session 221 * 222 * Session data is usually stored after your script 223 * terminated without the need to call HTTP_Session2::stop(), 224 * but as session data is locked to prevent concurrent 225 * writes only one script may operate on a session at any time. 226 * When using framesets together with sessions you will 227 * experience the frames loading one by one due to this 228 * locking. You can reduce the time needed to load all the 229 * frames by ending the session as soon as all changes 230 * to session variables are done. 231 * 232 * @return void 233 * @see session_write_close() 234 */ 235 public static function pause() 236 { 237 session_write_close(); 238 } 239 240 /** 241 * Frees all session variables and destroys all data 242 * registered to a session 243 * 244 * This method resets the $_SESSION variable and 245 * destroys all of the data associated 246 * with the current session in its storage (file or DB). 247 * It forces new session to be started after this method 248 * is called. It does not unset the session cookie. 249 * 250 * @return void 251 * @see session_unset() 252 * @see session_destroy() 253 */ 254 public static function destroy() 255 { 256 session_unset(); 257 session_destroy(); 258 } 259 260 /** 261 * Free all session variables 262 * 263 * @todo TODO Save expire and idle timestamps? 264 * @return void 265 */ 266 public static function clear() 267 { 268 $info = $_SESSION['__HTTP_Session2_Info']; 269 270 session_unset(); 271 272 $_SESSION['__HTTP_Session2_Info'] = $info; 273 } 274 275 /** 276 * Tries to find any session id in $_GET, $_POST or $_COOKIE 277 * 278 * @return string Session ID (if exists) or null 279 */ 280 public static function detectID() 281 { 282 if (self::useCookies()) { 283 if (isset($_COOKIE[self::name()])) { 284 return $_COOKIE[self::name()]; 285 } 286 } else { 287 if (isset($_GET[self::name()])) { 288 return $_GET[self::name()]; 289 } 290 if (isset($_POST[self::name()])) { 291 return $_POST[self::name()]; 292 } 293 } 294 return null; 295 } 296 297 /** 298 * Sets new name of a session 299 * 300 * @param string $name New name of a sesion 301 * 302 * @return string Previous name of a session 303 * @see session_name() 304 */ 305 public static function name($name = null) 306 { 307 if (isset($name)) { 308 return session_name($name); 309 } 310 return session_name(); 311 } 312 313 /** 314 * Sets new ID of a session 315 * 316 * @param string $id New ID of a sesion 317 * 318 * @return string Previous ID of a session 319 * @see session_id() 320 */ 321 public static function id($id = null) 322 { 323 if (isset($id)) { 324 return session_id($id); 325 } 326 return session_id(); 327 } 328 329 /** 330 * Sets the maximum expire time 331 * 332 * @param integer $time Time in seconds 333 * @param bool $add Add time to current expire time or not 334 * 335 * @return void 336 */ 337 public static function setExpire($time, $add = false) 338 { 339 if ($add && isset($_SESSION['__HTTP_Session2_Expire'])) { 340 $_SESSION['__HTTP_Session2_Expire'] += $time; 341 } else { 342 $_SESSION['__HTTP_Session2_Expire'] = $time; 343 } 344 if (!isset($_SESSION['__HTTP_Session2_Expire_TS'])) { 345 $_SESSION['__HTTP_Session2_Expire_TS'] = time(); 346 } 347 } 348 349 /** 350 * Sets the maximum idle time 351 * 352 * Sets the time-out period allowed 353 * between requests before the session-state 354 * provider terminates the session. 355 * 356 * @param integer $time Time in seconds 357 * @param bool $add Add time to current maximum idle time or not 358 * 359 * @return void 360 */ 361 public static function setIdle($time, $add = false) 362 { 363 if ($add && isset($_SESSION['__HTTP_Session2_Idle'])) { 364 $_SESSION['__HTTP_Session2_Idle'] += $time; 365 } else { 366 $_SESSION['__HTTP_Session2_Idle'] = $time; 367 } 368 if (!isset($_SESSION['__HTTP_Session2_Idle_TS'])) { 369 $_SESSION['__HTTP_Session2_Idle_TS'] = time(); 370 } 371 } 372 373 /** 374 * Returns the time up to the session is valid 375 * 376 * @return integer Time when the session idles 377 */ 378 public static function sessionValidThru() 379 { 380 if ( 381 !isset($_SESSION['__HTTP_Session2_Idle_TS']) 382 || !isset($_SESSION['__HTTP_Session2_Idle'])) { 383 return 0; 384 } 385 return $_SESSION['__HTTP_Session2_Idle_TS'] 386 + $_SESSION['__HTTP_Session2_Idle']; 387 } 388 389 /** 390 * Check if session is expired 391 * 392 * @return boolean 393 */ 394 public static function isExpired() 395 { 396 if ( 397 isset($_SESSION['__HTTP_Session2_Expire']) 398 && $_SESSION['__HTTP_Session2_Expire'] > 0 399 && isset($_SESSION['__HTTP_Session2_Expire_TS']) 400 && 401 ( 402 $_SESSION['__HTTP_Session2_Expire_TS'] 403 + $_SESSION['__HTTP_Session2_Expire'] 404 ) <= time()) { 405 return true; 406 } 407 return false; 408 } 409 410 /** 411 * Check if session is idle 412 * 413 * @return boolean Obvious 414 */ 415 public static function isIdle() 416 { 417 if ( 418 isset($_SESSION['__HTTP_Session2_Idle']) 419 && $_SESSION['__HTTP_Session2_Idle'] > 0 420 && isset($_SESSION['__HTTP_Session2_Idle_TS']) 421 && ( 422 $_SESSION['__HTTP_Session2_Idle_TS'] 423 + $_SESSION['__HTTP_Session2_Idle'] 424 ) <= time()) { 425 return true; 426 } 427 return false; 428 } 429 430 /** 431 * Updates the idletime 432 * 433 * @return void 434 */ 435 public static function updateIdle() 436 { 437 if (isset($_SESSION['__HTTP_Session2_Idle_TS'])) { 438 $_SESSION['__HTTP_Session2_Idle_TS'] = time(); 439 } 440 } 441 442 /** 443 * If optional parameter is specified it indicates whether the module will 444 * use cookies to store the session id on the client side in a cookie. 445 * 446 * By default this cookie will be deleted when the browser is closed! 447 * 448 * It will throw an Exception if it's not able to set the session.use_cookie 449 * property. 450 * 451 * It returns the previous value of this property. 452 * 453 * @param boolean $useCookies If specified it will replace the previous value of 454 * this property. By default 'null', which doesn't 455 * change any setting on your system. If you supply a 456 * parameter, please supply 'boolean'. 457 * 458 * @return boolean The previous value of the property 459 * 460 * @throws HTTP_Session2_Exception If ini_set() fails! 461 * @see session_set_cookie_params() 462 * @link http://php.net/manual/en/function.session-set-cookie-params.php 463 */ 464 public static function useCookies($useCookies = null) 465 { 466 $return = false; 467 if (ini_get('session.use_cookies') == '1') { 468 $return = true; 469 } 470 if ($useCookies !== null) { 471 if ($useCookies === true) { 472 $status = ini_set('session.use_cookies', 1); 473 } else { 474 $status = ini_set('session.use_cookies', 0); 475 } 476 if ($status === false) { 477 $msg = "Could not set 'session.use_cookies'. Please check your "; 478 $msg .= 'permissions to override php.ini-settings. E.g. a possible '; 479 $msg .= 'php_admin_value setting or blocked ini_set() calls '; 480 throw new HTTP_Session2_Exception($msg, self::ERR_SYSTEM_PERM); 481 } 482 } 483 return $return; 484 } 485 486 /** 487 * Gets a value indicating whether the session 488 * was created with the current request 489 * 490 * You MUST call this method only after you have started 491 * the session with the HTTP_Session2::start() method. 492 * 493 * @return boolean true when the session was created with the current request 494 * false otherwise 495 * 496 * @see self::start() 497 * @uses self::STARTED 498 */ 499 public static function isNew() 500 { 501 // The best way to check if a session is new is to check 502 // for existence of a session data storage 503 // with the current session id, but this is impossible 504 // with the default PHP module wich is 'files'. 505 // So we need to emulate it. 506 return !isset($_SESSION['__HTTP_Session2_Info']) || 507 $_SESSION['__HTTP_Session2_Info'] == self::STARTED; 508 } 509 510 /** 511 * Register variable with the current session 512 * 513 * @param string $name Name of a global variable 514 * 515 * @return void 516 * @see session_register() 517 */ 518 public static function register($name) 519 { 520 session_register($name); 521 } 522 523 /** 524 * Unregister a variable from the current session 525 * 526 * @param string $name Name of a global variable 527 * 528 * @return void 529 * @see session_unregister() 530 */ 531 public static function unregister($name) 532 { 533 session_unregister($name); 534 } 535 536 /** 537 * Returns session variable 538 * 539 * @param string $name Name of a variable 540 * @param mixed $default Default value of a variable if not set 541 * 542 * @return mixed Value of a variable 543 */ 544 public static function &get($name, $default = null) 545 { 546 if (!isset($_SESSION[$name]) && isset($default)) { 547 $_SESSION[$name] = $default; 548 } 549 return $_SESSION[$name]; 550 } 551 552 /** 553 * Sets session variable 554 * 555 * @param string $name Name of a variable 556 * @param mixed $value Value of a variable 557 * 558 * @return mixed Old value of a variable 559 */ 560 public static function set($name, $value) 561 { 562 $return = (isset($_SESSION[$name])) ? $_SESSION[$name] : null; 563 if (null === $value) { 564 unset($_SESSION[$name]); 565 } else { 566 $_SESSION[$name] = $value; 567 } 568 return $return; 569 } 570 571 /** 572 * Returns local variable of a script 573 * 574 * Two scripts can have local variables with the same names 575 * 576 * @param string $name Name of a variable 577 * @param mixed $default Default value of a variable if not set 578 * 579 * @return mixed Value of a local variable 580 */ 581 static function &getLocal($name, $default = null) 582 { 583 $local = md5(self::localName()); 584 if (!is_array($_SESSION[$local])) { 585 $_SESSION[$local] = array(); 586 } 587 if (!isset($_SESSION[$local][$name]) && isset($default)) { 588 $_SESSION[$local][$name] = $default; 589 } 590 return $_SESSION[$local][$name]; 591 } 592 593 /** 594 * Sets local variable of a script. 595 * Two scripts can have local variables with the same names. 596 * 597 * @param string $name Name of a local variable 598 * @param mixed $value Value of a local variable 599 * 600 * @return mixed Old value of a local variable 601 */ 602 static function setLocal($name, $value) 603 { 604 $local = md5(self::localName()); 605 if (!is_array($_SESSION[$local])) { 606 $_SESSION[$local] = array(); 607 } 608 $return = $_SESSION[$local][$name]; 609 if (null === $value) { 610 unset($_SESSION[$local][$name]); 611 } else { 612 $_SESSION[$local][$name] = $value; 613 } 614 return $return; 615 } 616 617 /** 618 * set the usage of transparent SID 619 * 620 * @param boolean $useTransSID Flag to use transparent SID 621 * 622 * @return boolean 623 */ 624 static function useTransSID($useTransSID = false) 625 { 626 $return = ini_get('session.use_trans_sid') ? true : false; 627 if ($useTransSID === false) { 628 ini_set('session.use_trans_sid', $useTransSID ? 1 : 0); 629 } 630 return $return; 631 } 632 633 /** 634 * Sets new local name 635 * 636 * @param string $name New local name 637 * 638 * @return string Previous local name 639 */ 640 static function localName($name = null) 641 { 642 $return = ''; 643 if (isset($GLOBALS['__HTTP_Session2_Localname'])) { 644 $return .= $GLOBALS['__HTTP_Session2_Localname']; 645 } 646 if (!empty($name)) { 647 $GLOBALS['__HTTP_Session2_Localname'] = $name; 648 } 649 return $return; 650 } 651 652 /** 653 * init 654 * 655 * @return void 656 */ 657 static function init() 658 { 659 // Disable auto-start of a sesion 660 ini_set('session.auto_start', 0); 661 662 // Set local name equal to the current script name 663 self::localName($_SERVER['SCRIPT_NAME']); 664 } 665 666 /** 667 * Regenrates session id 668 * 669 * If session_regenerate_id() is not available emulates its functionality 670 * 671 * @param boolean $deleteOldSessionData Whether to delete data of old session 672 * 673 * @return boolean 674 */ 675 public static function regenerateId($deleteOldSessionData = false) 676 { 677 if (function_exists('session_regenerate_id')) { 678 return session_regenerate_id($deleteOldSessionData); 679 680 // emulate session_regenerate_id() 681 } else { 682 683 do { 684 $newId = uniqid(dechex(rand())); 685 } while ($newId === session_id()); 686 687 if ($deleteOldSessionData) { 688 session_unset(); 689 } 690 691 session_id($newId); 692 693 return true; 694 } 695 } 696 697 /** 698 * This function copies session data of specified id to specified table 699 * 700 * @param string $target Target to replicate to 701 * @param string $id Id of record to replicate 702 * 703 * @return boolean 704 */ 705 public static function replicate($target, $id = null) 706 { 707 return self::$container->replicate($target, $id); 708 } 709 710 /** 711 * If optional parameter is specified it determines the number of seconds 712 * after which session data will be seen as 'garbage' and cleaned up 713 * 714 * It returns the previous value of this property 715 * 716 * @param int $gcMaxLifetime If specified it will replace the previous value of 717 * this property, and must be integer. 718 * 719 * @return boolean The previous value of the property 720 */ 721 public static function setGcMaxLifetime($gcMaxLifetime = null) 722 { 723 $return = ini_get('session.gc_maxlifetime'); 724 if (isset($gcMaxLifetime) && is_int($gcMaxLifetime) && $gcMaxLifetime >= 1) { 725 ini_set('session.gc_maxlifetime', $gcMaxLifetime); 726 } 727 return $return; 728 } 729 730 /** 731 * If optional parameter is specified it determines the 732 * probability that the gc (garbage collection) routine is started 733 * and session data is cleaned up 734 * 735 * It returns the previous value of this property 736 * 737 * @param int $gcProbability If specified it will replace the previous value of 738 * this property. 739 * 740 * @return boolean The previous value of the property 741 */ 742 public static function setGcProbability($gcProbability = null) 743 { 744 $return = ini_get('session.gc_probability'); 745 if (isset($gcProbability) && 746 is_int($gcProbability) && 747 $gcProbability >= 1 && 748 $gcProbability <= 100) { 749 ini_set('session.gc_probability', $gcProbability); 750 } 751 return $return; 752 } 753} 754 755/** 756 * init {@link HTTP_Session2} 757 * 758 * @see HTTP_Session2::init() 759 */ 760HTTP_Session2::init(); 761