Implementing Dynamic, Read-only Object Properties In PHP
I was recently working on a PHP project where i needed (or would of liked) an object to have publicly available properties which were read-only. Unfortunately PHP doesn't support this and the usual way of implementing something similar was to have private properties and standard getter methods. The trouble with getter methods is that sometimes they seem a little like overkill, especially if you're only accessing a name or an age, etc. Do you really need a method call to return a simple string or number that is always the same? Plus, in my opinion, they are harder to read than properties, although it could be argued only slightly.
But, take a look at these two lines of code:
$User->Name $User->GetName();
To me the first one is much more meaningful and better reflects what it represents, not only that but if this is concatenated within another string the readability of its simplicity increases.
Magic Methods
So in order to simulate properties that are read-only i need to use some PHP kung fu called magic methods.
So what are magic methods? Basically, they are special methods of a class that when defined are called when certain actions are performed on that class' instantiated object.1 One simple example would be, the __construct() magic method which is called whenever a class is instantiated.2 There are 14 magic methods in total as of PHP v5.3 but the two i'll be mainly using here are __get() and __set() .
The __get() method is called when you attempt to access a property of an object that is not accessible. It receives one parameter, the name of the property trying to be accessed and it returns one value, which will be treated as the value of that property.
The __set() method is called when you attempt to change the value of an object property that is not accessible. It receives two parameters, the name of the property trying to be accessed and the new value of that property.
Okay, so lets see some code. First i'll show you a simple class that provides a little framework for working with read-only properties.
class ReadOnlyProperties
{
protected $Data;
public function __get($Property)
{
if(array_key_exists($Property, $this->Data))
{
return $this->Data[$Property];
}
else
{
printf("No property named '%s'.", $Property);
exit;
}
}
public function __set($Property, $Value)
{
print("Properties are read-only.");
exit;
}
}
Here i'm using an array called $Data to hold the read-only properties instead of defining them in the class body. When code tries to access these properties and because they can't be found (in the class body) the __get() and __set() magic methods will trigger.
If you notice inside the __get() method i'm using array_key_exists() to test if the property trying to be accessed is actually within the $Data array. If it is, return its value, if it's not, display an error.
Conversely, if code tries to set a property value (which is not accessible) the __set() method is triggered and just shows an error. I show an error here because i want them all to be read-only.
To demonstrate, i'll create a simple class which inherits from the above class.
class User extends ReadOnlyProperties
{
public function __construct($Name, $Age)
{
$this->Data["Name"] = $Name;
$this->Data["Age"] = $Age;
}
}
$User = new User("Leeroy", 37);
echo $User->Name; //Displays 'Leeroy'
echo $User->Age; //Displays '37'
$User->Name = "Jenkins"; //Displays 'Properties are read-only.'
echo $User->Weight; //Displays 'No property named 'Weight'.'
Not bad eh? Because the code is trying to access properties that don't exist, i can re-route the request to the $Data array using the magic methods, very neat indeed! But wait, there's more.
Dynamic Property Loading
Say for example you have a huge class that is composed of smaller classes and that each of the smaller classes need database or file access in their constructors to initialise their objects properly. If you instantiated a huge object, all the little ones would usually be instantiated at the same time, accessing their individual resources whether you actually use them or not. What would be a better scenario is that you are able to instantiate a huge object and have each of the smaller ones instantiate themselves when actually accessed.
Using the above code as a starting point, i can add dynamically instantiating objects which initialise themselves when i actually access them as properties.
class User extends ReadOnlyProperties
{
public function __construct($Name, $Age)
{
$this->Data["Name"] = $Name;
$this->Data["Age"] = $Age;
}
public function __get($Property)
{
if(array_key_exists($Property, $this->Data))
{
return $this->Data[$Property];
}
else if ($Property == "Address")
{
$this->Data["Address"] = new Address($this->Name); //Read Database!
return $this->Data["Address"];
}
else
{
printf("No property named '%s'.", $Property);
exit;
}
}
}
Here i've overridden the __get() magic method in the User class with one that dynamically loads the Address property when accessed for the first time. Subsequent access to the same property is from the $Data array because array_key_exists() returns true on account of the Address property now exists in the $Data Array. Awesome!
I'll stop now as this post is a lot longer than i anticipated but hopefully you can see that using PHP's magic methods you can open up new avenues in dynamic object behaviour. Take a look at the magic methods page in the PHP documentation and see how they could help in your next project.
http://php.net/manual/en/language.oop5.magic.php



