Under the Hood of Yii’s Component Architecture, Part 1

This entry is part 2 of 3 in the series Under the Hood of Yii's Component Architecture

Under the Hood of Yii's Component Architecture

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.

Image via Filipchuk Oleg Vasiliovich / Shutterstock

Under the Hood of Yii's Component Architecture

<< Under the Hood of Yii’s Component Architecture, Part 2Under the Hood of Yii’s Component Architecture, Part 3 >>

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • http://www.whosup4.com Neill Jones

    A great, clear article. Can’t wait for part 2!

    • steve

      Thanks Neill :-)

  • http://okeowoaderemi.com Okeowo Aderemi

    i was recommended to Yii, by a friend so am eager read the next part, i’m sure i’ll be motivated to learn Yii after reading your findings on the internal workings of Yii, but the array configuration in PHP seems favorable compared to Zend application.ini

    • http://newicon.net Steve

      The great thing about Yii is that it doesn’t impose things on you I frequently use Zend libraries within my Yii projects.

      It is also a good one to learn as the source code is very well commented.

  • http://thebusypixel.com kenrick

    thanks for highlighting yii, its a great framework and I have built a pretty large application with it, and I’m still finding out cool little things it can do. I had used zend for a while but was overwhelmed by the hundreds of includes and memory overhead. Yii comes with so many great components right out of the box, that you don’t have to use, but are so easy to modify you can’t help but use them. Looking forward to part 2.

  • http://fomigo.net Igor

    Thank you, that’s very great!
    Yii is cool, and your article is very clear!
    Thanks for sharing.

  • Pierre Voisin

    Great article! Can’t wait for part #2 also… :)

  • http://Mediavince.com MediaVince

    Cool article, this reminds me of symfony too!
    Only thing though is to watch out for the camel casing in your tutorial…
    getfilePath is not the same as getFilePath

    • MediaVince

      In fact camel casing is fine for methods but out of clarity sake and to match variable constraint casing might appear relevant…
      However, the setter example is wrong it seems as the method checks for set $name where in this example the variable is setFilePath as in $uploader->setFilePath = “path/”;
      Where it should read
      $uploader->filePath = “path/”;
      Also __unset method needs parentheses like __unset()

    • http://steve-obrien.com Steve

      Well spotted media vince.
      Yes the typo makes this a little confusing.
      Weh you set filePath variable it should be
      $uploader->filePath = ‘path/’
      Then under the hood Yii appends ‘set’ to the $name variable so it calls:
      setfilePath (without camel casing) as function names in PHP are not case sensitive this works.
      This is probably clearer in the “getter” section. Glad you liked the article :-)

  • KS

    This is not aimed specifically at the author. PHPMaster folks, could you please add a “print version” link that allows printing only the article without navigation etc. and in decent layout? Thanks.

  • Daniel

    Nice Article!
    Keep up the good work!

  • Narek

    I vote for more articles about Yii . Thanks Steven BTW for this 1.

  • http://trickortip.com Andrés

    Steve,
    Greate guide. Thank you for taking the time and sharing it.
    Best,
    Andrés

  • Benito

    Hello, i understand all about Config. But i have a question, where ill save my config file array? in yii config file? or in another folder?
    Thanks.