Magic in PHP

12, Dec 2012

Contents

A brief overview of OOP

Before we move on to some more interesting things, let's quickly talk about what exactly Object Oriented Programming, or OOP, actually is. One can think of OOP as an evolution of Procedural Programming. In procedural programing one breaks down a task into an ordered set of steps and methods to achieve the desired goal; for OOP you create 'objects' that operate on self contained data and procedures. This encourages modular programing as you work to make it so that every thing is self contained and provides clean and consistent ways of interacting with other objects. You can think of an object as being anything from a Animal or Car, or for more programing related things: a User or a File for upload.

The purpose of this post though is to talk about some of the more advanced power offered by classes in OOP, specifically Magic Functions.

Let's talk Magic

Magic functions in PHP are builtin methods that allow you do things you can't other wise do. They provide hooks into the program execution that let you modify the class during run time.

Starting and Stopping

The magic function that you're probably most familiar with is __construct. If you've written PHP 4, this is equivalent to the old way of having a function with the same name as the class be the constructor. This function is called as soon as a new class is instantiated:

class Greeting {
    public function __construct($message) {
        echo "You said: '$message'";
    }
}

$Example = new Example("Hi there") // Output: You said: 'Hi there'

Using the __construct function saves you from having to write and call your own method to perform any actions on setup, as it is taken care of for you automatically. A function that pairs with __construct is __destruct. Just like __construct was called as soon as the class was initialized, __destruct is called as soon as the class is no longer needed. This is where you should put any code that needs to be run to 'clean up' after yourself; such as closing data base connections. If we modify the class above to add the following:

class Greeting {
    ...
    public function __destruct() {
        echo "\nGood bye!";
    }
    ...
}

The output now changes to:

You said: 'Hi there'
Good bye!

Accessing and Setting Properties

There may come a time when you're writing a class where you need to be able to work with unknown items. This is easy to accomplish with PHP! Let's say you've got a class that needs to be able to create new properties at run time - maybe you've got a recipe for a pie and you know you're going to need a crust and a filling, but then at run time a special order is created for pies with a topping also:

class Pie {
    public $crust, $filling;

    public function __set($name, $value) {
        $this->{$name} = $value;
    }
}

$Pie = new Pie();
$Pie->crust = 'graham cracker';
$Pie->filling = 'apple';
$Pie->topping = 'vanilla ice cream';
print "Make a $Pie->filling with $Pie->crust crust topped with $Pie->topping\n";

The magic function __set is called if the code attempts to set a property that the class does not actually have. It automatically receives the two arguments: the name of the property that the code wants to set, and the value thereof. In our example here, we create a new property of the same name and give it that value. Note though, that all properties created this way are public, and thus can be accessed directly.

What about the other way? What happens if you try and read the value of a property that doesn't exist? Well, there's some magic for that too! If we modify our class above to add a new function:

class Pie {
    ...
    public function __get($name) {
        return "Unknown: $name";
    }
    ...
}

...
print "The Pie was baked at $Pie->temperature degrees\n";

The new code will execute just as before except when it reaches the new print statement, it tries to access the property temperature in the Pie class it finds that there is no such property and returns the string 'Unknown temperature'' instead of throwing a warning or an error as it would do otherwise.

Calling Non-Existent Functions

The last magic function I want to mention here is needed to handle a case where your run time code tries to call a function that the class doesn't actually have. A common use for this would be handling Getters and Setters on a class. If you are encapsulating your data and have declared your properties as private, you're unable to access them directly and must therefor write getters and setters to modify and retrieve the data stored within. If you have a large number of properties, this can quickly get highly repetitive as you write the same functions over and over again, just with different names.

class Person {
    private $_name, $_age;

    public function __call($function, $args) {
        $prefix = substr($function, 0, 3);
        $property = '_' . strtolower(substr($function, 3, strlen($function)));
        if ($prefix == 'set') {
            $this->$property= $args[0];
        } else if ($prefix == 'get') {
            return $this->$property;
        } else {
            throw new Exception("$function does not exist in Person");
        }
    }
}

$Person = new Person();
$Person->setName('John');
$Person->setAge(24);
print $Person->getName() . " is " . $Person->getAge() . " years old\n";
$Person->run(); // Call a non-existent function

The code above will solve that issue for you. Instead of writing four functions we used the __call magic function to detect calls to unknown functions, then parse the call apart and if the first 3 letters of the function name are 'set' or 'get' then we take care of everything; otherwise if we still are unable to resolve the function call, throw an Exception saying so.

Comments

This space intentionally left blank.

Have Something to Say?

Questions? Comments? Concerns? Let me know what you’re thinking.

  • You can use Markdown formatting here.