Archive for June, 2008

Test Driven Development

What’s the best way to sleep at night and what’s the best way to be fairly sure that you’re code is as bullet proof as possible? Learn about test driven development and learn to love tests! There have been numerous articles that go in depth about this subject so I’ll {try} be brief about it…

No real purist will distill TDD into just a few guidelines but I will anyways:

  • Think about what functionality you want to build. Design and code a test that will test that functionality (before you code the actual functionality!).
  • Run the test and make sure it fails as expected.
  • Code your functionality.
  • Run your test again. Did it pass? Congrats, move on to the next task. Didn’t pass? You have some bug fixing to do.

The trick here is to put more emphasis on the design of the functionality than you’re used to. Think it through and design it so that it’s testable. If your test fails after coding the functionality and you feel compelled to fix the test rather than the functionality, then one of two things happened: 1) you did not give enough thought to the design of the functionality and make it easily testable or 2) you coded the test wrong. In either case, the design part of it all needed work.

Rinse, repeat, rinse, repeat.

The beauty in having a suite of tests is piece of mind. No, not just a warm fuzzy feeling. I’m talking about time-saving, bug-identifying, save-my-ass, look-ma-no-bugs piece of mind. Did you just add a new feature or refactor some code? Run your suite of tests. You’ll be able to much more quickly spot the bugs. Perhaps you’ll spot something that might have otherwise gone live.

I personally use SimpleTest to do my testing. PHPUnit is supposed to be pretty good as well.

Some links if you want to read more:
http://www.onpk.net/talks/fosdem2005/introduction_simpletest.html
http://www.developerspot.com/print/php/test-driven-development/

Comments (1)

Practice: Round 2

Passed again. Tempted to setup the real thing for this saturday.
2nd practice test

Leave a Comment

Not Bad . . .

After some on and off studying, I decided to take the ZCE practice test this evening. If the practice test is any indication than I’m looking forward to taking the real thing!

I passed 10 of the 12 areas and 5 of those with ‘excellent’. Funny thing was I failed “basic language.” It’s nice taking this in advance so I have an idea of the structure and difficulty of the real thing. It also gives me a chance to zero-in on the areas that I apparently have some trouble in. I’ll probably be scheduling the real exam pretty soon. Maybe take it within a month.
zce practice

Leave a Comment

Easily Build XML Sitemaps for SEO: Tutorial

Note: Keep an eye out for a new post with an updated sitemaps class which includes index creation. – april 17th

I’ll do this from time to time if I think it will help a lot of people: release some of my own code for your use. All I ask is that you keep my author tag in place.

Creating a sitemap based on the sitemaps.org protocol is a very smart thing to do SEO-wise. The protocol itself is backed by all the major search engines and it’s very easy to let the search engines know about the sitemap. You can also inform Google through webmaster tools and see reports on when it was downloaded, if there are any errors, etc.

Now I know there is a pear package that does this already. And I actually tried to use that before making my own class. However, I was dealing with a very large number of URL’s and the pear package was not up to par. It was VERY slow and hogged a lot of memory. My class on the other hand is very fast and uses a more manageable amount of memory. This is mostly attributed to this class writing each URL entry to the file on the fly. I’ve used it with the maximum allowed number of URL’s (50,000) and the memory usage was around 20mb I believe.

First, I’ll go through the basic usage of my sitemaps class.

Usage

<?php

/**
 * Example usage of the sitemaps class made by George Gonzalez
 */
class sitemap_example extends sitemaps
{
	var $urlList;

	function __construct()
	{
		$filename = '/full/path/to/your/sitemap.xml';
		parent::__construct($filename);
	}

	/**
	 * could get this information from a database. could also be a multi-dimensional
	 * array with prioritiy values (0.0 - 1.0) and frequency of change (always, hourly,
	 * daily, weekly, monthly, yearly, or never)
	 */
	public function getURLs()
	{
		$this->urlList = array('www.google.com', 'www.yahoo.com', 'www.cnn.com', 'www.lakers.com');
	}

	public function cycleURLs()
	{
		foreach($this->urlList as $url) {
			$this->addUrl($url, '0.5', 'weekly');
		}
	}
}

// make an instance of this
$sm = new sitemap_example();

// get our URL list
$sm->getURLS();

// iterate through our URL's and use addUrl() to add them to the sitemap
$sm->cycleUrls();

// important. close out our sitemap file and see any error notices
$sm->finish();
?>

You can customize getURLs() to optionally retrieve your URL’s from the database. For example, if you have URLs in the form: http://www.domain.com/article.php?id=45, then run a mysql query to grab all the article ID’s and add them to the string ‘http://www.domain.com/article.php?id=’.

Another recommended customization here is for the frequency of change and priority for each URL. If you have a quality/importance metric you can go by, grade each URL for the priority and give it a value between 0.0 and 1.0. This doesn’t guarantee better rankings but hints at the search engine that a 1.0 page is more important or relevant than a 0.1 page (and bots will be more likely to go to the higher priority ones more often).

Of course if you customize getURLs to make a multi-dimensional array, you’ll also have to customize cycleURLs to use that array properly.

Don’t forget to call the finish() function! It will give an error notice if the xml file is over 10mb or has over 50,000 URL’s. Both of those are limits set by the sitemaps.org protocol.

Over 50,000 URL’s?

If you have over 50,000 URL’s, you will need to make several sitemap files. Do sitemaps that hold up to 50,000 URL’s and then make a sitemaps index file. The index file is in this form:

<?xml version="1.0" encoding="UTF-8"?>
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
   <sitemap>
      <loc>http://www.example.com/sitemap1.xml.gz</loc>
      <lastmod>2004-10-01T18:23:17+00:00</lastmod>
   </sitemap>
   <sitemap>
      <loc>http://www.example.com/sitemap2.xml.gz</loc>
      <lastmod>2005-01-01</lastmod>
   </sitemap>
</sitemapindex>

Future Versions

In future versions of my sitemaps class I’ll also include a way to ping the search engines of the new sitemap file. In addition, I want to add a built in way to make sitemap index files.

The Class Itself

Please note that this requires PHP5.

<?php
/**
 * Tool to help build xml sitemaps according to sitemaps.org protocol.
 * DO NOT TAKE OFF THE AUTHOR TAG!
 * @author George Gonzalez <webmaster@socaltrailriders.org>
 * @version 1.0
 */

error_reporting(E_ALL);

/**
 * XML Sitemaps class that allows us to make sitemaps based on sitemaps.org
 * protocol which all the major search engines adhere to.
 * @link http://www.sitemaps.org Sitemaps Protocol
 * @author George Gonzalez <webmaster@socaltrailriders.org>
 */
class sitemaps
{
	/**
	 * @var mixed The file handler for the sitemap file (before it gets gzipped)
	 **/
	var $f;

	/**
	 * @var string The file, with full path, to the XML file we will create
	 * before it gets gzipped
	 **/
	var $xmlFile;

	/**
	 * $var integer Keeps a count of the URL's added - should be <= 50,000 for it to be within
	 * sitemaps.org spec
	 */
	var $urlCount = 0;

	/**
	 * Initialize this with the filename (including full path) of the file we
	 * want to do. This isn't including the .gz but can include the .xml. Also
	 * check to make sure the diretory is writable
	 * @param string $file The filename
	 */
	function __construct($file)
	{
		if (empty($file) || (strlen($file)) < 5) {
			trigger_error('The proposed filename doesnt appear to be valid.', E_USER_NOTICE);
		}

		$parts = explode('/', $file);
		$num = count($parts) - 1;
		unset($parts[$num]);

		$dir = implode('/', $parts);
		$dir = trim($dir, '/');
		$dir = '/'.$dir.'/';
		$result = is_writable($dir);

		if ($result === false) {
			trigger_error('The directory '.$dir.' is not writeable.', E_USER_ERROR);
		}

		$this->xmlFile = $file;
		$result = $this->_startXML();
	}

	/**
	 * Adds a URL to the sitemap. This adds it to the file on the fly
	 * @param string $url The URL to add to the sitemap
	 * @param string $priority The priority of the URL, relative to other pages on
	 * your site. From 0 - 1.
	 * @param string $freq The frequency that the content on the given URL
	 * changes. Can be: always, hourly, daily, weekly, monthly, yearly, or never
	 * @return boolean Returns true if a write went well, false if not
	 */
	public function addUrl($url, $priority, $freq)
	{
		// appends XML tags to the end of a file
		$xml = '<url><loc>'.$url.'</loc><changefreq>'.$freq.'</changefreq>
<priority>'.$priority.'</priority></url>';
		$bytes = fwrite($this->f, $xml);	

		if ($bytes === false) {
			trigger_error('Couldnt add URL '.$url.' to the file.', E_USER_NOTICE);
		}
		else {
			$this->urlCount++;
		}

   		return ($bytes === false) ? false : true;
	}

	/**
	 * Once we're done adding URL's, finish off the xml with the closing urlset tag
	 * and possibly gzip the xml contents + delete the original xml file
	 * @param boolean $doGz Whether or not to gzip our xml file (and delete the xml file)
	 * defaults to yes (true)
	 * @return boolean Possibly Description as well
	 */
	public function finish($doGz = true)
	{
		$this->_finishXML();

		if ($doGz == true) {
			$this->_gzipFile();
			$ul = unlink($this->xmlFile);

			if ($ul === false) {
				trigger_error('Couldnt delete '.$this->xmlFile, E_USER_NOTICE);
			}
		}

	}

	/**
	 * Open the xml file (or create it) with the first couple lines that
	 * follow the Sitemaps protocol
	 * @return boolean Returns true if all went well
	 */
	private function _startXML()
	{
		// creates a file and starts it
		$xml = '<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">';

		$this->f = fopen($this->xmlFile, 'w');

		if ($this->f === false) {
			trigger_error('Cant open file '.$this->xmlFile.' for writing.', E_USER_ERROR);
			return false;
		}
		else {
			$bytes = fwrite($this->f, $xml);
			return ($bytes === false) ? false : true;
		}
	}	

	/**
	 * Finish the XML file by adding a closing </urlset> and close the file
	 * @return boolean Possibly Description as well
	 */
	private function _finishXML()
	{
		// adds the ending/closing tags on the XML file
		$xml = '</urlset>';
		$bytes = fwrite($this->f, $xml);

		if ($bytes === false) {
			trigger_error('couldnt write the closing tag to the xml file', E_USER_NOTICE);
		}

		$close = fclose($this->f);

		if (filesize($this->xmlFile) > 10485760) {
			trigger_error($this->xmlFile.' is bigger than 10mb! This is not within sitemaps.org spec.', E_USER_NOTICE);
		}

		if ($this->urlCount > 50000) {
			trigger_error('You have over 50,000 URL\'s! This is not within sitemaps.org spec.', E_USER_NOTICE);
		}

		return ($close === false) ? false : true;
	}

	/**
	 * Read in the XML sitemap, then uses that content and gzips it using the
	 * same filename with '.gz' added onto it to designate the gzip
	 * @return boolean Returns true if the gzip operation worked
	 */
	private function _gzipFile()
	{
		$fh = fopen($this->xmlFile, 'r');

		if ($fh === false) {
			trigger_error('Couldnt open '.$this->xmlFile.' to take contents for gzip.', E_USER_ERROR);
		}

		$xml = fread($fh, filesize($this->xmlFile));

		if ($xml === false) {
			trigger_error('Couldnt read '.$this->xmlFile.' to take contents for gzip.', E_USER_ERROR);
		}

		if (fclose($fh) === false) {
			trigger_error('Couldnt close '.$this->xmlFile.' after reading for gzip.', E_USER_NOTICE);
		}

		$gzipFile = $this->xmlFile.'.gz';

		$gz = gzopen($gzipFile, 'w');

		if ($gz === false) {
			trigger_error('Couldnt open '.$gzipFile, E_USER_ERROR);
		}

		$bytes = gzwrite($gz, $xml);

		if ($bytes > 0) {

			echo "Wrote $gzipFile \n";

			gzclose($gz);
			return $gzipFile;
		}
		else {
			return false;
		}
	}
}
?>

Please comment if you have a question or suggestion.

Leave a Comment

fopen + fwrite + fclose vs. file_put_contents()

New in PHP 5 is a function called file_put_contents( );. It’s three things all in one: fopen, fwrite, and fclose. Want to add a line to a file? Now that’s possible with just one function call – file_put_contents( );

Of course being a more comprehensive function, one would assume that file_put_contents( ) is slower than any one of the other functions that it encompasses. But how much slower?

I ran a fairly unscientific benchmark to delve a little deeper into this issue. I have a script that generates an xml sitemap of about 1800 links. The current version first uses fopen( ) once, then uses fwrite( ) to append and then uses fclose( ) to close it at the end. What would happen if I replaced that with file_put_contents( ) every time I wanted to append to the xml file? Well I setup two scripts that do the identical thing. One uses fopen/fwrite/close and the other uses file_put_contents( ). Here are the results:

Run # file_put_contents( ) (secs) fopen/fwrite/fclose (secs)
1 2.6208 2.468
2 2.5520 2.5322
3 2.7706 2.5413
4 3.2533 2.5672
5 2.6072 2.5034
6 2.5591 2.4922
AVG: 2.6662 2.4857

So the fopen/fwrite/fclose version is about 7.3% faster. The speed increase makes sense. The majority of the script is a simple append of a URL to the xml file. During that operation, file_put_content( ) still has to do the functionality of fopen/fwrite/flcose every single time it adds a URL. Whereas the original script only has to do fwrite( ) when it adds URL’s.

Comments (2)

Intro to Classes

You can’t really have OOP without classes, so I thought I’d do a short intro to classes. In it’s basic form, a class can have properties and methods. They’re pretty much variables and functions, but the terms change when in the class context.

Here’s a basic class:

class car
{
   var $model;
   var $num_doors = 2;

    public function __construct($model, $num_doors)
    {
        $this->model = $model;
        $this->num_doors = $num_doors;
    }

    public function get_num_doors()
    {
        return $this->num_doors;
    }
}

While this may look a bit daunting if you’re not used to classes, it’s pretty simple if you take it one bit at a time. First of all, notice that the syntax for making a class is just

class someClass
{
}

Properties are typically declared before the methods. You can also initiate the value of a property:

var $num_doors = 2;

One thing to remember is that you can’t do any expressions when initializing them. For example, this would be an error:

var $date = date('r'); // produces an error

A method can be made the same way normal functions are made in PHP. In classes, you can also specify the visibility of a function. Visibility can be any of the following:

  • public – accessible from anywhere, even outside of the class
  • protected – can be accessed from within the same class or a descendant class
  • private – can only be accessed from within the same class
  • final – can be accessed from anywhere but not overridden in a descendant class
Usage
So once you have a class, how do you use it? You can either make an instance of it or use paamayim nekudotayim (yes, it’s a mouthful!). The more traditional way is to make an instance:
$myCar = new car('F430', 2);

By doing that, I’m making an instance of the car class and giving it the name “myCar”. Two parameters are passed to the class, ‘F430′ and 2. Those parameters get passed to the constructor, __construct(). From there I can access methods using “$myCar->methodName()”:

echo $myCar->get_num_doors(); // echos 2 to the screen

“paamayim nekudotayim” is basically just two colons. The usage is as follows:

echo car::get_num_doors(); // echos 2 to the screen
// pretty much... class::method();

Note that you can only do that if the visibility is either

  • not set
  • public
  • or final

Trying to access a method with “::” from outside of it’s declared visibility will lead to an error.

 

Talking about why classes are so useful, inheritance, why visibility is useful, and best practices all merit their own entry in the future…

 

 

Leave a Comment

Prepared Statements with PEAR’s MDB2

Prepared statements are a common thing among databases. They’re useful when you’re going to be running the same query multiple times but with different values inside of it. The common steps when working with prepared statements are as follows

  1. Declare your query and where the placeholders are.
  2. Run that prepared statement by just giving data – the data will fall into the placeholders
With MDB2, this is accomplished by doing (and I’ll use pear’s own example query …)
// our people to add to the database ...
$john = array('john', 34);
$bill = array('bill, 34);

// prepare the query. note the '?' placeholders
$q = 'INSERT INTO person ( name, age) VALUES (?, ?)';
$statement = $mdb2->prepare($q);

// execute the prepared statement with our people
$statement->execute($john);
$statement->execute($bill);

Of course you can rapidly execute lots of data pretty easily by using one array and a foreach to go through it . . .

// our people to add to the database ...
$people = (   ('john', 34),
              ('bill, 37),
              ('joe, 32),
              ('sarah, 55)  );

// prepare the query. note the '?' placeholders
$q = 'INSERT INTO person ( name, age) VALUES (?, ?)';
$statement = $mdb2->prepare($q);

// execute the prepared statement with our people
foreach($people as $person) {
    $statement->execute($person)
}

Benefit

The benefit to doing it this way, aside from the ‘look at me I’m a PHP/database whiz’ factor, is that it’s faster and easier on the database. Once a query is prepared, the database doesn’t have to parse through the query each time you run that query.

Returning Results

If you need to do a SELECT vs. something like an INSERT or an UPDATE, then you just have to include some parameters to prepare()…

// our people to find in the database ...
$john = array('john', 34);
$bill = array('bill, 34);

// prepare the query. now we need to add a third parameter to prepare()
// which states our return type.
$q = '    SELECT * FROM person WHERE name = ? AND age = ?';
$statement = $mdb2->prepare($q, null, MDB2_PREPARE_RESULT);

// execute the prepared statement with our people
$john_results = $statement->execute($john);
$bill_results = $statement->execute($bill);

That third parameter to prepare() can be either MDB2_PREPARE_RESULT or MDB2_PREPARE_MANIP. Both pretty self explanatory. The second parameter is to declare the data types that the placeholders will be. MDB2 guesses at this automatically so I won’t go into too much detail on it.

For more on MDB2, check out PEAR’s own documentation on it.

 

Comments (2)

Some General Tips

Documentation 
————————————————– 
* traditional PHP comments embedded in code that might be a little confusing (if borderline – comment!) 
* using phpDocumentor to at least thoroughly comment widely used base classes/code. use @package, @subpackage to group thing. see phpdocumentor website for more details: http://phpdoc.org/ 

Autoload 
————————————————– 
Using the new autoload functionality (http://us2.php.net/autoload), include pains are a thing of the past. Also ensures that files/classes are only included if and when needed. Remember to intelligently name classes & files. See me or the link for further information. 

OOP 
————————————————– 
Using objects and OOP design has many benefits including organization, reusability, multiplicity, and they’re easier to debug. 

Remember: Using classes != OOP 
————————————————– 
Quoted from http://www.onlamp.com/pub/a/php/2005/07/28/oo_php.html 

I myself experienced the pitfall in the beginning of my PHP enlightenment, but the light shone again and my eyes opened to the use of OO PHP. What I mean by OO PHP specifically is the use of classes, not just simple function blocks. Many new developers feel that classes are useless, cumbersome, and time-consuming. Given the unique nature of how PHP actually works, it is possible to throw functions in the code at any point on the page and start using them immediately. If this is the case, it is possible to include a page of functions at the top of the page and go to town with them. Now, while it is possible to do this–and using classes isn’t very far from it in some aspects–there are numerous benefits and advantages to actually using classes instead of simply using a list of functions. 

Testing & Code Testability 
————————————————– 
Design and code things so that they can be tested independently. Then run tests on those individual segments of code. Ideally also create a test page which runs through as many tests as possible, giving ‘pass’ or ‘fail’ notices for each test. Run the tests after new development to find bugs. 

Security 
————————————————– 
Validate as much as possible. Especially when accepting input from the browser/user. Never modify the database the database without escaping values. Never rely on register_globals (because of security and because it is now deprecated). 

Coding Standards 
————————————————– 
Following coding standards will make code easier to read and easier for code to be worked on by multiple people. See http://pear.php.net/manual/en/standards.php 

Don’t Re-Invent the Wheel – Use Pear! 
————————————————– 
Pear has a lot of very useful packages. It’s a good idea to get familiar with their offering and use the packages whenever possible. It saves dev. time. http://pear.php.net/ 

Note: MDB2 is their current database package. It’s quite useful and easy to learn. See http://www.phpied.com/db-2-mdb2/ for a quick tutorial.

Leave a Comment

Hello, World!

First entry of what will hopefully become fun little blog about PHP, CSS, web standards, and other fun stuff in the world of web development and related business. A little about myself: I am 24 and a professional PHP programmer working for a small internet startup. My first website was made about seven years ago. I like to think I have come pretty far in those seven years, although I know I have much to learn; that’s just part of the fun, though!

For the last few months I have been in a period of learning and honing my skills, specifically PHP. This is for a number of reasons.

  1. I am currently re-architecting a major part of the website/technology where I work. Because of this, I wanted to spend enough time planning and designing everything so that it is as flexible, powerful, and maintainable as possible.
  2. With an exam voucher already in my possession, I hope to become a Zend Certified Engineer in the next month or two.
  3. After a long plateau of just having enough PHP skill to work with and get by on, I have once again been bit by the PHP bug once I started to really discover the power and helpfulness of things like OOP, template systems, etc. I emphasized the “really” because I, like so many, was using classes but not to their full ability. Remember . . . classes != OOP.
So that’s a little bit about me. Next time I’ll put up a bunch of my general coding/dev tips.

Leave a Comment