Internet Performance Delivered right to your inbox

The Strange And Magical Features Of PHP

PHP is a server-side scripting language with some neat features. The downside is that some of these features can be a little obscure and not widely used, but we’re not discouraged by a challenge. Think of us as anthropologists who like to explore those strange and magical features of our programing languages to see if we can not improve our customers’ experience with our products and also improve our own language mastery in the process.

It is mostly during our Unroadmap days that we get some dedicated time to play around with all the neat stuff that doesn’t necessarily have an immediate return.

Below are a few PHP functions that the DynECT Email team has used to help improve system performance, development time and the features you want as quickly as we can.

__autoload()

Normally if you wanted to import a class into a file, you’d have to go through and use require_once() anywhere you thought you might need it. A downside: since PHP is an interpreted language, those require_once() usually declared at the start of the file include a lot of code that might not even be referenced at all. Pulling out the require_once() runs the risk of critical “class not found” errors. 

With __autoload, you can listen for these “class not found” events and then handle them before it crashes your code. Just remember to follow a consistent naming convention for class and file and  __autoload() will come to the rescue.

Example:

/**
* Auto load a class when the class is requested but not yet loaded.
*
* @param string $class_name The name of the class to load
*
* @return void
*/
function autoload_class($class_name) {
	// make sure we got a class name
	if ($class_name == '')
		return;

	// Build the file path and check to see if the file exists.
	$class_file = 'some/class/directory/here/' . $class_name . '.php';
	if (!is_file($class_file))
		return;

	// If it exists return
	require_once($class_file);
}
// Tie autoload_class() to the event handler
spl_autoload_register('autoload_class');

coding

__get()

Another one of PHP’s hidden gems is the __get() call. This function gets fired whenever you request a class property that does not exist. A great use would be if you have an object that belongs to another object. For example: Lets say you have a class of person:

class Person {
	public $person_id;
	public $name;
	public $age;
	public $gender;

	public function load($person_id) {
	//logic to load a person record into this class
	}
}

Now let’s say this person owns a car.

class Car {
	public $car_id;
	public $person_id;
	public $type;
	public $color;

	public function load($person_id) {
	// logic to load a car record into this class
	}

	public function load_by($field, $value) {
	// logic to load a car by a field into this class
	}
}

Wouldn’t it be great if you could reference the person’s car without making a new function to retrieve it? This is where __get() comes in. We can set it up so when someone calls $some_person->car; they get the car record attached to the person record. Let’s do some setup.

class Person {
	public person_id;
	public $name;
	public $age;
	public $gender;

	// A foreign key array.
	public $_fk = array('car');

	public function load($person_id) {
	// logic to load a person record into this class
	}

	/**
	* Overload of __get. Load an object by this classes id and attach it to this class.
	*
	* @param string $name The variable name to look up.
	*
	* @see Person::_fk
	*
	* @return mixed The object(s) loaded.
	*/
	public function __get($name) {

		// check to make sure we have foreign keys and that $name is one of them
		if($this->_fk == array() || !in_array($name, $this->_fk)) {
			throw new Exception('__get() :: Variable ' . $name . ' does not exist on ' . get_class($this));
			return;
		}

		// get the ID field for the class
		$id_field = strtolower(get_class($this)).'_id';

		//make the parameter on the class and set it equal to a loaded version of the object.
		$class_name = ucfirst($name);
		$this->$name = new $class_name();
		$this->$name->load_by($id_field, $this->$id_field);

		//return the object so the request works.
		return $this->$name;
	}
}

With this small chunk of code, we can do something like this:car

$some_person = new Person();
$some_person->load(5);

echo $some_person->car->color; //this will echo out the color of the car object with the id of person.

The cool thing about this is that the loading of the car seems to happen magically and it appears as if the car object was already there to begin with.

Note: In order for something like this to work, you have to be able to assume classes are structured a certain way and for this example, a person only owns one car. There are ways of handling these types of issues but I won’t be going over those in order to keep this post a readable length!

Now you might be thinking to yourself: ‘Well, what if I only wanted color? Do I really have to load the whole car object if all I wanted was the color?’ This can be solved with another one of PHP’s “magic” functions.

__call()

This function gets fired when a function is called that does not exist on the class. A use of this function: if you had a bunch of functions that all did the same thing but with different fields. In order to just get the color of the car, we need to first do some setup.

class Person {
	public $person_id;
	public $name;
	public $age;
	public $gender;

	// A foreign key array.
	public $_fk = array('car');

	public function load($person_id) {
	// logic to load a person record into this class
	}

	/**
	* Overload of __get. Load an object by this classes id and attach it to this class.
	*
	* @param string $name The variable name to look up.
	*
	* @see Person::_fk
	*
	* @return mixed The object(s) loaded.
	*/
	public function __get($name) {

		// check to make sure we have foreign keys and that $name is one of them
		if($this->_fk == array() || !in_array($name, $this->_fk)) {
			throw new Exception('__get() :: Variable ' . $name . ' does not exist on ' . get_class($this));
			return;
		}

		// get the ID field for the class
		$id_field = strtolower(get_class($this)).'_id';

		// make the parameter on the class and set it equal to a loaded version of the object.
		$class_name = ucfirst($name);
		$this->$name = new $class_name();
		$this->$name->load_by($id_field, $this->$id_field);

		// return the object so the request works.
		return $this->$name;
	}

	public function __call($name, $args) {

		// check to make sure we have foreign keys and that $name is one of them
		if($this->_fk == array() || !in_array($name, $this->_fk)) {
			throw new Exception('__call() :: Function ' . $name . ' does not exist on ' . get_class($this));
			return;
		}

		// get the ID field for the class
		$id_field = strtolower(get_class($this)).'_id';

		// get the class we are requesting
		$class_name = ucfirst($name);
		$class = new $class_name();

		// make sure the class we are requesting from has all the fields we are asking for.
		foreach($args as $field) {
			if(!property_exists($class, $field)) {
				throw new Exception('__call() :: Variable ' . $field . ' does not exist on ' . $class_name);
		return;
		}

		// ask your database for the field (i will use mysql for simplicity
		$query = 'SELECT ' . implode(',', $args) . ' FROM ' . $name . ' WHERE ' . $id_field . '=' . $this->$id_field;
		// Please note I'm assuming you have a database object and a fetch function in this example.
		$db = new DatabaseObject();
		$row = $db->Fetch($query);

		// if they only asked for one field only return a value not an array.
		if(count($args) == 1)
			return $row[$args[0]];

		//return an associative array of the fields.
		return $row;
	}
}

Now we can ask the person just for the color like so:

$some_person = new Person();
$some_person->load(5);

echo $some_person->car('color'); //this will echo out the color of the car object with the id of person without loading an entire car object to do it.

This can be super useful when you have large objects and you only need a few values from them.

Note: This function assumes you are using the _fk variable we created with __get() and that our data structure matches our class structure.

My hope is that you try out one of these functions, but I know I only scratched the surface of what these functions are capable of. As always, comments are welcome and happy coding!

Please Note: Though we use these functions in the application, our versions of them do not match the versions in this post. These functions have been simplified for the sake of this tutorial and in no way should be taken and used for production code as they could have major security flaws.


Share Now

Whois: John Norton

John Norton is a Senior Software Engineer at Oracle Dyn Global Business Unit, a pioneer in managed DNS and a leader in cloud-based infrastructure that connects users with digital content and experiences across a global internet.