Been a long time…

leave a comment

Sheesh! Things have been real busy, haven’t had much time to sit down and come up with a coherent post. My next update to phpVMS keeps getting pushed back; it’s been crunch time at work.

Though I’ve been messing with a few things – I cut memory usage by about 50% in phpVMS by using __autoload(). Perhaps that can be a good next post.

But I guess the big news (maybe), is that I’m discontinuing Codon. Not that it affects anybody really except my private projects and the few projects it was used on, but I’d rather concentrate my efforts on my new love – CodeIgniter. I’m really digging it, it’s almost as it’s an exact copy of Codon, with how the models work, etc. Though there are alot of PHP4-isms, I think it’s better for me to focus some efforts on learning and modding that. Codon was a great learning project for MVC (which is what it was always meant as). I don’t think there’s any better way to learn how something works than to build it by scratch. You really get to see the ins-and-outs, the pros and cons and the dilemmas faced by doing something like that.

I’ll be porting my date/time math module over to CI soon, but also some mods I’ve been working on to introduce autoload() into CI (drastic memory improvements), and also maybe implement ezSQL into it, since ezSQL is much more lightweight than their ActiveRecord class. I’m using CI now as part of two broader projects involving phpVMS; right now it’s being used as the code behind the remote API which all the phpVMS installs do talk to. I definately like to squeeze any bit of optimizations I can find.

Share and Enjoy:
  • Print
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • HackerNews
  • Netvibes
  • Reddit
  • StumbleUpon
  • Twitter
  • Yahoo! Buzz

Written by Nabeel

April 27th, 2009 at 5:30 pm

Posted in General

PHP Benchmarking

one comment

I came across a few sites which have some valuable information on the “cost” of certain functions or practices in PHP. It comes in handy to keep in mind and avoid doing, and also to remember that there are better ways of doing things. If your application is coming down to relying on micro-optimization, your application itself needs to be looked at. But there’s no harm in knowing what methods take more time than others. Or of course, you can use ‘em to impress a prospective employer ;)

There are also some lectures and talks on optimizing:

Some good stuff there to look over and to keep in mind, especially obvious things to avoid, such as including the count() inside every for() loop. Actually any of the stuff on http://talks.php.net are a good read, and good to see where PHP has evolved from and where it’s evolving to, and to understand how to leverage the language.

To benchmark your scripts, you can use the PEAR Benchmark package; I have PEAR in my path, so for me it was as simple as including the same code that was in the examples.

Share and Enjoy:
  • Print
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • HackerNews
  • Netvibes
  • Reddit
  • StumbleUpon
  • Twitter
  • Yahoo! Buzz

Written by Nabeel

March 18th, 2009 at 9:14 am

Posted in General

Tagged with

Pretty PHP URLS without mod_rewrite

leave a comment

Not everyone has access to mod_rewrite, or doing rewrite rules through nginx or lighttpd, but in PHP, you can do this pretty simply. I’ll go through making this mess:

index.php?module=amodule&page=tasklist&id=10

To:

index.php/amodule/tasklist/10

I’ll be going through the code from Codon, specifically the rewrite module, which can handle this. So a few requirements:

  1. The first argument is the controller we goto (the module in Codon’s case)
  2. Arguments can vary based on the controller
  3. Have a default ruleset if we don’t specify for a module
  4. The ability to “dynamically” change the arguments on the fly, to accommodate different actions
  5. Also parse the traditional query string rules
  6. We can specify types for each argument, as a security measure.
  7. Use a static class, and have the parameters available through $_GET, as well as from the class

PHP stores the query string in the $_SERVER['REQUEST_URI'] variable as:

echo $_SERVER['REQUEST_URI'];

# Will output:
/index.php/testing/our/parameters

Being relative to the base URL. We’ll work on parsing this later. First we need to build our list of “rewrite rules”. What we’re going to do is store the rules in an associative array, based on our module, and each key has an array, with the rules in order, and we specify the types in that. What’d he say? This will explain it best:

Array
(
	[default] => Array
	(
		[action] => string
		[id] => int
	)

	[users] => Array
	(
		[action] => string
		[username] => string
	)
)

So if we look at default:

index.php/{action}/{id}

Where action and ID will be treated as strings. So we’ll build a function for that (Note: This is all in a static class)

public static function AddRule($module, $params)
{
	$set_params=array();
	$module = strtolower($module);

	# Format the rules, make sure we arrange

	foreach($params as $key=>$value)
	{
		# If it wasn't done as $key=>$value, just $key,
		#	set the default value type as a string

		if(is_numeric($key))
			$set_params[$value]='string';
		else
			$set_params[$key]=$value;
	}

	self::$rewrite_rules[$module] = $set_params;

	# This is for if we've already processed the rules
	#	once. This will allow the rules to be changed
	#	"on the fly", for example, inside a controller
	if(self::$run == true)
	{
		# Reprocess the rules
		self::ProcessModuleRewrite($module);
	}
}

There is the self::$run “clause”, which sees whether the rules have been parsed or not, and if so, then we’ll just process that particular module. This is to then if the rules are changed on the fly, after we reprocess, then the entire procedure doesn’t have to be done. This will make more sense later.

So we go through, and set the proper types for the parameters.
We call this as:

CodonRewrite::AddRule('default', array('action'=>'string', 'id'=>'int'));
# If we do it as keys, it'll be strings as default, as we'll see later
CodonRewrite::AddRule('users', array('action', 'username'));

Now we can actually parse the URL, which is the bit we’re interested in. I use explode()’s to parse out the pieces, as I find that to be the most legible:

public static function ProcessRewrite()
{
	$URL = $_SERVER['REQUEST_URI'];

	# Get everything after the .php/ and before the ?
	$params = explode('.php/', $URL);
	$preg_match = $params[1];

	$params = explode('?', $preg_match);
	$split_parameters = $params[0];

	# Now check if there's anything there (we didn't just have
	#	index.php?query_string=...
	# If that's all, then we grab a configuration setting that
	#	specifies the default rewrite, ie: news/showall
	#	Which would eq. passing index.php/news/showall
	if($split_parameters == '')
	{
		$split_parameters = Config::Get('DEFAULT_MODULE');
	}		

	# Now we split it all out, and store the peices
	self::$peices = explode('/', $split_parameters);

	$module_name = strtolower(self::$peices[0]);
	if($module_name == '') # If it's blank, check $_GET
	{
		$module_name = $_GET['module'];
	}

	self::$current_module = $module_name;
	$_GET['module'] = $module_name;

	# Create the object to hold all of our stuff
	self::$get = new stdClass;

	# If we haven't specified specific rules for a module,
	#	Then we use the rules we made for "default"
	if(!array_key_exists($module_name, self::$rewrite_rules))
	{
		$module_name = 'default';
	}

	# This parses now the rules for a specific module
	self::ProcessModuleRewrite($module_name);

	# And this tacks on our $_GET rules
	parse_str($_SERVER['QUERY_STRING'], $get_extra);
	$_GET = array_merge($_GET, $get_extra);

	# Add the $_GET to our object
	foreach($_GET as $key=>$value)
	{
		self::$get->$key = $value;
	}

	self::$run = true;
}

Some of it is pretty straight forward, but that’s half of the “workhorse”, next is the ProcessModuleRewrite() function:

public static function ProcessModuleRewrite($module_name)
{
	# Make sure it's valid
	if(is_array(self::$rewrite_rules[$module_name]))
	{
		# Walk through every peice of the array, $key is the
		#	index name, and $type is well, the type

		$i=1;
		foreach(self::$rewrite_rules[$module_name] as $key=>$type)
		{
			# Each peice, which was saved above as the exploded URL
			$val = self::$peices[$i++];

			# Convert to type specified
			if($type == 'int')
				$val = intval($val);
			elseif($type == 'float')
				$val = floatval($val);

			# We can do any other processing we want here

			# Add it both into the $_GET array, and into
			#	our object
			self::$get->$key = $val;
			$_GET[$key] = $val;
		}
	}
}

So as we see, this is where the types come into play, and we do our conversions. We can also strip any slashes here, and whatever other stuff. So now we can do:

CodonRewrite::AddRule("default", array('action'=>'string', 'id'=>'int'));
CodonRewrite::AddRule("users", array('action', 'username'));
CodonRewrite::ProcessRewrite();

echo CodonRewrite::$get->module;

What I do is for a controller, I’ll assign $this->get to CodonRewrite::$get, so inside a controller, I can do:

class MyModule extends CodonModule
{
	...

	function Controller()
	{

		if($this->get->action == ...)
		...
	}
}

And since rules can be “redone” on the fly:

class MyModule extends CodonModule
{
	...

	function __construct()
	{
		# Since I see it's a save, I'm modifying the rules a bit here
		#   so I can include another extra parameter in
		if($this->get->action == 'save')
		{
			CodonRewrite::AddRule('mymodule', array('page', 'action', 'id'=>'int', 'content'));
		}

		...
	}

	function Controller()
	{

		if($this->get->action == 'save')
		...
	}
}

I call this the Rewrite functions as the first thing in my main controller, and then I can base which module/controller to call based on these rules here. Make’s it simple for parsing, and pretty easy to see how the rules are made.

I hope that helps someone!

Share and Enjoy:
  • Print
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • HackerNews
  • Netvibes
  • Reddit
  • StumbleUpon
  • Twitter
  • Yahoo! Buzz

Written by Nabeel

March 3rd, 2009 at 4:12 pm

Posted in php

Tagged with , , ,

ezSQL (more stuff)

3 comments

I think I’m going to setup a SVN repository for ezSQL, since I’ve got a few ideas. I’m going to integrate memcache as one of the caching options, in addition to the flat-file caching it does now. Any other suggestions?

Share and Enjoy:
  • Print
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • HackerNews
  • Netvibes
  • Reddit
  • StumbleUpon
  • Twitter
  • Yahoo! Buzz

Written by Nabeel

February 25th, 2009 at 11:56 am

Posted in General

Tagged with ,

Nginx + PHP Query Strings

2 comments

Nginx (Engine-X) is the sweetest web server I’ve used. It’s running my VPS now, CPU usage has barely budged, even with a decent number of visitors. I ran into a problem, which I was searching for a solution before I cracked at it myself. The solution ended up being incredibly simple, but still may help someone else who’s searchin’ for it.

With nginx, PHP is passed of to FastCGI PHP process to parse and execute. It relies on a location rule to find out what are PHP files. But it would fall apart using URLs like:

index.php/some/query/string/parameters

Apache doesn’t have any trouble, with it, but nginx, if you looked at the error log, it’ll be about the directory not existing. (Hmm, parsing that in PHP sounds like a good idea for another post… noted).

So if we check out the current rewrite rules:

location ~ \.php$ {
		include /etc/nginx/conf/fastcgi_params;
		fastcgi_pass  127.0.0.1:9000;
		fastcgi_index index.php;
		fastcgi_param SCRIPT_FILENAME /path/to/public/$fastcgi_script_name;
}

The fix is pretty simple:

location ~ \.php<strong>(.*)</strong>$ {
		include /etc/nginx/conf/fastcgi_params;
		fastcgi_pass  127.0.0.1:9000;
		fastcgi_index index.php;
		fastcgi_param SCRIPT_FILENAME /path/to/public/$fastcgi_script_name;
}

Adding in the (.*) after the .php allows the PHP files to be picked up, and those additional query string parameters will get passed along into FastCGI.

Simple fix!

Share and Enjoy:
  • Print
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • HackerNews
  • Netvibes
  • Reddit
  • StumbleUpon
  • Twitter
  • Yahoo! Buzz

Written by Nabeel

February 24th, 2009 at 6:30 pm

Posted in General

Tagged with , , ,

Shell scripts no more!

13 comments

This weekend, I started my server migration, over to Slicehost. It went well, now I’m running on a lean ‘n mean nginx install. As I was moving my Subversion repositories, I was dreading having to move all my shell scripts, which I used to build and deploy some of my applications (outlined in this post). I was thinking there had to be a better way, after all, Ruby has Capistrano, and though it can be used with PHP, I didn’t want to have to install Ruby, etc etc. After some searching (not much), I found Phing, which looked like exactly what I needed. Sweet!

Phing takes an XML file, which you can define all the transformations and instructions. It has sets called “targets”, which are different stages of the build and transformation. They have a basic example here, but I’ll go through my phpVMS build.xml, since I do a number of things they don’t show in the examples, like checking out from Subversion, some variable replacements, and building tars for different “stages”. Having targets was great, since you can select which targets to run, so now I can combine a “beta” and “release” builder, instead of having separate files for it (as I did with the shell scripts). My goals are the same as in the SSH scripts:

  • Automatically build beta, full, and update versions from SVN
  • Update revision numbers within several files using tokens or regex
  • Copy it to the test site
  • Automatically generate PHPDoc API documentation

Phing is perfect for this. I had sets of painful-to-maintain and update SSH scripts to do this same thing. And it has a simple PEAR installer. I installed it using:

sudo pear channel-discover pear.phing.info
sudo pear install phing/phing

And that was it! Installed. They do have instructions for a I now had “phing” available on the command line:

phing -version
Phing version 2.3.3

Now we can get started, building our XML file. The file starts with:

<xml version="1.0">
<project basedir="." default="beta" name="phpvms">

</project>

That’s our basic container; as you can see I set the project name, the default “target” or stage to run, and the base directory.  Everything we do/add will go inside the project tags.

Now, I’m going to add some common “properties”, or variables, that will be used throughout the script, to make it easier, and not have to type out paths over and over:

<!-- Build properties -->
<property name="project.wcdir" value="/path/to/phpvms.wc" />
<!-- This is the path to where a clean export can be done of SVN -->
<property name="project.exportdir" value="/path/to/phpvms.export" />
<!-- This is the path where the build binaries can go -->
<property name="dest.dir" value="/path/to/public/files/" />
<!-- This is the path where the "test build" goes (phpvms.net/test) -->
<property name="test.dir" value="/path/to/public/test/" />
<!-- This is where the docs generated by phpdoc will go -->
<property name="php.doc" value="/path/to/public/docs/api/" />
<!-- URL to our SVN repository -->
<property name="repository.url" value="svn://phpvms.net/phpvms/trunk" />
<!-- Username and password, blank since the above can be accessed anon -->
<property name="repository.user" value="" />
<property name="repository.pass" value="" />

Use absolute paths if you can, otherwise, it’s relative to the basedir property defined in the project tag.

The reason for having a working copy and an export directory for SVN is this – the checkout directory will be used to determine the latest revision, since the svnlastrevision command requires the path to a working copy. The export directory is to hold a clean export; without all the subversion specific data that’s included in a checkout. We’ll package that up, and use that to copy to a test site.

Next, I define the targets, or stages, I’m going to use: prepare, build, subs, phpdoc, beta, and release.  They are setup this way in roughly the order they will run. The order doesn’t matter, since we determine the order of the targets by using the “depends” property. I’ll explain each one in detail:

<target name="build" depends="prepare">

</target>

<!-- This is where we will do our variable substitutions
	 It depends on the target above, primarily in our case,
	 replace all version numbers -->
<target name="subs" depends="build">

</target>

<!-- This is the PHP doc target, it's its own target
	 since we will call it from beta and release -->
<target name="phpdoc">

</target>

<!-- In this target, we will create a "beta" release.
	 It depends on the subs build. We will call this
	 target, or the release target specifically from the
	 command line when we run phing -->
<target name="beta" depends="subs">

</target>

<!-- In this target, we create a "release", and it depends on
	 the "subs" target above. We'll also specifically call this
	 from the command line -->
<target name="release" depends="subs">

</target>

So now that we have an outline, we can go through each target, doing what we need to do, first our “prepare” stage:

<target name="prepare">

	<!-- First we are going to delete the export directory
		 So we have a clean copy that we export from SVN.
		 Then we re-create it using the mkdir command -->
	<delete dir="${project.exportdir}" failonerror="true" verbose="false" includeemptydirs="true" />

	<mkdir dir="${project.exportdir}" />

	<!-- Now we create the working-copy directory,
		 where the checkout will be -->
	<mkdir dir="${project.wcdir}" />

</target>

We’re using our properties which we defined, as ${propertyname}. Don’t need to do any escaping, etc, just use ‘em right in the strings.

Next is our build target stage, which will do the Subversion operations of getting the latest revision.

<target name="build" depends="prepare">

	<!-- We are checking it out to the checkout directory -->

	<svncheckout todir="${project.wcdir}" repositoryurl="${repository.url}" nocache="true" force="true" password="${repository.pass}" username="${repository.user}" svnpath="/usr/bin/svn" />

	<!-- We're now going to get the last revision
		 from the above checkout. The last revision
		 number will be stored in the propety called
		 svn.lastrevision, which we can access by
		 ${svn.lastrevision}. No more parsing results
		 from the command line... wahoo! -->
	<svnlastrevision svnpath="/usr/bin/svn" propertyname="svn.lastrevision" workingcopy="${project.wcdir}" />

	<!-- Export a clean copy which we can package up -->
	<svnexport todir="${project.exportdir}" repositoryurl="${repository.url}" nocache="true" force="true" password="${repository.pass}" username="${repository.user}" svnpath="/usr/bin/svn" />

</target>

It’s great how Subversion operations can easily be done. Next target is subs, to replace any tokens and variables we define in the files.

<target name="subs" depends="build">

<!-- Reflexive allows work on a set of files, and
	 a "reflexive" block is required for a filterchain-->

<reflexive>

  <!-- Here we define the files that we're going to
	   work in using a filterchain -->
  <fileset dir="${project.exportdir}">
	<include name="changelog.htm" />
	<include name="install/install.php" />
	<include name="install/update.php" />
	<include name="install/install.sql" />
  </fileset>

  <!-- Setup a filterchain now, the above files
	   pass through these filters -->

  <filterchain>

	<!-- Instead of doing a regular expression match,
		 Phing supports token replacement. I use the
		 ##REVISION## token wherever I want it to be -->

	<replacetokens endtoken="##" begintoken="##">
	  <!-- Replace the REVISION token with our svn.lastrevision property -->
	  <token value="${svn.lastrevision}" key="REVISION" />
	</replacetokens>

  </filterchain>
</reflexive>

</target>

Next is our phpDoc function, which is sort of standalone, in the fact that there are no depends for it.

<target name="phpdoc">

  <!-- Setup the basic options -->

  <phpdoc title="API Documentation" output="HTML:Smarty:PHP" sourcecode="no" destdir="${php.doc}">

  <!-- These are the files that are going to be
	   included as part of the documentation -->

  <fileset dir="${project.exportdir}">
	<include name="core/classes/*.php" />
	<include name="core/common/*.php" />
	<include name="core/modules/**/.php" />
  </fileset>

  <!-- This lists the files which are part of the
	   official documentation -->

  <projdocfileset dir="${project.exportdir}">
	<include name="changelog.htm" />
  </projdocfileset>

</phpdoc>
</target>

Now that we’ve got the basics down, the next two targets are “beta” and “release”. Each of these handles a different set of tasks, though they are basically the same. First for beta:

<target name="beta" depends="subs">

	<echo msg="Creating archive..." />

	<!-- Delete the files first, since they are not updated,
		 The tar function will just append onto them -->
	<delete file="${dest.dir}/phpvms.beta.tar.gz" />
	<delete file="${dest.dir}/phpvms.beta.zip" />

	<!-- Create the tar file, specifying the files to include -->
	<tar compression="gzip" destfile="${dest.dir}/phpvms.beta.tar.gz">
	  <fileset dir="${project.exportdir}">
		<include name="*" />
	  </fileset>
	</tar>

	<!-- I was having trouble with the built-in ZIP functionality
		 So I resorted to using the command line. Either way, this
		 shows off the command line functionality that Phing has -->

	<exec escape="false" command="cd ${project.exportdir}; zip -D -r ${dest.dir}phpvms.beta.zip ." />
	<echo msg="Copying to test site" />

	<!-- Copy it to the test site, so there's an updated
		 copy available there to debug-->

	<copy todir="${test.dir}" overwrite="true">
	  <fileset dir="${project.exportdir}">
		<include name="*" />
	  </fileset>
	</copy>

	<!-- Call the phpDoc target that we created -->
	<echo msg="Creating phpDoc" />

	<phingcall target="phpdoc" />

	<echo msg="Files copied and compressed in build directory OK!" />

</target>

And now for the release target. Release a bit different than beta – it creates two different archives – one full version, and one update. The difference essentially is that the update copy has the local configuration file removed, where database settings are stored, and other settings which are kept locally.

<target name="release" depends="subs">

	<echo msg="Creating release builds..." />

	<!-- Delete all the files, start fresh -->
	<delete file="${dest.dir}/phpvms.full.tar.gz" />
	<delete file="${dest.dir}/phpvms.full.zip" />
	<delete file="${dest.dir}/phpvms.update.tar.gz" />
	<delete file="${dest.dir}/phpvms.update.zip" />

	<!-- Create the tar file -->
	<tar compression="gzip" destfile="${dest.dir}/phpvms.full.tar.gz">
	  <fileset dir="${project.exportdir}">
		<include name="*" />
	  </fileset>
	</tar>

	<!-- Create the zip file -->
	<exec escape="false" command="cd ${project.exportdir}; zip -D -r ${dest.dir}phpvms.full.zip ." />

	<!-- Now create the update build, which basically deleting the
				local.config.php file -->
	<echo msg="Full release created, creating update" />

	<!-- Delete the local.config.php file, since that's not distributed
		 with an update-->
	<delete file="${project.exportdir}/core/local.config.php" />

	<!-- Create the tar file -->
	<tar compression="gzip" destfile="${dest.dir}/phpvms.update.tar.gz">
	  <fileset dir="${project.exportdir}">
		<include name="*" />
	  </fileset>
	</tar>

	<!-- Create the zip file, using command line instead
		  I kept getting out of memory errors using the built-in
		  ZIP functionality. -->

	<exec escape="false" command="cd ${project.exportdir}; zip -D -r ${dest.dir}phpvms.update.zip ." />

	<!-- Call the phpDoc target -->
	<echo msg="Creating phpDoc" />

	<phingcall target="phpdoc" />

	<echo msg="Files copied and files moved, done!" />

</target>

And that’s it! Now we can run it using:

phing -buildfile /path/to/build.xml beta
phing -buildfile /path/to/build.xml release

Using the appropriate one we want to use. In my SVN post-commit file, I run the beta line, in order to have that build every time.

I attached a fully doc’d version here (same as the above, just in one file). I hope it helps!

Share and Enjoy:
  • Print
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • HackerNews
  • Netvibes
  • Reddit
  • StumbleUpon
  • Twitter
  • Yahoo! Buzz

Written by Nabeel

February 16th, 2009 at 5:49 pm

Posted in Projects, php, phpVMS

Tagged with , ,

ezSQL Database Library (Improved!)

9 comments

Note: I’ve made a dedicated page for ezSQL-related items, you can view it here

I’ve been using ezSQL for a long time, as my “database driver”. It’s an awesome little class, that you can easily use to get database results, and return in different formats. I’ve made a bunch of changes and updates to it, so I thought I’d put them out there and share them.

Along the way, I’ve made some updates and changes to it, to suite my requirements, including format PHP5 support. I’ve updated the error handling/logging, so now you can call:

$sql->error(); // Get the error string
$sql->errno(); // Get the error number

Respectively after queries to return the “real” status of a query. the debug() function has been added to, so at call time, you can pass to bool whether to display the result on the screen, or pass it back as a string

I’ve also added several utility functions:

$sql->quick_select();
$sql->quick_update();
$sql->quick_insert();

// quick_select() example:
$columns = array('column1', 'column2');
$sql->quick_select('table_name', $columns, 'LIMIT 10');

To make it easier to do simple SELECT’s and UPDATEs, and INSERTs.

Another thing it was missing with MySQLi support, so that has been added (though I have not had the time to add support for statements (though that can be accessed rather easily). I had added MSSQL support, but the file has gone AWOL (Icreated and used it for a specific project a while ago). I will update when I get a hold of it.

And another thing I added was a static interface class, which I use all the time on PHP5 projects:

DB::init('mysql');
DB::connect('username', 'password', 'database_name', 'localhost');
// Now anywhere in your script:
$row = DB::get_row('...');
// Or
$results = DB::get_results('...', ARRAY_A);

Makes it much easier to access the database function, without having to do $db = DB::getInstance(), in every single function to get a singleton object of the database. Since that’s for PHP5 only, the whole library has been updated for PHP 5 support, with public/private functions and constructors/destructors as well.

I also condensed it to just one include() now, just keep all the files in the same place (include ‘…/DB.class.php’). From that you can either use the static class, or declare a new ezSQL object.

I also added in phpDoc blocks on all the functions, since IntelliSense is awesome and really helpful.

I hope these improvements make it easier for everyone. Of course, the original credit goes to Justin Vincent. There are some docs and examples here as well.

You can download it from here

Share and Enjoy:
  • Print
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • HackerNews
  • Netvibes
  • Reddit
  • StumbleUpon
  • Twitter
  • Yahoo! Buzz

Written by Nabeel

February 3rd, 2009 at 11:52 am

Posted in Fun, General, Projects, php

Reducing HTTP Requests

leave a comment

In doing some optimizing for phpVMS, I noticed I have a lot of HTTP requests:

requests_before_2

As you can see, there are 27 HTTP requests, 15 of them JavaScript files. This means, the browser has to reach out 15 times to grab individual JS files. This seems like a straight forward problem, if we can just condense those 15 files into one, then we can almost half the number of total requests.

So we can do this manually – copy and paste each file, into one master file. The problem is, when you update one file, you have to go and search around in your other files, and figure out exactly where to update, or just redo the entire condensing process. It’s a pain. but these seems like a good place to write some code to do it.

As part of Codon now, I’ve included a class called CodonCondenser. It’s just a basic class that will condense requested files into one file. I set out some requirements:

  1. It’s a class, so we can reuse it
  2. Use it for any file types
  3. Have a cached file of our condensed batch, so we don’t need to dynamically condense it every time, which could be expense.
  4. Be able to add/remove files at will, and it will handle that

Since we have some basic requirements, let’s plan how it’ll work:

  1. Set our options, the path to the files, the URL to the files since our function will return the URL to the generated file, set the file type, and a cache
  2. Pass an array, with the list of our files
  3. Generate a MD5 hash of the array we’ve passed, this will be our file name. If the array changes, as in we add files, or remove files, the hash will change, so we satisfy requirement #4… OR… use a filename that we generate, or pass in.
  4. Check this file name, see if it exists; if it does, see if the file age is older than we want (satisfying #3). If it’s not too old, then use this file
  5. If it’s too old, or doesn’t exist, then generate a new condensed file and return the URL to it.

So let’s start:

class CodonCondenser
{
	public $path;
	public $url;
	public $timeout = 24;
	public $file_ext = '';
	public $filename;
}

Our basic class, with the settings. Next is our function to set these options:

public function SetOptions($path, $url, $file_ext, $timeout=24)
{
	$this->path = $path;
	$this->url = $url;
	$this->timeout = $timeout;
	$this->file_ext = $file_ext;
}

We pass the $path, which is the absolute path to the files, and $url, which is the path to where the file is will be publicly accessible.

$file_ext is the extension of the condensed file (js, css, htm, etc), and $timeout is the time that the condensed file is considered ‘fresh’. Passing this as blank ($timeout=’’), will disable the time check.

Next is our function to check the cached version. We’ll return true or false if the file passed is valid:

protected function getCachedFile()
{
	if(!file_exists($this->path.'/'.$this->filename))
	{
		return false;
	}

	# Check if the version that exists
	#    is older than the timeout we have alloted
	# Value of "" skips the time check
	if($this->timeout == '')
	{
		if ((time() - @filemtime($file)) > ($this->timeout*3600))
		{
			# It is older, so delete it
			@unlink($this->path.'/'.$file);
			return false;
		}
	}

	# The cache file is ok
	return true;
}

So what we are checking, if the file doesn’t exist, then return false. Next, if the timeout value is blank, just skip the check, and only base it on whether the condensed file exists or not (as we saw the option up above). Otherwise, we check the modification time of the file, and if the current time – the time file created (in seconds), is greater than our timeout value, then return false. If we have 0 (zero) as a timeout value, it will generate a new file every time.

Note that the function is protected, so we can’t access it directly, and instead, go through the main function:

public function GetCondensedFile($files, $filename='')
{
	if($filename != '')
	{
		$this->filename = $filename;
	}
	else
	{
		$this->filename = md5(implode('',$files));
		$this->filename .= '.'.$this->file_ext;
	}
}

First, here we are checking if we passed an optional filename. I use the filename option, depending on whether it’s being used in the admin area, or in the front-end client area. I’ll explain this later.

Otherwise, we will build a filename, we check for the cached file, using the function above:

# Check if we've already made this condensed cache file
#    If we have, then just give the URL of that file
if($this->getCachedFile() == true)
{
	return $this->url.'/'.$this->filename;
}

If the getCachedFile() returns true, that mean’s that the cached file is okay, and we exit and just return the full URL path to the condensed file. If it’s not condensed, then we build our condensed file:

$fp = fopen($this->path.'/'.$this->filename, 'w');

foreach($files as $file)
{
	fwrite($fp, file_get_contents($this->path.'/'.$file));
}

fclose($fp);
return $this->url.'/'.$this->filename;

This is pretty straightforward, we just open the condensed file, and write every file into it, then return the full URL path to the file. So this is how we’ll use it:

$condenser->SetOptions('/var/www/lib/js', 'http://mysite.com/lib/js', 'js');

$files = array( 'jquery.min.js', 'jquery-ui.js',
				'jquery.dimensions.pack.js',
				'jquery.form.js', 'jqModal.js',
				'jquery.bigiframe.js',
				'jquery.sparklines.js',
				'jquery.autocomplete.js',
				'jquery.tablesorter.pack.js',
				'jquery.tablesorter.pager.js',
				'jquery.metadata.js', 'jquery.impromptu.js',
				'jquery.listen-min.js', 'nicEdit.js');

$url = $condenser->GetCondensedFile($files);

And then to link the Javascript into our page:

<script type="text/javascript" src="<?php echo $url?>"></script>

Our result in the end:

requests_after_2

14 less requests! Almost half the number of requests. Woohoo! While it’s a basic class, and we can certainly expand it by adding features such as compression (such as implementing JSMin-PHP into), to minify and pack the JS. Be careful if your JS files are already packed if you re-pack them, it could cause errors. I use files that have already been packed, and merge them together this way.

You can also use this for HTML files, CSS, any other text files which are all brought in together in separate requests.

Click to download the full script.

Share and Enjoy:
  • Print
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • HackerNews
  • Netvibes
  • Reddit
  • StumbleUpon
  • Twitter
  • Yahoo! Buzz

Written by Nabeel

January 8th, 2009 at 3:25 pm

Posted in General

Airplane Movie Quotes

one comment

I just did a quick thing for the phpVMS admin panel, displaying random quotes from the movie Airplane! in the footer. Just call the function randquote(), and it’ll return the string, so you can do whatever you want with it.

And don’t call me Shirly…

Click to download it (zip file)

Share and Enjoy:
  • Print
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • HackerNews
  • Netvibes
  • Reddit
  • StumbleUpon
  • Twitter
  • Yahoo! Buzz

Written by Nabeel

January 2nd, 2009 at 12:26 pm

Posted in Fun, General, phpVMS

Tagged with , ,

MySQL “Improved”

leave a comment

Found an old post I wrote, about the new “MySQLi” functions in PHP 5+. PHP6 is almost on the horizon, and I personally haven’t seen too much wide adoption of this new MySQL functionality.

The i in “MySQLi” stands for i”mproved”. While most of the functions between MySQL and MySQLi will work exactly the same (just by replacing the mysql_ with mysqli such as mysqli_query()), there are several cool things that come with this new "improved" MySQL library. One thing that’s available is to use the database connector itself as a "class", making it easy to create your own database library that is customized to your application. This makes it real easy to separate the logic and extend your database code, for those of us who are OO nuts ;)

Another advantage is that you can also use an SSL connection easily with the database, without jumping through hoops by just providing the path to the certificate. But the best improvement is the ability to use prepared statements. This makes queries (more) secure by telling MySQL exactly which types of values to expect, and also stores a template of those values in memory. The result is security, and since it’s cached, it ends up being much faster on repeat queries and inserts.

I will use the procedural version of the code in these examples, to keep it a bit easier, and I’m also leaving out error checking, for the sake of example.

//First we create our connection
$db_link = mysqli_connect('localhost', 'username', 'password', 'db name');

//Now we want to prepare our statement
// So we first have to initialize it.
//Now we prepare the actual statement. The ? replace the variables
// We're going to "insert" those later on
mysqli_stmt_prepare($db_link, 'SELECT * FROM data WHERE stringvalue=? AND numbervalue=?');

So we’re prepared the statement, putting in question marks where there are going to be values placed in. We are “binding” a value into there, so we have to use a function mysqli_stmt_bind_param().  If we look at the parameters for this function, it is:

mysqli_stmt_bind_param(statement, types, variables);

The types, are defined as:

  • i – integer (any whole number)
  • d – double (number, with decimals)
  • s – string
  • b – blob

Since we have to variables in the query above (string value and number value), our "types" are going to be "si". The first is a string (s) and the second is a number (i).

mysql_stmt_bind_param($statement, 'si', $string_value, $number_value);

You might be wondering “What’s the point?”. It helps a great deal with security, when it comes to inserting data:

$statement = mysqli_prepare($link, 'INSERT INTO table (name, idnumber, address) VALUES (?, ?, ?)');

mysqli_stmt_bind_param($statement, 'sis', $name, $idnumber, $address);

$name = 'First Last';
$idnumber = 123456789;
$address = 'I live here';

mysqli_stmt_execute($statement);
mysqli_stmt_close($statement);

As you can see, MySQL is now expecting certain types for the data which is being inserted, and treats it as such – strings as strings, integers as integers, and so on, and will automatically take the best precautions to ensure the data is valid.

For more info, check out the Zend information on MySQLi.

Share and Enjoy:
  • Print
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • HackerNews
  • Netvibes
  • Reddit
  • StumbleUpon
  • Twitter
  • Yahoo! Buzz

Written by Nabeel

January 1st, 2009 at 9:00 pm

Posted in General