Tag Archives: zend framework

How to Overcome Zend_Cache_Frontend_Page’s Problem with Cookies

Zend_Cache_Frontend_Page

First of all there are several things to know about Zend Framework and caching. Whenever you work on a big web application caching is one of the mostly used mechanisms of speeding up the app and improve user performance. In general the task and the solution are pretty simple and natural.

As the application grows up the visitors become more and more impatient about what they receive. A single page is becoming slower and slower and the result is painful. First of all every time a user hits a page the application server uses the web server, a script interpreter, a database server and potentially the file system. But that’s not all. After all this output is generated on the server, as HTML in the most cases, it is sent to the client where again CSS and JavaScript engines parse and execute them.

In this scenario it’s easy to imagine how many time is spent. While there are several techniques to optimize the client side by optimizing JavaScript, CSS and the static images used for the design of the site, here I’m going to talk more about the backend.

Beside the Optimization

Let’s assume we’ve one of the very used combination between Apache (as a webserver), PHP (as server scripting language) and MySQL (as database server). Here you can choose to optimize all three of them. However beside the optimization of them one of the most simple steps you can do is to cache the output generated by these three branches of your web app.

Caching the Content

In fact you can cache only single parts of the whole process. For instance you can cache only the result returned by some slow database query. Let’s imagine a query takes about 2 seconds to execute. Now you can cache the result into a file and during the cache is active, i.e. it has not expired, the application takes it from a file stored somewhere in the file system.

In a typical Zend Framework scenario you can first setup the frontend and backend options of the cache.

$frontendOptions = array(
	'lifetime'                => 600, // in seconds - this is 10 seconds
	'automatic_serialization' => true,
);
$backendOptions = array('cache_dir' => 'cache/');
$cache = Zend_Cache::factory('Core', 'File', $frontendOptions, $backendOptions);
 
$cacheKey = md5('mykey');
 
if (!$cache->load($cacheKey)) {
	$slowQueryResult = $article->fetchAll();
	$cache->save($slowQueryResult, $cacheKey);
} else {
	$slowQueryResult = $cache->load($cacheKey);
}

You can setup different options here by setting up the cache directory, lifetime, etc.

Note that the cache directory must exists and with write permissions and Zend Framework doesn’t create it for you and will throw error.

The problem here is that now you cache only part of the generated content and in many cases this is still too slow for most of the users. However all the work (in most of the cases) of the web server, the script interpreter and the database server result in a simple HTML output. What if you have this output generated or cached for you and when the user hit the page the server will return this pre-generated code?

This is indeed very fast, because it’s similar to return a text file, as the HTML is simply formatted text.

Caching the Entire Page

Before I proceed, I’d like to say that I work with Zend Framework 1.9.x. Now in the latest versions of ZF there are new mechanisms of caching the output even Zend_Cache_Frontend_Page works fine on them.

You can simply setup the page cache within few simple lines of code:

$fo = array(
    'lifetime' => 600,
    'regexps' => array(
        '^/' => array(
	'cache' => true,
         'cache_with_cookie_variables' => true,
        ),
    )
);
 
$bo = array(
    'cache_dir' => 'cache/'
);
 
$cache = Zend_Cache::factory('Page', 'File', $fo, $bo);
$cache->start();

However my advise is to place this code as high as possible, because this will cache everything generated as output. It’s a good practice if you place this even in the bootstrap before you make the connection with the database. Actually you don’t need a database connection when you’ve to return a simple text(html) file.

This will improve your app’s performance a lot!

However there are few things to know. When you setup the cache to work even with cookie variables, you can see that hitting the page with different browsers Zend Framework will generated different cache pages. This is quite useless because than you don’t have any benefit of caching the content.

First of all let me say that THIS IS NOT A BUG! of ZF. Simply the framework will use the cookie variables to generate the cache key. It’s obvious that different browsers, even more different users, will have different cookie set and the framework will generate different cache keys for them.

Thus you’ve to change the setting to generate the cache key from cookie variable by explicitly set this option to false:

$fo = array(
    'lifetime' => 600,
    'regexps' => array(
        '^/' => array(
		 'cache' => true,
         'cache_with_cookie_variables' => true,
         'make_id_with_cookie_variables' => false,
        ),
    )
);
 
$bo = array(
    'cache_dir' => 'cache/'
);
 
$cache = Zend_Cache::factory('Page', 'File', $fo, $bo);
$cache->start();

Note that in this example we cache every single page generated by the framework explained in the regexps. This is not so good especially when the users have the possibility to login and to see customized content for them, so you can be careful what you cache.

A typical problem that this solution solves is when your application uses Google Analytics. As you may know Google Analytics sets up a cookie every time when an user hits the page, so every time the framework will generate a different cache for him and in result he won’t see any benefit and performance improvement from your site.

Returning JSON in a Zend Controller’s Action

There are three basic ways that you can achieve that. First of all what’s the task? You’ve an array, either from a database result or whatever, and you encode it JSON with Zend_Json::encode($array)

// IndexController.php
class IndexController extends Zend_Controller_Action
{
	public function indexAction()
	{
		$data = array(...);
 
		$this->view->data = Zend_Json::encode($data);	
	}	
}

The result in general is a specially formatted string. So you can simply set it up to a view member variable and pass it to the view.

// index/index.phtml
echo $this->data

In that case you’ve a .phtml file to maintain, so lets just return the string and “setNoRender” the view in our second try.

// IndexController.php
class IndexController extends Zend_Controller_Action
{
	public function indexAction()
	{
		$data = array(...);
 
		echo Zend_Json::encode($data);	
 
		$this->_helper->viewRenderer->setNoRender(true);
	}	
}

Actually this is pretty much the most clear solution, but actually you can output the JSON string and simply exit() as it’s shown in our third example.

// IndexController.php
class IndexController extends Zend_Controller_Action
{
	public function indexAction()
	{
		$data = array(...);
 
		echo Zend_Json::encode($data);	
 
		exit();
	}	
}

Which one is to be used is up to the developer’s choice, mine is the third one as it’s the minimal one.

Models in Zend Framework – Initialize All Methods with init()

In the Zend Framework’s documentation there are lots of examples how you can initialize all the actions in a given controller – by simply adding the init() public method in the controller’s code:

<?php
 
class IndexController extends Zend_Controller_Action
{
	public function init()
	{
		echo 'foo';	
	}	
 
	public function indexAction()
	{
		// first the 'foo' string will be printed
		echo 'bar';	
	}
}

But did you know that you can do the same thing with any model in ZF? However you can setup a cache for every method or something else, but definitely it will execute for every method:

<?php
 
class MyModel
{
	public function init()
	{
		// prepare the cache setup
	}	
 
	public function readAll()
	{
		// the cache is already setup
		$sql = '...';
		// ...
	}
}

Of course that means that directly calling the readAll() function the init() method is called also – automatically.

Conclusion

There are good and bad parts about this. You’ll have this code executed for every method and if you have twenty of them and the init() method is practically used for only a couple of the member functions – than this will be useless.

Setting Up Zend Framework with Modules

Typical Zend App

Typically you’ve one module in your Zend App – the default one. In the basic installation of the framework, you put all the controllers, models and views directly in the application folder, as described below.

/application
	- /controllers
		- /IndexController.php
	- /models
		- /MyModel.php
	- /views
		- /scripts
			- /index/index.phtml
/library
	- /Zend
/public_html
	- /images
	- /scripts

Bigger Apps – More Code

When the application becomes bigger and bigger the controller, models and views/scripts directories contain more and more files. That’s a bit odd, because it becomes difficult to maintain, and than the modules come in hand.

Modules in a Zend App

When it comes to setting up modular Zend App there are tons of articles in the web, but let me show you a simple directory layout and … sample code that sets up the framework.

/application
	- /modules
		+ /admin
			- /controllers
				- /IndexController.php
			- /views
				- /scripts
					- /index/index.phtml
		+ /default
			- /controllers
				- /IndexController.php
			- /views
				- /scripts
					- /index/index.phtml
	- /models
/library
	- /Zend
/public_html
	- /images
	- /scripts

Source

Simply add this into the bootstrap:

$frontController->addModuleDirectory(APPLICATION_PATH . '/modules');

Fetching Rows With Zend_Db fetch()

Fetching the Entire Row Set

Rows

What is really handy in Zend Framework is that you can fetch the entire row set with the fetchAll() method. It comes with some parameters that you can use for limiting the result or ordering, but in general you can use it without specifying parameters. Let say this is the model:

<?php
class User extends Zend_Db_Table
{
 
	public function listAll()
	{
		$query = "SELECT * FROM user";
 
		// exec query
		$rs = $this->getAdapter()->query($query);
 
		return $rs->fetchAll();
 
	}
 
}

You can simply return the row set with the fetchAll() method as described in the example, but what if you have to loop through the rows and to modify somehow the values?

Fetching a Row

By simply change the code like so:

class User extends Zend_Db_Table
{
 
	public function listAll()
	{
		$query = "SELECT * FROM user";
 
		// exec query
		$rs = $this->getAdapter()->query($query);
 
		// fetch
		$list = array();
		while ($row = $rs->fetch()) {
			// removing the password column value
			$row['password'] = '';
 
		    $list[] = $row;
		}
 
		return $list;
	}
}

you can modify the rows and you’ll have the same result.