There’s been a lot of buzz surrounding the use of frameworks for quite a while now and there are many great PHP frameworks to choose from. I was blown away by the simplicity and power of the base CComponent class in the Yii framework. What does the class do?
- Properties – adds getter and setter properties to classes
- Configuration – enables an elegant class and application level cascading configuration system
- Events – adds the ability to raise and call custom events
- Behaviors – adds the ability to use behaviors (also called mix-ins)
Every class in the framework extends from the CComponent
class, which means that all subclasses work as components and can raise and handle events as well as be reusable and configurable. This packs a lot of punch for such a little class!
I’d like to share with you the core component that is at the heart of the Yii framework, and I’ve organized the tour of the CComponent
class into three articles. In this article I’ll discuss how Yii utilizes PHP magic methods to create class properties and an elegant configuration system. In the next article I’ll discuss how you can use event-based programming and how the Yii component class implements this to make raising and handling events easy. The final article will walk through using behaviors (also called mix-ins), what they are, how you can use them, and how Yii implements them.
So let’s get started on our first topic.
Properties
A property defines a configurable aspect of a class; it exposes the class’ interface enabling you to manipulate it to do your bidding. For example, properties on an image uploader class might be the “file path”, and the “maximum allowed file size”. These properties can be exposed and manipulated by writing setter and getter methods such as setFilePath()
and getFilePath()
, or simply by adding a public property on the class like public $filePath
.
However, there is a small problem with this. If you define properties as public variables you potentially have many areas in your application that can refer to the property directly and you have no control over what other parts of the application may assign to it. When setting the filePath
property I may want to check if it’s a valid path first and throw an error message if it’s not. In order to do this I need to create a setFilePath()
method. Using $uploaderObject->filePath = "/file/path/"
just doesn’t cut it.
Yii’s component class implements the magic __get()
and __set()
functions which implements a devilishly simple convention allowing you to create getter and setter methods but still access properties as if they were public variables. Basically, Yii assumes a method starting with “get” or “set” is referring to a property with the same name as the method without the “get” or “set” prefix. This lets you easily expand your code at a later date. You can go ahead and add your public variables when you first start building your class; don’t worry about adding getter and setter methods. Then you can add getter and setter methods and make the original property private but you don’t have to refactor the rest of your code.
<?php
echo $uploaderObject->filePath;
will actually call:
<?php
echo $uploader->getFilePath();
and using…
<?php
$uploaderObject->filePath = "my/path";
will call:
<?php
$uploaderObject->setFilePath("my/path");
Of course you can still call the getFilePath()
or setFilePath()
directly too if you wanted to.
Let’s take a look at how Yii achieves this.
Yii’s Magic
Diving into the CComponent
class of Yii you’ll find the two magic methods responsible for this wizardry. Let’s start with the first __get()
function.
<?php
public function __get($name){
$getter = "get" . $name;
if (method_exists($this, $getter)) {
return $this->$getter();
}
throw new CException("Property '$name' is not defined.");
}
When you call echo $uploader->filePath;
, PHP looks for the public filePath
property. If it doesn’t find it (or if it’s private), PHP will delegate to the magic method __get()
. PHP calls the __get()
function and passes it the name of the property. In this example, $name
will store the value “filePath”. Then Yii does the following:
- Prepends the text “get” to the value of the
$name
variable and assigns this to the$getter
variable, making$getter
equal to “getfilePath” (remember, function names in PHP are case insensitive). - Checks if there is a method called
getfilePath()
defined within this object. - Calls the method if it exists. The code
$this->$getter();
is really a call to$this->getfilePath()
in this example. - If the method isn’t found then an exception will be thrown complaining, you guessed it, the property can’t be found.
The same process is applied with the magic __set()
function. When you assign setFilePath
a value like so:
<?php
$uploader->filePath = 'my/path';
PHP searches first for the public property. If it doesn’t exist (or it’s private), PHP calls the magic __set()
function. The __set()
function works in a similar way to __get()
, though PHP also passes the value you are setting, so $value
would store “my/path” in the example.
<?php
public function __set($name, $value) {
$setter = "set" . $name;
if (method_exists($this, $setter)) {
return $this->$setter($value);
}
if (method_exists($this, "get" . $name)) {
throw new CException("Property '$name' is read only.");
}
else {
throw new CException("Property '$name' is not defined.");
}
}
The implementation of the __set()
function:
- Prepends the value of
$name
with “set” and assigns this to$setter
variable which becomes “setfilePath”. - Checks if a method exists with the name
setfilePath
. - If the method exists, then it’s called like
$this->setfilePath("my/path");
. - If the
set
method doesn’t exist then a check is made if there is a getter method for the property. If there is then the property is read-only and an exception is thrown stating as much. If there is no getter and no setter method then an exception is thrown stating the property does not exist.
In only a few lines of code Yii has implemented a very nice property system based on using the PHP’s magic __get()
and __set()
functions.
Configuration
Another advantage to using this method of defining properties is that you can easily use arrays to configure your classes if you so desire:
<?php
$config = array(
"myProperty" => 1234,
"anotherProperty" => 5678);
You could configure your component class with the above array simply by doing the following:
<?php
foreach ($config as $property => $value) {
$this->$property = $value;
}
The code uses the array keys as properties setting them to be equal to the value defined in the array. This is amazingly simple and is how Yii itself handles its application-level configuration. Here’s an example of a very basic demo Yii configuration file:
<?php
return array(
"name" => "My Amazing App",
"timezone" => "Europe/London",
"components" => array(
"db" => array(
"username" => "root",
"password" => ""
),
"user" => array(
"allowAutoLogin" => true
)
)
);
The array is passed to the Yii application class which then loops through each configuration key. The application class must have properties defined for “name”, “timezone”, and “components”.
<?php
class MyApplication extends CComponent
{
public $name;
public $timezone;
public function setComponents(array $components) {
// handle the array of components
}
}
The components key calls a setComponents()
function that expects an array. This function loads in each class and passes it its own array of configuration and so on until everything has its configuration properties set up. It’s Incredibly fast, completely non intrusive, and there’s no need for separate configuration methods scattered throughout your code.
At a later date you can expand your class using setter and getter methods instead, if required.
<?php
class MyApplication extends CComponent
{
private $name;
private $timezone;
public setName($name) {
// do something with the $name
}
public setTimezone($timezone) {
// do something with $timezone
}
// TODO: Add getter functions
public function setComponents(array $components) {
// handle the array of components
}
}
Summary
In this article I’ve given you a quick snapshot of how magic methods are used in Yii’s component class to create properties and a simple but powerful configuration system. Of course Yii doesn’t stop there. It also implements the magic __isset()
and __unset()
methods, which means you can use isset()
to determine if a property has a value and use unset()
to destroy the property.
In Part 2 I’ll talk about events, another key principle in creating highly reusable code in a component based architecture. I’ll show you how you can use event-based programming in PHP and how the Yii enables its subclasses of the component to raise and handle events.