After a welcome break, over the late summer public holiday here in England, here’s the final part in my object-oriented PHP series. Sorry about the rather extended pause since ”Part 3: Taking Relationships Further”.
Having already looked at definition, relationships and taking those relationships further, I’m going to look at how PHP5’s new Object Model introduces more advanced functionality.
Most PHP developers will be sufficiently acquainted with standard constants:
define('ELVIS', 'The King!');
echo ELVIS; // Outputs "The King!"
In PHP5 we are able to define class specific constants that behave much like static
members; ie. we can’t access them from an instantiated object.
To define a class constant, we write the following code:
class englandSquad
{
const goalie = 'Robbo';
function display()
{
echo self::goalie;
}
}
In the above example our display()
method will output our goalie
constant. We could also echo this constant like so:
echo englandSquad::goalie;
However, the following code would not work because we’re trying to use an instantiated object:
$team = new englandSquad();
echo $team::goalie;
echo $team->goalie;
A constant must always be just that; a constant expression. It cannot be a variable, the result of a function call or a class method.
Abstract classes contain abstract methods - a special kind of method that simply allows a developer to define a template for methods that should be included in the child classes of our abstract class. Although we can define the method’s signature, we cannot define the implementation.
The child classes of our abstract class (ie. those that inherit from it) must contain a definition of the abstract methods within that class. Those methods should also be defined with the same (or weaker) visibility.
To better illustrate what I mean, I’m going to directly lift the example from the PHP manual since I feel it performs a perfect explanation of this particular feature:
abstract class AbstractClass
{
// Force Extending class to define this method
abstract protected function getValue();
abstract protected function prefixValue($prefix);
// Common method
public function printOut() {
print $this->getValue() . "\n";
}
}
class ConcreteClass1 extends AbstractClass
{
protected function getValue() {
return "ConcreteClass1";
}
public function prefixValue($prefix) {
return "{$prefix}ConcreteClass1";
}
}
class ConcreteClass2 extends AbstractClass
{
public function getValue() {
return "ConcreteClass2";
}
public function prefixValue($prefix) {
return "{$prefix}ConcreteClass2";
}
}
$class1 = new ConcreteClass1;
$class1->printOut();
echo $class1->prefixValue('FOO_') ."\n";
$class2 = new ConcreteClass2;
$class2->printOut();
echo $class2->prefixValue('FOO_') ."\n";
This example will output the following:
ConcreteClass1 FOO_ConcreteClass1 ConcreteClass2 FOO_ConcreteClass2
As you can see from the example, our abstract class can still contain other members that we wish the child classes to inherit - even though they’re not abstract themselves.
Taking this idea of “templates” one step further, we are able to specify object interfaces.
In much the same way that we can define method templates (abstract methods) in our abstract classes, interfaces allow us to specify which methods a class must implement. We do not have to specify how those methods are handled, however.
We specify an interface like so:
interface iObserver
{
public function update();
}
Notice that we haven’t defined the contents of the method update()
. Also, notice that the method is declared as public
. All methods declared in an interface must be public as this is the very nature of an interface.
We can now apply our interface to a class using the implements
operator. Once we have done this, all the methods within our interface must be included in our class definition:
class StatisticsDisplay implements iObserver
{
public function update()
{
// As an observer, StatisticsDisplay
// checks for updates.
// Because we're using an interface,
// this code is still class specific!
}
public function display()
{
// Display our statistics
}
}
class ForecastDisplay implements iObserver
{
public function update()
{
// As an observer, StatisticsDisplay
// checks for updates.
// Because we're using an interface,
// this code is still class specific!
}
public function display()
{
// Display our forecast
}
}
As you can see, we’ve included the update()
method in both our classes since we’re implementing our iObserver
interface.
Incidentally, anyone who understands Design Patterns might already have spotted that I’m using the Observer Pattern in this particular example.
We could create another interface for our display()
methods (since they seem to be common) and also apply it to our classes like so:
interface iDisplay
{
public function display();
}
// Notice the comma in the following line of code
class StatisticsDisplay implements iObserver, iDisplay
{
public function update()
{
// As an observer, StatisticsDisplay
// checks for updates.
// Because we're using an interface,
// this code is still class specific!
}
public function display()
{
// Display our statistics
}
}
class ForecastDisplay implements iObserver
{
public function update()
{
// As an observer, StatisticsDisplay
// checks for updates.
// Because we're using an interface,
// this code is still class specific!
}
public function display()
{
// Display our forecast
}
}
It’s worth remembering, however, that a class cannot implement two interfaces that share function names, since it would cause ambiguity.
There are a number of “magic” methods included in PHP5 that allow a developer to extend the functionality of classes at runtime. I’m going to look at a few of these here but for more information on all these methods, please take a look at Magic Methods in the PHP manual.
Also, before I get started, thanks go out to Brendon Kozlowski for requesting this information since I hadn’t intended on including it until he raised the point!
The magic methods set()
, get()
, isset()
and unset()
allow a developer to overload the specific behaviour of that method for a given class.
For example, the __get()
method allows you to overload behaviour when getting class members:
class MyClass
{
public $member;
private function __get($name)
{
echo "Getting {$name}...\n";
}
}
$obj = new MyClass();
$obj->member = 1;
echo $obj->member;
The above code will output the following:
Getting member... 1
The __set()
method is slightly different because it expects two parameters - one for member name and one for the value being passed to it:
class MyClass
{
public $member;
private function __set($name, $value)
{
echo "Setting {$name} to {$value}...\n";
}
}
$obj = new MyClass();
$obj->member = 1;
echo $obj->member;
The above code will now output the following:
Setting member to 1... 1
We can also overload our calls to class methods by using the __call()
method in much the same way.
The __call()
method takes two parameters - the name of the function called and an array of the arguments that were passed.
To save time thinking up examples, here’s one from the PHP manual:
class Caller
{
private $x = array(1, 2, 3);
private function __call($m, $a)
{
print "Method $m called:\n";
var_dump($a);
return $this->x;
}
}
$foo = new Caller();
$a = $foo->test(1, "2", 3.4, true);
var_dump($a);
This example will output the following:
Method test called: array(4) { [0]=> int(1) [1]=> string(1) "2" [2]=> float(3.4) [3]=> bool(true) } array(3) { [0]=> int(1) [1]=> int(2) [2]=> int(3) }
Another useful magic method is the __toString()
method. This basically allows us to overload the behaviour performed when converting an object to a string. A nice simple example of this would be:
class TestClass
{
public $foo;
public function __construct($foo) {
$this->foo = $foo;
}
public function __toString() {
return $this->foo;
}
}
$class = new TestClass('Hello');
echo $class;
Which would output the following:
Hello
Well, we’ve reached the end of our journey through the wonders of object-oriented PHP and I feel I should clarify that I’ve really only scratched the surface of the object-oriented power of the language. I’m sure I’ve missed all kinds of functionality that experienced developers will be sending me snooty emails about for years to come - all I can say is, for more information, please read the manual!
Hopefully now you’ll have more than a little understanding in the general techniques used in an object-oriented system and, although you might not be 100% confident that you understand the methodology, with use you’ll be an object-oriented developer in no time. So what are you waiting for? Go out and start using them!
Categories: