Snippets

Create an account or login to be able to add, comment and rate snippets.

Navigation

Snippets by user dave furf Snippets by user dave furf

easy cookie validation for user login systems

If you're building a site with a user login system (like Askeet) and your PHP is configured to store session variables in a client-side cookie, the following snippet will improve the usability for users who have disabled cookies.

The following example assumes you already have at least a simple user login system on your site. If not, check out the Askeet tutorial for a great example to get you started.

All users who have disabled cookies will be unable to log into any site that relies on client-side cookies to store session variables. If you don't validate cookies and provide notification, these users will never know why they couldn't log in to your site.

Try the following if you'd like to see this firsthand.

Unfortunately, Askeet also provides the perfect example here as well. (Sorry guys!)

Not much happened, right? You're not logged in and you don't know why. (Well, you do now.)

What we're going to do is augment an existing login system to provide users with notification that your site requires cookies.

To do this, we're going to attempt to set a cookie on the login page and verify that it was created once the login form is submitted. Since a cookie is stored only after the page is loaded, it takes two pages to validate the user's setting. Fortunately, the login process takes two pages!

Edit your existing login action code by adding the following two setCookie() functions.

The first setCookie() sets the validation cookie when the login action is first loaded. (If you use your login on several pages as a component/partial, move this first setCookie() to the login form component.)

The second setCookie() deletes the validation cookie upon successful login. If this cookie is not deleted, the validation could return a false positive if the user disables cookies at a later date. (When cookies are disabled, new cookies can't be written, but old cookies can be read.)

module_dir/actions/actions.class.php:

public function executeLogin()
{
  if ($this->getRequest()->getMethod() != sfRequest::POST)
  {      
    sfContext::getInstance()->getResponse()->setCookie('cookies_enabled', 'true', time()+60*60*24, '/');    
  }
  else
  {
    sfContext::getInstance()->getResponse()->setCookie('cookies_enabled', '', 0, '/');    
    $this->redirect('@homepage');
  }
}

Enable cookie validation by adding the following code to your existing login validation. The validator takes two parameters, cookie_name and cookie_error. The cookie_name parameter is, oddly enough, the name of the cookie we'll use to validate and it should match the cookie set in the above code.

The other parameter, cookie_error, is the error message that will be returned if the user has cookies disabled. Typically, validation errors are phrases like "Invalid username" or "Password must contain 6-8 characters". But we're going to use "cookies_disabled" and I'll show you why in a few minutes.

IMPORTANT: The cookie validation should occur first.

module_dir/validate/login.yml:

methods:
  post: [username]
 
names:
  username:
    required:     true
    required_msg: 
    validators:   [cookiesEnabledValidator, userValidator]
 
cookiesEnabledValidator:
  class:          myCookiesEnabledValidator
  param:
    cookie_name:  cookies_enabled
    cookie_error: cookies_disabled
 
userValidator:
  class:          myLoginValidator
  param:
    password:     password
    username_error: Invalid username.
    password_error: Invalid password.

Copy the following code to one of your lib directories. Since it only deals with the login action, I choose to keep it in my user module's lib directory.

module_dir/lib/myCookiesEnabledValidator.class.php:

class myCookiesEnabledValidator extends sfValidator
{    
  public function initialize($context, $parameters = null)
  {
    // initialize parent
    parent::initialize($context);
 
    // set defaults
    $this->setParameter('cookie_name', sfContext::getInstance()->getStorage()->getParameter('session_name'));
    $this->setParameter('cookie_error', 'This site requires cookies.');
    $this->getParameterHolder()->add($parameters);
 
    return true;
  }
 
  public function execute(&$value, &$error)
  {    
    if (sfContext::getInstance()->getRequest()->getCookie($this->getParameter('cookie_name')) === null)
    {
      $error = $this->getParameter('cookie_error');
      return false;
    }
    return true;
  }
}

Now, since it takes two pages to set and read a cookie it wouldn't make sense to return the user to the form right away. If they enable their cookies they'll still have to submit the form twice before they'll login successfully. I prefer to send them to a page which notifies them that the site requires cookies and explains how they can enable them. (Plus, this gives us the extra click we need to set that validation cookie!)

So we'll check for the cookie validation error before returning to the form so we can redirect the user to our help page if necessary. The error we check for in the username parameter must match the cookie_error we defined in the login.yml.

module_dir/actions/actions.class.php:

public function handleErrorLogin()
{
  if (sfContext::getInstance()->getRequest()->getError('username') == 'cookies_disabled')
  {
    $this->redirect('@about_cookies');
  }
  return sfView::SUCCESS;
}

So that's it. Throw in an about cookies page and you're all set.

If I made any mistakes, I apologize. It's 5am on a school night.

by dave furf on 2007-05-11, tagged cookie  login  session  storage  usability  validation 
(1 comment)

unsharp mask for sfThumbnail plugin

I modified some found code (http://vikjavev.no/computing/ump.php?id=35) to add a sharpening feature to sfThumbnail.

Simply add this code to the sfThumbnail class:

  /**
   * Unsharp Mask for PHP
   * Unsharp mask algorithm by Torstein Hønsi
   * Please leave this notice.
   * @author     Torstein Hønsi <thoensi@netcom.no>
   * @author     Modified for use with Symfony by Dave Furfero <furf@furf.com>
   * @version    2.0, 2003-06
   *
   * WARNING! Due to a known bug in PHP 4.3.2 this script is not working
   * well in this version. The sharpened images get too dark. The bug is
   * fixed in version 4.3.3. 
   *
   * From version 2 (July 17 2006) the script uses the imageconvolution
   * function in PHP version >= 5.1, which improves the performance
   * considerably. 
   * 
   * Unsharp masking is a traditional darkroom technique that has proven
   * very suitable for digital imaging. The principle of unsharp masking
   * is to create a blurred copy of the image and compare it to the
   * underlying original. The difference in colour values between the
   * two images is greatest for the pixels near sharp edges. When this
   * difference is subtracted from the original image, the edges will be 
   * accentuated. 
   *
   * Amount simply says how much of the effect you want. 100 is
   * 'normal'
   *
   * Radius is the radius of the blurring circle of the mask.
   *
   * Threshold is the least difference in colour values that is allowed
   * between the original and the mask. In practice this means that
   * low-contrast areas of the picture are left unrendered whereas edges
   * are treated normally. This is good for pictures of e.g. skin or
   * blue skies. 
   *
   * Any suggenstions for improvement of the algorithm, expecially
   * regarding the speed and the roundoff errors in the Gaussian blur
   * process, are welcome. 
   * 
   * @param int amount (0-500)
   * @param float radius (0-50)
   * @param int threshold (0-255)
   * @return void
   * @access public
   */
  public function unsharpMask ($amount, $radius, $threshold)
  {
    // Attempt to calibrate the parameters to Photoshop:
    $amount    = min($amount, 500) * 0.016;
    $radius    = min($radius, 50) * 2;
    $radius    = abs(round($radius)); // Only integers make sense
    $threshold = min($threshold, 255);
 
    if ($amount == 0 || $radius == 0) {
      return;
    }
 
    $w = $this->getThumbWidth();
    $h = $this->getThumbHeight();
 
    $imgCanvas  = imagecreatetruecolor($w, $h);
    $imgCanvas2 = imagecreatetruecolor($w, $h);
    $imgBlur    = imagecreatetruecolor($w, $h);
    $imgBlur2   = imagecreatetruecolor($w, $h);
 
    imagecopy($imgCanvas,  $this->thumb, 0, 0, 0, 0, $w, $h);
    imagecopy($imgCanvas2, $this->thumb, 0, 0, 0, 0, $w, $h);
 
    // Gaussian blur matrix:
    //  1  2  1
    //  2  4  2
    //  1  2  1
 
    imagecopy($imgBlur, $imgCanvas, 0, 0, 0, 0, $w, $h); // background
 
    for ($i = 0; $i < $radius; $i++)  {
 
      if (function_exists('imageconvolution')) { // PHP >= 5.1
        $matrix = array(
          array(1, 2, 1),
          array(2, 4, 2),
          array(1, 2, 1)
        );
        imageconvolution($imgCanvas, $matrix, 16, 0);
      } else {
 
        // Move copies of the image around one pixel at the time
        // and merge them with weight according to the matrix.
        // The same matrix is simply repeated for higher radii.      
        imagecopy($imgBlur, $imgCanvas, 0, 0, 1, 1, $w - 1, $h - 1);
        imagecopymerge($imgBlur, $imgCanvas, 1, 1, 0, 0, $w, $h, 50);
        imagecopymerge($imgBlur, $imgCanvas, 0, 1, 1, 0, $w - 1, $h, 33.33333); 
        imagecopymerge($imgBlur, $imgCanvas, 1, 0, 0, 1, $w, $h - 1, 25);
        imagecopymerge($imgBlur, $imgCanvas, 0, 0, 1, 0, $w - 1, $h, 33.33333); 
        imagecopymerge($imgBlur, $imgCanvas, 1, 0, 0, 0, $w, $h, 25);
        imagecopymerge($imgBlur, $imgCanvas, 0, 0, 0, 1, $w, $h - 1, 20 );
        imagecopymerge($imgBlur, $imgCanvas, 0, 1, 0, 0, $w, $h, 16.666667);
        imagecopymerge($imgBlur, $imgCanvas, 0, 0, 0, 0, $w, $h, 50);
        imagecopy($imgCanvas, $imgBlur, 0, 0, 0, 0, $w, $h);
 
        // During the loop above the blurred copy darkens,
        // possibly due to a roundoff error. Therefore the sharp
        // picture has to go through the same loop to produce a
        // similar image for comparison. This is not a good
        // thing, as processing time increases heavily.
        imagecopy($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h);
        imagecopymerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 50);
        imagecopymerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 33.33333);
        imagecopymerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 25);
        imagecopymerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 33.33333);
        imagecopymerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 25);
        imagecopymerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 20 );
        imagecopymerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 16.666667);
        imagecopymerge($imgBlur2, $imgCanvas2, 0, 0, 0, 0, $w, $h, 50);
        imagecopy($imgCanvas2, $imgBlur2, 0, 0, 0, 0, $w, $h);
      }
    }
 
    // Calculate the difference between the blurred pixels
    // and the original and set the pixels
    for ($x = 0; $x < $w; $x++)  {
      for ($y = 0; $y < $h; $y++)  {
 
        $rgbOrig = imageColorAt($imgCanvas2, $x, $y);
        $rOrig = (($rgbOrig >> 16) & 0xFF);
        $gOrig = (($rgbOrig >> 8) & 0xFF);
        $bOrig = ($rgbOrig & 0xFF);
 
        $rgbBlur = imageColorAt($imgCanvas, $x, $y);
        $rBlur = (($rgbBlur >> 16) & 0xFF);
        $gBlur = (($rgbBlur >> 8) & 0xFF);
        $bBlur = ($rgbBlur & 0xFF);
 
        // When the masked pixels differ less from the original
        // than the threshold specifies, they are set to their original value.
        $rNew = (abs($rOrig - $rBlur) >= $threshold)
          ? max(0, min(255, ($amount * ($rOrig - $rBlur)) + $rOrig))
          : $rOrig;
        $gNew = (abs($gOrig - $gBlur) >= $threshold)
          ? max(0, min(255, ($amount * ($gOrig - $gBlur)) + $gOrig))
          : $gOrig;
        $bNew = (abs($bOrig - $bBlur) >= $threshold)
          ? max(0, min(255, ($amount * ($bOrig - $bBlur)) + $bOrig))
          : $bOrig;
 
        if (($rOrig != $rNew) || ($gOrig != $gNew) || ($bOrig != $bNew)) {
          $pixCol = imageColorAllocate($this->thumb, $rNew, $gNew, $bNew);
          imageSetPixel($this->thumb, $x, $y, $pixCol);
        }
      }
    }
  }

Then call it like this from your script:

$thumb = new sfThumbnail(160, 120);
$thumb->loadFile($original_img);
$thumb->unsharpMask(40, 0.5, 3);
$thumb->save($img_path, 'image/png');

The three parameters are amount (degree of sharpening from 0-500), radius (pixel radius of blur, from 0-50), and threshold (degree of difference between original and blur, from 0-255). Read the included comments for more info.

It could probably use some better validation and error checking, but it's worked flawlessly for me so far.

by dave furf on 2006-08-31, tagged plugin  sfthumbnail