GENERATING THUMBNAIL IMAGES 241 If you want to delete the original image after the thumbnail has been created, pass true as the second argument to the constructor like this: $upload = new Ps2_ThumbnailUpload('C:/upload_test/', true); The class has the following public methods: • setThumbDestination(): This sets the path to the folder where the thumbnail images are to be saved. If you dont call this method, the thumbnails are stored in the same folder as the original images. • setThumbSuffix(): Use this to change the suffix inserted into the thumbnail names. The default is _thb. • move(): This uploads the original image(s) and generates the thumbnail(s). By default, images that have the same name as an existing one are renamed. To overwrite existing images, pass true as an argument to this method. It also inherits the following methods from the parent Ps2_Upload class: • getMessages(): Retrieves messages generated by the upload and the thumbnail. • getMaxSize(): Gets the maximum upload size. The default is 50kB. • setMaxSize(): Changes the maximum upload size. The argument should be expressed as the number of bytes permitted. • addPermittedTypes(): This allows you to add other MIME types to the upload. The Ps2_Thumbnail class rejects MIME types that it doesnt recognize, but the files are uploaded as normal to the main destination folder. The Ps2_ThumbnailUpload class wasnt designed with mixed uploads in mind, so refine the messages generated by the Ps2_Thumbnail class if you want to use the inherited addPermittedTypes() method. Because the Ps2_ThumbnailUpload class is dependent on the Ps2_Upload and Ps2_Thumbnail classes, you need to upload all three class definition files to your remote web server when using this class on a live website. Chapter summary This has been quite an intense chapter, showing not only how to generate thumbnails from larger images, but also introducing you to extending an existing class and overriding inherited methods. Designing and extending classes can be confusing at first, but it becomes less intimidating if you concentrate on what each method is doing. A key principle of class design is to break large tasks down into small, manageable units. Ideally, a method should perform a single task, such as creating the image resource for the original image. This isnt always possible. For example, the createThumbnail() and processFile() methods perform multiple operations. The real advantage of using classes is the time and effort they save once you have defined them. Instead of typing dozens of lines of code each time you want to add file or thumbnail upload functionality to a website, calling the class involves just a few simple lines. Also dont just think of the code in this chapter as being for creating and uploading thumbnail images. Many of the subroutines in the class files could be adapted for use in other situations. CHAPTER 8 242 In the next chapter, youll learn all about PHP sessions, which preserve information related to a specific user and play a vital role in password-protecting web pages. Download from Wow! eBook <www.wowebook.com> 243 Chapter 9 Pages That Remember: Simple Login and Multipage Forms The Web is a brilliant illusion. When you visit a well-designed website, you get a great feeling of continuity, as though flipping through the pages of a book or a magazine. Everything fits together as a coherent entity. The reality is quite different. Each part of an individual page is stored and handled separately by the web server. Apart from needing to know where to send the relevant files, the server has no interest in who you are. Each time a PHP script runs, the variables exist only in the servers memory and are normally discarded as soon as the script finishes. Even variables in the $_POST and $_GET arrays have only a brief life span. Their value is passed once to the next script and then removed from memory unless you do something with it, such as store the information in a hidden form field. Even then, it persists only if the form is submitted. To get around these problems, PHP uses sessions. After briefly describing how sessions work, Ill show you how you can use session variables to create a simple file-based login system and pass information from one page to another without the need to use hidden form fields. In this chapter, youll learn about the following: • Understanding what sessions are and how to create them • Creating a file-based login system • Checking password strength with a custom-built class • Setting a time limit for sessions • Using sessions to keep track of information over multiple pages What sessions are and how they work A session ensures continuity by storing a random identifier—the session ID—on the web server and on the visitors computer (as a cookie). The web server uses the cookie to recognize that its communicating with the same person (or, to be more precise, with the same computer). Figures 9-1 through 9-3 show the details of a simple session created in my local testing environment. CHAPTER 9 244 As Figure 9-1 shows, the cookie stored in the browser is called PHPSESSID, and the content is a jumble of letters and numbers. This random string is the sessions ID. Figure 9-1. PHP sessions store a unique identifier as a cookie in the browser. A matching file, which contains the same jumble of letters and numbers as part of its filename, is created on the web server, as shown in Figure 9-2. Figure 9-2. The content of the cookie identifies the session data stored on the web server. When a session is initiated, the server stores information in session variables that can be accessed by other pages as long as the session remains active (normally until the browser is closed). Because the session ID is unique to each visitor, the information stored in session variables cannot be seen by anyone else. This means sessions are ideal for user authentication, although they can be used for any situation PAGES THAT REMEMBER: SIMPLE LOGIN AND MULTIPAGE FORMS 245 where you want to preserve information for the same user when passing from one page to the next, such as with a multipage form or a shopping cart. The only information stored on the users computer is the cookie that contains the session ID, which is meaningless by itself. This means there is no danger of private information being exposed through someone examining the contents of a cookie on a shared computer. The session variables and their values are stored on the web server. Figure 9-3 shows the contents of a simple session file. As you can see, its in plain text, and the content isnt difficult to decipher. The session shown in the figure has one variable: name. The variables name is followed by a vertical pipe, then the letter “s”, a colon, a number, another colon, and the variables value in quotes. The “s” stands for string, and the number indicates how many characters the string contains. So, this session variable contains my name as a string, which is five characters long. Figure 9-3. The details of the session are stored on the server in plain text. This setup has several implications. The cookie containing the session ID normally remains active until the browser is closed. So, if several people share the same computer, they all have access to each others sessions unless they always close the browser before handing over to the next person, something over which you have no control. So, its important to provide a logout mechanism to delete both the cookie and the session variables, keeping your site secure. You can also create a timeout mechanism, which automatically prevents anyone from regaining access after a certain period of inactivity. Storing session variables in plain text on the web server is not, in itself, a cause for concern. As long as the server is correctly configured, the session files cannot be accessed through a browser. Inactive files are also routinely deleted by PHP (in theory, the lifetime is 1,440 seconds—24 minutes—but this cannot be relied upon). Nevertheless, it should be obvious that, if an attacker manages to compromise the server or hijack a session, the information could be exposed. So, although sessions are generally secure enough for password protecting parts of a website or working with multipage forms, you should never use session variables to store sensitive information, such as passwords or credit card details. As youll see in “Using sessions to restrict access” later in this chapter, although a password is used to gain access to a protected site, the password itself is stored (preferably encrypted) in a separate location, and not as a session variable. Sessions are supported by default, so you dont need any special configuration. However, sessions wont work if cookies are disabled in the users browser. It is possible to configure PHP to send the session ID through a query string, but this is considered a security risk. Creating PHP sessions Just put the following command in every PHP page that you want to use in a session: session_start(); This command should be called only once in each page, and it must be called before the PHP script generates any output, so the ideal position is immediately after the opening PHP tag. If any output is generated before the call to session_start(), the command fails and the session wont be activated for that page. (See “The Headers already sent error” section later for an explanation.) CHAPTER 9 246 Creating and destroying session variables You create a session variable by adding it to the $_SESSION superglobal array in the same way you would assign an ordinary variable. Say you want to store a visitors name and display a greeting. If the name is submitted in a login form as $_POST['name'], you assign it like this: $_SESSION['name'] = $_POST['name']; $_SESSION['name'] can now be used in any page that begins with session_start(). Because session variables are stored on the server, you should get rid of them as soon as they are no longer required by your script or application. Unset a session variable like this: unset($_SESSION['name']); To unset all session variables—for instance, when youre logging someone out—set the $_SESSION superglobal array to an empty array, like this: $_SESSION = array(); Do not be tempted to try unset($_SESSION) . It works all right—but its a little too effective. It not only clears the current session but also prevents any further session variables from being stored. Destroying a session By itself, unsetting all the session variables effectively prevents any of the information from being reused, but you should also invalidate the session cookie like this: if (isset($_COOKIE[session_name()])) { setcookie(session_name(), '', time()-86400, '/'); } This uses the function session_name() to get the name of the session dynamically and resets the session cookie to an empty string and to expire 24 hours ago (86400 is the number of seconds in a day). The final argument ('/') applies the cookie to the whole domain. Finally, destroy the session with the following command: session_destroy(); By destroying a session like this, there is no risk of an unauthorized person gaining access either to a restricted part of the site or to any information exchanged during the session. However, a visitor may forget to log out, so its not always possible to guarantee that the session_destroy() command will be triggered, which is why its so important not to store sensitive information in a session variable. You may find session_register() and session_unregister() in old scripts. These functions are deprecated. Use $_SESSION['variable_name'] and unset($_SESSION['variable_name']) instead. PAGES THAT REMEMBER: SIMPLE LOGIN AND MULTIPAGE FORMS 247 Regenerating the session ID When a user changes status, such as after logging in, its recommended as a security measure to regenerate the session ID. This changes the random string of letters and numbers that identify the session, but preserves all the information stored in session variables. In PHP Pro Security (Apress, 2005, ISBN 978-1-59059-508-4), Chris Snyder and Michael Southwell explain that “the goal of generating a fresh session ID is to remove the possibility, however slight, that an attacker with knowledge of the low- level security session might be able to perform high-security tasks.” To regenerate the session ID, simply call session_regenerate_id() and redirect the user to another page or reload the same one. The “Headers already sent” error Although using PHP sessions is very easy, theres one problem that causes beginners a great deal of head banging. Instead of everything working the way you expect, you see the following message: Warning: Cannot add header information - headers already sent Ive mentioned this problem several times before in conjunction with the header() function. It affects session_start() and setcookie() as well. In the case of session_start(), the solution is simple: make sure that you put it immediately after the opening PHP tag (or very soon thereafter), and check that theres no whitespace before the opening tag. Sometimes, the problem occurs even if there is no whitespace ahead of the PHP tag. This is usually caused by editing software inserting the byte order mark (BOM) at the beginning of the script. If this happens, open your script editors preferences and disable the use of the BOM in PHP pages. When using setcookie() to destroy the session cookie, though, its quite likely that you may need to send output to the browser before calling the function. In this case, PHP lets you save the output in a buffer using ob_start(). You then flush the buffer with ob_end_flush() after setcookie() has done its job. Youll see how to do this in PHP Solution 9-2. Using sessions to restrict access The first words that probably come to mind when thinking about restricting access to a website are “username” and “password.” Although these generally unlock entry to a site, neither is essential to a session. You can store any value as a session variable and use it to determine whether to grant access to a page. For instance, you could create a variable called $_SESSION['status'] and give visitors access to different parts of the site depending on its value, or no access at all if it hasnt been set. A little demonstration should make everything clear and show you how sessions work in practice. PHP Solution 9-1: A simple session example This should take only a few minutes to build, but you can also find the complete code in session_01.php, session_02.php, and session_03.php, in the ch09 folder. 1. Create a page called session_01.php in a new folder called sessions in the phpsols site root. Insert a form with a text field called name and a submit button. Set the method to post and action to session_02.php. The form should look like this: CHAPTER 9 248 <form id="form1" method="post" action="session_02.php"> <p> <label for="name">Name:</label> <input type="text" name="name" id="name"> </p> <p> <input type="submit" name="Submit" value="Submit"> </p> </form> 2. In another page called session_02.php, insert this above the DOCTYPE declaration: <?php // initiate session session_start(); // check that form has been submitted and that name is not empty if ($_POST && !empty($_POST['name'])) { // set session variable $_SESSION['name'] = $_POST['name']; } ?> The inline comments explain whats going on. The session is started, and as long as $_POST['name'] isnt empty, its value is assigned to $_SESSION['name']. 3. Insert the following code between the <body> tags in session_02.php: <?php // check session variable is set if (isset($_SESSION['name'])) { // if set, greet by name echo 'Hi, ' . $_SESSION['name'] . '. <a href="session_03.php">Next</a>'; } else { // if not set, send back to login echo 'Who are you? <a href="session_01.php">Login</a>'; } ?> 4. If $_SESSION['name'] has been set, a welcome message is displayed along with a link to session_03.php. Otherwise, the page tells the visitor that it doesnt recognize whos trying to gain access, and provides a link back to the first page. Take care when typing the following line: echo 'Hi, ' . $_SESSION['name'] . '. <a href="session03.php">Next</a>'; The first two periods (surrounding $_SESSION['name'] ) are the PHP concatenation operator. The third period (immediately after a single quote) is an ordinary period that will be displayed as part of the string. PAGES THAT REMEMBER: SIMPLE LOGIN AND MULTIPAGE FORMS 249 5. Create session_03.php. Type the following above the DOCTYPE to initiate the session: <?php session_start(); ?> 6. Insert the following code between the <body> tags of session_03.php: <?php // check whether session variable is set if (isset($_SESSION['name'])) { // if set, greet by name echo 'Hi, ' . $_SESSION['name'] . '. See, I remembered your name!<br>'; // unset session variable unset($_SESSION['name']); // invalidate the session cookie if (isset($_COOKIE[session_name()])) { setcookie(session_name(), '', time()-86400, '/'); } // end session session_destroy(); echo '<a href="session_02.php">Page 2</a>'; } else { // display if not recognized echo "Sorry, I don't know you.<br>"; echo '<a href="session_01.php">Login</a>'; } ?> If $_SESSION['name'] has been set, the page displays it, then unsets it and invalidates the current session cookie. By placing session_destroy() at the end of the first code block, the session and its associated variables cease to be available. 7. Load session_01.php into a browser, type your name in the text field, and click Submit. You should see something like the following screenshot. At this stage, there is no apparent difference between what happens here and in an ordinary form. 8. When you click Next, the power of sessions begins to show. The page remembers your name, even though the $_POST array is no longer available to it. Theres a problem, though, with that headers already sent error message. Youll fix that later. CHAPTER 9 250 9. Click the link to Page 2 (just below the error message). The session has been destroyed, so this time session_02.php has no idea who you are. 10. Type the address of session_03.php in the browser address bar and load it. It, too, has no recollection of the session and displays an appropriate message. You need to get rid of the warning message in step 8, not only because it looks bad but also because it means setcookie() cant invalidate the session cookie. Even though session_start() comes immediately after the opening PHP tag in session_03.php, the warning message is triggered by the DOCTYPE declaration, the <head>, and other HTML being output before setcookie(). PHP Solution 9-2: Buffering the output with ob_start() Although you could put setcookie() in the PHP block above the DOCTYPE declaration, you would also need to assign the value of $_SESSION['name'] to an ordinary variable, because it ceases to exist after the session is destroyed. Rather than pull the whole script apart, the answer is to buffer the output with ob_start(). Continue working with session_03.php from the previous section. 1. Amend the PHP block above the DOCTYPE declaration like this: . work in practice. PHP Solution 9-1 : A simple session example This should take only a few minutes to build, but you can also find the complete code in session_01 .php, session_02 .php, and session_03 .php, . not empty if ($_POST && !empty($_POST['name'])) { // set session variable $_SESSION['name'] = $_POST['name']; } ?> The inline comments explain. about PHP sessions, which preserve information related to a specific user and play a vital role in password-protecting web pages. Download from Wow! eBook <www.wowebook.com> 243 Chapter