Monday, August 8, 2011

Use ApcCache to cache any PHP data in Symfony2

APC is the most well-known caching system for PHP, and maybe one of the simpliest. Symfony2 strongly suggests to enable it, as well on development than production environment.


Sf2 natively uses APC to cache PHP files as OPCODE. But you can cache what you want in your code, any kind of PHP data.

APC makes the distinction between PHP OPCODE cache and custom cache :
  • File cache gets PHP OPCODE,
  • User cache gets what developer decides to cache.

Here is an example of a controller action displaying some RSS streams on a homepage. We don't want to load the stream each time a user browses homepage so we put in cache the whole response (!) :

   /**
    * Route ; _home_rssNews
    * @return Symfony\Component\HttpFoundation\Response
    */
   public function rssAction()
   {
      //We get the cache before anything else
      $cacheDriver = new ApcCache();

      //If the cache exists and is not expired for _home_rssNews, we simply return its content !

      if ($cacheDriver->contains('_home_rssNews'))
      {
         return $cacheDriver->fetch('_home_rssNews');
      }

      //If not, we build the Response as usual and then put it in cache !

      $viewVars = array ();

      // FLUX RSS BLOG
      $rssB = $this->fluxRssBlog('my_blog_rss_stream'));
      //FLUX RSS TWITTER
      $rssT = $this->fluxRssTwitter('my_twitter_rss_stream'));

      $viewVars['rssB'] = $rssB;
      $viewVars['rssT'] = $rssT;
      $response = $this->render('XxxYourBundle:Default:rssNews.html.twig', $viewVars);

      //We put this response in cache for a 15 minutes period !
      $cacheDriver->save('_home_rssNews', $response, "900");

      return $response;
   }

Thursday, August 4, 2011

Redirection with Cookie sending in Symfony2

As i apply a redirection after switching language, i'd like to send a cookie to store the last selected language for the user. But Symfony2 RedirectResponse default constructor doesn't allow to do this....

Here is a class extanding RedirectResponse that takes an array of Cookie objects as third parameters :

This component is downloadable and forkable on my Git repository : http://github.com/alterphp/components

<?php

/*
 * A RedirectResponse object with cookie sending
 */

namespace AlterPHP\Component\HttpFoundation;

use Symfony\Component\HttpFoundation\Cookie;
use Symfony\Component\HttpFoundation\RedirectResponse;

/**
 * RedirectResponseWithCookie represents an HTTP response doing a redirect and sending cookies.
 */
class RedirectResponseWithCookie extends RedirectResponse
{

   /**
    * Creates a redirect response so that it conforms to the rules defined for a redirect status code.
    *
    * @param string  $url    The URL to redirect to
    * @param integer $status The status code (302 by default)
    * @param Symfony\Component\HttpFoundation\Cookie[] $cookies An array of Cookie objects
    */
   public function __construct($url, $status = 302, $cookies = array ())
   {
      parent::__construct($url, $status);

      foreach ($cookies as $cookie)
      {
         if (!$cookie instanceof Cookie)
         {
            throw new \InvalidArgumentException(sprintf('Third parameter is not a valid Cookie object.'));
         }
         $this->headers->setCookie($cookie);
      }
   }
}

Example :

/* in a controller action */

$cookie = new Cookie('Language', $culture, '2037-01-01');

return new RedirectResponseWithCookie($this->generateUrl($route, $params), 302, array ($cookie));

Wednesday, August 3, 2011

Disable client-side HTML5 form validation while developing

HTML5 provides new features that will make the job simplier for web developers, and also for users. Among them, the client-side validation with required attribute.


Symfony2 framework enables the generation of this attribute in its forms. But when i'm developping, i don't want this client-side validation cause it's locking the process before the server-side validation. I want my site to be compatible with HTML4 and HTML5 so i can't rely on this client-side validation (anyway i never rely on client-side treatments) and i don't want to use a HTML4 browser for development. So I looked for an option in FF5 to disable this browser HTML5 feature, but i never found it...

So, I used JavaScript and jQuery and wrote this piece of code :

window.onload = function ()
{
  $("[required]").each(function()
    {
      $(this).removeAttr('required');
    }
  );
}

Then winzou suggested a better solution with less form tags than input to treat :

window.onload = function ()
{
  $('form').each(function()
    {
      $(this).attr('novalidate', 'novalidate');;
    }
  );
}

And i include this script in my Twig layout, only in dev environment :

{% if app.environment == 'dev' %}
<script src="{{ asset('bundles/mybundle/js/dev.js') }}" type="text/javascript"></script>
{% endif %}