Documentation is available at session-defs.php
- <?php
- /* ******************************************************************** */
- /* CATALYST PHP Source Code */
- /* -------------------------------------------------------------------- */
- /* This program is free software; you can redistribute it and/or modify */
- /* it under the terms of the GNU General Public License as published by */
- /* the Free Software Foundation; either version 2 of the License, or */
- /* (at your option) any later version. */
- /* */
- /* This program is distributed in the hope that it will be useful, */
- /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
- /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
- /* GNU General Public License for more details. */
- /* */
- /* You should have received a copy of the GNU General Public License */
- /* along with this program; if not, write to: */
- /* The Free Software Foundation, Inc., 59 Temple Place, Suite 330, */
- /* Boston, MA 02111-1307 USA */
- /* -------------------------------------------------------------------- */
- /* */
- /* Filename: session-defs.php */
- /* Author: Paul Waite */
- /* Description: Definitions for managing SESSIONS. */
- /* We regard the session as a generic thing which will be */
- /* constant in functionality and data across any */
- /* application we might write. It deals only with the */
- /* the basics of user identity, group membership and */
- /* maintaining this across multiple page accesses within */
- /* the website. For context variables which will differ */
- /* from application to application, see context-defs.php. */
- /* */
- /* ******************************************************************** */
- /** @package core */// SESSION DATABASE OPTIONS
- // The user session can be backed by a database, or not. This allows
- // simple code-only websites to be implemented, where there is no
- // need to track users, sessions and other data.
- /** Session is backed by a database connection */
- ("SESS_DATABASE_BACKED", true );
- /** Session is standalone, no database */
- ("SESS_STANDALONE", false );
- // -----------------------------------------------------------------------
- // SESSION LIFETIMES
- // This value is the number of seconds that you wish a session cookie
- // lifetime to be, before it is deemed to have expired. Common choices
- // are provided as defines, otherwise use your own value.
- /** Session lasts forever.. well, 10 years is near enough. */
- ("SESS_FOREVER", SECS_10_YEARS);
- /** Session lasts for 1 year */
- ("SESS_1_YEAR", SECS_1_YEAR);
- /** Session lasts for 1 month */
- ("SESS_1_MONTH", SECS_1_MONTH);
- /** Session lasts for 1 week */
- ("SESS_1_WEEK", SECS_1_WEEK);
- /** Session lasts for 24 hours */
- ("SESS_1_DAY", SECS_1_DAY);
- /** Session lasts for 12 hours */
- ("SESS_12_HOURS", SECS_12_HOURS);
- /** Session lasts for 8 hours */
- ("SESS_8_HOURS", SECS_8_HOURS);
- /** Session lasts for 4 hours */
- ("SESS_4_HOURS", SECS_4_HOURS);
- /** Session lasts for 1 hour */
- ("SESS_1_HOUR", SECS_1_HOUR);
- /** Session lasts for 20 minutes */
- ("SESS_20_MINS", SECS_20_MINS);
- /** Session lasts as long as user browser is open */
- ("SESS_BROWSER_LIFETIME", -1);
- /** Session has no lifetime, immediate expiry */
- ("SESS_ZERO_LIFETIME", 0);
- // -----------------------------------------------------------------------
- // SESSION LOGIN LIMIT ACTION
- // Possibilities for handling a login limit which has been exceeded.
- /** Login limit exceeded: Allow, assume app. takes action */
- ("SESS_ALLOW", 0);
- /** Login limit exceeded: Allow session, cull oldest */
- ("SESS_ALLOW_CULL", 1);
- /** Login limit exceeded: Block session, nice message */
- ("SESS_BLOCK_MSG", 2);
- /** Login limit exceeded: Block session, no message */
- ("SESS_BLOCK_SILENT", 3);
- /** Login limit exceeded: Block session, redirect to URL */
- ("SESS_BLOCK_REDIRECT", 4);
- /** Login limit exceeded: Block session, login as guest instead */
- ("SESS_BLOCK_GUEST", 5);
- // -----------------------------------------------------------------------
- // SESSION LOGIN TYPE CODES
- // This flags the way the user originally logged in.
- /** No known login has been achieved */
- ("LOGIN_UNKNOWN", 0);
- /** Login by using guest account */
- ("LOGIN_BY_GUEST", 1);
- /** Login by remote IP address match */
- ("LOGIN_BY_IP", 2);
- /** Login by standard username/password */
- ("LOGIN_BY_PASSWD", 3);
- /** Login by using authorisation code (MD5 string usually) */
- ("LOGIN_BY_AUTHCODE", 4);
- // -----------------------------------------------------------------------
- /**
- * THE SESSION CLASS
- * A class to manage user sessions. A session is simply a thing which
- * contains information about a user who has logged on to the system,
- * so in fact the session is just an extension of a user.
- * To access the system a user must either create a new session, or
- * recover an existing session. A new session is created if the user
- * provides login details: userid/password or unique $authid (MD5).
- * An existing session may be 'recovered' if the login details are
- * absent, and if a cookie is sent containing a valid session key.
- * @package core
- */
- class session extends user {
- /** The ID of this session */
- var $session_id = false;
- /** The type of this session */
- var $db_backed = SESS_DATABASE_BACKED;
- /** The session record complete */
- var $session_record;
- /** The session lifetime, in seconds */
- var $lifetime = 0;
- /** True if we should limit 'guest' to browser lifetime */
- var $guest_browser_lifetime = false;
- /** The session cookie name */
- var $cookiename = "";
- /** Time of last login (Unix timestamp) */
- var $last_logintime = 0;
- /** Error condition message if any */
- var $error_message = "";
- /** Option to take on logins exceeded for user */
- var $logins_exceeded_option = SESS_ALLOW_CULL;
- /** Custom message to deliver when blocking */
- var $logins_exceeded_msg = "";
- /** URL to redirect to on logins exceeded */
- var $logins_exceeded_redirect = "";
- /** Login type for this session */
- var $login_type = LOGIN_BY_PASSWD;
- // .....................................................................
- /**
- * Constructor
- * Create a new session.
- * Initial creation of the session object does nothing.
- * The activate() method sets it up, when called.
- */
- function session() {
- $this->session_clear();
- } // session
- // .....................................................................
- /**
- * Retrieve the session cookie
- * This function looks for the cookie which is appropriate to the
- * current application, and returns the session ID if the cookie
- * was submitted. Returns false otherwise..
- * @return mixed The session ID as a string, or false
- * @access private
- */
- function get_session_cookie() {
- $session_id = false;
- $cookiename = $this->cookiename;
- global $$cookiename;
- if (isset($$cookiename)) {
- $session_id = $$cookiename;
- }
- return $session_id;
- } // get_session_cookie
- // .....................................................................
- /**
- * Identify the user/client
- * Here is where we activate our session. This involves searching
- * for the cookie, username/password sequence, or authorisation
- * code which will allow us to identify the requester and create
- * the proper session for them to access the website..
- * @return bool True if we succeeded in identifying the user, else false
- */
- function identify_user() {
- // Access to the vars..
- global $tbxUsername, $user; // Userid textbox fieldname submitted from a form
- global $tbxPassword, $pass; // Password textbox fieldname submitted from a form
- global $tbxLogoff; // Logoff hidden field sent from a form
- global $authid; // Authorisation code (MD5) from form or URL
- global $REMOTE_ADDR; // IP address of remote browser/client
- // Map alternative fieldnames onto standard ones..
- if (isset($user) && !isset($tbxUsername)) $tbxUsername = $user;
- if (isset($pass) && !isset($tbxPassword)) $tbxPassword = $pass;
- // Initialise some variables..
- $this->session_clear();
- // If no database we are not tracking sessions..
- if (!$this->db_backed) return true;
- // Turn tracing on..
- debug_trace($this);
- // SUBMITTED COOKIE
- // Gather in any submitted cookie..
- $session_id = $this->get_session_cookie();
- // LOGIN via SUBMITTED AUTHORISATION
- // Now we check for login/logoff action. Clear out any session id
- // if they have submitted a login, or an authorisation code..
- if (((isset($tbxUsername) && $tbxUsername != "") && (isset($tbxPassword) && $tbxPassword != ""))
- || (isset($authid))
- ) {
- // Clear any existing session..
- debugbr("authorisation detected, clearing session");
- $session_id = false;
- $this->session_clear();
- }
- // EXISTING SESSION RECOVERY
- // If we still have a session id, then we try recovery..
- if ($session_id) {
- debugbr("session id detected.");
- if ($this->recover($session_id)) {
- // Check if they want to logoff..
- debugbr("recovered session: '$this->session_id'");
- if (isset($tbxLogoff) && $tbxLogoff == "Logoff") {
- debugbr("logging off, deleting session");
- $this->session_delete();
- }
- else {
- // Special case: where recovered session is a GUEST session. In
- // this case we check for a possible IP login, so that these
- // users don't get marooned in a guest login session..
- if ($this->login_type == LOGIN_BY_GUEST) {
- debugbr("guest recovered: checking for IP authentication..");
- $login_type = $this->authenticate_ipaddress($REMOTE_ADDR);
- if ($login_type != LOGIN_UNKNOWN) {
- $this->session_create($login_type);
- }
- else {
- // Keep the original guest authentication..
- debugbr("falling back to guest");
- $this->valid = true;
- $this->authenticated = true;
- }
- }
- }
- }
- } // existing session
- // NEW SESSION CREATION
- // If no real session id exists at this point then check
- // for a username and password or an authorisation code
- // and log them in..
- if (!$this->session_id) {
- $authenticated = false;
- debugbr("no session, looking to create new one");
- // AUTH CODE AUTHENTICATION..
- if (!$authenticated && isset($authid)) {
- debugbr("trying authid authentication..");
- $login_type = $this->authenticate_authid($authid);
- if ($login_type != LOGIN_UNKNOWN) {
- $this->session_create($login_type);
- $authenticated = true;
- }
- }
- // IP ADDRESS AUTHENTICATION..
- if (!$authenticated && !isset($tbxUsername)) {
- debugbr("trying IP authentication..");
- $login_type = $this->authenticate_ipaddress($REMOTE_ADDR);
- if ($login_type != LOGIN_UNKNOWN) {
- $this->session_create($login_type);
- $authenticated = true;
- }
- }
- // USERNAME/PASSWORD AUTHENTICATION..
- // Login with userid/password. If none provided then we default
- // them to 'guest'. For websites which are subscriber-only we
- // expect the application code to handle it.
- if (!$authenticated) {
- // Default to guest, if no login supplied..
- if (!isset($tbxUsername)) {
- debugbr("username not set, falling back to guest");
- $tbxUsername = "guest";
- }
- if (!isset($tbxPassword)) $tbxPassword = "";
- $tbxUsername = trim($tbxUsername);
- debugbr("user=$tbxUsername, passwd=$tbxPassword");
- // Authenticate the login attempt. If successful, this
- // populates the user and usergroups information..
- $login_type = $this->authenticate_userid($tbxUsername, $tbxPassword);
- if ($login_type != LOGIN_UNKNOWN) {
- $authenticated = true;
- $this->session_create($login_type);
- }
- }
- } // new session
- // Return status..
- if (debugging() && $this->session_valid()) {
- debug("session type: ");
- switch ($this->login_type) {
- case LOGIN_BY_GUEST:
- debugbr("guest");
- break;
- case LOGIN_BY_IP:
- debugbr("authorised by IP address");
- break;
- case LOGIN_BY_PASSWD:
- debugbr("authorised by userid/password");
- break;
- case LOGIN_BY_AUTHCODE:
- debugbr("authorised by auth code");
- break;
- default:
- debugbr("unknown");
- } // switch
- }
- return $this->session_valid();
- debug_trace();
- } // identify_user
- // .....................................................................
- /**
- * Setup session parameters
- * Here is where we set up our session to suit..
- * @access private
- */
- function set_sessionparms() {
- global $RESPONSE;
- debug_trace($this);
- $RESPONSE->datasource->set_datestyle("ISO");
- $RESPONSE->datasource->set_char_encoding("UTF-8");
- debug_trace();
- } // set_sessionparms
- // ....................................................................
- /**
- * Check if we should bump our login count.
- * @access private
- */
- function track_logins() {
- if ($this->login_type != LOGIN_BY_GUEST) {
- debug_trace($this);
- // Timestamp of now...
- $tinow = time();
- $dtnow = timestamp_to_datetime($tinow);
- // Now check last login time. If the current time is more
- // than 4 hours later, then we increment the login count.
- if ( $tinow > ($this->last_logintime + SESS_4_HOURS) ) {
- if ($this->valid) {
- // Update the user record..
- $uQ = new dbupdate("ax_user");
- $uQ->set("total_logins", ($this->total_logins + 1));
- $uQ->set("last_login", $dtnow);
- $uQ->where("user_id='" . addslashes($this->userid) . "'");
- $uQ->execute();
- // Update the session record..
- $uQ = new dbupdate("ax_wwwsession");
- $uQ->set("login_datetime", $dtnow);
- $uQ->where("session_id='$this->session_id'");
- $uQ->execute();
- // Set the resident var..
- $this->last_logintime = $tinow;
- }
- }
- debug_trace();
- }
- } // track_logins
- // ...................................................................
- /**
- * Set logins exceeded action
- * This sets the action for when the number of logins for a given
- * user of the system exceeds a maximum, if specified. The options
- * for the action to take are:
- * SESS_ALLOW Allow, assume app. will take action
- * SESS_ALLOW_CULL Allow session, cull oldest
- * SESS_BLOCK_MSG Block session, nice message
- * SESS_BLOCK_SILENT Block session, no message
- * SESS_BLOCK_REDIRECT Block session, redirect to URL
- * SESS_BLOCK_GUEST Block session, login as guest instead
- */
- function on_logins_exceeded($option=SESS_ALLOW_CULL, $parm="") {
- $this->logins_exceeded_option = $option;
- switch ($option) {
- case SESS_BLOCK_REDIRECT:
- $this->logins_exceeded_redirect = $parm;
- break;
- case SESS_BLOCK_MSG:
- $this->logins_exceeded_msg = $parm;
- break;
- } // switch
- } // on_logins_exceeded
- // ....................................................................
- /**
- * Check for any excess sessions over and above the number which are
- * allowed for this user. We may cull oldest sessions first, or take
- * other actions as specified in the $RESPONSE.
- * @access private
- */
- function check_excess_sessions() {
- global $RESPONSE;
- debug_trace($this);
- // Check for multiple login limit restriction..
- if ($this->limit_logins > 0) {
- debugbr("checking login limit of $this->limit_logins.");
- $q = "SELECT session_id";
- $q .= " FROM ax_wwwsession";
- $q .= " WHERE user_id='" . addslashes($this->userid) . "'";
- $q .= " ORDER BY login_datetime";
- $loginQ = dbrecordset($q);
- // We do +1 to take account of the login we will be
- // adding as part of this process shortly..
- $excesslogins = 1 + $loginQ->rowcount - $this->limit_logins;
- if ($excesslogins > 0) {
- debugbr("oops: " . ($loginQ->rowcount) . " logins already; $excesslogins too many.");
- switch ($this->logins_exceeded_option) {
- case SESS_ALLOW:
- // Do nothing. This is for when the developer
- // wants to handle it at application level..
- break;
- case SESS_ALLOW_CULL:
- // Delete the number we need, starting with the oldest..
- for ($ix=0; $ix < $excesslogins; $ix++) {
- $sessid = $loginQ->field("session_id");
- dbcommand("DELETE FROM ax_wwwsession WHERE session_id='$sessid'");
- $loginQ->get_next();
- } // for
- error_log(APP_NAME . ": User '$this->userid' excess logins($excesslogins) triggered a session cull.", 0);
- break;
- case SESS_BLOCK_MSG:
- // Stop the process dead, with nice message in the browser..
- if ($RESPONSE->browser_type == BROWSER_HTML || $RESPONSE->browser_type == BROWSER_XHTML) {
- $msg = $RESPONSE->logins_exceeded_msg;
- if ($msg == "") {
- $msg = "<h2>". APP_NAME . " Login Count Exceeded</h2>\n";
- $msg .= "<p>You have exceeded your login count. If you closed your browser ";
- $msg .= "on another computer, and you are trying to login somewhere else ";
- $msg .= "you will have to log off the first machine before you can do so ";
- $msg .= "due to the login restrictions on your account.</p>";
- $msg .= "<p>Sorry for any inconvenience.</p>";
- }
- die("<html><body>\n" . $msg . "</body></html>\n");
- }
- else {
- die();
- }
- break;
- case SESS_BLOCK_SILENT:
- // Stop the process dead, no message, no nuthin'
- // and lucky to get that!
- die();
- break;
- case SESS_BLOCK_REDIRECT:
- // Redirect to a given page instead of logging in..
- header("Location: $this->logins_exceeded_redirect");
- break;
- case SESS_BLOCK_GUEST:
- // Let them log in, but as a guest instead..
- if (!($this->user("guest") && $this->enabled)) {
- $this->crash(500);
- }
- break;
- } // switch
- }
- else {
- debugbr("no excess logins.");
- }
- }
- debug_trace();
- } // check_excess_sessions
- // .....................................................................
- /** Recover session
- * Recover an existing session. This will obliterate any pre-existing
- * session information in this object, since we expect it to succeed..
- * @return mixed Session ID or else false
- */
- function recover($session_id) {
- debug_trace($this);
- $this->session_clear();
- if (!empty($session_id)) {
- $session = dbrecordset("SELECT * FROM ax_wwwsession WHERE session_id='$session_id'");
- if ($session->hasdata) {
- // Find user record and check enabled..
- $this->user($session->field("user_id"));
- if ($this->enabled) {
- $this->session_id = $session_id;
- $this->userid = $session->field("user_id");
- $this->session_record = $session->current_row;
- $this->login_type = $session->field("login_type");
- $this->last_logintime = datetime_to_timestamp($session->field("login_datetime"));
- // This writes login total to the uuser record, and
- // login datetime to the wwwsession record..
- $this->track_logins();
- // This sets the database session environment up..
- $this->set_sessionparms();
- debugbr("found session record.");
- }
- else {
- debugbr("session rejected: user '" . $session->field("user_id") . "' no longer enabled");
- $this->valid = false;
- }
- }
- else {
- // Invalidate session and clear cookie..
- debugbr("no valid session record found: clearing session data and cookie.");
- $this->session_clear();
- $this->delete_cookie();
- }
- }
- debug_trace();
- // Return session id, or else 'false'..
- return $this->session_id;
- } // recover
- // .....................................................................
- /**
- * Set the session cookie.
- * @param string $content The content of the cookie
- * @param integer $expires The Unix time() value for expiry datetime of the cookie
- */
- function set_cookie($content, $expires=false) {
- global $RESPONSE;
- global $_SERVER;
- if (!isset($RESPONSE)
- || $RESPONSE->browser_type == BROWSER_TYPE_HTML
- || $RESPONSE->browser_type == BROWSER_TYPE_XHTML
- ) {
- if (!$expires) {
- if ($this->lifetime == SESS_BROWSER_LIFETIME
- || ($this->guest_browser_lifetime && $this->userid == "guest")
- ) {
- // Cookie lasts for browser lifetime
- debugbr("cookie lifetime: browser");
- $expires = 0;
- }
- else {
- // Cookie lasts for given number of seconds..
- $expires = time() + $this->lifetime;
- debugbr("cookie lifetime: $this->lifetime secs");
- }
- }
- if (isset($RESPONSE) && $RESPONSE->browser == BROWSER_NETSCAPE) {
- debugbr("Setting Netscape cookie ".$this->cookiename.": $content, $expires");
- setcookie($this->cookiename, $content, $expires);
- }
- else {
- if ($_SERVER["SERVER_ADDR"] == "127.0.0.1") {
- debugbr("Server running on localhost - setting cookie ".$this->cookiename.": $content, $expires, /");
- setcookie($this->cookiename, $content, $expires, "/");
- }
- else {
- debugbr("Setting cookie ".$this->cookiename.": $content, $expires, /, ".$this->http_host);
- setcookie($this->cookiename, $content, $expires, "/", $this->http_host);
- }
- }
- }
- } // set_cookie
- // .....................................................................
- /**
- * Delete session cookie
- * Deletes the session cookie from the user's browser.
- */
- function delete_cookie() {
- global $RESPONSE;
- debugbr("Expiring cookie ".$this->cookiename);
- if (!isset($RESPONSE)
- || $RESPONSE->browser_type == BROWSER_TYPE_HTML
- || $RESPONSE->browser_type == BROWSER_TYPE_XHTML
- ) {
- if (isset($RESPONSE) && $RESPONSE->browser == BROWSER_NETSCAPE) {
- setcookie($this->cookiename, "", time() - SESS_1_HOUR);
- }
- else {
- setcookie($this->cookiename, "", time() - SESS_1_HOUR, "/", $this->http_host);
- }
- }
- } // delete_cookie
- // ...................................................................
- /**
- * Set session database backing type
- * The database backing 'type' can be either SESS_DATABASE_BACKED, or
- * SESS_STANDALONE.
- * @param bool $type Session type
- */
- function set_sessiontype($type=SESS_DATABASE_BACKED) {
- $this->db_backed = $type;
- } // set_sessiontype
- // ...................................................................
- /**
- * Set session lifetime
- * Set the session cookie lifetime in seconds.
- * @param integer $secs Seconds lifetime for the session cookie
- */
- function set_lifetime($secs=SESS_1_DAY) {
- $this->lifetime = $secs;
- } // set_lifetime
- // ...................................................................
- /**
- * Set session cookie name
- * @param string $name Cookie name to use for session ID
- */
- function set_cookiename($name="session_id") {
- $this->cookiename = $name;
- } // set_cookiename
- // ...................................................................
- /**
- * Set session guest browser lifetime flag
- * If set True this causes the cookie lifetime to be forced to the
- * browser lifetime if the user is 'guest'.
- * @param bool $guest_browser_lifetime True if guest cookie limited to browser lifetime
- */
- function set_guest_browser_lifetime($guest_browser_lifetime=false) {
- $this->guest_browser_lifetime = $guest_browser_lifetime;
- } // set_guest_browser_lifetime
- // .....................................................................
- /**
- * Create new session
- * Make a brand new session for the user.
- * @param integer $logintype Type of login for this session
- */
- function session_create($logintype=LOGIN_BY_PASSWD) {
- debug_trace($this);
- // Check for any excess sessions first..
- $this->check_excess_sessions();
- // Get next session key, and create session..
- $this->set_sessionparms();
- // Generate a unique session handle..
- $this->session_id = md5(uniqid(rand(),1));
- // Initialise the login detail..
- $this->login_type = $logintype;
- $this->last_logintime = time() - SESS_FOREVER;
- // Create the new session..
- $ugroups = "";
- if (isset($this->user_groups)) {
- $ugroups = implode("|", $this->user_groups);
- }
- $wwwQ = new dbinsert("ax_wwwsession");
- $wwwQ->set("session_id", $this->session_id);
- $wwwQ->set("user_id", $this->userid);
- $wwwQ->set("user_groups", $ugroups);
- $wwwQ->set("login_type", $logintype);
- // Non-guests get initial login datetime set very early so
- // that track_logins() will increment the login counter. Guests
- // will always get the database default of 'now'..
- if ($this->login_type != LOGIN_BY_GUEST) {
- $wwwQ->set("login_datetime", timestamp_to_datetime($this->last_logintime));
- }
- // Create the record..
- $wwwQ->execute();
- // Track logins for this user..
- $this->track_logins();
- // Everything went Ok, so set the cookie for next time..
- if ($this->browser_type == BROWSER_TYPE_HTML || $this->browser_type == BROWSER_TYPE_XHTML) {
- // Set client session cookie, with appropriate lifetime..
- $this->set_cookie($this->session_id);
- }
- debug_trace();
- // Return session id, or else 'false'..
- return $this->session_id;
- } // session_create
- // .....................................................................
- /**
- * Delete the session
- * Delete the current session from the system.
- */
- function session_delete() {
- debug_trace($this);
- if ($this->session_id) {
- dbcommand("DELETE FROM ax_wwwsession WHERE session_id='$this->session_id'");
- $this->session_clear();
- }
- debug_trace();
- } // session_delete
- // .....................................................................
- /**
- * Clear session vars
- * Common method for clearing out the current session info
- * from the object variables.
- */
- function session_clear() {
- $this->session_id = false;
- if (isset($this->session_record)) {
- unset($this->session_record);
- }
- $this->last_logintime = 0;
- $this->error_message = "";
- } // session_clear
- // .....................................................................
- /**
- * Is session valid
- * Return validity status. If there is a session ID and a valid user
- * then the whole session is deemed valid, otherwise not.
- * @return bool True if this session is valid
- */
- function session_valid() {
- return ($this->valid && $this->session_id);
- } // session_valid
- } // session class
- // -----------------------------------------------------------------------
- ?>
Documentation generated by phpDocumentor 1.3.0RC3