top-image

LATEST ARTICLES

Everyone has probably already seen every caching class there ever was and ever will be. However, when I was searching for a class that could easily be switched from one data store to the next I couldn't find a thing. Every caching class I seemed to come by was written specifically for a single back end and with their predefined static keys/column names. Well, those silly restrictions have come to an end!

I present to you the Extensible PHP Caching Library (Hosted at GitHub) - This collection of classes makes it easy to customize key or column names to your needs as well as switch from one data store to another. This extensibility is baked into the core of the library via an abstract class. This abstract Cache class takes an associative array of keys/columns and determines how to use those keys based on the type of cache back end you are using. For example: Suppose you are using a RDBMS such as MySQL. In this case the associative array will be parsed and the query built such that the key is the column name and the value is what you want to query on. However, if you chose to use a NoSQL key/value store such as Memcache then the associative array is sorted and imploded to create a single string as the key.

Since we always specify the key as an associative array, switching between different data stores is as simple as changing the Class name from say MCache to SQLCache in your code. Nothing else is required to change data stores. Keys, expiration dates, and data processing all stay the same and all functions are called with the same arguments. This functionality is attributed to the abstract base Cache class which guides and regulates the inheriting classes.

Lets get to some examples:

In the following example we create a new SQLCache object and pass it our associative array of keys and values. Check if there is cached data, if there isnt then do something to generate the data and then cache it. Notice how you dont have to pass the key in again when setting the cache. The key is stored within the object so you can get and set as many times as you need without having to set the key every time. Lastly we delete the cache.

// Get Cache
$cache = new SQLCache(array('column1' => 123, 'column2' => 'blah', … ));
$output = $cache->getCache();
 
if(!$output) {
  // Do something to generate data
  $output = 'some datas';
  // Set Cache
  if($cacheNow) {
    // Force a write to cache now
    $cache->setCache($output, '+1 day', true);
  } else {
    // Setting no time defaults to time() + 86400, one day from now.
    $cache->setCache($output);
  }
}
 
print $output;
 
// Delete the cache
$cache->deleteCache();
 

If ever you needed to update this caching strategy to include Memcached the only change would be to change SQLCache to Mcache:

$cache = new SQLCache(array('column1' => 123, 'column2' => 'blah', … ));
to
$cache = new MCache(array('key1' => 123, 'key2' => 'blah', … ));
 

The column and key names are arbitrary and may be set to anything you want to name it. For SQL caches make sure you create a cache table that has the corresponding column names and that they are indexed optimally.

While I have only created classes for SQL (MySQL - since there is a LIMIT 1), Memcached and File based caching, the base class can be extended to include any key/value store or any database with columns (MongoDB, CouchDB, Tokyo, Postgress, Oracle, etc). Just update the back end calls and you are good to go.

For anyone who updates or adds functionality please let me know so I can give credit where credit is due. This library is available under the LGPLv3.

Zend has a lot of tools to help speed up the application development process.  One such tool I found useful, was Paginator.   I am using php-activerecord in my project using Zend_Framework as the backend, to tie the two together is very simple.  Paginator requires two methods, it needs to be able to pull a count to get the total and it also needs to be able to pull in a subset of the data.  Take a look at the following example:

 
<?php
class My_Paginator implements Zend_Paginator_Adapter_Interface {
	public function __construct($table,$conditions = array())
	{
		if(!is_array($conditions))
 
			$conditions = array( $conditions );
 
		$this->conditions = $conditions;
 
		$this->table	  = new $table;
 
	}
 
	public function getItems($offset, $itemCountPerPage)
	{
			return $this->table->find('all', array('limit' => $itemCountPerPage, 'offset' => $offset, 'conditions' => $this->conditions));
	}
 
	public function count()
	{
		return $this->table->count(array('conditions' => $this->conditions));
	}
}
?>
 

The two methods the Zend_Paginator_Adapter_Interface expects are count() and getItems(). The above example is a little "raw", it should serve to guide you in what to do when extending the Paginator with it's own adapter regardless of what your database layer is. In the case of $conditions, these are the parameters you are passing to SQL:

 
$paginator = new Zend_Paginator(new My_Paginator('User',' active = "Y" '));
 

We want to access the User model, and only want to pull out users who are active. Easy enough, you can certainly put in more complicated SQL here, but for a general use purpose it solves 99% of what I want with ActiveRecord and paging, pass in a model to the adapter, and some basic conditions for listing.

 
    public function pageUserAction()
    {
 		$paginator = new Zend_Paginator(new My_Paginator('User',' active = "Y" '));
		$paginator->setCurrentPageNumber($this->_getParam('page', 1));
		$paginator->setItemCountPerPage('25');
		$this->view->paginator = $paginator;
    }
 

This is a basic usage of Zend_Paginator, you are passing in the current page you are on, and the results per page and pushing it out to the view, and on the view side:

 
<div id="userlist">
<?php if (count($this->paginator)){
	foreach ($this->paginator as $user){
		echo $user->username."<br>";
	}
}
?>
</div>
 
<?= $this->paginationControl($this->paginator, 'Elastic', '/common/paginator.phtml'); ?>
 

If anyone has any problems or questions getting the two to work together let me know in the comments and I will do my best to answer your questions.

For those web masters dealing with user feedback looking to weight content finding the right algorithm can be challenging. From experience, there is going to be no out of the box solution since each site and the requirements will be unique. Getting started and putting a solid foundation is the first step and of course, refining over time to get just the right recipe. The following is a binomial proportion confidence interval (what?).  It is a PHP implementation using the Wilson Score Interval to weight the feedback.

...click here to read more

GAPI 1.3 released this past month. First, read the Google Analytics Data API Reference and then read up on dimensions, metrics, and valid combinations of the two.  The quotas apply to a single web property, so each analytics profile (site1, site2, etc) are subject to their own individual quotas.   There is no per user account limit for accessing the API.    If you plan on making any kind of on demand application I suggest you query in bulk and cache the results locally (analytics is not real time anyways).

...click here to read more

A couple months ago I wrote about the terrible performance and a work around for SOLR / Lucene search engine. I discovered that performance would drop off a cliff while using filter queries to narrow search results for search queries on common terms in large indexes.  Although, it looks like the issue has been addressed in some of the latest nightly SOLR builds and is scheduled for official release with SOLR v1.4. Previous to this new version the filter queries were applied after the main query ran. This is all well and good but it doesn't help speed your query up like you think it should. The new version applies the filters in parallel to the main query significantly speeding up searches with common queries and query filters by 30% to 80% along with a 40% smaller memory footprint.

However, even with this speed improvement you still should consider how you structure your queries. There is no need to do a query across every field if you know you really want to filter everything down with a single filter query. Try moving that filter query (fq) into the actual query (q) as :. You might be surprised by the results...

Page 1 of 7:1 2 3 4 »Last »
bottom-img