PHP Classes

How to Use PHP Sessions to Handle Many Simultaneous Accesses

Recommend this page to a friend!
  Blog PHP Classes blog   RSS 1.0 feed RSS 2.0 feed   Blog How to Use PHP Sessio...   Post a comment Post a comment   See comments See comments (2)   Trackbacks (0)  

Author:

Viewers: 429

Categories: PHP Tutorials, PHP Performance

Most PHP applications use sessions to keep track of users and store data that is relevant to them.

By default, PHP uses local files to store session data. That is fine for sites and applications with few simultaneous users.

Read this article to learn more about PHP sessions and how you can improve the way you use PHP sessions to be ready to handle better the situation when your PHP sites or applications start getting many simultaneous users.




Loaded Article

Introduction to Sessions

Session is a feature in web programming languages to maintain the state of a logged-in user.

Prerequisite for Understanding Sessions

One must know 3 tier architecture to understand the session. If someone hasn't heard about this then to describe in short, the 3 tier architecture consists of a client machine (basically browser), web server and database.  A dynamic web application consists of 3 tier architecture.


Browser communicates with a web server to get dynamically generated HTML content with the help of a database.


Sessions come into picture for maintaining communication between browser and web server.


To understand the working of the session we will be using PHP for this article. For practical purposes one can install an XAMPP application available for free on the internet.

Fundamentals of session

Web applications use HTTP/HTTPS protocol for interaction between Browser and the web server and these protocols are stateless. Stateless means that the server and browser doesn’t maintain the state of the connection for recurring requests. For every request the TCP connection is established between browser and the web server. The dynamically generated HTML content is delivered and the TCP connection is closed.


Session is a feature in web applications that maintains the state of a logged-in user between concurrent requests between browsers and the web application server.


Cookies play an important role on the client side for maintaining sessions. Cookies are stored on browsers end which are bonded with respective domains. The cookie name, values pair are attached/sent as a part of headers with every request to the domain web server for every request from the browser.

Going the PHP Way

Let's start with a simple Hello world in the index.php file.


<?php

echo 'Hello World';

?>


When executed via browser the output is as below.


On inspect element we will be able to see the new screen as below.



When I inspect the browser we will be able to see similar headers for index.php as below.



This simple inspection will be required to help you understand the flow of the session. When we refer to headers it is to look for details as mentioned in the above screen.

session_start()

The function used to handle sessions in PHP is session_start() on the server end.

How does session_start() work?

Let's understand this with an example.

 

<?php

session_start();

echo 'Hello World';

?>


O/P - Hello World


The O/P is very expected. But in the background there are many checks happening.


When a request is made to the server from the browser the function session_start() checks for a cookie with name PHPSESSID. If this cookie does not exist then the function makes sure to create one in the browser via the headers in the HTML response. The value of the cookie PHPSESSID is an alphanumeric string. This string value is prepended with a default value ("sess_" set in php.ini) to be used as the name of the file to be created in the temporary folder on the server. Such files are created for every user where respective user SESSION data is stored.


Headers on first access:


Request

GET /rnd/index.php HTTP/1.1

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

Upgrade-Insecure-Requests: 1

Host: localhost:8080

User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.3 Safari/605.1.15

Accept-Language: en-GB,en;q=0.9

Accept-Encoding: gzip, deflate

Connection: keep-alive


Response

HTTP/1.1 200 OK

Set-Cookie: PHPSESSID=pct9tjl6imbuepdl4scp513962; path=/

Pragma: no-cache

Content-Type: text/html; charset=UTF-8

Expires: Thu, 19 Nov 1981 08:52:00 GMT

Date: Sun, 13 Mar 2022 09:29:05 GMT

Cache-Control: no-store, no-cache, must-revalidate

Keep-Alive: timeout=5, max=100

Content-Length: 11

Connection: Keep-Alive

Server: Apache/2.4.52 (Unix) PHP/8.1.3

X-Powered-By: PHP/8.1.3


On every subsequent request to the server, the browser contains this cookie with name PHPSESSID, which is consumed by the server to open the respective session file whose name can be manipulated via the value of the cookie PHPSESSID for read/write operation. Every user has a unique cookie value generated by the website. This helps in uniquely identifying the user. This fundamental helps the server to identify a request of the user and avoid collusion with other users session data.


Headers for Second / Subsequent request:


Request

GET /rnd/index.php HTTP/1.1

Cookie: PHPSESSID=pct9tjl6imbuepdl4scp513962

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

Upgrade-Insecure-Requests: 1

Host: localhost:8080

User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.3 Safari/605.1.15

Accept-Language: en-GB,en;q=0.9

Accept-Encoding: gzip, deflate

Connection: keep-alive


Response

HTTP/1.1 200 OK

Pragma: no-cache

Content-Type: text/html; charset=UTF-8

Expires: Thu, 19 Nov 1981 08:52:00 GMT

Cache-Control: no-store, no-cache, must-revalidate

Date: Sun, 13 Mar 2022 09:06:06 GMT

Keep-Alive: timeout=5, max=100

Content-Length: 11

Connection: Keep-Alive

X-Powered-By: PHP/8.1.3

Server: Apache/2.4.52 (Unix) PHP/8.1.3


This is how cookies support maintaining the sessions between browser and the web server.


Let us see what happens on the server end when we try to create session specific data.


<?php

$userID = 1;

$firstName = 'Ramesh';


session_start();

$_SESSION[id]         = $userID;

$_SESSION['name']  = $firstName;


echo 'Hello World';

?>


First we need to find the path where session files are created. For this we can use the below shell command.


$ php -r 'echo "PHP Sessions PATH: ".((session_save_path()) ? session_save_path():sys_get_temp_dir() ).PHP_EOL;'

PHP Sessions PATH: /var/folders/2g/2hsh1sjx2_v2gkrp6_s_3_4r0000gn/T

$ cd /var/folders/2g/2hsh1sjx2_v2gkrp6_s_3_4r0000gn/T


$ ls -l

…..

drwx------  3 ramesh  staff     96 Mar 14 17:49 homed

drwx------  2 ramesh  staff     64 Mar 14 17:49 icdd

drwx------  3 ramesh  staff     96 Mar 14 18:09 itunescloudd

drwx------  2 ramesh  staff     64 Mar 14 19:25 metrickitd

$


On executing the above code this generates a session file in this folder.


$ ls -l

…..

drwx------  3 ramesh  staff     96 Mar 14 17:49 homed

drwx------  2 ramesh  staff     64 Mar 14 17:49 icdd

drwx------  3 ramesh  staff     96 Mar 14 18:09 itunescloudd

drwx------  2 ramesh  staff     64 Mar 14 19:25 metrickitd

drwx------  2 ramesh  staff     24 Mar 14 21:59 sess_pct9tjl6imbuepdl4scp513962

$


So, the file is created. Let us check the content of the file.


$ cat sess_pct9tjl6imbuepdl4scp513962

id|i:1;name|s:6:"Ramesh"


One can easily see the session file content as id|i:1;name|s:6:"Ramesh"

The key / value pair in $_SESSION is serialized the PHP way and stored in a session file.

So, we can use the session_start() function as below for login.


<?php

$username = $_POST['username'];

$password = $_POST['password'];


$userdetails = getUserDetails($username);


if(isValidPassword($userdetails['password'], $password)) {


    session_start();

    $_SESSION['user_id']    = $userdetails['user_id'];

    $_SESSION['username']    = $userdetails['username'];

    $_SESSION['firstname']    = $userdetails['firstname'];


    header('Location: dashboard.php');

}

?>


From the above fundamentals one must be clear that session data is stored on the server in the respective user file which can be opened via the session id provided as a part of cookie by the browser.


Since, the session data is stored in files for each user session. There is a possibility that a website has many users actively online and many session files respectively.

Testing the session handling with Apache Benchmark

Apache Benchmark (ab) is a tool for benchmarking your server application. It is designed to give you an impression of how your current web application performs. This especially shows you how many requests per second your web application is capable of serving.


So, we will try to trigger a load of 1,000 concurrent requests per sec on the web application to see what happens in regards to the session.


This load will try to create 1,000 session files on the server and ultimately lead to depletion of inodes required for creation of files and your web application stops responding.


Inodes help the system organize data. Even though inodes don't store the actual content of the file, they hold the locations of the various chunks for every file, along with other metadata, including: The size of the file. Various storage devices and locations where files are stored.


The creation of a file on a storage media is dependent on the inodes availability. If inodes get exhausted no more files can be created.


To overcome this situation one can switch to another mode of saving session data. For this PHP provides a function session_set_save_handler() to customize the way the session should work.


This function takes 9 function names as arguments in order as below.


function open(string $savePath, string $sessionName) : bool {}

function close(): bool {}

function read(string $sessionId): string {}

function write(string $sessionId, string $data): bool {}

function destroy(string $sessionId): bool {}

function gc(int $lifetime): bool {}

function create_sid(): string {} //optional

function validate_sid(string $key): bool {} //optional

function update_timestamp(string $key, string $val): bool {} //optional


Example:

<?php

session_set_save_handler(

    'open',

    'close',

    'read',

    'write',

    'destroy',

    'garbageCollector',

    'createSessionID',//optional

    'validateSessionID',//optional

    'updateTimestamp'//optional

);

?>


As you can see the function takes 9 string values of which 3 are optional. These strings are the name of the function handling respective functionality.


To demonstrate the flow of session we will try with creating each function and placing an echo statement for the function name with __FUNCTION__ as below


<?php

ini_set('session.use_strict_mode',true);


function open($sessionPath, $sessionName): bool

{

    echo '<br/>'.__FUNCTION__;

    return true;

}

function close(): bool

{

    echo '<br/>'.__FUNCTION__;

    return true;

}

function read($sessionID): string

{

    echo '<br/>'.__FUNCTION__;

    Return '';

}

function write($sessionID, $sessionData): bool

{

    echo '<br/>'.__FUNCTION__;

    return true;

}

function destroy($sessionID): bool

{

    echo '<br/>'.__FUNCTION__;

    return true;

}

function garbageCollector($varSessionMaxlifetime): bool

{

    echo '<br/>'.__FUNCTION__;

    return true;

}

function createSessionID(): string

{

    echo '<br/>'.__FUNCTION__;

    return uniqid('', true);

}

function validateSessionID($sessionID): bool

{

    echo '<br/>'.__FUNCTION__;

    return  true;

}

function updateTimestamp($sessionID, $sessionData): bool

{

    echo '<br/>'.__FUNCTION__;

    return true;

}


session_set_save_handler(

    'open',

    'close',

    'read',

    'write',

    'destroy',

    'garbageCollector',

    'createSessionID',//optional

    'validateSessionID',//optional

    'updateTimestamp'//optional

);


session_start();

echo '<br/>Hello World';

?>

On executing above PHP code we will observe the different order in which the supplied function executes for first and concurrent requests.


O/P Below when above code executed first time.

open

createSessionID

read

Hello World

write

close


O/P Below for concurrent execution.

open

validateSessionID

read

Hello World

write

close


session_set_save_handler:

Session Storage

Till now we were refering to storing the session data in the filesystem. There are other modes we can support to store session data like databases.


As we know to change the mode of saving data we can use the session_set_save_handler() function. By customizing we can boost the session handling capacity of the web application.


Here one can use MySQL as a database for demonstrating this.


Let's start with creating a table “session” in “phpSession” database as below.


//Database phpSession

CREATE TABLE IF NOT EXISTS `sessions` (

    `sessionID` CHAR(32) NOT NULL,

    `sessionLastAccessTimestamp` INT UNSIGNED NOT NULL,

    `sessionData` TEXT,

    PRIMARY KEY (`sessionID`)

) ENGINE=InnoDB;


As we all know, data saved in a table corresponds to saving data to the corresponding table file for MySQL database. The data for each user will be differentiated in the form of rows for each user. Since we are using a single table for all users, all users session data will be stored in one single file, So, no worries for inodes depletion.


Let's start with coding as below.


File: db_session.php


<?php

define('DB_HOSTNAME', 'localhost');

define('DB_USERNAME', 'username');

define('DB_PASSWORD', 'password');

define('DB_DATABASE', 'phpSession');


$db = mysqli_connect(DB_HOSTNAME, DB_USERNAME, DB_PASSWORD, DB_DATABASE);


function open($sessionPath, $sessionName): bool

{

    return true;

}

function close(): bool

{

    GLOBAL $db;

    $db = null;

    return true;

}

function read($sessionID): string

{

    GLOBAL $db;


    $sessionData = '';

    $varCurrentTimestamp = time();

    $sql = "

        SELECT

            `sessionData`

        FROM

            `sessions`

        WHERE

            `sessionID` = '$sessionID';";

    $result = mysqli_query($db, $sql);

    if (mysqli_num_rows($result) === 0) {

        $sql = "

            INSERT INTO 

                `sessions`

            SET

                `sessionID` = '$sessionID',

                `sessionLastAccessTimestamp` = $varCurrentTimestamp;";

        mysqli_query($db, $sql);

    }

    return $sessionData;

}

function write($sessionID, $sessionData): bool

{

    GLOBAL $db;


    $sql = "

        UPDATE 

            `sessions`

        SET

            `sessionData` = '$sessionData'

        WHERE

            `sessionID` = '$sessionID';";

    mysqli_query($db, $sql);

    return true;

}

function destroy($sessionID): bool

{

    GLOBAL $db;


    $sql = "

        DELETE FROM  

            `sessions`

        WHERE

            `sessionID` = '$sessionID';";

    mysqli_query($db, $sql);

    return true;

}

function garbageCollector($varSessionMaxlifetime): bool

{

    GLOBAL $db;


    $varCurrentTimestamp = time();

    $sql = "

        DELETE FROM  

            `sessions`

        WHERE

            `sessionLastAccessTimestamp` + $varSessionMaxlifetime < $varCurrentTimestamp;";

    mysqli_query($db, $sql);

    return true;

}

function createSessionID(): string

{

    return uniqid('', true);

}

function validateSessionID($sessionID): bool

{

    GLOBAL $db;


    $varCurrentTimestamp = time();

    $sql = "

        SELECT

            `sessionID`

        FROM

            `sessions`

        WHERE

            `sessionID` = '$sessionID';";

    $result = mysqli_query($db, $sql);

    return (mysqli_num_rows($result) === 0) ? false : true;

}

function updateTimestamp($sessionID, $sessionData): bool

{

    GLOBAL $db;


    $varCurrentTimestamp = time();

    $sql = "

        UPDATE 

            `sessions`

        SET

            `sessionLastAccessTimestamp` = $varCurrentTimestamp

        WHERE

            `sessionID` = '$sessionID';";

    mysqli_query($db, $sql);

    return true;

}

?>


The params to session_set_save_handler() function are basically names of these created functions in respective order.


<?php

session_set_save_handler(

    'open',

    'close',

    'read',

    'write',

    'destroy',

    'garbageCollector',

    'createSessionID',//optional

    'validateSessionID',//optional

    'updateTimestamp'//optional

);


session_start();

echo '<br/>Hello World';

?>


Once you have set the behavior functions as per above you can start with using sessions as you were while programming. Regarding $sessionData how it is serialized, the details on serialization methods can be found here on official PHP website.


<?php

require_once 'db_session.php';


$username = $_POST['username'];

$password = $_POST['password'];


$userdetails = getUserDetails($username);


if(isValidPassword($userdetails['password'], $password)) {


    session_start();

    $_SESSION['user_id']    = $userdetails['user_id'];

    $_SESSION['username']    = $userdetails['username'];

    $_SESSION['firstname']    = $userdetails['firstname'];


    header('Location: dashboard.php');

}

?>


One can change the code as per their database preferences. Like if someone wants to use a NoSql database like Redis or other databases then they need to change the code as per their requirement.


There is one more way to change the mode of saving data, and that is to change the value of param session.save_handler in the php.ini file. The value to this variable defines the name of the handler with which the session needs to be handled for storing and retrieving session data. [Defaults to files.]


By default session.save_handler supports files, sqlite, redis, memcached. Among these files is the default value.


session.save_handler = files


To support other tools one can use session_set_save_handler(). This will override the behavior set via session.save_handler setting in php.ini.


The major difference between working of session via session.save_handler in php.ini and session_set_save_handler() function is session.save_handler has predefined behavior/logic for managing the session; where as via session_set_save_handler() one can customize the behavior as required.


To explain what it means with predefined behavior, let’s assume session.save_handler = files. Here sessions implement locking behavior for a web request.


What does locking mean in session?


Let us assume a user requests a restricted web page content which requires a session. The page content loads and triggers a few ajax calls immediately and the response HTML content is loaded inside the browser.


Since there are multiple requests coming to the server from the same user the cookie with name PHPSESSID has the same value for all the requests, this will point to the same session file (while saving session data in files) for accessing session data simultaneously. The session file is accessed once per each request. The first request to access the session file locks it for its use and other requests have to wait till the lock is released. The lock is released when the PHP script execution gets completed and is then available for others. So your ajax requests are served in sequential manner as per the availability of the session file for I/O operation.


This is the default behavior of session.save_handler and to override this locking behavior one can use  session_set_save_handler().


As we have seen, in the implementation of session_set_save_handler() for MySQL, On Similar analogy, one can check the locking behavior of a file via executing below script with multiple web requests. 


File: file_session_rnd.php

<?php

$startTimestamp = time();

session_start();

sleep(10);

$endTimestamp = time();

$timeTakenByScript = ($endTimestamp - $startTimestamp);

echo 'Time taken by script = '.$timeTakenByScript. ' seconds';

?>


Execute the same script in multiple browser tabs. Expected result for both the tabs is "Time taken by script = 10 seconds". But the first executed tab will tell the time taken is 10 seconds whereas the second tab will tell a figure more than 10 seconds.


The point to note is we are saving the timestamp at the start of the script before starting the session and then sleeping for 10 seconds after starting the session. Once the session file lock is released by tab1 execution, this is then available for the next request. Till that time the session file lock is released; tab2 script waits and we see an increase in the execution time. 


Lets see how we can unlock this locking behavior of file based sessions via session_set_save_handler() function.


File: custom_file_session.php


<?php

function open($sessionPath, $sessionName): bool

{

    return true;

}

function close(): bool

{

    return true;

}

function read($sessionID): string

{

    $sessionData = '';

    $tempFolder = sys_get_temp_dir();

    $sessionFile = "{$tempFolder}/{$sessionID}";

    if(file_exists($sessionFile)) {

        $sessionData = file_get_contents($sessionFile);

    }

    return $sessionData;

}

function write($sessionID, $sessionData): bool

{

    $tempFolder = sys_get_temp_dir();

    $sessionFile = "{$tempFolder}/{$sessionID}";

    return file_put_contents($sessionFile,$sessionData);

}

function destroy($sessionID): bool

{

    $tempFolder = sys_get_temp_dir();

    $sessionFile = "{$tempFolder}/{$sessionID}";

    return unlink($sessionFile);

}

function garbageCollector($varSessionMaxlifetime): bool

{

    return true;

}

function createSessionID($varSessionMaxlifetime): string

{

    return uniqid('', true);

}

function validateSessionID($sessionID): bool

{

    $tempFolder = sys_get_temp_dir();

    $sessionFile = "{$tempFolder}/{$sessionID}";

    return file_exists($sessionFile);

}

function updateTimestamp($sessionID, $sessionData): bool

{

    $tempFolder = sys_get_temp_dir();

    $sessionFile = "{$tempFolder}/{$sessionID}";

    return touch($sessionFile);

}


session_set_save_handler(

    'open',

    'close',

    'read',

    'write',

    'destroy',

    'garbageCollector',

    'createSessionID',//optional

    'validateSessionID',//optional

    'updateTimestamp'//optional

);


$startTimestamp = time();

session_start();

sleep(10);

$endTimestamp = time();

$timeTakenByScript = ($endTimestamp - $startTimestamp);

echo 'Time taken by script = '.$timeTakenByScript. ' seconds';

?>


In the above code every param function is not dependent on a global file pointer. Instead the file operations are done within the respective function itself. This means read and write operations can be performed on a single file at the same moment. No lock for session files so ajax call execution dont need to wait for previous request to get completed for accessing the session file.


So, if you are thinking that switching the behavior from session.save_handler to session_set_save_handler() will increase the performance; the answer is NO. There are few precautions to be taken care of.

When should one remove locking from a session?

Session is used to save data in $_SESSION which can be accessed across requests until the key/data value is removed.


If there is locking the data set in $_SESSION for earlier request X will be available for next request Y. This is because the session data reading and writing operations are in sequential manner for each request until all the requests are served.


Contrary, when you remove locking it is possible that the session data read/write operation is performed at the same moment. This may lead to data loss. For e.g, requests X, Y are triggered at the same time by a logged-in user. These will try to perform read/write operations simultaneously. But when request X adds a key/value pair in $_SESSION which will get saved once done with the execution. The request X additional key/value data won’t be available in request Y. Because the time when they both accessed the data for $_SESSION, was the same for both. But the saving of the data may occur at different timestamps. Suppose, request Y does not make any changes to key/value pairs in $_SESSION and the request takes some more time than request X to get completed. The request X which had changes are written to destination which may then be replaced by request Y $_SESSION data which was the original data. So, this may lead to loss of data.


You need to be careful with this when you are not using a session without locking behavior.


The thumb rule to use sessions without locking is to save data in session only at the time of login and not from anywhere in the web application. If there is a requirement for transferring one data to the redirected URL, then you can use the GET method to transfer one time data across requests or redirects.

Why should one go for sessions without locks?

Because sessions are meant for maintaining login details and not for adding / deleting data which are used across requests. If you need data across requests use the GET method as mentioned.


The advantage of using session without lock is, the session data will be set only once when the user login and henceforth  requests have no write operations to be performed on session. This way concurrent requests have uniform $_SESSION data.


Without locks sessions can take the performance of web applications to a next level. Since there is only read operation for the session data for all requests after login,

PHP provides a feature where script only reads the session data and closes the session immediately after loading the data into $_SESSION despite the requests are yet to complete its execution. 


This can be achieved by passing params to the session_start() function supporting this feature as below.


<?php

session_start([

    'read_and_close'  => true,

]);

?>


So, we can say for session_set_save_handler


<?php

session_set_save_handler(

    'open',

    'close',

    'read',

    'write',

    'destroy',

    'garbageCollector',

    'createSessionID',//optional

    'validateSessionID',//optional

    'updateTimestamp'//optional

);


session_start([

    'read_and_close'  => true,

]);


echo '<br/>Hello World';

?>

If we check the output of session function names via above code, its as below.


O/P for session functions sequence:

open

validateSessionID

read

close

Hello World


From above one can learn that the session is read and closed in a single statement itself.


session_start([

    'read_and_close'  => true,

]);


This loads the session data and instantaneously closes it making the script available with $_SESSION. There might be a chance that in a particular request you may want to perform the write operation in the session. For this we can start the session as normal and do required operations.


<?php

session_start([

    'read_and_close'  => true,

]);


echo '<br/>Hello World 1';


session_start();


echo '<br/>Hello World 2';

?>


O/P for session functions sequence:

open

validateSessionID

read

close

Hello World 1


open

validateSessionID

read

Hello World 2

write

close


Next, suppose someone wants to write the changed content immediately in the session before the execution of request is completed. PHP has provided us with a session_write_close() function for this.


<?php

session_set_save_handler(

    'open',

    'close',

    'read',

    'write',

    'destroy',

    'garbageCollector',

    'createSessionID',//optional

    'validateSessionID',//optional

    'updateTimestamp'//optional

);


session_start([

    'read_and_close'  => true,

]);


echo '<br/>Hello World 1';


/*

 * PHP code

*/

session_start();

$_SESSION[id] = 1;

$_SESSION[name] = Ramesh;


echo '<br/>Hello World 2';


session_write_close();


echo '<br/>Hello World 3';


/*

 * rest of your php code

*/

?>


O/P for session functions sequence:

open

validateSessionID

read

close

Hello World 1


open

validateSessionID

read

Hello World 2

write

close


Hello World 3

Handling Multiple Different Sessions for the Same Domain

For this PHP provides with a function session_name()


Example 1:

<?php

session_name('PHPSESSID_GROUP_1');

session_start();


echo '<pre>';

print_r($_SESSION);

echo '</pre>';

?>


Response

HTTP/1.1 200 OK

Set-Cookie: PHPSESSID_GROUP_1=zxt9tjl6imbuepdl4scp513962; path=/



Example 2:

<?php

session_name('PHPSESSID_WEBSITE_ID');

session_start();


echo '<pre>';

print_r($_SESSION);

echo '</pre>';

?>


Response

HTTP/1.1 200 OK

Set-Cookie: PHPSESSID_WEBSITE_ID=abt9tjl6imbuepdl4scp513962; path=/


Check the session cookie name in the Response header for both the examples. This is how other tools based on PHP handle their session without interfering with the way they handle the session. If any tools conflict with the cookie name then you may observe miracles. :)

Sessions for Horizontal scaling

The other scenario where your file based session may not work is when there is horizontal scaling of a web application server e.g. many web servers behind the load balancer.


The root cause of this is there is a possibility that  one request is served from server X and the next request from server Y for the same user.


In such a scenario we may end up with loss of session since sessions are stored in the filesystem and it is possible that server Y may not find server X session files. The tweak to this is we can enable sticky sessions on LB, which identifies the server on which the request was served earlier via LB coming into picture and  adding additional cookies so that it can guess the server on which the first request originated.


The advantage of storing session data into a database is this supports multiple web servers behind LB. Since is a one point of contact for storing session data and all data related to the session is stored at a single place which is accessible to all servers on the network.

Benchmarking Session

There are many tools available for benchmarking a website. As mentioned earlier ab (Apache Benchmark) is one among them.


With a benchmarking tool one can set the total number of requests to be served along with the number of concurrent requests at an instance. Basically load on the server.


Session is used to read / write into destination mode. E.g, files / database.


Let's start with login.php where a user is validated and $_SESSION if filled with key / value pairs.


File: login_case_1.php

<?php

session_start();


$username = $_POST['username'];

$password = $_POST['password'];


$userdetails = getUserDetails($username);


if(isValidPassword($userdetails['password'], $password)) {


    $_SESSION['user_id']    = $userdetails['user_id'];

    $_SESSION['username']    = $userdetails['username'];

    $_SESSION['firstname']    = $userdetails['firstname'];


    header('Location: dashboard.php');

}

?>


File: login_case_2.php

<?php

$username = $_POST['username'];

$password = $_POST['password'];


$userdetails = getUserDetails($username);


if(isValidPassword($userdetails['password'], $password)) {


    session_start();

    $_SESSION['user_id']    = $userdetails['user_id'];

    $_SESSION['username']    = $userdetails['username'];

    $_SESSION['firstname']    = $userdetails['firstname'];


    header('Location: dashboard.php');

}

?>


In login_case_1 the session is started at the beginning. So every request to this file leads to session execution. Whereas in login_case_2 the session is started only when a valid user is found. Hope you understand the unnecessary load in login_case_1.


Secondly, let's consider a restricted page accessible only after login. E.g, dashboard page.


File: dashboard_case_1.php

<?php

session_start();


/*

 * PHP code

 */

?>

open

createSessionID

read

PHP code

write

close


File: dashboard_case_2.php

<?php

session_start([

    'read_and_close'  => true,

]);


/*

 * PHP code

 */

?>

open

validateSessionID

read

close

PHP code


The dashboard_case_1 will try to perform a read and write operation for the session whereas, the dashboard _case_2 is simply a read operation.


So, you will observe a huge difference while benchmarking for cases 1 and 2.

Locking MySQL implementation

Implementing Locking behavior when using MySQL.


MySQL provides functions for locking for a supplied period in seconds as below.


​​SELECT GET_LOCK('lock1',10);

SELECT GET_LOCK('lock2',10);

SELECT RELEASE_LOCK('lock2');

SELECT RELEASE_LOCK('lock1');


Lock1 and lock2 are the dynamic string and we can use sessionID for this.


More details about these functions can be found on the official MySQL website here.

One can create multiple versions of db_session.php script like for e.g. lock_db_session.php and db-sesison.php.


Now you can use respective <>session.php files as required. We can have a set of scripts for using only read operation and other to do read and write operation into DB for session.

SESSION without COOKIE

To use sessions without cookies one can use them as below.


<?php

ini_set('session.use_cookies', 0);

ini_set('session.use_only_cookies', 0);

ini_set('session.use_trans_sid', 1);

session_start();

?>


This will append session id details in the URL and via the same the session can be managed.

Heavily loaded web application SESSION

As discussed, we have seen different modes of saving session data like for example files and databases.


When it comes to heavily loaded websites which may have trillions of requests per hour. It will be difficult to maintain the files or database mode. SInce the setup will be behind load balancer and if we choose the database as a mode for saving session data then making trillions of DB connections is a huge cost to handle.

So, to tackle such scenario we can save the session data in cookies. This will change the flow of saving session data inside the cookies and is manageable at some extra bandwidth cost; but reduce headache of cost involved for the setup required to handle session data.


Although, session_set_save_handler() has support for saving session data via other modes, This does not support a way for saving session data inside COOKIES. So, the other way around to do this is as below.


<?php

// start.php

ob_start(); // Turn on output buffering

 

// Store the key and IV somewhere safe

$key = openssl_random_pseudo_bytes(32); // 256-bit key

$iv = openssl_random_pseudo_bytes(16); // 128-bit IV


// Encryption

function encryptSess($plaintext, $key, $iv)

{

return openssl_encrypt($plaintext, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);

}


// Decryption

function decryptSess($ciphertext, $key, $iv)

{

return openssl_decrypt($ciphertext, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);

}


// Session Cookie name

$sessCookieName = session_name();


$_SESSION = json_decode(base64_decode(decryptSess($_COOKIE[$sessCookieName], $key, $iv)));


// Code.php

function testEcho() {

    echo $_SESSION['id'];

}

testEcho();

$_SESSION['id'] = 1;


// end.php

$op = ob_get_clean(); // Get current buffer contents and delete current output buffer

$encryptedData = encryptSess(base64_encode(json_encode($_SESSION)), $key, $iv);

setcookie($sessCookieName, $encryptedData, time() + (ini_get("session.gc_maxlifetime")), '/');

echo $op;

?>




You need to be a registered user or login to post a comment

Login Immediately with your account on:



Comments:

2. PHP Cookie Session Handler - Ramesh Narayan Jangid (2024-09-18 14:35)
Handler class that stores session data in cookies... - 0 replies
Read the whole comment and replies

1. Cookie for handling session data - Ramesh Narayan Jangid (2023-08-11 14:23)
Using Cookie for managing session data with encryption.... - 0 replies
Read the whole comment and replies



  Blog PHP Classes blog   RSS 1.0 feed RSS 2.0 feed   Blog How to Use PHP Sessio...   Post a comment Post a comment   See comments See comments (2)   Trackbacks (0)