Ransford Okpoti's Dev Notes

July 20, 2010

How To Add A Custom Id Generation Strategy To Doctrine 2.0

Filed under: PHP — Tags: , , , — admin @ 10:30 am

Object-Relational Mapping (ORM) has caught up with PHP. In case you have been living under a rock, here’s the list of the popular ORM solutions in PHP:

With the advent of Doctrine 2.0.0 (which at the time of writing this article is at Beta 2), which requires a minimum of PHP version 5.3, our domain objects (models) are now free from subclassing a mandatory superclass as imposed by its predecessors (previous versions before 2.0.0).
Below is a code snippet of a User model as would be implemented in earlier versions of Doctrine before version 2.0.0.

class User extends BaseUser
{
}

The 2.0 version is totally different in that it now resembles the popular ORM solution in Java, Hibernate.
Now your entity classes can be a plain old PHP object (POPO) with annotations providing persistence information on how to update the persistent store with the field values of an instance of the entity class. There is also the option of using YAML, XML, or plain PHP to provide persistence information to the persistence manager.

I will just stick to the topic of this post by not delving into the nitty-gritties of how to setup and use Doctrine because it can boast of one of the best documentations, and tutorials on how to get it up and running.

In this artcle, we will be looking at how to use a UUID or GUID, for the primary keys of our entities instead of the traditional auto incremented unsigned numeric values, to demonstrate how easy it is to add a custom id generation strategy to your ever popular Doctrine.

If you ever decide to use a Universally Unique Identifier (UUID) or Globally Unique Identifier (GUID) as the primary keys for tables in your database, for various reasons such as having an application used by various clients in various geographical locations in a disconnected environment where each client has its own copy of the database with the likelihood of the various databases been merged into a centralized database server in the near future, then this would be particularly helpful.

1. In order to stick to the adopted convention, id generation classes reside in the Doctrine\ORM\Id namespace or package or directory (or which ever jargon you prefer to use), we let our UUIDGenerator extend the AbstractIdGenerator abstract class.

Below is the AbstractIdGenerator abstract class provided by Doctrine to be extended by all id generation strategies.

namespace Doctrine\ORM\Id;

use Doctrine\ORM\EntityManager;

abstract class AbstractIdGenerator
{
    abstract public function generate(EntityManager $em, $entity);

    public function isPostInsertGenerator()
    {
        return false;
    }
}

Now, we have to provide an implementation for the generate abstract method, and let the isPostInsertGeneration return false since the id will be generated and assigned to the entity/model before insertion into the related table. Various implementations of the UUID algorithm can be found on the net, so if you decide to implement your own version or find a more reliable and appropriate one just feel free to plug it in. By the way, I’ve even forgot the source of the UUID implementation am using in this article, so please forgive me if i haven’t credited the author(s) of the code.

Create UUIDGenerator.php in Doctrine\ORM\Id with the code below.

/**
 * @author Ransford Okpoti
 */
namespace Doctrine\ORM\Id;

use Doctrine\ORM\EntityManager;

class UUIDGenerator extends AbstractIdGenerator {
    /**
     * Generates an ID for the given entity.
     *
     * @param object $entity
     * @return string
     * @override
     */
    public function generate(EntityManager $em, $entity) {
        return self::v4();
    }

    /**
     * @return boolean
     * @override
     */
    public function isPostInsertGenerator() {
        return false;
    }

    public static function v3($namespace, $name) {
        if(!self::is_valid($namespace)) return false;

        // Get hexadecimal components of namespace
        $nhex = str_replace(array('-','{','}'), '', $namespace);

        // Binary Value
        $nstr = '';

        // Convert Namespace UUID to bits
        for($i = 0; $i < strlen($nhex); $i+=2) {
            $nstr .= chr(hexdec($nhex[$i].$nhex[$i+1]));
        }

        // Calculate hash value
        $hash = md5($nstr . $name);

        return sprintf('%08s-%04s-%04x-%04x-%12s',

                // 32 bits for "time_low"
                substr($hash, 0, 8),

                // 16 bits for "time_mid"
                substr($hash, 8, 4),

                // 16 bits for "time_hi_and_version",
                // four most significant bits holds version number 3
                (hexdec(substr($hash, 12, 4)) & 0x0fff) | 0x3000,

                // 16 bits, 8 bits for "clk_seq_hi_res",
                // 8 bits for "clk_seq_low",
                // two most significant bits holds zero and one for variant DCE1.1
                (hexdec(substr($hash, 16, 4)) & 0x3fff) | 0x8000,

                // 48 bits for "node"
                substr($hash, 20, 12)
        );
    }

    public static function v4() {
        return sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x',

                // 32 bits for "time_low"
                mt_rand(0, 0xffff), mt_rand(0, 0xffff),

                // 16 bits for "time_mid"
                mt_rand(0, 0xffff),

                // 16 bits for "time_hi_and_version",
                // four most significant bits holds version number 4
                mt_rand(0, 0x0fff) | 0x4000,

                // 16 bits, 8 bits for "clk_seq_hi_res",
                // 8 bits for "clk_seq_low",
                // two most significant bits holds zero and one for variant DCE1.1
                mt_rand(0, 0x3fff) | 0x8000,

                // 48 bits for "node"
                mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)
        );
    }

    public static function v5($namespace, $name) {
        if(!self::is_valid($namespace)) return false;

        // Get hexadecimal components of namespace
        $nhex = str_replace(array('-','{','}'), '', $namespace);

        // Binary Value
        $nstr = '';

        // Convert Namespace UUID to bits
        for($i = 0;
        $i < strlen($nhex);
        $i+=2) {
            $nstr .= chr(hexdec($nhex[$i].$nhex[$i+1]));
        }

        // Calculate hash value
        $hash = sha1($nstr . $name);

        return sprintf('%08s-%04s-%04x-%04x-%12s',

                // 32 bits for "time_low"
                substr($hash, 0, 8),

                // 16 bits for "time_mid"
                substr($hash, 8, 4),

                // 16 bits for "time_hi_and_version",
                // four most significant bits holds version number 5
                (hexdec(substr($hash, 12, 4)) & 0x0fff) | 0x5000,

                // 16 bits, 8 bits for "clk_seq_hi_res",
                // 8 bits for "clk_seq_low",
                // two most significant bits holds zero and one for variant DCE1.1
                (hexdec(substr($hash, 16, 4)) & 0x3fff) | 0x8000,

                // 48 bits for "node"
                substr($hash, 20, 12)
        );
    }

    public static function is_valid($uuid) {
        return preg_match('/^\{?[0-9a-f]{8}\-?[0-9a-f]{4}\-?[0-9a-f]{4}\-?'.
                '[0-9a-f]{4}\-?[0-9a-f]{12}\}?$/i', $uuid) === 1;
    }

}

2. Next, add GENERATOR_TYPE_UUID = 6, just ensure that the assigned number is unique within the other GENERATOR_TYPE constants, to Doctrine\ORM\Mapping\ClassMetadataInfo.php

const GENERATOR_TYPE_UUID = 6;

3. Finally, we need to add our UUID generation strategy to the list of provided generation strategies. Go to the _completeIdGeneratorMapping function in Doctrine\ORM\Mapping\ClassMetadataFactory.php, and add the code below to the switch condition just before the default keyword.

            case ClassMetadata::GENERATOR_TYPE_UUID:
                $class->setIdGenerator(new \Doctrine\ORM\Id\UUIDGenerator());
                break;

I guess you are expecting a step 4? But am sorry to disappoint you, that is all it takes to accomplish our task. We can now make use of UUIDs as demonstrated through the use of annotations below:

/**
 * @Entity
 */
class User{
    /**
     *
     * @Id
     * @Column(type="string", length=36)
     * @GeneratedValue(strategy="UUID")
     */
    private $id;
}

Need I remind you that the downside of this approach is that you'll have to repeat this process each time you decide to upgrade to a higher version, since Doctrine does not implicitly have UUIDs as one of its id generation strategies and there is no convenient way to programmatically register an id generation strategy at runtime. In my honest opinion, this 2 minutes step is well worth the effort.

August 24, 2009

Building Modifiers and Functions In Smarty

Filed under: PHP,Smarty — Tags: , , — admin @ 10:17 am

In this article, we will be looking at how to build modifiers and functions in Smarty, a very popular templating engine in PHP. If you don’t know how to setup Smarty up in your project, you can refer to my earlier post on Smarty Templating Overview which is accompanied by a screencast.

A quick look into the Smarty plugins directory should give you an idea of what’s under the hood, the obvious thing you would notice is the file naming convention adopted:
type.function_name.php
where type could be modifier, function, etc.
So, you will typically end up with file names of this structure:

  • modifier.[modifier name goes here].php
  • function.[function name goes here].php
  • etc

MODIFIERS

To be a little curious, lets open modifier.lower.php

<?php
// Comments removed for brevity
function smarty_modifier_lower($string)
{
    return strtolower($string);
}
?>

The above file only shows you that a Smarty plugin is just a normal PHP function, as you may be aware functions are first class citizens in PHP, which accepts arguments and returns a value that would be placed at the location it was called in the template file it self. So, {'Hmm, Interesting Discovery' | lower} in a template file would result in hmm, interesting discovery being substituted in the place of {'Hmm, Interesting Discovery' | lower} in the generated file from that template.
General usage:
{ argument_to_pass_to_the_modifier | modifier_name_goes_here}

The modifier always appears at the right, and there is a pipe | separating it and the argument it will work on.

Ok, so now what? Do you have a website with funny, and uninteresting URLs like
http://mysite.com/article.php?id=2,
where 2 may refer to an article with the title “All You Need To Know To Build Smarty Plugins”.
Let’s try and create a Search Engine Optimization or pretty URL for that article which would result in us having a new friendly URL like

http://mysite.com/article/all-you-need-to-know-to-build-smarty-plugins-2*

* To show the article, you will need to use apache mod_rewrite, but that is not the topic for discussion now, we just want to transform or slugify All You Need To Know To Build Smarty Plugins to all-you-need-to-know-to-build-smarty-plugins. And as a personal preference, i always append the unique id at the end of the generated URL so I do not run into problems when the title of the article is changed.

STEPS

Go into the plugins directory and create a file with the name modifier.seourl.php
Open the file and place the content below into it.

<?php
/**
 * Smarty seourl modifier plugin
 *
 * Type:     modifier<br>
 * Name:     seourl<br>
 * Purpose:  creates a Search Engine Optimized string
 *
 * @author   Ransford Okpoti <ranskills at yahoo dot co dot uk>
 * @param string
 * @return string
 */
function smarty_modifier_seourl($string) {
    $notAcceptableCharactersRegex = '#[^-a-zA-Z0-9_ ]#';
    $string = trim(preg_replace($notAcceptableCharactersRegex, '', strtolower($string)));
    return preg_replace('#[-_ ]+#', '-', $string);
}
 
?>

Once the file is saved, you can go to your template and use it like any normal modifier. E.g.
{$articleTitle | seourl}, {'Just what to see it working' | seourl}.
That’s it for modifiers, so just feel at home and create one if you see the need to.

FUNCTIONS

Smarty functions are similar in nature to their modifiers counterpart,but there is substantial difference in the way they are used within templates.

Usage:
{function_name optional_arguments}
where optional_arguments is in the form key1=value [, key2=value2]*

Lets create a very useful function which would enable us to dynamically include javascript files into our template.
This is extremely helpful if you want to stick to the generally accepted standard of adding javascript files to the tail end of your application or site to improve the performance of the site in terms of how long it takes the site to be loaded.


If you are interested in improving the performance of your site, you can follow the Best Practices for Speeding Up Your Web Site on yahoo which is an interesting and revealing information for any serious web developer.

A practical usage would be to include our little function we are about to develop in the main template of the site and to dynamically add the javascript files depending on which template(s) needs them.

Within the plugins directory, create a file named function.javascript.php with the content below.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<?php 
/**
 * Smarty plugin
 * @package Smarty
 * @subpackage plugins
 */
 
/**
 * Smarty {javascript} function plugin
 *
 * Type:     function<br>
 * Name:     javascript<br>
 * Purpose:  outputs javascript tags for the provided srcs
 * @author Ransford Okpoti <ranskills at yahoo dot co dot uk>
 * @version 1.0
 * @example {javascript srcs='/script location'}
 *          {javascript srcs=$scripts}
 * @param array parameters
 * @return string
 */
function smarty_function_javascript($params) {
	$retval = '';
 
	if(isset($params['srcs'])){
		$scriptTemplate = '<script type="text/javascript" src="{0}"></script>';	
 
		$srcs = $params['srcs'];
	    if(is_array($srcs)){
	        foreach($srcs as $src){
	            $retval .= str_replace('{0}', $src, $scriptTemplate);
	        }
	    }
	    else{
	        $retval = str_replace('{0}', $params['srcs'], $scriptTemplate);
	    }			
	}
 
	return $retval;;
}
 
?>

Usage:
You could have this in your php file that is responsible for rendering the template.

...
// You could have some logic here to determine if a javascript file is required on a certain page
$scripts = array(
       '/public/js/contentslider.js',
       '/public/js/flash.js'
);
 
 
$this->smarty->assign('srcs', $scripts);
$this->smarty->display('template.xhtml');

The template showing just the line where our newly created Smarty function is used.

?View Code SMARTY
{javascript srcs=$srcs}

would result in this:

?View Code HTML4STRICT
<script type="text/javascript" src="/public/js/contentslider.js"></script>
<script type="text/javascript" src="/public/js/flash.js"></script>

This is just one approach of building Smarty plugins, the other approach is to use your existing classes or functions and register them using the function register_type*(…) on a Smarty instance/object.

*
Type could be any of the following:

  1. modifier
  2. function
  3. block
  4. outputfilter
  5. etc

Go! Go!! Go!!! Let Smarty work for you by developing your own plugins to provide extra functionalities.

[SCREEN CAST IS COMING SOON...]

August 14, 2009

A hack to automatically upgrade all WordPress plugins with a single click

Filed under: Javascript,PHP,WordPress — Tags: , , — admin @ 1:45 pm

WordPress rocks!! I love wordpress as much as I love to watch Friends,one of my favourite television series courtesy ViaSat 1. You care to know who my favourite character is? Hmm, am not telling you. Lets hit the road running now. As a blogging tool, wordpress has created a niche for itself among it peers. It has great features like assigning an entry, or a post, to multiple categories, has a nice tagging system and recently, you can actually upgrade/update your WordPress Plugins automatically without the need to download the plugin and extract it into the plugins folder of your wordpress installation. The plugins upgrade indicator which displays the number of plugins that need upgrading is a pretty cool enhancement, so you don’t go looking for which plugins have latest releases you are not aware of.

Despite the fact that it is a great tool, upgrading a plugin can be a real pain in the a** of users since you need to click on the upgrade automatically link , depending on your chosen translation, for each of the plugins that need upgrading. 20 clicks for 20 different plugins that need upgrading to give you a clearer picture. So I posed to question to myself “Couldn’t this process have been made a lot easier on the user?“. And obviously, the answer was a “YES!!, it can.”

A WordPress admin console showing the need to click on upgrade automatically link for each installed plugin

A WordPress admin console showing the need to click on upgrade automatically link for each installed plugin

WHAT WE WANT TO ACHIEVE

We want to select the plugins to be upgraded, using the checkboxes provided beside each plugin, from the plugins page in the admin console, and choose Upgrade from the list of actions under the Bulk Actions menu, and click on the Apply button to upgrade all the selected plugins that need upgrading.

HACKING WORDPRESS

Since we don’t want to mess with the codes in plugins.php too much, we’ll make use of javascript and a little bit of PHP to achieve our task by doing the following:

  • Adding the Upgrade option to the list of Bulk Actions
  • Attaching a click event listener to the Apply action/button.
  • Creating a JSON (Javascript Object Notation) object to contain the plugins that requires upgrading.

Here is the code that does the trick.

<script type="text/javascript">
/*<![CDATA[*/
var json = {
	<?php
	if ( ! empty($upgrade_plugins) ){
		$url = 'update.php?action=upgrade-plugin&amp;plugin=';
		$noUpgradePlugins = count($upgrade_plugins);
		$i = 0;
		foreach($upgrade_plugins as $plugin=>$v){
			$i++;
			$actionurl = $url.$plugin;
			$action = 'upgrade-plugin_'.$plugin;
			$pluginurl=  str_replace( '&amp;', '&amp;', urldecode(wp_nonce_url($actionurl, $action)));
			$pluginId = str_replace('-','', substr($plugin,0,strpos($plugin,'/')));
	?>
			"<?php echo $pluginId ;?>": "<?php echo $pluginurl ;?>" <?php if($noUpgradePlugins>$i) echo ",\n";?>
	<?php	}
	}
	?>
}

/*
the 1 and 2 at the end of the some of the variable names represents
the top and down instances of the component respectively
*/
var btnApply1 = document.getElementsByName('doaction_active')[0];
var btnApply2 = document.getElementsByName('doaction_active')[1];

var cbo1 = document.getElementsByName('action')[0];
var cbo2 = document.getElementsByName('action2')[0];

// This adds the Upgrade option to the list of bulk actions
cbo1.options.add(new Option("Upgrade","upgrade-selected"));
cbo2.options.add(new Option("Upgrade","upgrade-selected"));

btnApply1.onclick = function(){
	if(cbo1.value!='upgrade-selected'){
		return true;
	}

	upgradePlugins();
	return false;
}

btnApply2.onclick = function(){
	if(cbo2.value!='upgrade-selected'){
		return true;
	}

	upgradePlugins();
	return false;
}

// Upgrades only the checked plugins that have upgrades/updates available
function upgradePlugins(){
	var tags = document.getElementsByName('checked[]');

	for(var i=0; i<tags.length; i++){
		if(tags[i].checked){
			p = tags[i].value;
			pluginId = p.substring(0,p.indexOf('/'));
			pluginId = pluginId.replace(/-/g,''); // replaces all instances of -

			url='';
			try{
				url = eval('json.'+pluginId);
			}
			catch(e){}

			if(typeof url=='string' &amp;&amp; url.length>0){
				var frame = document.createElement('frame');
				frame.src = url;
				document.getElementsByTagName('head')[0].appendChild(frame);
			}
		}
	}

}
/*]]>*/

</script>

All you need to do now is to copy the above codes and paste it at the bottom of the plugins.php file located in the wp-admin folder in your preferred editor. Save the changes, and refresh your plugins page, and give it a try.

HOW IT WORKS

WordPress automatic upgrade hack in action

WordPress automatic upgrade hack in action


On the plugins page, select Upgrade from the Bulk actions list, check the plugins you want to upgrade and click on the Apply button.

CONCLUSION

This demonstrates a practical application of DOM manipulation using Javascript. The downside of our little practical javascript solution to this problem is that each time you update WordPress itself, the plugins.php file will be replaced with the one from the latest version of WordPress.
I hope it was helpful.

Older Posts »

Powered by WordPress