Calculating Age From Date of Birth In PHP

I imagine it’s a fairly common task to calculate an age in years from a date of birth. However most of the solutions I’ve seen either seem to be unnecessarily complicated or else fail to take in into account leap years or the current month and date as well as year. The following function is very fast and pretty simple.

1
2
3
4
5
6
7
8
9
10
11
12
13
function age_from_dob($dob) {

    list($d,$m,$y) = explode('-', $dob);
   
    if (($m = (date('m') - $m)) < 0) {
        $y++;
    } elseif ($m == 0 && date('d') - $d < 0) {
        $y++;
    }
   
    return date('Y') - $y;
   
}

It expects a date of birth in the format “DD-MM-YYYY”, however this can be modified by changing the order of the variables in the list function, for example to change to “MM-DD-YYYY” you would just edit like so:

1
list($m,$d,$y) = explode('-', $dob);

One note to bear in mind – it is tempting to accept a string in any valid format and parse it with strtotime. This will work, but because it relies upon a Unix timestamp, it will not work for dates before 1 January 1970. If this is not an issue for you, the following function can be used instead, and saves having to massage dates into the required format beforehand:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function age_from_dob($dob) {

    $dob = strtotime($dob);
    $y = date('Y', $dob);
   
    if (($m = (date('m') - date('m', $dob))) < 0) {
        $y++;
    } elseif ($m == 0 && date('d') - date('d', $dob) < 0) {
        $y++;
    }
   
    return date('Y') - $y;
   
}

Remove Button Focus Outline Using CSS

Introduction

Alot of people will use the outline CSS property to get rid of the focus outline around links using something like this:

1
2
3
* {
    outline: none;
}

However one thing you may have noticed if you’ve ever tried this is that it has no effect on the focus border of buttons:

Button with a focus outline

It is possible to remove (or style) this border, although be aware that it only works in Gecko based browsers (As far as I know – it’s certainly possible webkit has something similar now. Don’t hold your breath for IE though!).

The Method

The way to achieve this is to use the propriety mozilla pseudo-class ::-moz-focus-inner. This targets the focus outline and allows it to be styled. Note that it is the border property not the outline property which handles the style of the outline itself.

1
2
3
4
5
6
7
button::-moz-focus-inner,
input[type="reset"]::-moz-focus-inner,
input[type="button"]::-moz-focus-inner,
input[type="submit"]::-moz-focus-inner,
input[type="file"] > input[type="button"]::-moz-focus-inner {
    border: none;
}

The ugly black border is banished:

The same button with the CSS applied

To style the border rather than remove it completely, a more accessible solution, you can use more standard pseudo-classes such as :focus and apply some normal CSS rules:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
button::-moz-focus-inner,
input[type="reset"]::-moz-focus-inner,
input[type="button"]::-moz-focus-inner,
input[type="submit"]::-moz-focus-inner,
input[type="file"] > input[type="button"]::-moz-focus-inner {
    border: 1px dotted transparent;
}

button:focus::-moz-focus-inner,
input[type="reset"]:focus::-moz-focus-inner,
input[type="button"]:focus::-moz-focus-inner,
input[type="submit"]:focus::-moz-focus-inner,
input[type="file"] > input[type="button"]:focus::-moz-focus-inner {
    padding: 3px;
    border-color: #F3F3F3;
}

A Note on Accessibility

I’m going to end this post with a word about accessibility. The technique described here is not very friendly to people using a keyboard to navigate the site either by choice or due to a disability. Personally I believe that accessibility and design need to work together as much as possible, and this is not something I would use on a public site. However I believe that in some situations, it is perfectly acceptable to sacrifice accessibility over design.

At my place of work for example, I develop and maintain a CMS which I am in the process of redeveloping/redesigning. I also have control over the user’s computers and the software on them, including the browser. Because I know the audience of the site, and know what browser they will be using as well as the fact that they will not be adversly affected by this, I made the design decision to use this technique. If you don’t know your audience this well I’d urge you to consider taking the hit on the design for the sake of users who might be affected by the lack of button focus indication.


counterCache and deleteAll: How to Make Them Friends

counterCache is one example of why I like CakePHP so much. It’s a little detail which puts a smile on my face. In case you haven’t come across it before, it is (as it’s name suggests), a cached count of the models which belong to another model. For example, applied to the scenario where a Group hasMany Users, counterCache can be used to keep track of the number of users belonging to each group without having to use lots of User->count() calls.

This works by having a field in the ‘parent’ model’s table which stores the count. Every time a child is added to or removed from the parent, this field is incremented or decremented accordingly. You can even set a scope for updating this value using counterScope. For example, you may want to only account for Users which have their deleted field set to 0.

I won’t go in to how to set this up because it is all explained in the book, and is not what this article is about. Suffice to say it all works very well. Until you come to use deleteAll() that is. deleteAll (again as it’s name suggests) takes a condition string, and deletes all records which match it. What it doesn’t do however is update the counterCaches as it goes. This can leave you with out of synch counts in the database.

One reaction to this might be to just call Model->del() in a loop – but this seems quite cumbersome and inefficient to me. Luckily however there is a method of the Model class called updateCounterCache which can fix everything. It is pretty simple to use, but the description in the API of the parameters is a little confusing so hopefully I can clarify with an example.

Going back to the example of Users and Groups, let’s assume I’ve just run deleteAll() and got rid of a few Users from Group 3. Running the following will keep the counter in the Groups table correct.

1
$this->User->updateCounterCache(array('group_id' => 3));

You can also update multiple counts in different parent models too. Assuming you want to update the count of all Users in Group 3, and all Users in Country 28:

1
2
3
4
$this->User->updateCounterCache(array(
    'group_id' => 3,
    'country_id' => 28
));

Update Multiple Rows With Different Values and a Single SQL Query

Sometimes you may need to update multiple rows of data in a table. This is no problem a lot of the time, as the fields to update may all need to be changed to the same value, in which case you might run a query like the following.

1
UPDATE mytable SET myfield = 'value' WHERE other_field = 'other_value';

But what about if you want to update multiple rows but you need the field set to a different value for each row? For example, maybe you have a table of categories with a field to store their display order. How would you update the display order field for each category when the order changes? Most often you will see people just run multiple queries. For example, if you are using PHP you might think to do something like this:

1
2
3
4
foreach ($display_order as $id => $ordinal) {
    $sql = "UPDATE categories SET display_order = $ordinal WHERE id = $id";
    mysql_query($sql);
}

Of course this will work fine, but queries in loops make me start to sweat nervously. It’s all to easy to start hammering the database with a crazy number of queries. Fortunately there is a better way! The SQL is a little more complex, but it is possible to achieve the same result using only one query. The syntax is as follows.

1
2
3
4
5
6
7
UPDATE mytable
    SET myfield = CASE other_field
        WHEN 1 THEN 'value'
        WHEN 2 THEN 'value'
        WHEN 3 THEN 'value'
    END
WHERE id IN (1,2,3)

Mapping this to the categories example, we get the following query.

1
2
3
4
5
6
7
UPDATE categories
    SET display_order = CASE id
        WHEN 1 THEN 3
        WHEN 2 THEN 4
        WHEN 3 THEN 5
    END
WHERE id IN (1,2,3)

This is fairly simple to understand. Rather than setting a field to a particular value, a CASE operator is used to determine which of a set of values is used based on a given condition, in this case if the value of the id field matches the specified id.

The WHERE clause is not needed as such in that it doesn’t affect the logic of the query, but it does improve performance by ensuring that the logic is only applied to the smallest number of rows possible. In this example only 3 rows are being updated, and the WHERE clause ensures that only those 3 rows are tested. Without it the every row in the table would be tested (unnecessarily, since they will never match).

What about when you need to update multiple fields? This is easily done just by adding another CASE block.

1
2
3
4
5
6
7
8
9
10
11
12
UPDATE categories
    SET display_order = CASE id
        WHEN 1 THEN 3
        WHEN 2 THEN 4
        WHEN 3 THEN 5
    END,
    title = CASE id
        WHEN 1 THEN 'New Title 1'
        WHEN 2 THEN 'New Title 2'
        WHEN 3 THEN 'New Title 3'
    END
WHERE id IN (1,2,3)

Now this is all well and good, but the real beauty of this comes when the technique is combined with a scripting language such as PHP in order to build these queries dynamically. Let’s examine one technique for doing this, using the category ordering example.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// An array containing the category ids as keys and the new positions as values
$display_order = array(
    1 => 4,
    2 => 1,
    3 => 2,
    4 => 3,
    5 => 9,
    6 => 5,
    7 => 8,
    8 => 9
);

$ids = implode(',', array_keys($display_order));
$sql = "UPDATE categories SET display_order = CASE id ";
foreach ($display_order as $id => $ordinal) {
    $sql .= sprintf("WHEN %d THEN %d ", $id, $ordinal);
}
$sql .= "END WHERE id IN ($ids)";
echo $sql;

Now in this particular example, only 8 rows are being updated, but 7 queries have been trimed, which is not insignificant. Apply this technique to a situation where hundreds or thousands of rows have to be updated and you can imagine the benefits it will have.


Fixing Broken Flash Player After Upgrading From Ubuntu Hardy 8.10 to Jaunty 9.04

I recently upgraded from Ubunty Hardy 8.10 to Jaunty 9.04 and found that my flash player no longer worked. It was a very simple fix, but I’ll post the process here in case it helps someone with the same problem.

First you need to uninstall the existing flashplayer:

1
$ sudo apt-get remove flashplugin-* --purge

Then it’s just a case of reinstalling the latest version (which also fixes a serious security bug):

1
$ sudo apt-get install flashplugin-nonfree

Restart your browser and it should all be working.


PHP Database Abstraction Layer

This article will demonstrate how to create a database abstraction layer using Interfaces in PHP5.

Introduction

An abstraction layer is basically a way of hiding the methods used to implement some kind of functionality. To get an idea of why this is so useful, imagine you have just spent months working on a project for a site that uses a MySQL database. Now you find that you suddenly cannot use MySQL anymore but instead you need to use SQL Server. This is suddenly a major problem! You now need to go through months of work and change every mysql-specific PHP function such as mysql_connect() before your code will work.

This situation is avoided through the use of an abstraction layer.

Code Breakdown

The first part of the abstraction layer is a database object interface. To quote the PHP manual, an object interface “allows you to create code which specifies which methods a class must implement, without having to define how these methods are handled”. In other words, we can set some rules about what classes must be able to do, but without having to worry about how it is done. For example, it is possible to specify that database classes must all allow us to connect to a database through a connect() method, and that they must all allow us to execute a query via a query() method, but the actual details about how this functionality is implemented are not important.

This is the code for the database object interface:

1
2
3
4
5
6
7
8
9
10
11
12
13
interface DB {
    public function connect();
    public function error();
    public function errno();
    public static function escape_string($string);
    public function query($query);
    public function fetch_array($result);
    public function fetch_row($result);
    public function fetch_assoc($result);
    public function fetch_object($result);
    public function num_rows($result);
    public function close();
}

Because an interface is a set of rules for any class which implements it, any classes implementing this interface must have a connect() method, an error() method, an errno() method etc. We have also specified where arguments must be accepted by the methods, for example the fetch_assoc() method must accept a $result argument.

Be aware that the classes implementing this interface can have more methods than are specified here, but it must have at least these methods. This means that we can be confident that any database class we choose to use will have a connect() method for example.

The next part of the abstraction layer is a base class which all individual, database-specific classes will extend. This allows certain properties to be specified which are common to all databases without having to specify them within each individual class.

1
2
3
4
5
6
7
8
9
10
class DBBase {
    public $last_sql;
    public $encryption = "md5";
    protected $host = "localhost";
    protected $port = 80;
    protected $user = "root";
    protected $pass = "password";
    protected $dbname  = "mydb";
    protected $link;
}

This class has no methods at all, but just a set of properties. Note that instead of using private to declare the non-public properties, we need to use protected. The difference between private and protected is that private properties can only be accessed by the class to which they belong, whereas protected properties can be accessed from the class to which they belong, and any children of that class (but nowhere else).

The actual properties themselves are common properties which will be used no matter which database class is chosen. $last_sql for example will be used to hold the last executed query, regardless of whether that query performed on a MySQL database, SQL Server database, PostGreSQL database or anything else.

The final part of the puzzle is the child class that implements the DB interface and extends the DBBase class. This is where you would write the database-specific code. In this example I will show you a MySQL implementation.

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
50
51
52
53
54
55
56
57
58
59
60
61
62
class MySqlDB extends DBBase implements DB {
    private $new_link = true;
    private $client_flags = 0;
   
    public function __construct() {
        $this->connect();
        $this->select_db($this->dbname);
    }
   
    public function __destruct() {
        $this->close();
    }
   
    public function connect() {
        $this->link = mysql_connect($this->host, $this->user, $this->pass, $this->new_link, $this->client_flags);
    }

    public function errno() {
        return mysql_errno($this->link);
    }

    public function error() {
        return mysql_error($this->link);
    }

    public static function escape_string($string) {
        return mysql_real_escape_string($string);
    }

    public function query($query) {
        $this->last_sql = $query;
        return mysql_query($query, $this->link);
    }
   
    public function fetch_array($result, $array_type = MYSQL_BOTH) {
        return mysql_fetch_array($result, $array_type);
    }

    public function fetch_row($result) {
        return mysql_fetch_row($result);
    }
   
    public function fetch_assoc($result) {
        return mysql_fetch_assoc($result);
    }
   
    public function fetch_object($result)  {
        return mysql_fetch_object($result);
    }
   
    public function num_rows($result) {
        return mysql_num_rows($result);
    }
   
    public function close() {
        return mysql_close($this->link);
    }
   
    public function select_db($db) {
        return mysql_select_db($db, $this->link);
    }
}

The first important thing to note here is how the class is defined:

1
class MySqlDB extends DBBase implements DB

This defines the name of the class (MySQLDB), makes it a child of DBBase (meaning it inherits all of the properties defined in DBBase) and implements the DB interface – and all in one line!

Next some properties are defined which are used just for MySQL (if they were to be used by all database classes they would be defined in DBBase). If some properties are required that only MySQL cares about, there is no sense in making them available to other databases.

After the properties, the methods which are required by the DB interface are defined. I wont go into each one, as you can see they are mostly just wrappers for PHP’s MySQL functions. However there are a couple of points to note.

1
2
3
4
public function __construct() {
    $this->connect();
    $this->select_db($this->dbname);
}

The method named __construct() is a special method name used by PHP called a constructor. When an object is created, any code in the __construct() method will be automatically run. In this case when a database object is created it will automatically connect and select the correct database.

Notice that the constuctor calls the connect() method which uses class properties such as host, user, pass etc to actually connect to the database. Remember these properties are defined in the class’ parent (DBBase) so they are available here. This means if that if the password to the database is changed for example, it only has to be changed in one place, and the code will automatically work with whichever database class is currently in use.

1
2
3
public function __destruct() {
    $this->close();
}

Also worth mentioning is __destruct(). Again this is a build in PHP method name which is run when an object is destroyed (either explicitly or at the end of script execution). This is an ideal oppertunity to close the database connection.

1
2
3
4
public function query($query) {
    $this->last_sql = $query;
    return mysql_query($query, $this->link);
}

Also note that the query() method sets the last_sql property. Again this property is available because it is a property of the parent class.

Finally note that the interface does not specify a select_db() method. Remember that it is permissable to have more methods in the class than are specified in the interface, but not less. If it is likely that there might be a need to use select_db in the main code somewhere, it would be wise to include it in the interface so that the code does not break if select_db is called on an object which does not have that method defined.

Useage

First an instance of the class is created in the usual way:

1
$db = new MySQLDB;

Despite the fact that interface implentation and class extension needs to take place, nothing special has to be done to create an object – the parent class and the interface are handled ‘behind the scenes’. We just tell PHP that we want a MySQL database object and it takes care of the rest.

If there was a need to change to a different database provider, this is the only line of code which would have to be changed. For example, it might be changed to:

1
$db = new SQLServerDB;

This is all that is needed in order to run an SQL server back end (although a corresponding SQL server class is required of course).

Now everywhere that database functionality is required, the generic methods such as connect(), fetch_assoc() etc instead of database-specific alternatives such as mysql_connect() and mysql_fetch_assoc() will always work, and will never need to be updated or replaced in the future.

1
2
3
4
5
$result = $db->query("SELECT * FROM users WHERE userid = 1 LIMIT 1");
while ($row = $db->fetch_assoc($result)) {
    extract($row);
    // etc
}

Google Search Position Checker

I was recently asked if there was a simple way to check the position a particular page comes in a google search result for a given set of keywords – and without doing a manual search for each keyword and counting down from the top! Luckily Google’s AJAX API along with a bit of PHP can be used for this, so I wrote a small class to do the job.

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
<?php

class GPositionFinder {
   
    private $_keyword;
    private $_page;
    private $_offset = 0;
    private $_apiUrl = "http://ajax.googleapis.com/ajax/services/search/web?v=1.0&hl=en&rsz=large&q=%s";
    private $_referer = "http://www.karlrixon.co.uk";
    private $_results;
    private $_position = 0;
    private $_output;
    private $_outputMethod = 'text';
   
    public function __construct($keyword=null, $page=null) {
        $this->setKeyword($keyword);
        $this->setPage($page);
    }
   
    public function setReferer($value) {
        if ($value) {
            $this->_referer = $value;
            return true;
        }
        return false;
    }
   
    public function setKeyword($value) {
        if ($value) {
            $this->_keyword = urlencode($value);
            return true;
        }
        return false;
    }
   
    public function setPage($value) {
        if ($value) {
            $this->_page = basename($value);
            return true;
        }
        return false;
    }
   
    public function setOutputMethod($value) {
        switch ($value) {
            case 'json':
                $this->_outputMethod = 'json';
                break;
            case 'text':
                $this->_outputMethod = 'text';
                break;
            default:
                return false;
        }
        return true;
    }
   
    public function getPosition() {
        return $this->_position;
    }
   
    public function getOutput() {
        return $this->_output;
    }
   
    public function go() {
        if (!$this->_setApiUrl()) {
            trigger_error('You must set a valid keyword before calling go().', E_USER_ERROR);
        }
        if (!$this->_page) {
            trigger_error('You must set a valid page to check before calling go().', E_USER_ERROR);
        }
        while(!$this->_position && $this->_offset < 40) {
            $this->_getNextResultSet();
            if (empty($this->_results) || !is_object($this->_results)) {
                break;
            }
            $this->_checkPosition();
        }
   
        $output = null;
        switch ($this->_outputMethod) {
            case 'json':
                $output = json_encode(array(
                    'position' => $this->_position,
                    'keyword' => urldecode($this->_keyword),
                    'page' => $this->_page
                ));
                break;
            case 'text':
                $output = "position={$this->_position}&keyword=" . rawurldecode($this->_keyword) .
                          "&page={$this->_page}";
            break;
        }
        return $this->_output = $output;
    }
   
    private function _setApiUrl($url=null) {
        if (!$this->_keyword) {
            return false;
        }
        $this->_apiUrl = sprintf($this->_apiUrl, $this->_keyword);
        return true;
    }
   
    private function _getNextResultSet() {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $this->_apiUrl . "&start={$this->_offset}");
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_REFERER, $this->_referer);
        $body = curl_exec($ch);
        curl_close($ch);
        $this->_results = json_decode($body);
    }
   
    private function _checkPosition() {
        foreach ($this->_results->responseData->results as $key => $result) {
            if (preg_match("#^{$this->_referer}.*{$this->_page}$#", $result->unescapedUrl)) {
                $this->_position = $this->_offset + 1;
                return;
            } else {
                $this->_offset++;
            }
        }
    }
   
}

?>

Usage

Usage is pretty simple. You either need to edit the default value of the $_referer property, or else you can use the setReferer() method. It is important to correctly set the referrer to your own site. Not only does Google require this, the class also uses this value to differentiate between your about.php and someone else’s about.php when examining results.

1
2
3
$posFinder = new GPositionFinder('my keyword phrase', 'mypage.htm');
$posFinder->setReferer('http://www.mysite.com');
echo $posFinder->go();

As you can see from the previous example, keyword and page can be passed in to the constructor when an instance of the class is created, but they can also be set later using the setKeyword() and setPage() methods:

1
2
3
4
$posFinder = new GPositionFinder;
$posFinder->setKeyword('my keyword phrase');
$posFinder->setPage('mypage.htm');
echo $posFinder->go();

If you are going to use this method though, be sure to set both a keyword and page before calling go() or else you will get a fatal error.

The class can output results in a few different ways. By default it will output a string deleimited in the same way as a URL query string, containing the position of the page, the keyword used and the page used. These last two components may be useful in AJAX implementations as they save you having to keep track of these values on the client side. Here’s an example of the default output:

1
2
3
$posFinder = new GPositionFinder('keyword', 'mypage.htm');
echo $posFinder->go();
// outputs position=1&keyword=keyword&page=mypage.htm

Another option is to have the output returned as a JSON encoded string:

1
2
3
4
$posFinder = new GPositionFinder('keyword', 'mypage.htm');
$posFinder->setOutputMethod('json');
echo $posFinder->go();
// outputs {"position":1,"keyword":"keyword","page":"mypage.htm"}

As you can see the go() method returns the output itself, but if you can also access the output at any time via the getOutput() method.

1
2
3
4
$posFinder = new GPositionFinder('keyword', 'mypage.htm');
$posFinder->go();
// some other code
echo $posFinder->getOutput();

You may also want to access the position you achieved directly without using an output string:

1
2
3
4
$posFinder = new GPositionFinder('keyword', 'mypage.htm');
$posFinder->go();
echo $posFinder->getPosition();
// outputs 1 (hopefully!)

Limitations

The class will only examine the top 40 results. I believe Google limits the number of results to 64, and for my own purposes, anything not in the top 40 results (the top 4 pages of a default google search) is not really worth worrying about anyway. You can of course extend this, but be sure to keep it less than the Google imposed limit or else the class may break (I haven’t tried it).

Another thing to bear in mind is that this class will only search google.com. This is due to the nature of the API, and as far as I know, nothing can be done to search local Google sites. If anyone knows differently please let me know as google.co.uk results would be useful to me!

Another limitation to consider is that this class will only work in PHP >= 5.2.0. This is due to the need for the json_decode function as the API returns results in JSON format. It could be rewritten to work on earlier versions of PHP4 using the PEAR Services_JSON package, and defining the following functions at the top of the class file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
include("JSON.php");

if( !function_exists('json_encode') ) {
    function json_encode($data) {
        $json = new Services_JSON();
        return $json->encode($data);
    }
}

if( !function_exists('json_decode') ) {
    function json_decode($data) {
        $json = new Services_JSON();
        return $json->decode($data);
    }
}

This will allow the code to function, and if the server is ever upgraded the PEAR package will automatically become redundant in favour of the native PHP versions of json_encode and json_decode.

Finally bear in mind this class was written to solve a specific need I had. It may not be ideal for you, and it may be lacking features you like. If you make any improvements to it (or find any bugs!) please let me know.