The ‘Rasys Constructor’ in PHP

Years ago I had the pleasure of being a co-worker of Chris Rasys, a great programmer and an even greater friend. While developing a PHP web application he came up with a technique which I call the ‘Rasys Constructor’ for instantiating objects. Today I will explain how it works.

Note: Everyone mispronounces Chris’ name. It is ‘ray-see’, not ‘ray-sis’.

The Intent and Goal

The purpose of the technique is to allow for constructing an object like so:

$lobby = new User(["name" => "Lobby Jones", "age" => 36]);

This will set the $name and $age properties of the object. So the constructor appears similar to how other programming languages make use of keywords, somewhat similar to Smalltalk and the Common Lisp Object System. We will create this type of constructor and use traits to make it available to classes, meaning that we must be using PHP 5.4 or later.

An Example Class

Let’s begin with an example of a class that uses the technique.

/**
 * A rather generic class representing a user, the kind we might
 * implement for a web forum, for example.
 */
class User
{
        use RasysConstructor;

        private $id;
        private $name;
        private $age;
        private $groups;

        /**
         * This method accepts the name of a group (a string) and
         * returns a boolean indicating whether or not the user
         * belongs to that group.
         */
        public function belongsToGroup($group)
        {
                return in_array($group, $this->groups, true);
        }
}

We define no explicit constructor. As we’ll see later the RasysConstructor trait implements that for us. All we do is tell the class to use the trait and then define the class properties and one method. One of the main benefits of the technique is that it lets us assign values to properties in the constructor without having to write a constructor that performs those assignments manually. And if we add a new property later the constructor will accept it automatically.

Implementing the Trait

We begin with an implementation that delivers the behavior described in the previous section.

trait RasysConstructor
{
        public function __construct(array $properties)
        {
                foreach ($properties as $name => $value)
                {
                        if (property_exists($this, $name))
                        {
                                $this->$name = $value;
                        }
                }
        }
}

The code is simple and straightforward. The trait provides a constructor that accepts an array of properties, which we expect to be key-value pairs. If any key matches the name of a class property then we assign the associated value from the $properties array. With this trait and the User class above we can write our original example:

$lobby = new User(["name" => "Lobby Jones", "age" => 36]);
var_dump($lobby);

The addition of var_dump() will show how the object has values for its $name and $age properties.

Robbed of Logic

Experienced programmers may have seen an immediate problem with the trait: it takes away our ability to have any custom constructor behavior. Since the RasysConstructor trait implements the constructor method there is no way for classes using the trait to tack on additional logic. This is a serious problem if a class needs to do more than assign values to properties when creating a new object.

We address this issue in a way that will make some PHP developers uncomfortable: we will define a magic method containing code to execute as part of the constructor, rewriting the trait like so:

trait RasysConstructor
{
        /**
         * The last thing the construct does is execute this method.
         * This provides us with a place to put any necessary, custom
         * constructor logic since the design of the trait does not
         * allow us to redefine __construct().  By default the method
         * does nothing and classes using the trait are not required
         * to define the method.
         */
        private function __instantiate() {}

        public function __construct(array $properties)
        {
                foreach ($properties as $name => $value)
                {
                        if (property_exists($this, $name))
                        {
                                $this->$name = $value;
                        }
                }

                $this->__instantiate();
        }

}

Now for the sake of example let’s say we want to dump all of the information about each User object we create. That would be something we could do in the constructor, but since the trait defines that we have to put that code in this newly defined method.

private function __instantiate()
{
        echo "Our User\n--------------------\n\n";
        var_dump($this);
}

Any code we place inside of __instantiate() will run at the end of the constructor. This effectively makes it a second constructor, one that we have tacked on to the end of PHP’s. I suspect that some programmers will consider this an ugly hack. Furthermore, PHP reserves all function names that begin with two underscores. It does not explicitly prevent us from defining such functions, but a future version of PHP could introduce a standard __instantiate() method and that would break this whole design. Nonetheless, I feel that using two underscores helps emphasize that the method is fundamental to the class.

Conclusion

So that is the so-called ‘Rasys Constructor’ in trait form. The name is not meant to imply that Chris is the original creator of the technique, because I don’t know if that is true or not. But he introduced me to it, hence the name. Recently he even uses a different approach.

Using this trait brings along its share of cons, such as forcing all of the constructor parameters into a single array, negating the ability to perform stricter type checking (e.g. if certain parameters are themselves objects). Nonetheless, I find it to be convenient in many situations and I hope you can see some value in the approach. I welcome comments for any suggestions about improvement or glaring concerns about its use.

Advertisements

One thought on “The ‘Rasys Constructor’ in PHP

  1. Heh, I totally missed this the first time you posted it. Funnily, my boss at work found it while attempting to find humorous references relating to my last name.

Add Your Thoughts

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s