PHP Sessions. A detailed description of labor is also an explanation of the mechanism.

Introduction
How are the sessions arranged and how are they working?
Application area.
Possible problems and their elimination.
Security
Additional Information:
Example of authorization using sessions
Comments

Introduction
Sessions - it's actually very simple.
You just need to understand what they are for and how they are arranged.
First we answer the first question.
As shown in the corresponding section of this FAQ, the web server does not support permanent connection with the client, and each request is treated as new, without any connection with the previous ones.
That is, you can not track requests from the same visitor, nor save variables for it between views of individual pages. To solve these two problems, sessions were invented.
Actually, the session, in a nutshell - is a mechanism that allows you to uniquely identify the browser and create a file for this browser on the server in which session variables are stored.

I will not detail the need for such a mechanism. These are such textbook cases as the shopping cart in the e-store, authorization, as well as not entirely trivial problems, such as protecting the interactive parts of the site from spam.

In principle, it's fairly easy to make your own analog of sessions, not as functional as the built-in PHP, but similar in nature. On the cookies and the database.
When requesting the script, see if the cook came with a specific name. If there is no cookie, then put it and write to the database a new line with the user's data. If there is a cook, then we read the data from the database. Another request removes old records from the database and we have a mechanism for sessions. Quite simply. But there are some nuances that make it preferable to use the built-in session mechanism.

How are the sessions arranged and how are they working?
First you need to somehow identify the browser. To do this, you must give him a unique identifier and ask him to pass it on with every request. It's a shame to admit, but when I first learned about the sessions, I thought it was some kind of special mechanism, some new way of communicating the browser with the server - "sessions". That the session identifier is passed in some special way. Disappointment was cruel.
Sessions use standard, well-known methods of data transmission. Actually, others simply do not.
An identifier is an ordinary variable. By default, its name is PHPSESSID.
The task of PHP is to send it to the browser to return it with the next request. From the already mentioned FAQ section it is clear that the variable can be transferred only in two ways: in cookies or POST / GET request.
PHP uses both.
For this, there are two settings in php.ini:
session.use_cookies - if it is 1, PHP passes the identifier in the cookie, if 0 is not.
session.use_trans_sid if it is 1, then PHP passes it by adding to the URL and form, if 0 is not.
You can change these and other session settings the same way as other PHP settings - in the php.ini file, as well as with the ini_set() command or in the web server configuration files

If only the first is enabled, then at the start of the session (each time session_start () session_start () ) The client is set to a cookie. The browser regularly returns this cookie at each next request and PHP has a session ID. Problems start if the browser does not return a cookie. In this case, without getting the cookie with the identifier, PHP will always start a new session, and the mechanism will not work.

If only the second is on, the cookie is not set. And what happens for what, basically, actually, and it's worth using the built-in mechanism of sessions. After the script does its job and the page is fully formed, PHP looks at it and appends the session ID to each link and to each form. It looks something like this:
<a href="/index.php">Index</a> turns into
<a href="/index.php?PHPSESSID=9ebca8bd62c830d3e79272b4f585ff8f">Index</a>
And a latent field is added to the forms
<input type="hidden" name="PHPSESSID" value="00196c1c1a02e4c37ac04f921f4a5eec" />
And the browser when clicking on any link, or when you click on the button in the form, will send the requested variable to us in the request - the session ID!
For obvious reasons, the identifier is added only to relative links.

Theoretically, in our self-made sessions on the cookies and the database, you can manually assign all the links to all the links with the id - and then our own sessions will work regardless of the cook. But, you will agree - it is more pleasant when someone else does this work? ;-)

By default, the latest versions of PHP include both options. How does PHP come in this case? Cook always shows. A link is auto-complete only if PHP does not detect the cookie with the session ID. When a user visits the site several times during this session, he is given a cookie and links are added. On the next request, if the cookie is supported, PHP sees the cookie and stops adding links. If the cookie does not work, then PHP continues to add the id to the links, and the session is not lost.
Users who work with cookies will see a long link with id once.

Fuh. With the transfer of the identifier finished.
Now it remains to bind to it a file with data on the server side.
PHP will do it for us. It is enough simply to write
session_start ();
$_SESSION [ 'test' ]= 'Hello world!' ;
And PHP will write the variable test into the file associated with this session.
Here is a very important point.
The $_SESSION array is special.
In it, in fact, there are variables that we go to make available in various scripts.
To put a variable in a session, just assign it to the $ _SESSION array element.
To get its value - just go to the same element. The example will be a little lower.

Collection of garbage - deleting obsolete files PHP is also engaged in itself. As well as coding data and a bunch of other necessary things. As a result of this care, working with sessions is very simple.
So, we, actually, approached the example of the work of the sessions.
The example is very small:
<?
session_start
();
if (!isset(
$_SESSION [ 'counter' ])) $_SESSION [ 'counter' ]= 0 ;
echo
"Вы обновили эту страницу " . $_SESSION [ 'counter' ]++. " раз. " ;
echo
"<br><a href=" . $_SERVER [ 'PHP_SELF' ]. ">обновить" ;
?>
<?
session_start
();
if (!isset(
$_SESSION [ 'counter' ])) $_SESSION [ 'counter' ]= 0 ;
echo
"Вы обновили эту страницу " . $_SESSION [ 'counter' ]++. " раз. " ;
echo
"<br><a href=" . $_SERVER [ 'PHP_SELF' ]. ">обновить" ;
?>
We check if we have a counter variable in the session, if not, then create it with a value of 0, and then output its value and increase it by one. The increased value will be written to the session, and the next time the script is called, the variable will have a value of 1, and so on.
Everything is very simple.

In order to access the session variables on any pages of the site, you must write ONLY (!) A line at the very beginning of EVERY file, in which we need the session:
session_start ();
And then refer to the elements of the array $ _SESSION. For example, the authorization check will look something like this:
session_start ();
if (
$_SESSION [ 'authorized' ]<> 1 ) {
header ( "Location: /auth.php" );
exit;
}

Removing variables from a session.
If you have register_globals=off , then just write
unset( $_SESSION [ 'var' ]); If not, then next to it you need to write
session_unregister ( 'var' );
Application area.
It is very important to understand what the session should be used for, and why not.

First, remember that sessions can only be used when they are needed by the user himself, and not in order to obstruct him. After all, he can get rid of the identifier at any time!
For example, when checking for what a person completes the form, and not a script, the user is interested in the session working - otherwise he will not be able to send the form! But to limit the number of requests to the script, the session is no longer suitable - a malicious script will simply not return the identifier.

Secondly. It is important to clearly imagine the fact that a session is a session of working with the site, as a person understands it. I came, worked, closed the browser - the session was over. Like a movie session. If you want to see one more, buy a new ticket. Start a new session. There is also a technical explanation for this. Guaranteed the mechanism of sessions works only until the closing of the browser. After all, the client may not work with cookies, and in this case, naturally, all the links added with the identifier will be lost with its closure.
True, the session can disappear without closing the browser. Due to the limitations discussed in the most important section of this FAQ, the session mechanism can not determine when the user closed the browser. For this purpose, a timeout is used - a predetermined time, after which we believe that the user left the site. By default this parameter is 24 minutes.
If you want to save user information for a longer period, use cookies and, if necessary, a database on the server. In particular, this is how all popular authorization systems work:
- Upon the identification of the user, the session starts and the authorization sign is transmitted in it.
- If you need to "remember" the user, then he is put a cookie that identifies him.
- The next time the user logs on to the site, in order to authorize, he must either enter the password, or the system will recognize it himself on the previously delivered cookie, and start the session. New session, but not continuing the old one.

Thirdly, do not start the session indiscriminately, to everyone who enters the site. This will create a completely unnecessary workload. Do not use sessions over trifles - for example, in counters. What the spider calls sessions is considered, of course, based on stats statistics, and not by using a session mechanism similar to php-shnom.
In addition, let's take a search engine that indexes your site. If the crawler does not support cookies, then php will supply PHPSESSID links by default, which - consonance - may not like the search engine very much, which, according to rumors, does not favor dynamic links, and here in general at every call - a new address !!
If sessions are used to restrict access to a closed section of the site, then everything is just a search engine and should not be indexed.
If you have to show the same page to both authorized and unauthorized users, then this trick will help - start the session only to those who entered the password, or to those who have already started the session.
For this, at the session_start () each page, instead of just session_start () session_start () Write
if (isset( $_REQUEST [ session_name ()])) session_start (); Thus, We will start the session only to those who sent the identifier.
Accordingly, it is necessary to send it to the user for the first time - at the moment of authorization.
If the name is correct, write session_start () session_start () !!

Possible problems and their elimination.

The most common errors that PHP gives when trying to work with sessions are:
Two of them,
Warning: Cannot send session cookie - headers already sent
Warning: Cannot send session cache limiter - headers already sent

Are caused by the same reason, the solution is described in this fake here
The third,
Warning: open(/tmp\sess_SID, O_RDWR) failed: No such file or directory (2) in full_script_path on line number (before it looked like Warning: Failed to write session data (files). Please verify that the current setting of session.save_path is correct (/tmp) ),
If you translate it from English, explains the problem in detail: the path to the directory in which the session files are written is not available in php.ini. This error is most easily corrected. Just register a directory that exists and is available for writing, for example,
session.save_path = c:\windows\temp
And do not forget to restart the apache after that.

As it turns out, human cleverness has no limits, and therefore I have to explain:
The message about the third error (it is impossible to find the directory) INEVITABLE will result in the appearance of the first two, because the error message is output to the browser and after it headers can not be used. So do not rush to look for a premature conclusion, but first write down the right way!

The next most common problem with working with sessions is the heavy legacy register_globals. Do NOT give script variables the names that match the indexes of the $ _SESSION array!
With register_globals = on, the values ​​will overwrite each other, and you will get confused.
And with register_globals = off, there will be another error: "Your script possibly relies on a session side-effect which existed until PHP 4.2.3.", In the case that the script has a session variable that does not matter, and a global variable with the same name . To get rid of it, you must always initialize variables before using (or at least check for existence) and not give global variables the names that match the indexes of the $ _SESSION array.

If it does not work, but no messages are displayed, add two lines to the very beginning of the script, which are responsible for displaying ALL errors on the screen - it is possible that there are errors, but you simply can not see them.
ini_set ( 'display_errors' , 1 );
error_reporting ( E_ALL );
Or see errors in error_log. In general, the topic of displaying error messages is beyond the scope of this article, so just make sure that you can see them. A little more about the search for errors can be found in this section .

If you are sure that there are no errors, but the above example does not work anyway, then, probably, PHP does not include sending id through the URL, and cookies for some reason do not work .
See what you have with the cookies.
In general, if you do not "work" the session, then first try to transfer the session ID manually, that is, make a link and assign the identifier to it:
<?
session_start
();
if (!isset(
$_SESSION [ 'counter' ])) $_SESSION [ 'counter' ]= 0 ;
echo
"Вы обновили эту страницу " . $_SESSION [ 'counter' ]++. " раз.<br>
<a href="
. $_SERVER [ 'PHP_SELF' ]. '?' . session_name (). '=' . session_id (). ">обновить</a>" ;
?>
<?
session_start
();
if (!isset(
$_SESSION [ 'counter' ])) $_SESSION [ 'counter' ]= 0 ;
echo
"Вы обновили эту страницу " . $_SESSION [ 'counter' ]++. " раз.<br>
<a href="
. $_SERVER [ 'PHP_SELF' ]. '?' . session_name (). '=' . session_id (). ">обновить</a>" ;
?>
Make sure that the session.use_only_cookies directive is not enabled, which prevents PHP from accepting the session ID if it was passed through a URL

If this example does not work, then the problem is either in trivial misspellings (half of the "problems" with the sessions comes from the incorrectly written variable name) or in the too old version of PHP: session support appeared in version 4.0, and the array $_SESSION - in 4.1 (Up This was used by $HTTP_SESSION_VARS ).
If it works, then the problem is in the cookies. Track - what kind of cookie the server puts to the browser, whether the browser returns it. It's very useful to search by browsing by browsing through the exchange of HTTP headers between the browser and the server.
The explanation of the working principle of the cook goes beyond this and too much of the text, but at least make sure that the server sends the cookie with the identifier, and the browser - returns. And thus identifiers coincide with each other =)
Setting the cookie should look like
Set-Cookie: PHPSESSID=prlgdfbvlg5fbsbshch6hj0cq6; or how
Set-Cookie: PHPSESSID=prlgdfbvlg5fbsbshch6hj0cq6; path=/ Set-Cookie: PHPSESSID=prlgdfbvlg5fbsbshch6hj0cq6; path=/ (if you are requesting a script not from the root directory)
The server response should look like
Cookie: PHPSESSID=prlgdfbvlg5fbsbshch6hj0cq6 or
Cookie: PHPSESSID=prlgdfbvlg5fbsbshch6hj0cq6; b=b Cookie: PHPSESSID=prlgdfbvlg5fbsbshch6hj0cq6; b=b if the browser returns other cookies, except for the session identifier.

If the cookie does not return a cookie, check if cookies are working at all.
Make sure that the domain you are accessing has a normal name (which has at least one point and does not contain forbidden characters, for example, underscores) and clean the browser's cache - these are the two main reasons why cookies may not work.

If the example works from here, and your own code is not, then the problem is obviously not in the sessions, but in the algorithm. Look for where you lost the variable in the steps.

Another problem can arise if you use redirect through header or navigation using JavaScript.
The fact is that PHP automatically appends the session identifier only to links of the form <a href=> , but does not do it for the header, javascript, meta tags.
Therefore, you need to add an identifier with your hands, for example, like this:
header ( "Location: /script.php?" . session_name (). '=' . session_id ());
It should be remembered that php locks the session file. That is, if one of your scripts starts the session and is executed for a long time, while the other one tries to start it with the same identifier at that time, it will hang. Therefore, in long running scripts, you should start the session only when it is needed, and then close it, using session_write_close()

Also, it is very rare, and it's completely unclear where the emerging one comes from, the problem is that setting session.save_handler is different from files. If it's not, correct it.

Security
Security sessions - the topic is extensive. Therefore, I will focus on a few key points.
The most textbook is not to pass the identifier through the address bar. This is written even in php.ini, but this limits the functionality of the sessions. If you decide to follow this advice, then except session.use_trans_sid = 0, do not forget session.use_only_cookies = 1
It is desirable to bind the session to the IP address: thus, if the identifier is stolen, the villain will still not be able to use it in most cases.
It is recommended that you use the session.save_path directive, which you use to set your own directory for saving session files. This is more secure than when they are stored in the default temporary server directory.

Additional Information:
  • In addition to the cook, the session mechanism also sends headers that prohibit page caching (the same cache limiter). For html this is correct and necessary. But when you try to send a file to the script that verifies the authorization, the Internet explorer refuses to download it. It is because of this header. Call
    session_cache_limiter ( "private" ); Before the start of the session should solve the problem.
  • Strange as it may seem, but in array $_SESSION you can not use numeric indices - $_SESSION [ 1 ], $_SESSION [ '10' ] $_SESSION [ 1 ], $_SESSION [ '10' ] - the session will not work.
  • Somewhere between versions 4.2 and 5.0, it was impossible to session.use_trans_sid with ini_set () ini_set () . Since 5.0 it is already possible again.
  • Prior to version 4.3.3, the cookie was sent to PHP only if there was no identifier at the start of the session in the request. Now the cookie is sent each time session_start () session_start ()

    Example of authorization using sessions
    Let's illustrate all of the above with a small example:
    Create the auth.php file:
    <?
    if (isset( $_POST [ 'auth_name' ])) {
    $name = mysql_real_escape_string ( $_POST [ 'auth_name' ]);
    $pass = mysql_real_escape_string ( $_POST [ 'auth_pass' ]);
    $query = "SELECT * FROM users WHERE name='$name' AND pass='$pass'" ;
    $res = mysql_query ( $query ) or trigger_error ( mysql_error (). $query );
    if (
    $row = mysql_fetch_assoc ( $res )) {
    session_start ();
    $_SESSION [ 'user_id' ] = $row [ 'id' ];
    $_SESSION [ 'ip' ] = $_SERVER [ 'REMOTE_ADDR' ];
    }
    header ( "Location: http://" . $_SERVER [ 'HTTP_HOST' ]. $_SERVER [ 'REQUEST_URI' ]);
    exit;
    }
    if (isset(
    $_GET [ 'action' ]) AND $_GET [ 'action' ]== "logout" ) {
    session_start ();
    session_destroy ();
    header ( "Location: http://" . $_SERVER [ 'HTTP_HOST' ]. "/" );
    exit;
    }
    if (isset(
    $_REQUEST [ session_name ()])) session_start ();
    if (isset(
    $_SESSION [ 'user_id' ]) AND $_SESSION [ 'ip' ] == $_SERVER [ 'REMOTE_ADDR' ]) return;
    else {
    ?>
    <form method="POST">
    <input type="text" name="auth_name"><br>
    <input type="password" name="auth_pass"><br>
    <input type="submit"><br>
    </form>
    <?
    }
    exit;
    ?>