Tuesday, July 14, 2009

Advanced PHP for Web Professionals By Christopher Cosentino Chapter 2

Introduction to Session Management in PHP4
This chapter introduces you to session management using PHP4's built-in session management techniques.

Session management allows you to track variables associated with a user for a specific Web session. A Web session is the time that a user spends on pages on your site using the same browser instance. If the user closes the browser (or you explicitly end the session), then the session variables are no longer associated with that user, unless you provide for a way that the variables can be saved and later reassociated with the user.

Initial php.ini Settings for Session Management
Before you get started with this chapter, you may have to make a couple of minor changes to your php.ini file so that sessions work correctly.

On Windows
If you are using a Windows version of PHP, the first thing you need to do is to edit your php.ini file. The default session setting in php.ini will not work correctly under Windows.

Open your php.ini file, which is found in c:\windows or c:\winnt, in a text editor and search for the line:

session.save_path = /tmp

Change it to a directory in which you keep temporary files, for example:

session.save_path = C:/temp

You could also leave the value as /tmp and create a directory named "tmp" at the root of the drive on which your Web server resides. For example, if your Web server was located in D:/apache/bin, then you could create the directory d:/tmp and you would not have to change the session.save_path setting in php.ini.

A good indication that the session.save_path has not been set correctly on Windows is if Apache crashes when you try to load a session-enabled page.

On Linux
If you are using Linux, you need to make sure that your /tmp directory can be written to by the user who runs the Web processes. Typically this is the user nobody, and most systems, by default, allow the nobody user to write to the /tmp directory.

The rest of the default session settings should work fine for you in the examples in this chapter.

General Considerations
You should not store the session files in any directory which is viewable from your Web server. If you are using Apache, then that would be any directory under the htdocs directory. The reason you do not want to place session files in a directory that is viewable from your Web server is because malicious users may be able to open those files and view individual session data, and even hijack user's sessions in this manner.

Sessions are typically tracked by PHP by placing a cookie on the user's browser. The cookie references a session ID, which you can view in your scripts by calling the $PHPSESSID variable. However, using cookies is not a foolproof method to track session variables. Some users may set up their browsers so that they do not accept cookies. In these cases, your session variables cannot be tracked using the cookie method.

PHP supports link rewriting, which gets around the problems in using cookies to store session information. With link rewriting, PHP alters any links on your page so that they automatically include the $PHPSESSID variable. This does make the links look a little messy to the user, but in some cases it is the only way to be sure that you are accurately tracking a user's session.

Starting a Session
You cannot track variables across a user session unless you start the session on each page on which you want to use or alter those variables. Starting a session uses the session_start() function:

session_start();

session_start() takes no arguments. If you are starting a new session, then the function initializes the session and creates the necessary temp files to track the session. If a $PHPSESSID is found by the function, either by a cookie or a GET variable, then the function resumes the current session and the page has access to any variables that have been registered to the session.

Once you have started the session, you need to register some variables with it. The session will not track variables until they have been registered using the session_register() function:

session_register(STRING);

The STRING argument to session_register() should be the name of the variable that you want to register with the session so that it may be accessed across any session-enabled pages.

Once you have started the session and registered one or more variables, you can use those variables across any session enabled pages on your site. Script 2-1, session.php, provides a simple example of starting a session and registering a variable.

Script 2-1 session.php
1. <?
2. session_start();
3. if(!isset($count)) {
4. session_register("count");
5. $count = 1;
6. }
7. ?>
8. You have been to this page <?=$count?> times.
9. <?
10. $count++;
11. ?>

Script 2-1. session.php Line-by-Line Explanation LINE
DESCRIPTION

2
Start the session or continue an existing session.

3
The if statement checks to see if the $count variable has been set. If the $count variable has been set, then the script assumes that this is a continuation of a session and takes no action and the script continues onto line 7.

4
If the $count variable has not been set, then the script assumes that this is the beginning of a new session and registers the variable $count.

5
Since the script has just registered the variable $count in the preceding line, it uses this line to assign a default value of 1 to $count.

8
Display a line of text stating the number of times the page has been viewed.

10
Increment the $count variable by one.

Run this script and click the reload button several times. Notice that the number of times the page has been viewed increases by one each time you reload the page. Figure 2-1 shows an example of this. Closing the browser then reloading the page in a new browser resets the $count to '1', as closing the browser effectively ends the session.

Figure 2-1. session.php

The Contents of Session Files
You can view the actual contents of session variables by looking in the directory specified in the session.save_path setting in your php.ini file. In that directory, you should see a file named something along the lines of "sess_c4a9722c1745304544dc5cc1 b3e08996." The important part is that the file name begins with "sess_." The random string of characters after that is the unique identifier that PHP assigns to each session. If you are on a shared site or you have run any sessions in the past, then there may be several files that look like PHP session files. Try to find the one with the timestamp closest to when you accessed the example, but opening any of the files will give you an idea of how the session variables are stored. Opening the file in a text editor displays the following:

count|i:6;

The actual number displayed in the file will be different, depending on how many times you reloaded the page. The contents of the file display the variable name, followed by a pipe symbol, followed by the variable type identifier—in this case "i" for integer—and finally the value of the variable followed by a semicolon. If you have multiple variables registered in the session, then they would be listed in the order they were registered, separated in most cases by semicolons. For example, if you had two integer variables registered in the session, $count and $day, then they would appear as follows:

count|i:6;day|i:7;

Table 2-1 details some of the common variable types you might see in a typical session. file.

Table 2-1. Variable Type Identifiers in Session Files SYMBOL
VARIABLE TYPE

i
Integer

s
String

b
Boolean

d
Float

a
Array

O
Object

N
NULL

Tracking Variables across Pages during a Session
Now that you know how to start a session and track some variables, how about putting this to some use? There are countless uses for sessions, but the real benefit of sessions is to reduce the amount of work for you and your Web server. Think of a typical site that has registered users. This site may have message boards, feature stories that users can respond to, and various other features that users can choose to enable or disable.

Typically, you'd store these preferences in some sort of database and load the values when the user logs into the site. You need a way to remember these values across each page that the user views. If users don't want to see a Poll feature on one page, then it's safe to assume they don't want to see it on other pages as well. You could query the database each time the user loads a page to see which features should be enabled, or you could store that information in a cookie, but there are drawbacks to each method. Querying the database each time a page loads can lead to some serious server resource problems for large sites. A cookie can only hold one value, so storing cookies for each feature option quickly turns into a coding nightmare: When you have more than one or two features, you'd need a cookie for each one!

This is where sessions can help. You can store multiple values that can be accessed across many pages. You keep the database queries to a minimum by loading the user preferences at the start of the session, and you only need to worry about storing one cookie on the user's browser, eliminating the worry of exceeding the finite number of cookies any one site can store on a browser. If you use PHP's session link rewriting feature, you don't even have to worry about that one cookie!

In this next example, you'll load some default variables into a session as if they were coming from a database query and then verify that the session variables work in Script 2-3, track-prefs2.php. The result will appear in Figure 2-2.

Script 2-2 track_prefs1.php
1. <?
2. session_start();
3. session_register("first");
4. session_register("last");
5. session_register("email");
6. session_register("news_prefs");
7. ?>
8. <html>
9. <head>
10. <title>Welcome</title>
11. <style type="text/css">
12. p, ul, h3 {font-family: verdana, helvetica, sans-serif;}
13. .enabled {font-weight: bold; color: green;}
14. .disabled {font-weight: bold; color: red;}
15. </style>
16. </head>
17. <body>
18. <?
19. function load_user_data(){
20. global $first, $last, $email, $news_prefs;
21. $first = "Faye";
22. $last = "Valentine";
23. $email = "faye@bebop.com";
24. $news_prefs = array(
25. "Local" => 0,
26. "Nation" => 1,
27. "World" => 1,
28. "Comics" => 0,
29. );
30. }
31. load_user_data();
32. ?>
33. <h3>Welcome</h3>
34. <p>Welcome back <b><?=$first?></b>
35. <p>Your settings have been loaded.
36. <p><a href=track_prefs2.php>View Your Settings</a>.
37. </body>
38. </html>

Figure 2-2. track_prefs2.php

Script 2-2. track_prefs1.php Line-by-Line Explanation LINE
DESCRIPTION

2
Start the session or continue an existing session.

3–6
Register session variables for use throughout the session.

8–17
Print out the beginning of the HTML page.

19
Declare a new function, load_user_data().

20
Allow the function to use and modify variables in the global scope of the script. These variables correspond to the session variables that were registered earlier in the script.

21–23
Assign some strings to the session variables.

24
Assign an array to the session variable $news_prefs.

25–29
Assign items to the array.

31
Run the load_user_data() function

33–36
Notify the users that their settings have been loaded and provide a link to view the settings.

37–38
Close out the HTML for the page.

Script 2-3 track_prefs2.php
1. <?
2. session_start();
3. ?>
4. <html>
5. <head>
6. <title>View Settings</title>
7. <style type="text/css">
8. p, ul, h3 {font-family: verdana, helvetica, sans-serif;}
9. .enabled {font-weight: bold; color: green;}
10. .disabled {font-weight: bold; color: red;}
11. </style>
12. </head>
13. <body>
14. <h3>View Your Settings</h3>
15. <p>Hello <b><?=$first?> <?=$last?></b>,
16. <p>Email: <?=$email?>
17. <p>Your settings:<ul>
18. <?
19. while(list($key,$value) = each($news_prefs)) {
20. if($value) {
21. $value = "Enabled";
22. } else {
23. $value = "Disabled";
24. }
25. print("<li class=$value>$key: $value</li>");
26. }
27. ?>
28. </ul>
29. </body>
30. </html>

Script 2-3. track_prefs2.php Line-by-Line Explanation LINE
DESCRIPTION

2
Start the session or continue an existing session.

4–13
Print out the beginning of the HTML page.

14–17
Greet the users and show some of their current settings.

19–26
Execute a while loop that goes though the user's settings in the $news_prefs session variable. Print the key of the array item to the screen. If the value of the current array item is enabled, then print "Enabled" on the screen; otherwise, print "Disabled" on the screen. In either case, the value determines which CSS class should be used to display the setting.

27–30
End the HTML page.

Unregistering Session Variables
You may find that some of the session variables that you register are only needed on a few pages and not across an entire site. You can unregister these variables so that they are no longer tracked with the session. This is done using the session_unregister() function:

session_unregister(STRING);

The STRING argument is the name of the variable that you want to unregister from the session. Note that using session_unregister doesn't delete the contents of the variable on the current page. However, the variable value will not be passed to other pages in the session once it has been deleted from the session file.

You may also want to first check to see if the variable is even registered by using the session_is_registered() function:

session_is_registered(STRING);

For example:

//If $name has been registered with the session.
if(session_is_registered("name")) {
unset($name);
session_unregister("name");
}

This has the effect of deleting the variable from the session, as well as deleting the variable from the current page. Also, you don't have to unset the variables one at a time. You can unset all session variables by using the command session_unset():

session_unset();

session_unset() takes no arguments and simply deletes the values of any variables currently registered with the session. session_unregister() and session_unset() are independent of each other. However, do not call session_unregister() before session_unset() if you also want to unset the value of the variable you are unregistering. Unregistering the variable makes that variable untouchable by session_unset(), for it is no longer a session variable. The proper order to unset the variable $name, as well as unregister it, is:

session_unset();
session_unregister("name");

You could also do the opposite if you wanted to make sure that a variable was registered and had a value:

//If $name has not been registered with the session.
if(!session_is_registered("name")) {
session_register("name");
$name = "Spike";
}

Destroying Sessions
If you have been looking at the contents of the session files in your session.save_path directory, you may have noticed that some files have been building up in there, depending on how many session examples you have opened. The reason these files are still there and not deleted when the user ends the session by closing the browser is because we have not made an effort to destroy the session. You have to explicitly destroy sessions for the session files to be deleted. The reason for this is that you have no real way of knowing how long users may be in sessions. It is perfectly feasible for users to start sessions, then go home for the evening (or even a long weekend!) and come back the next day with the browser still sitting open on the desktop. If users have been doing some shopping, you surely wouldn't want to delete their sessions after a set finite amount of time, precisely for the reasons above.

There are two ways to combat the buildup of files in your temporary directory that will not, in most cases, adversely affect your users' sessions.

The first is to make use of the session_destroy() function:

session_destroy();

session_destroy() takes no arguments. session_destroy() unregisters all session variables associated with the user session and removes any session files created by the session. Remember that even if a variable is unregistered with a session, the variable still exists with its value intact on the current page.

This next script demonstrates the use of the session_destroy() function.

Script 2-4 session2.php
1. <?
2. session_start();
3. if(isset($destroy)) {
4. session_destroy();
5. unset($name);
6. } else {
7. if(!session_is_registered("name")) {
8. session_register("name");
9. $name = "Spike";
10. }
11. }
12. ?>
13. <p>SESSID: <?=$PHPSESSID?>
14. <p>Name: <?=$name?>
15. <form action=session2.php method=post>
16. <input type="submit" name="reload" value="Reload Session"><br>
17. <input type="submit" name="destroy" value="Destroy Session">
18. </form>

Script 2-4. session2.php Line-by-Line Explanation LINE
DESCRIPTION

2
Start the session or continue an existing session.

3–5
Check to see if the $destroy variable is set. If it is, then the user has pressed the "Destroy" button.

6–10
If $destroy has not been set, then register a session variable named $name and assign that variable the value "Spike".

13–14
Print out the current session ID and the value of the $name variable.

15–18
Display a form to the user that allows him or her to reload the current page or destroy the session variable associated with the current session. If the user reloads the page (by clicking "Reload" or using the browser's reload button), then the session variables are reset and the value for session ID is displayed. If the user clicks the "Destroy" button, then the session file associated with the page is deleted, which in turn deletes the value of the session variable.

Note the files in your temporary directory while clicking the "Reload Session" and "Destroy Session" buttons. Each time you click "Destroy Session," notice that the session file with the corresponding session ID is deleted from your session.save_path directory. Each click of "Reload Session" causes the file to be recreated in that directory.

The other way to manage the orphaned session files is to make use of PHP's automatic "garbage" cleanup of these session files. Open your php.ini file in a text editor and scroll down to the [session] settings. Look for the following two settings:

; Percentile probability that the 'garbage collection' process is started
; on every session initialization.
session.gc_probability = 1
; After this number of seconds, stored data will be seen as 'garbage' and
; cleaned up by the garbage collection process.
session.gc_maxlifetime = 1440

The first setting, session.gc_probability, sets the percentage probability that the files identified as garbage are deleted. The default of "1" means that there is a one-percent probability that all of the items identified as garbage are deleted for every session started.

The second setting, session.gc_maxlifetime, sets the lifetime, in seconds, that a session file can exist before being labeled as garbage. The default of "1440" means that every session file that is older than 24 minutes is considered junk.

A Simple Session-Based Shopping Cart
The following example is a simple session-based shopping cart that allows users to add or remove items from their cart.

The application, as shown in Figure 2-3, first asks for the user's name and email address. Users are then brought to the main shopping page where they can add or remove items from their cart by modifying the quantity text field for each item and clicking the "Add Items" button.

Figure 2-3. session_cart.php Sign-In Screen

When the users have added at least one item, they are presented with a "Check Out" button. They can continue shopping or check out. This is shown in Figure 2-4.

Figure 2-4. session_cart.php Shopping Screen

When users click "Check Out", they are brought to a confirmation page, which displays the contents of their cart. They then have the choice of finishing the order by clicking the "Finish Order" button or going back and adding additional items to their cart by clicking the "Add More Items" button. This is shown in Figure 2-5.

Figure 2-5. session_cart_checkout.php

Script 2-5 session_cart.php
[View full width]
1. <?
2. session_start();
3. $inventory = array(
4. "001" => "Tooth Paste",
5. "002" => "Facial Tissue",
6. "003" => "Cotton Swabs",
7. "004" => "Shampoo",
8. "005" => "Conditioner",
9. "006" => "Deodorant"
10. );
11. function populate_cart() {
12. global $name, $email, $items;
13. $items = array(0, 0, 0, 0, 0, 0);
14. session_register("name");
15. session_register("email");
16. session_register("items");
17. }
18. function get_user_info() {
19. ?>
20. <h3>Please provide us with the following before continuing:</h3>
21. <form action=session_cart.php method=post>
22. <p>Name: <input type="text" name="name">
23. <br>Email: <input type="text" name="email">
24. <p><input type="submit" name="infosubmit" value="Submit">
25. </form>
26. <?
27. }
28. function shop() {
29. global $name, $email, $items, $inventory;
30. ?>
31. <p>Shop Below:
32. <form action=session_cart.php method=post><p>
33. <?
34. $total = 0;
35. for($i = 0; $i < sizeof($inventory); $i++) {
36. ?>
37. Item ID: <b><?=key($inventory);?></b> Description: <b><?=$inventory[key(
$inventory)]?></b>
38. Qty: <input type="text" name="items_in[<?=$i?>]" value="<?=$items[$i]?>" size="2"
maxlength="2"><br>
39. <?
40. next($inventory);
41. $total = $total + $items[$i];
42. }
43. ?>
44. <p>You have <?=$total?> items in your cart.
45. <p><input type="submit" name="additems">
46. </form>
47. <?
48. if($total > 0) {
49. ?>
50. <form action=session_cart_checkout.php>
51. <input type="submit" name="checkout" value="Check Out!">
52. </form>
53. <?
54. }
55. }
56. /* MAIN */
57. if(isset($additems)) {
58. $items = $items_in;
59. echo("<h3>Cart Updated!</h3>");
60. shop();
61. }elseif(!isset($name)) {
62. get_user_info();
63. } elseif(isset($infosubmit)) {
64. populate_cart();
65. shop();
66. } else {
67. shop();
68. }
69. ?>

Script 2-5. session_cart.php Line-by-Line Explanation LINE
DESCRIPTION

2
Start the session or continue an existing session.

3–10
Create an array to be used as the items available in the store.

11
Declare a new function populate_cart(). This function sets up the user's shopping cart.

12
Declare some global variables for the user. The $items variable is the items in the user's shopping cart.

13
Zero out the items in the user's shopping cart.

14–16
Register the variables that have been declared with the session.

18–27
Declare a function to get the user info when the user first arrives at the shopping cart. The function asks for the user's name and email.

28
Declare the shop function. This function does the work of adding and removing items from the user's shopping cart.

29
Allow the function to access and modify the $name, $email, $items, and $inventory variables.

32
Create the shopping form.

34
Assign a value of 0 to the $totals variable. This variable is used to show how many items are in the user's cart, which is calculated as the user adds or removes items from the cart.

35
Loop through the items in the $inventory array.

37–38
Display the ID (key) and Description (value) for each item in the store's inventory. At the same time, display how many items are in the user's cart in a text input field. If the user has not added any items into the cart, then all values read as 0, which is the value that was used to initialize the cart.

40
Use the next() function so that the next key is available the next time the loop is executed.

41
Add the current total of items in the user's cart for this loop to the running total of items.

42
Exit from the for loop.

44
Display the current item total to the user.

45
Display an "Add Items" button. It is used to execute the additems clause in the main program.

48–54
If the user has at least one item in the cart, then display the "Check Out" button, which brings the user to the session_cart_checkout.php page.

55
End the function.

56
Begin the main program.

57–59
Check to see if the "Add Items" button has been pushed. Display a message to the user that the cart has been updated after the $items session variable is updated.

58
If the "Add Items" button has been pushed, then replace the session variable $items with the items from the form $items_in. The session variable is not automatically updated when you submit variables from a form. You need to explicitly assign a new value to the session variable.

59
Display a message to the user that the cart has been updated after the $items session variable is updated.

60
Run the shop() function so that the user can add or remove additional items.

61
If the "Add Items" button has not been pushed and the $name variable has not been set, then execute the get_user_info() function, since this is the first time the user has visited the page.

63–65
If none of the above is true, but the $infosubmit variable has been set, then we know the user has just entered the information. Execute the populate_cart() function to initialize the cart.

66–68
If nothing else has happened, just execute the shop function. Since the session variable for $name must be set then, we know the user has entered the information and the cart has been initialized, but no items have been added to the cart.

Script 2-6 session_cart_checkout.php
[View full width]
1. <?
2. session_start();
3. $inventory = array(
4. "001" => "Tooth Paste",
5. "002" => "Facial Tissue",
6. "003" => "Cotton Swabs",
7. "004" => "Shampoo",
8. "005" => "Conditioner",
9. "006" => "Deodorant"
10. );
11. if(!isset($name)) {
12. ?>
13. <h2>Sorry, you need to <a href=session_cart.php>add some items</a> before you can
check out.
14. <?
15. }elseif(isset($finish)) {
16. ?>
17. <h3>Order Complete!<h3>
18. <?
19. // send email confirmation...
20. // send order to order processing script...
21. session_unset();
22. session_destroy();
23. } else {
24. ?>
25. <h2>Checkout</h3>
26. <p>Thank you for shopping with us <?=$name?>, your order is below:
27. <p><table border=1>
28. <?
29. $total = 0;
30. for($i = 0; $i < sizeof($inventory); $i++) {
31. ?>
32. <tr><td>Item ID: <b><?=key($inventory);?></b></td>
33. <td>Description: <b><?=$inventory[key($inventory)]?></b></td>
34. <td>Qty: <?=$items[$i]?></td></tr>
35. <?
36. next($inventory);
37. $total = $total + $items[$i];
38. }
39. ?>
40. <tr><td colspan=3 align=right>Total: <?=$total?></td></tr>
41. </table>
42. <p>A confirmation of your order will be sent to the email address: <b><?=$email?></b>,
after you have finished your order.
43. <p>
44. <form action=session_cart.php>
45. <input type="submit" name="continue" value="Add More Items">
46. </form>
47. <form action=session_cart_checkout.php>
48. <input type="submit" name="finish" value="Finish Order">
49. </form>
50. <?
51. }
52. ?>

Script 2-6. session_cart_checkout.php Line-by-Line Explanation LINE
DESCRIPTION

2
Start the session or continue an existing session.

3–10
Recreate an array to be used as the items available in the store.

11
Verify that the $name variable is set. If it is, then we know that there is existing session data.

12–14
If the $name variable is not set, then we know that the there is no session data or that it is incomplete. Redirect users back to the shopping cart page so that they can reregister their name and email and add some items to their cart.

15
Check to see if the "Finish Order" has been pushed.

16–20
If the "Finish Order" button has been pushed, then notify the user that the order has been completed. Then do whatever processing is required, such as sending email or sending the order to an order-processing script.

21
Delete all the session variables, since we no longer need the data.

22
Destory the session to remove the session file from the server's disk.

This has the added benefit of not allowing the user to click "Reload" or the "Back" button and produce multiple orders for the same thing.

23
If the order has not been finalized and we know the $name variable has been set in the session, then execute the remainder of the script.

25–41
Display a table using the data from the $inventory array and from the $items in the user's shopping cart. This table is similar to the one used in the session_cart.php script that displays the shopping form, except that the fields are not editable.

42
Display a message providing details to users of what happens when they click the "Finish Order" button.

43–46
Provide a button so that users can go back and edit items in their cart.

47–49
Display the "Finish Order" button so that users can confirm their order.

51
Close out the if/elseif/else statement started on line 11.

0 comments: