Category Archives: PHP

Build-in time bombs

I’ve been refactoring and refactoring some old code, and it’s kind of odd what short-cuts (or even time bombs), you’ll find in code, which apparently wasn’t supposed to live on for years.
In a now retired CMS system, we had an issue every new year, when some kind of bug would reset all “schedules” for upcoming stories and content. No-one ever got around to fix it, as the system was soon to be decommissioned – but sadly the bug did survive a few years anyway.

These days, I’m was working in the system, which came to replace the broken system from before. Here’s another odd thing – It was apparently hard coded into the editor in the backend, that stories need to have a “published date” between 1996 and 2011. As there really isn’t any documentation and the original developer has left the company, it’s hard to know why it was made so. While there probably was a reason, it’s lost by now.

 <select name="year" size="1" style="width:55px;" title="Year">
 <?php
   for ($i = 1996; $i < 2011; $i++) {
       printf("<option value=\"%1\$d\"%2\$s>%1\$d</option>",$i,($year == $i) ? " selected" : "");
   }
   ?>
</select>

We fixed it by making the latest “published date” go between 1996 and current year plus one.

While it was an odd thing, I’m surely glad that we from time to time do some sort of maintenance of old code – even while it seems to work perfectly. It’s far from the only bad thing we found, and while most people worry mainly about new code, sometimes you should also remember the old stuff.

PHP Dynamic Caching with ZendPlatform

I recently had a little fun playing with the dynamic cache available in the Zend Platform. The Zend Platform is a commercial product, which provides a number of cool professional features to a PHP setup – one of these is the option to do dynamic caching.

With dynamic caching you can cache the output of a function for a determined period and instead of doing database queries or web service calls for a feature, you can cache the results, save resources and get faster pages.

If you have access to a PHP setup with Zend Platform, but haven’t looked at dynamic page caching, here’s a little example to get you inspired and started.

Dynamic Caching Example

Let’s suppose you have a busy news website and publish lists of news from categories – on the front page, along side news articles and several other places. to retrieve the most recent articles for each category, we’ve created a class with a function to retrieve the five most recent stories from a category specified by a parameter.

Our first version may look something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class NewsCategory
{
        public function getCurrent($category)
        {
                $newsLines = Array();
		if (intval($category)) {
                $sql = sprintf('SELECT * FROM news WHERE category="%n"
                                     ORDER BY date LIMIT 5', $category);
                /* Asume connection to dbserver exists and is valid */
                $res  = mysql_query('newsdb', $sql);
                $data = mysql_fetch_array($res);
                $newsLines = Array();
                while ($newsItem = mysql_fetch_array($res)) {
                        array_push($newsLines, $newsItem);
                }
        }
        return $newsLines;
        }
}

Since news doesn’t happen too often and the lists of news are displayed on many pages, we want to cache the results for an hour – and we want to make sure, that the site works, even if we for some reason decide to disable the Zend Platform.

Rewriting the class to utilize the caching API takes a few changes and the Dynamic Cache-enabled version looks like this:

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
42
43
44
45
46
47
48
49
/*
 * SAMPLE CODE; NOT FOR PRODUCTION USE
 */
class NewsCategory
{
        const CACHE_TIME = 3600; // caching time in seconds. Global caching set to 1 hour.
        const CACHE_KEY  = 'NewsCategory_getCurrent_'; // prefix for caching key
 
        /**
         * Returns array with current newsitems from the specified category.
         *
         * @param integer $category
         * @return array with news items
         */
        public function getCurrent($category)
        {
                if (function_exists('output_cache_fetch')) { // caching available?
                        return unserialize(output_cache_fetch(
                                self::CACHE_KEY . $category,
                                "serialize(self::fetchCurrent($category))",
                                self::CACHE_TIME));
                } else { // No ZendPlatform Available
                        return self::fetchCurrent($category);
                }
        }
 
        /**
         * Internal function for looking up the current news identified by $category.
         *
         * @param integer $category
         * @return array - returns empty array if no news is found.
         */
        protected function fetchCurrent($category)
        {
                $newsLines = Array();
		if (intval($category)) {
                $sql = sprintf('SELECT * FROM news WHERE category="%n"
                                     ORDER BY date LIMIT 5', $category);
                /* Asume connection to dbserver exists and is valid */
                $res  = mysql_query('newsdb', $sql);
                $data = mysql_fetch_array($res);
                $newsLines = Array();
                while ($newsItem = mysql_fetch_array($res)) {
                        array_push($newsLines, $newsItem);
                }
        }
        return $newsLines;
        }
}

The changes are:

  • The public function from the first version is replaced by a method, that handles the caching. The function doing the work is now hidden in a protected method in the class.
  • If the caching API isn’t available, the new function just acts a proxy passing data through.
  • The new caching functions creates caching keys from the method name and the parameter to make them predictable and avoid namespace clashes.
  • do notice that the cached results are serialized in the cache – the cache can only store string values.

Before going crazy with Dynamic Caching do consider a few things:

  • Is the data suited for caching? (or do you need the most accurate data every time)
  • What level do you expect the cache ratio to be at? (how often is the cache utilized)
  • Are other caching mechanisms in place? (ie. proxy-servers or likewise)

Loading data from a file with PHP

In programming languages common tasks should be easy, and surprisingly often I find my self loading data from some source file into a database. With PHP loading a CSV file into the database – or posting the data to an API – is quite easy.

The only trick is to know the functions file_get_contents, split (and possibly list). The routine goes something like this:

1
2
3
4
5
6
7
8
9
10
$fileName = 'rawData.csv';
 
$content = file_get_contents($fileName);
$rows = split("\n", $content );
 
foreach ($rows as $row) {
	list($item1, $item2, item3) = split(';', $row);
 
	// Do something interesting...
}

The script gets the contents of file into a variable ($contents). Split the contents into an array ($rows) and the forach loop across each row. The first line of the loop splits the row into three fictitious items and the rest of the loop could do something interesting to the data read from the file.

I usually run scripts like these from the command-line (yes, you can do that with PHP scripts).

PHP: Removing an item from an array

If you have a php array, but need to nuke an item from it, the unset function is just the tool to do that. When iterating through the array after removing the item, you should be slightly careful.

The unset function does remove the item, but it doesn’t “reindex” the array, so you should traverse the array by index-numbers after removing the item. The issue is probably best illustrated by this example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$myArray = Array('item 1', 'item 2', 'item 3');
 
// remove item
var_dump($myArray);
 
unset($myArray[1]);
var_dump($myArray);
 
// Failing bad loop
for ($loop = 0; $loop < count($myArray); $loop++) {
        echo $myArray[$loop] . "\\n";
}
 
// Good loop
foreach ($myArray as $item) {
        echo $item . "\\n";
}

You can dowload the above example code here.

PHP Coding Standards

If you’re part of a larger team developing (and sharing) code, it is a pretty good idea to have some common standards. In the PHP work, such a set of common standards include the PEAR Coding Standards. While it may be a challenge to agree on standards, it can be done – the hard part is following the standards.

One excellent tool to help you check for standards compliance is the PHP CodeSniffer. We’ve had it running for quite awhile and it seems to work as advertised. We’ve made our own (“TDC”) coding standards which is a variation over the PEAR coding standards, and checking if you follow them is as simple as running a simple command line.While it doesn’t enforce following of the coding standards, it does make it a no-brainer to check if you do follow the common standards.

The output from the codesniffer could look something like this:

mahler@mahler$ phpcs --standard=TDC Example.php

FILE: Example.php
--------------------------------------------------------------------------------
FOUND 44 ERROR(S) AND 2 WARNING(S) AFFECTING 38 LINE(S)
--------------------------------------------------------------------------------
[LINE 2] ERROR: "require_once" is a statement, not a function. No parentheses are required.
[LINE 5] ERROR: "require_once" is a statement, not a function. No parentheses are required.
[LINE 7] ERROR: Space before opening parenthesis of function call prohibited
[LINE 9] ERROR: Space before opening parenthesis of function call prohibited
[LINE 20] ERROR: Line indented incorrectly. Expected 0 tabs but found 1.
[LINE 49] ERROR: Line indented incorrectly. Expected 1 tabs but found 2.
[LINE 51] WARNING: Equals sign not aligned correctly. Expected 1 space, but found 2 spaces.
[LINE 59] ERROR: Space after opening parenthesis of function call prohibited
[LINE 61] ERROR: Line indented incorrectly. Expected 2 tabs but found 3.
[LINE 61] ERROR: Expected "while (...) {\n" found "while(...){\n".
[LINE 63] WARNING: Equals sign not aligned with surrounding assignments. Expected 3 spaces, but found 1 space.
[LINE 74] ERROR: Line indented incorrectly. Expected 1 tabs but found 2.
...

Syntax checking PHP on the commandline

I’m sure most people only thing of PHP as a Weblanguage due to be called through a browser. It has however since version 4.3.0 also been possible to use PHP on the commandline – as you do with Perl, Shell scripts and likewise. If you’re using Linux (or an other Unix-like operating system – including Mac OSX) you probably have a few small programs available which can make it a breeze to check if the syntax in all you PHP scripts is correct.

Here’s how. On the commandline type:

find *.php |xargs -I {} php -l {}

… and here’s an explaination of what it does:

  • “find *.php” findes all files with a dot php ending.
  • the pipe-character “|” sends the result to a command called xargs.
  • “xargs -I {} php -l {}” takes the lines one by one password from the find and call “php -l <line input>”.
  • “php -l <line input>” (where line input would be the php-files found in bullit one) runs php with the “lint” parameter which does the syntax checking.

Image sizes and PHP

If you have the GD extension available, you can do a few useful things. One of the simple things is getting an image size is a simple matter. You can either fetch the height and width in seperate variables – or even fetch the height and width in a string pre-formatted for use in an image tag.

Here’s the example in source:

1
2
3
4
5
6
7
8
9
10
11
12
$load_ext = get_loaded_extensions();
 
if (!in_array(gd, $load_ext)) {
echo "GD is NOT available";
die();
}
 
$nikonFile   = './aarhus_demo_photo.jpg';
list($width, $height, $type, $img_txt)  = getimagesize($nikonFile);
 
echo "The image is ".$width . " by ".$height."\n";
echo "<img $img_txt src='$nikonFile'/>\n";

And here’s how it looks (source).

Reading Exif data with PHP

Within most photos from digital cameras besides the actual image, there’s a little ”information block” call EXIF data. If you have the correct PHP extension installed on your server – the one called ”exif” – it’s pretty easy to read the EXIF data and display them, as you like.

First, let’s check if the extension is available?

1
2
3
4
5
6
$load_ext = get_loaded_extensions();
if (!in_array(exif, $load_ext)) {
echo "Exif is NOT available";
} else {
echo "Exif extension is available.";
};

If you have the extension available, reading the data is a simple matter:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$nikonFile   = './aarhus_demo_photo.jpg';
$exifNikon   = getExifData($nikonFile);
 
$olympusFile = './zanzibar_demo_photo.jpg';
$exifOlympus = getExifData($olympusFile);
 
echo "&lt;h2&gt;Olympus EXIF data&lt;/h2&gt;";
echo "&lt;pre&gt;";
print_r($exifOlympus);
echo "&lt;/pre&gt;";
 
echo "&lt;h2&gt;Nikon EXIF data&lt;/h2&gt;";
echo "&lt;pre&gt;";
print_r($exifNikon);
echo "&lt;/pre&gt;";
 
function getExifData($file) {
$exif = exif_read_data($file, 0, true);
return $exif;
}

As you may notice, there’s a photo from an Olympus camera and a Nikon camera as an example. The data available from each photo isn’t quite the same. Most generic fields are available, but some fields are camera specific (or maker specific).

See the example above in action (source). The two images are from Aarhus (Nikon) and Zanzibar (Olympus). You can read more on EXIF in Wikipedia.