Menu

Magically dynamic PHP

This is an old post from 2007. As such, it might not be relevant anymore.

These two functions are designed to replace the setXyz() and getXyz() calls for each and every variable which could possibly be changed. It also simplifies the programming logic and allows us to perform some pretty neat tricks. So without further ado, let’s jump into an example.

A simple example

To take full advantage of these two functions, you need data whose name will not change but their value can. The first thing that springs to mind is license plates. Here in the UK we have unique license plate numbers which belongs to a car; license plates can be moved from car-to-car… sorry about the boring subject, fluffy cats and dogs would have much more fun.

<?php
class LicensePlate
{
    private $plate = array('ABC123' => 'Ford Focus');
    
    public function __set($plate, $car) {
        $this->plate[$plate] = $car;
    }
    
    public function __get($plate) {
        return $this->plate[$plate];
    }
}

So what is going on here? Well, we have the plate array which will be the repository for any license plates we create, the __set function to set them, and the __get function to, well, get them. Not hard really. Now, we need to write the code to access this class, and it couldn’t be simpler.

<?php
$plates = new LicensePlate();
$plates->ABC123 = 'Mini Cooper';
echo($plates->ABC123);

Line 3 is the first magic method, and as you can see, we refer directly to the array index of the license plate ‘ABC123’. Since there is nothing in the class such as a function called ‘ABC123’, and since we are setting a value, the __set() function kicks into action. The function accepts 2 parameters, the actual licence plate (in this case ‘ABC123’, and the value, in this case ‘Mini Cooper’. The setter then simply writes these details to the class array. Line 4 uses the same technique except that it is not trying to set anything; rather it is trying to output something. PHP knows this, and so fires up the __get() function. This function only accepts one parameter, the license plate. The function simply returns the value of that license plate, ‘Mini Cooper’.

This example is extremely basic and contains lots of holes. For instance, what if the array index of ‘ABC123’ doesn’t exist? The next script shows a fully working script which has a logger to show you what is going on under the hood.

<?php
class LicensePlate
{
    private $log = array();
    private $plate = array();
 
    public function addPlate($plate, $car) {
        if (array_key_exists($plate, $this->plate))
            $this->plate[$plate] = $car;
            $this->setLogItem('Added ' . $plate . ' as a ' . $car);
        } else {
            $this->setLogItem('The plate ' . $plate . ' already exists.');
        }
    }
    
    public function __set($plate, $car) {
        if (array_key_exists($plate, $this->plate)) {
            $this->plate[$plate] = $car;
            $this->setLogItem('Changed ' . $plate . '\'s car to ' . $car);
        } else {
            $this->setLogItem('Could not set the car for ' . $plate);
        }
    }
    
    public function __get($plate) {
        if (array_key_exists($plate, $this->plate)) {
            $this->setLogItem('Returned ' . $plate . '\'s car');
            return $this->plate[$plate];
        } else {
            $this->setLogItem('Could not return the car for ' . $plate);
        }
    }
    
    private function setLogItem($item) {
        $this->log[] = $item;
    }
    
    public function getLog() {
        return print_r($this->log);
    }
}
 
$plates = new LicensePlate();              // Create the instance
$plates->addPlate('ABC123', 'Ford Focus'); // Create ABC123
$plates->ABC123 = 'Mini Cooper';           // Amend ABC123
$foo = $plates->ABC123;                    // Get ABC123's car
$plates->XYZ789 = 'Land Rover';            // Should produce an error
 
$plates->getLog();                         // Let's see what we did

Whilst running this code (why not give it a try?) should produce the following:

Array
(
    [0] => Added ABC123 as a Ford Focus
    [1] => Changed ABC123's car to Mini Cooper
    [2] => Returned ABC123's car
    [3] => Could not set the car for XYZ789
)

Advantages

No one likes creating loads and loads of getters and setters—they make the script bigger and harder to navigate whilst all providing the same functionality. These two functions dispose of all those functions and allow for streamlined applications. But what happens when an attribute of a class needs special attention? Well you can create a unique getter and setter for that sole attribute.

They really start to shine though when dealing with data which is constant and require only a set amount of variables. One such case is a database table. A database table only accepts a strict set of fields; creating the __get() and __set() functionality in a higher class would save a lot of hassle.