Tips for PHP Developers Pt. 2 Focus on Modularity

by Mike Willbanks on April 12th, 2006

Part two of this series I am focusing on a subject as the first time I did not. This post will focus primarily on modularizing an application and its components.

Prefix your functions and classes
Another big thing is many people do not prefix their functions and/or classes. This is very important otherwise you can run into naming conflicts. Take for instance you just spent a day writing and implementing a class in several different files only to find out that there is already a class used by PHP or PEAR that causes this to not work in different environments. Now you have to go through all of the files and replace this with the new naming and retest. Yes there are search and replace functions in just about any text editor but for design sakes PLEASE prefix your classes!

A good example of this is the PEAR Date class. A while back there was a big issue with a naming conflict that caused a date class to not be included by default into php. Take this as a serious warning!

Checking for defined constants before setting a constant
I am a huge fan of constants for items like regular expressions, mostly because the pattern will probably not change and I want to use pretty much the same pattern through out a whole site. Now I also define these in the class file itself which allows to basically set a default.

Now these default constants are mainly in place to set the constant if there is not one previously defined. This allows for great flexibility seeing as I use an email regular expression in around 5 or 6 places. PHP has the most wonderful function “defined()” which allows you to check if something has already been defined. So if someone came along and wanted to override that regular expression they could simply define it before it was defined. For those that need something a little more see the following example of how I normally do this:

//validate_telephone.class.php
if (!defined('REGEX_US_TELEPHONE') {
	define('REGEX_US_TELEPHONE', '/^\(?([0-9]{3})\)?[\s\.\-]*([0-9]{3})[\s\.\-]*([0-9]{4})$/');
}
class dsValidateTelephone extends dsValidate {
	public function validate($tele) {
		if (preg_match(REGEX_US_TELEPHONE, $tele)) {
			return true;
		}
		return false;
	}
}
 
//someform.php
define('REGEX_US_TELEPHONE', 'mycustomregex');

As you can see this can be extremely useful….

Using a database abstraction library
If you are not currently using a database abstraction library there are several reasons to do so. The main advantage is you can deploy to more than a single database and if you ever move databases there is hardly any code that requires you to modify the current functionality. Also most of these abstraction libraries allow you to debug the queries to the database much better than if you were to use the API’s method for retrieving database error messages and numbers.

There are several database abstraction libraries out there. If you are using PHP 5 definitely use PDO. For PHP 4 I would say to use PEAR DB, ADODB or ADODB Lite.

Writing a renderer with formatted strings for output that could be changed by a user
One of my personal pet peeves is when there is a class that outputs a string or content that I can not customize without destorying the class or heavily rewriting it. One of the things that HTML_QuickForms got right was how they provide an interface to modify the output that it produces. By simply extending a class and setting those variables you are set to go. Say for instance we have a class that returns a string used for output that we use for providing links from a datbase (I will provide a PHP4 and PHP5 example since most people are still running it):

//php4
class LinkRenderer {
	var $enclose = '<ul>%s</ul>';
	var $tag = '<li><a href="%s">%s</a></li>';
	var $_links = array();
	function addLink($url, $text) {
		$this->_links[] = array($url, $text);
	}
	function toHTML() {
		$output = '';
		while(list(,$l) = each($this->_links)) {
			$output .= sprintf($this->tag, $l[0], $l[1]);
		}
		return sprintf($enclose, $output);
	}
}
 
//php5
class LinkRenderer {
	public $enclose = '<ul>%s</ul>';
	public $tag = '<li><a href="%s">%s</a></li>';
	protected $_links = array();
	public function addLink($url, $text) {
		$this->_links[] = array($url, $text);
	}
	public function toHTML() {
		$output = '';
		while(list(,$l) = each($this->_links)) {
			$output .= sprintf($this->tag, $l[0], $l[1]);
		}
		return sprintf($enclose, $output);
	}
}

Grouping code that is reused into functions or classes
The main point of this is to keep maintenance time down. If there is a small bug the developer no longer needs to run through all of the code and try to find exactly every place that this code was copied or written. Even although this is a novice mistake, I see it come up extremely often when reviewing other peoples code.

Checking for php version and functions before usage
Sometimes you will deal with circumstances where different versions of php implement functions you need or there are restrictions on certain functions and you would like to implement the function yourself. Here is an examples:

//implement file_put_contents if not php5 or function doesn't exist:
if (!function_exists('file_put_contents') || version_compare(PHP_VERSION, "5", "< ")) {
	function file_put_contents($filename, $data) {
		if (is_writable($filename)) {
			$fp = fopen($filename, 'w');
			$ret = fwrite($fp, $data);
			$fclose($fp);
			return $ret;
		}
		return false;
	}
}

In the upcoming part 3 I will focus on some simple security tricks that should help you lock down your code.

From PHP

3 Comments
  1. Actually you should use PEAR::MDB2 instead of PEAR::DB these days. It beats the crap out of DB in terms of performance, features, extensibility.

  2. Oh one more thing .. use PEAR’s PHP_Compat and PHP_CompatInfo to determine what functions/constants may be missing in your version of PHP that you are using in your code and to then load these as PHP userland implementations.

  3. Lukas,

    Thanks for the comments. I suppose PEAR::MDB2 probably does beat the crap out of the other ones, I should probably take a peak at it :)

    I’ve known about PHP_Compat for a little while, however actually never implemented it. It looks like something that I should probably be doing from now on.

    Thanks :)

Leave a Reply

Note: XHTML is allowed. Your email address will never be published.

Subscribe to this comment feed via RSS