Late static binding
PHP is one step closer to being an OOM language
One of the best features of php 5.3, better even than namespaces, is Late Static Binding.
Late whatnow? Depending on how you look at it, late static binding is either a wonderful new addition to php, or an ugly fix to one of its older faults.
Depending on how you look at it, it might also be one those buzzwords you heard floating around but never bothered to investigate: that was me anyway.
So what does it do? Well here's what the manual says, but let's do an example instead.
Writing extensible code
Say you want to abstract away a common task: writing a user class. The class has a constructor, some get and set methods for username, password & id and two factory methods: A fetch($id) method that retrieves a user from the database and a create(...) method that creates a whole new user and inserts it into the database*.
You might not agree with this approach, perhaps you'll want to use the constructor to fetch and/or create - and that's fine - but bear with me a little, that's not what this example's all about.
Any other methods, say getFaxNumber() or maybe getPlayerId() will be implemented only in projects that require such methods and so they can be left out of the generic class. Here's what the first half of the User class could look like
<?
/**
* The once-in-a-lifetime generic but simple User class
*/
class GenericUser
{
/**
* Creates a new GenericUser
*/
protected function __construct(array $data)
{
// Set all the little settings
}
// ... get and set methods go here
/**
* Fetches a user from the database
* @param $id int A unique identifier for this user
*/
public static function fetch($id)
{
$data = fetch_from_database($id);
return new GenericUser($data); // wrong!
}
}
?>
Now let's extend it with a project specific user class.
<?
/**
* A User class for a specific project
*/
class ProjectSpecificUser extends GenericUser
{
public function __construct(array $data)
{
parent::__construct($data);
$this->faxNumber = '12345678';
}
public function getFaxNumber()
{
return $this->faxNumber;
}
}
?>
Still looks good right? But now let's see what happens:
<?
// Fetch user number 1
$user = ProjectSpecificUser::fetch(1);
// And show us what you got
echo get_class($user);
?>
Unfortunately the output of this code will reveal the returned object to be of the type GenericUser.
Looking a the code, this might be forgiven, after all, the fetch() method specifically calls for the creation of a GenericUser object. So fair enough, but what happens if we use the self keyword instead?
<?
public static function fetch($id)
{
$data = fetch_from_database($id);
return new self($data); // still wrong!
}
?>
Unfortunately this has exactly the same result: the self keyword appears inside the code for GenericUser and so php decides it refers to the GenericUser class, ignoring any rules of inheritance.
Enter the static keyword
Up until php 5.3 this was all you could do, but luckily the situation has been remedied in a backwards compatible - and slightly ugly - fashion: by adding another function to the existing static keyword. So our method becomes:
<?
// ...
public static function fetch($id)
{
$data = fetch_from_database($id);
return new static($data); // Hurray!
}
}
// Let's try again!
$user = ProjectSpecificUser::fetch(1);
echo get_class($user);
?>
Returns
ProjectSpecificUser
And all is well in the world.
Even better
A second use is to call static methods - but allow overwriting. For example, imagine we want to add a static login() method to our user class that takes a username and password as input parameters and returns a User object if the login is succesful. The way the password is checked can change from project to project and so it would be a good idea to make this easy to overwrite. Using the static keyword this can be accomplished as follows:
<?
/**
* The once-in-a-lifetime generic but simple User class
*/
class GenericUser
{
/**
* Creates a new GenericUser
*/
protected function __construct(array $data)
{
// Set all the little settings
}
// ... get and set methods go here
/**
* Fetches a user from the database
* @param $id int A unique identifier for this user
*/
public static function fetch($id)
{
$data = fetch_from_database($id);
return new static($data);
}
/**
* Logs a user in
* @param $username
* @param $password
* @return a new User object or null if the login failed
*/
public static function login($username, $password)
{
// Check password
if (static::checkPassword($username, $password))
{
// Fetch id from database
$id = get_id_by_username($username);
// Return new user
return static::fetch($id);
}
return null;
}
/**
* Checks a user / password combination
* @return True if valid
*/
protected static function checkPassword($username, $password)
{
return ($username == 'michael' && $password == 'superSecretPassword');
}
}
?>
Now any extending class with a more advanced checkPassword() routine can overwrite the checkPassword method but still use the old login() method.
<?
class ProjectSpecificUser extends GenericUser
{
protected static function checkPassword($username, $password)
{
return ($username == 'michael' && $password == 'veryVerySecretPassword');
}
}
// This user should no longer be allowed in
$user = ProjectSpecificUser::login('michael', 'superSecretPassword');
echo is_object($user) ? 'True' : 'False';
echo '<br />';
// This one should
$user = ProjectSpecificUser::login('michael', 'veryVerySecretPassword');
echo is_object($user) ? 'True' : 'False';
?>
Returns:
False
True
And so - as long as you remember to use static in all the right places - it has suddenly become possible to write much more extensible classes in php.
Jul 18th, 2010
Comments
No comments yet! Feel free to post some using the form below.
If you wish to add code to your comment you can use code tags, like this: <code class="php">yourCodeHere</code>.
Quite a large number of languages are supported, although I can't guarantee it'll be pretty. Inside the code tags you can use any characters except for the string "</code>".