Thứ Tư, 11 tháng 6, 2014

What Are Sessions? [Sessions]

A session is a mechanism for storing information across page visits for a specific user  on a web site. Sessions are stored in a cookie and  are assigned a unique ID, allowing the web site to access information in the cookie  and  associate that
information with specific user.  Once a session has been created, Drupal core an contributed modules may use that
session to store and  retrieve information from the cookie  as the user meanders around your site, without having to go backto the database to retrieve values.
Drupal utilizes sessions to store  information about authenticated users an occasionally for anonymous users, in cases where
module utilizes sessions to store context across page loads; other wise sessions are not generated for anonymous users.

Usage

Drupal uses sessions for several important functions internally to store transient information regarding an individual user’s
state or preferences. For example, drupal_set_message() needs to carry over a status message or an error message for the user from thepage on which the error occurred to the next page. This is done by storing the messages in an array named messages inside the user’s session:
/**
*   Set  a  message  that   reflects the  status of  the  performed operation.
*
*   If the  function is  called with  no arguments,  this  function returns all set
*   messages  without  clearing  them.
*
*   @param  $message
*         The message  should  begin  with  a  capital letter and always  ends  with  a
*         period  '.'.
*   @param  $type
*         The  type  of  the  message.  One  of  the  following  values  is possible:
*         - 'status'
*         - 'warning'
*         - 'error'
*   @param  $repeat
*         If this  is FALSE  and the  message  is already  set,  then  the  message  won't
*         be repeated.
*/
function  drupal_set_message($message  = NULL,  $type  = 'status',  $repeat  = TRUE)  if ($message)  {
if (!isset($_SESSION['messages'][$type]))  {
$_SESSION['messages'][$type]  = array();}
if ($repeat  || !in_array($message,  $_SESSION['messages'][$type]))  {
$_SESSION['messages'][$type][]  = $message;}
// Mark  this  page as being  not  cacheable.
drupal_page_is_cacheable(FALSE);}
// Messages  not  set when  DB  connection  fails.
return  isset($_SESSION['messages'])  ?  $_SESSION['messages']  : NULL;}
Another example is from poll.module, where the session is used to prevent the user from hitting the page cache:
if (!$user->uid)  {
// The  vote  is recorded  so the  user  gets  the  result  view  instead  of  the
// voting  form  when  viewing  the  poll.  Saving  a  value  in  $_SESSION  has the
// convenient  side  effect of  preventing  the  user  from  hitting  the  page
// cache.  When  anonymous  voting  is allowed,  the  page cache  should
// contain  only  the  voting  form,  not  the  results.
$_SESSION['poll_vote'][$node->nid]  = $choice;}

Drupal also uses sessions to keep a handle on file uploads when a node is being previewed, to remember viewing
preferences when filtering the list of site content or the list of recent log entries at Reports -> Recent log entries, and  for
the installation and update systems (install.php and  update.php).
Drupal creates sessions for users that are logged into a site (authenticated users). In the row of the sessions table
representing an anonymous user,  the uid column is set to 0. Because sessions are browser-specific (they’re tied to the
browsers cookie),having multiple browsers open on a single computer results in multiple sessions.

The actual data stored in a session is stored as serialized data in the session column of the sessions table. Two rows of a
typical sessions table are shown in Table 17-1. The table shows records for thsuperuser (uid  1), an authenticated user
(uid  3), and  ananonymous user (uid  0). The superuser has watchdog filtering settings (used by the dblog  module) stored inthe session.

Table 17-1. Example Rows from a typical  Sessions table
uid
sid
ssid
hostname
timestamp
cache
session
1
11diqzKTOnxI_zlUs9jDlLIzlrA xgGmM3l0oo-ux7Ws
NULL
1.2.3.4
120846400
106
dblog_overview
_filter|a:0:{}
3
1WgbUe5UGP26vIY8wkxbkF A8odnqoy4x6bC7uV8lss
NULL
5.6.7.8
1208460845
0
--

The sessions table is cleaned when PHP’s session garbage collection routine runs. The length of time  a row remains in the table is
determined by the session.gc_maxlifetime setting in settings.php. If a user logs out, the row for that session iremoved from the database immediately. Note that if a user is logged in via multiple browsers (not browser windows) omultiple IP addresses at the
same time,  each browser has a session; therefore logging out from one browser doesn’t log the user out from the other browsers.

Session-Related Settings

There are three places where Drupal modifies session-handling settings: in the .htaccess file, in the settings.php file, and  in the
bootstrap code  in the includes/bootstrap.inc file.

In .htaccess

Drupal ensures that it has full control over when sessions start by turning off PHP’s session.auto_start functionality in the Drupal
installation’s default .htaccess file with the following line:

php_flag  session.auto_start      false

session.auto_start is a configuration option that PHP cannot change at runtime, which is why it lives in the .htaccess file instead of
settings.php.

In settings.php

You’ll set most session settings within the settings.php file, located at sites/default/settings.php or sites/example.com/settings.php.

ini_set('session.gc_probability', 1); ini_set('session.gc_divisor',  100); ini_set('session.gc_maxlifetime',  200000); ini_set
('session.cookie_lifetime', 2000000);

Having  these settings in settings.php instead of .htaccess allows subsites to have different settings and allows Drupal to modify the
session settings on hosts running PHP as a CGI (PHP directives in .htaccesdont work in such a configuration).

Drupal uses the ini_set('session.save_handler', 'user'); function to override the default session handling provided by PHP and
implement its own session management; user-defined in this context means “defined by Drupal”(see www.php.net/manual

In bootstrap.inc

Session settings included in bootstrap.inc are:

ini_set('session.use_cookies', '1'); ini_set('session.use_only_cookies', '1'); ini_set('session.use_trans_sid', '0');
// Don't  send  HTTP  headers  using  PHP's session  handler.
ini_set('session.cache_limiter',  'none');
// Use httponly session cookies.
ini_set('session.cookie_httponly', '1');

PHP provides built-in session-handling functions but allows you to override those functions if you want  to implement your own
handlers, such as storing sessions in memcache or MongoDB instead of MySQL. PHP continues to handle the cookiemanagement,
while Drupals implementation does the back-end handling of session storage.
The call to drupal_session_initialize() during the DRUPAL_BOOTSTRAP_SESSION phase of bootstrapping sets the handlers to functions in includes/sessions.inc and  starts session handling:

case  DRUPAL_BOOTSTRAP_SESSION:
require_once DRUPAL_ROOT  . '/' . variable_get('session_inc', 'includes/session.inc');
drupal_session_initialize(); break;
The drupal_session_initialize() function within session.inc sets the handlers to the following values:
session_set_save_handler('_drupal_session_open', '_drupal_session_close', '_drupal_session_read', '_drupal_session_write',  '_drupal_session_destroy', '_drupal_session_garbage_collection');

Notice that the file being included in bootstrap.inc is defined by a Drupal variable named session_inc. This means that you can
cleanly implement your own session handling and  plug in that instead of using Drupal’s default session handling. For example,the
memcache module (drupal.org/project/memcache) implements the  _drupal_session_open(), _drupal_session_close(),
_drupal_session_read(), _drupal_session_write(), _drupal_session_destroy(), and _drupal_session_garbage_collection() session-related functions. Setting the session_inc Drupal variable causes Drupal to use this code  for sessions instead of using default session handling:'session_inc' => './sites/all/modules/memcache/memcache-session.inc',
You could also override the variable by setting it in your settings.php file:
$conf  = array('session_inc' => './sites/all/modules/memcache/memcache-session.inc,...);

Requiring Cookies
If the browser doesn’t accept cookies, a session cannot be established because the PHP directive sessions_use_only_cookies
has been set to 1 and  the alternative (passing the PHPSESSID in the query string  of the URL) has been disabled by setting
sessions.use_trans_sid to 0. This is a best practice, as recommended by Zend (see http://php.net/session.configuration):
URL-based sessi on management has   additional s ecurity risks c ompared t o cookie- based session management.
Users m ay send a URL that  contains an active session ID to their fri ends by e-mail or users may  save aURL
that  contains a session ID t o their bookmarks and access your site with the same session ID always,  for example.

When  PHPSESSID appears in the query string  of a site, it’s typically a sign that the hosting provider has locked down PHP and
doesn’t allow the ini_set() function to set PHP directives at runtime. Alternatives are to move  the settings into the.htaccess file (if
the host  is running PHP as an Apache module) or into a local php.ini file (if the host  is running PHP as a CGI executable).

To discourage session hijacking (where someone grabs  a session ID out of an old cookie  and attempts to reuse that session ID—see http://en.wikipedia.org/wiki/Session_hijacking), the session ID is regenerated when a user logs in (see theuser_login_
finalize() function in modules/user/user.module). The session is also regenerated when a user changes his or her password.

Storage

Session information is stored in the sessions table, which associates session IDs with Drupal user IDs during the DRUPAL_BOOTSTRAP_SESSION phase of bootstrapping (see Chapter 16 to learn more about Drupal’s bootstrapping process). In fact, the $user
object, which is used extensively throughout Drupal, is first built during this phase by _drupal_session_read() in includes/sessions.
inc (see Chapter 6 to see how the $user object is built)Table 17-2 shows the table  structure in which sessions are stored.

Table 17-2. The Structure of the sessions Table
Field
Type
Length
Description
uid
int, unsigned
10
User ID of authenticated user (0 for anonymous user)
sid
varchar
128S
Session ID generated by PHP
ssid
varchar
128
Secure session ID generated by PHP
hostname
varchar
128
IP address that last used this session ID
timestamp
int
11
Unix timestamp of last page request
cache
int
11
Time of user’s last post, which is used to enforce minimum cache lifetime
session
long
blob
Serialized contents of data stored in $_SESSION

When  Drupal serves  a page,  the last task completed is to write the session to the sessions table (see_drupal_session_write() in
includes/session.inc). This is done only if the browser has presented a valid cookie  to avoid bloating the sessions table  with
sessions for web crawlers or if a module has stored data in $_SESSION.

Session Life Cycle

The session life cycle begins (see http://api.drupal.org/api/function/drupal_session_initialize/7) with a check to see if a session
cookie  exists, and  if so, to initialize the session, otherwise the session is started on demand only when something needs to be stored
in a session. This approach allows anonymous users to browse a site without the need of a session cookie unless they perform an
operation, like submitting a form, that requires a session. This allows Drupal to serve up cached pages from a reverse proxy server,
like Varnish, for anonymous users. Drupal does,  however, create a unique session identifier even if a session cookie  isnt required, in preparation for case where a session will be needed. An example would be the case where a module called  drupal_get_token()this function needs to know the session ID in advance of the session being generated.

If a session is required, Drupal checks the sessions table for the existence of a row with the session ID as the key. If found, the
_drupal_session_read() function in includes/sessions.inc retrieves the session data and  performs an SQL JOIN on the row from
the sessions table and  on the corresponding row from the users table. The result of this join is anobject containing all fields and
values from both rows. This is the global $user object that’s  used throughout the rest of Drupal (see Chapter 6). Thus, session datais also available by looking in the $user object, specifically in $user->session, $user->sid$user->hostname, $user->timestamp,
and  -$user->cache. Roles for the current user are looked up and assigned to $user->roles in _drupal_session_read() as well.

But what  happens if there’s no user in the users table with a user ID that matches the user ID in the session? This is a trick
question. Because Drupal’s installer creates a row in the users table with the user ID of 0, and  because unauthenticated 
(anonymous) users are assigned the uid of 0 in the sessions table, the join always works.

If you want  to find out the last time  the user accessed a page,  you could look at either $user->timestamp, which is based on
the time stamp recorded in the sessions table or $user->access, which is kept in the users table. Of the two, $user->timestamp
will give you more accurate results if it is present, because updating $user->access in the userstable is subject to throttling so that
writes  do not happen more often than every 180 seconds by default. This value can be changed by setting the Drupal variable
session_write_interval, that can be found in the _drupal_session_write() function inincludes/session.inc:
// Last  access time  is updated no more frequently than  once  every  180  seconds.
// This  reduces  contention in  the  users   table.
if ($user->uid && REQUEST_TIME    - $user->access > variable_get('session_write_interval', 180)) {
db_update('users')->fields(array('access' => REQUEST_TIME))->condition('uid',  $user->uid)->execute();}

Of course, neither $user->login nor $user->access will be present for users visiting for the first time or for anonymous users without a session, as no time stamp has been saved  yet.
When  the web page has been delivered to the browser, the last step is to close the session. PHP invokes the _drupal_session_
write() function in includes/session.inc, which writes  anything that was stashed in $_SESSION (during the request) to the sessions
table. It is a good idea to store data in $_SESSION only if you absolutely need to.

Session Conversations

Here are some examples of what  happens when you visit Drupal in your browser, from a sessions perspective.

First Visit

Browser:  Hi, I’d like a page,  please.

Drupal: May I see your cookie?

Browser: Sorry, I don’t  have a cookie;  this is my first time  here.

Drupal: Here’s the page you requested.

Browser:  My user  did something that generated a message.

Drupal: Ok, I’ll create a session and  store the message in the session (cookie).
Here it is.
Browser:  Thanks for the cookie.

Second Visit

Browser: May I have another page,  please?

Drupal: May I see your cookie?

Browser:  Right here. It says session number 6tc47s8jd6rls9cugkdrrjm8h5.
Drupal: Hmm, I can’t find you in my records. But here’s  your page anyway. I’ll make a note of you in case you visit again.

User with an Account

[The user  has created an account and  clicke the Log In button.] Browser:  Hi, I’d like a page,  please.
Drupal: May I see your cookie?
Browser: Right here. It says session number 31bfa29408ebb23239042ca8f0f77652.
Drupal: Hi, Joe! [Mumbling] You’re user ID 384, and  you like your comments nested and  your coffee black. Here’s a new
cookie  so your session doesn’t get hijacked. I’ll make a note that you visited. Have a nice day.

Common Tasks

Here are some common ways in which you might want  to use sessions or tweak  session settings.

Changing the Length of Time Before a Cookie Expires

The length of time  before the cookie  containing the session ID expires is controlled by session.cookie_lifetime in settings.php and  set by default to 2,000,000 seconds (about 23 days). Modifying this value to 0 causes the cookie  to be destroyed when theuser
closes  the browser.

Changing the Name of the Session

A common problem with sessions arises  when deploying web sites on multiple subdomains. Because each site uses the same default value for session.cookie_domain and  the same session.name of PHPSESSID by default, users find themselves able to log intoonlyone site at any given time.  Drupal solves this problem by creating a unique session name for each  site.
The session name is based on a sha-256 hash, with some modifications, of the base  URL for the site.
The automatic generation of the session name can be bypassed by uncommenting a line isettings.php and  specifying the value of
the $cookie_domain variable. The value should contain alphanumeric characters only. Here is the relevant section of settings.php:

/**
*   Drupal automatically generates a  unique  session cookie   name for  each  site
*   based  on its full domain name. If you have  multiple domains pointing at
*   the  same Drupal site, you can  either redirect them all to  a  single domain
*   (see comment  in  .htaccess), or  uncomment  the  line below  and specify their
*   shared  base  domain.  Doing so  assures that   users   remain logged  in  as  they
*   cross between  your  various  domains.
*/
#  $cookie_domain  = 'example.com';

Storing Data in the Session

Storing data in a user’s session is convenient, because the data is automatically stored by the sessions system. Whenever you want
to store data that you want  to associate with a user  during a visit (omultiple visits up to session.cookie_lifetime), use the
$_SESSION superglobal: $_SESSION['favorite_color'] = $favorite_color;

Later, on a subsequent request, do the following to retrieve the value:
$favorite_color = $_SESSION['favorite_color'];

Summary
After reading this chapter, you should be able to
     Understand how Drupal modifies PHP’s session handling.
     Understand which files contain session configuration settings.
     Understand the session life cycle and  how Drupal’s $user object is created during a request.
     Store data in and  retrieve data from a user’s  session.

Không có nhận xét nào:

Đăng nhận xét