Error Handling | Forms and Views

The login page is a staple of the internet—most websites will require that you authenticate, one way or another. Incidentally, it’s also a typical example of a forms-based page which will include a lot of functionality:

  • Enter e-mail and password, click “Login”.
  • If you have no account, enter an e-mail (never displayed on the website), your name (to be displayed on the website), and the password twice, click “Sign Up”.
  • If you made a mistake on either of these operations, the page appears again with an error message indicating what happened.
  • You also have a “reset my password” link. If you follow the link, you are sent to a page that tells you to enter your e-mail. Submitting your e-mail redirects you to a page telling you a mail has been sent with a link. Visiting that link leads you to a page where you can enter the new password for your account.
  • If you entered an incorrect password while trying to log in, the “reset my password” button assumes the e-mail you entered was the correct one and sends the mail straight away (but contains a link that lets you specify another e-mail).
  • If you try to sign up but your login already exists, you are sent a password reset mail for that login, and a page explains what happened.
  • If you succeed in logging in, signing up, or changing your password, you are redirected to a default page, or to the page you were trying to access before you registered.

And there’s a lot more to be done if you want to: you can for instance include a javascript to verify all fields on the signup form (even one that connects to the server to determine if an e-mail address is already taken).

As far as controllers go, you will need five to handle this:

  • A ‘GET’ controller that displays the forms (possibly with error messages). For instance, ‘http://domain/login‘.
  • A ‘POST’ controller that processes the login form, and redirects either to the login page (if login failed) or the requested page. For instance, ‘http://domain/do-login‘.
  • A ‘POST’ controller that processes the signup form, and redirects either to the login page (if it failed) or the requested page. For instance, ‘http://domain/do-signup‘.
  • A ‘POST’ controller that reacts to password reset requests. If it’s a verified visit with an appropriate verification token, it changes the password. For instance, ‘http://domain/do-reset-password‘.
  • A ‘GET’ controller that is linked to by the “reset password” mail. If visited with the appropriate randomly chosen token (sent in the mail) it displays a form that lets the user reset the password. It is also linked to by the “reset password” button. Otherwise, it sends a mail with a new token. For instance, ‘http://domain/reset-password‘. If no mail is specified, it provides a form for entering one.

While it may seem unusual to have dashes in controller names, our controllers are not classes (what for?) but mere PHP scripts that may include any character in their names.

exclamationThe GET/POST difference, as well as the fact that not everything happens in the same file, is an important one: GET requests get data from the server (in this case, a login page) while POST requests send data to the server (in this case, login credentials) and get a status report or a redirection in return.This is what your browser expects to happen: you can refresh a GET page, but attempting to refresh a POST page will display a warning message (“refreshing this page will submit the form again”) and although resubmitting a POST request is not dangerous on a login page, it could be dangerous on a “post message” or “post new thread” page.

Last but not least, in the case of a successful login request, you will redirect the user anyway (either to the home page, or to whatever page he requested). Moving the login POST handling to another controller merely adds a redirect in a situation that happens rarely (a missed authentication attempt) while allowing clean separation of signup, login and password reset events into their own files.

Before we move on to the next step, it would be interesting to implement a common utility for redirecting the user.

<?php  // utils/redirect.php
  class RedirectUtils
  {
      public static function Temporary($url)
      {
          header('HTTP/1.1 307 Temporary Redirect');
          header('Location: ' $url);
          while (@ob_end_clean());
          exit;
      }

      public static function Permanent($url)
      {
          header('HTTP/1.1 301 Moved Permanently');
          header('Location: ' $url);
          while (@ob_end_clean());
          exit;
      }

      public static function SeeOther($url)
      {
          header('HTTP/1.1 303 See Other');
          header('Location: ' $url);
          while(@ob_end_clean();
          exit;
      }
   }

Redirection is a classic HTTP operation which happens in three main flavors: the requested content is temporarily available at another address (such as when a server is undergoing maintenance and data has been moved to a different server, or when a certain piece of data may be requested from one of several servers at random to split the load), the requested content is permanently moved to another address (this happens when a website changes its infrastructure, for instance moving from Joomla! to WordPress, and all the old links have to point to the new ones), and the request was taken into account and the next step is at another address (such as when the user posts a message on a forum and is then redirected to the thread he created).

My choice of using HTTP/1.1 instead of HTTP/1.0 lets me specify the exact nature of my redirects, since HTTP/1.0 supports only 301 (permanent move) and 302 (which the standard defines as a temporary redirect and most browsers and servers mistake for a see-other). HTTP/1.1 support being what it is (all modern browsers support it), I am not afraid of compatibility problems here (read more about HTTP versions in the RFC2616).

To redirect, one specifies the HTTP status (which indicates the type of redirect) and the new location (by use of the Location: HTTP header). By default, PHP automatically specifies an HTTP status of 302 (unless another status was already specified) when a Location: header is sent, which I need to avoid. Last but not least, I don’t want any output to be send with my redirect, so I forcibly flush all text present in the output buffer (if any text was sent before the headers, the PHP server will create an error, since headers are always to be sent before content). Then, I exit the script to avoid anything else being sent. As such, using any of the redirect functions immediately interrupts the execution of the script, and as such they should always be performed last, once everything else has been done.

Error Handling | Forms and Views

0 Responses to “5. Login Page”


  1. No Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>



1170 feed subscribers
(readers who polled a feed this week)