Laravel’s MacroableTrait

Like this article? Frustrated by Magento? Then you’ll love Commerce Bug, the must have debugging extension for anyone using Magento. Whether you’re just starting out or you’re a seasoned pro, Commerce Bug will save you and your team hours everyday. Grab a copy and start working with Magento instead of against it.

Another quick primer this week, but this time it’s Laravel specific.

The idea of a “macro” has a long history in computer science and programming. This long history means it’s one of those words with numerous different overloaded meanings. The first time I encountered something called a macro it was in mid-to-late-1990s visual basic/vb-script, where a macro is a function (or subroutine) with no parameters. The C programming language has macros, but in C they’re a small programming language within a programming language that lets you change the contents of your program before sending it to the compiler. Even Microsoft Office has macros, which are small recordable programs that allow you to automate tasks.

While each of these things are different, they all share the common theme of performing a programmatic task, but with limited access to the full features of the real programming environment.

With that context, we’re going to take a look at Laravel’s implementation of the macro concept.

What Does “Macroable” Mean in Laravel

To start, let’s add the following to our app/routes.php file.

#File: app/routes.php
class Hello{}

Route::get('testbed', function(){

    $hello = new Hello;

    $hello->sayHi();

});

In real life we’d never define a class in the app/routes.php file, but it’s convenient for demo/learning purposes.

If we load the testbed route in a browser

http://laravel.example.com/testbed

We’ll get the following PHP error

Call to undefined method Hello::sayHi()

This is unsurprising, as we never defined a sayHi method for the Hello class.

Next, let’s add the MacroableTrait to our class

#File: app/routes.php   
class Hello
{
    use Illuminate\Support\Traits\MacroableTrait;
}

Here we’ve used the full trait name, namespace and all. If you’re not familiar with traits, checkout last week’s primer. Traits follow the same namespace rules as classes, and using a trait will invoke the PHP autoloader. If we load the page with the above in place, you’ll still see the same error

Call to undefined method Hello::sayHi()

However, now try adding the following code to your route

#File: app/routes.php    
class Hello
{
    use Illuminate\Support\Traits\MacroableTrait;
}
Route::get('testbed', function(){
    // include '/tmp/test.php';

    $hello = new Hello;
    Hello::macro('sayHi', function(){
        echo "Hello","\n<br>\n";
    });
    $hello->sayHi();
    Hello::sayHi();
});

Here we’ve called the static method macro on Hello. While Hello doesn’t define a static method named macro, it does inherit one from the Illuminate\Support\Traits\MacroableTrait trait.

If you reload the page with the above in place, you’ll see the following.

Hello 
Hello 

This is what the MacroableTrait does — it allows you to add a method (or, a “macro”) to a PHP class programmatically. By calling the macro method, we’ve effectively added the sayHi method to our Hello class, and all objects instantiated from that class. The above example is a little silly (again, for pedagogical reasons) — in real life you’ll see the MacroableTrait used in some global bootstrap-ish code to make a method available to other programmers. An example you might be familiar with from Laravel 4.2 is the Form helper macros.

How MacroableTrait Works

The MacroableTrait is simpler than you might think. You can find its definition in the following file

vendor/laravel/framework/src/Illuminate/Support/Traits/MacroableTrait.php

The most obvious place to start your investigation is the macro method we called earlier

#File: vendor/laravel/framework/src/Illuminate/Support/Traits/MacroableTrait.php
public static function macro($name, callable $macro)
{
    static::$macros[$name] = $macro;
}

This method stashed the anonymous PHP function in a static array property, indexed by $name. If you scroll down a bit in the file, you’ll see our old friends __call and __callStatic. The __call method

#File: vendor/laravel/framework/src/Illuminate/Support/Traits/MacroableTrait.php
public function __call($method, $parameters)
{
    return static::__callStatic($method, $parameters);
}

Simply passes the method call on to the __callStatic method. The __callStatic method

#File: vendor/laravel/framework/src/Illuminate/Support/Traits/MacroableTrait.php    
public static function __callStatic($method, $parameters)
{
    if (static::hasMacro($method))
    {
        return call_user_func_array(static::$macros[$method], $parameters);
    }

    throw new \BadMethodCallException("Method {$method} does not exist.");
}

will fetch the anonymous function or PHP callback added with the call to macro, and then call the anonymous function/callback using PHP’s call_use_func_array method.

If you didn’t follow that, let’s trace things again, this time with our example code. First, when we called

#File: app/routes.php      
Route::get('testbed', function(){    
    //...
    Hello::macro('sayHi', function(){
        echo "Hello","\n<br>\n";
    });
    //...
});    

we were telling our class to store the anonymous function inside of static::$macros['sayHi'].

Next, here’s where we called sayHi (both statically and via an instance method)

#File: app/routes.php       
Route::get('testbed', function(){        
    //...
    $hello->sayHi();
    Hello::sayHi();
    //...
});    

Since sayHi isn’t defined on the class Hello, PHP ends up calling __call (for the -> invokation) and __callStatic (for the :: invocation), per the rules of PHP Magic Methods. The __call method simply passes the method call onto __callStatic. This means __callStatic actually executes for both instance and static method calls.

If we put on some x-ray variable specs, the method call looks something like this

#File: vendor/laravel/framework/src/Illuminate/Support/Traits/MacroableTrait.php        
public static function __callStatic('sayHi', $parameters)
{
    if (static::hasMacro('sayHi'))
    {
        return call_user_func_array(static::$macros['sayHi'], $parameters);
    }

    throw new \BadMethodCallException("Method {'sayHi'} does not exist.");
}

Or, expanding that out even further, like this

#File: vendor/laravel/framework/src/Illuminate/Support/Traits/MacroableTrait.php    
public static function __callStatic('sayHi', $parameters)
{
    if (static::hasMacro('sayHi'))
    {
        return call_user_func_array(function(){
            echo "Hello","\n<br>\n";
        }, $parameters);
    }

    throw new \BadMethodCallException("Method {'sayHi'} does not exist.");
}    

By leveraging PHP’s magic methods and static functions, the MacroableTrait trait gives you the ability to dynamically add methods to objects at runtime — a feature usually reserved for “more advanced” dynamic languages like ruby/python.

Gotchas

While powerful, keep in mind macros (by design) have no knowledge of the other properties and methods of a class/object. Since you’re using an anonymous function (or PHP callback) to add your method, this function/callback won’t have access to the usual variables like $this, self, or static. This means the methods you add via a MacroableTrait will never be more than stateless helper methods. If you need access to an object or class’s state, then a macro is the wrong tool for your job.

The other thing you should keep in mind when considering the MacroableTrait is whether you want to add another item to the already busy “static method namespace” in your Laravel program. We’ll talk more about this next time, when we dive deeper into the various different things a static method call might do in Laravel.

Originally published October 26, 2014

PHP Traits

Like this article? Frustrated by Magento? Then you’ll love Commerce Bug, the must have debugging extension for anyone using Magento. Whether you’re just starting out or you’re a seasoned pro, Commerce Bug will save you and your team hours everyday. Grab a copy and start working with Magento instead of against it.

It’s another quick primer this time. Today we’re going to talk about a PHP 5.4+ feature called traits. While Laravel doesn’t make heavy use of traits they are sprinkled around the core codebase, so you’ll want to get familiar with them.

The Problem

Sometimes when you’re working in a class based object oriented system, you’ll find yourself in a situation where you want functionality from another class in your own class. If your class doesn’t have a parent class, you can accomplish this by extending the class whose behavior your want. Pretend you’re creating a messenger class.

class Messenger
{
}

While you’re working on the messenger class, other members of your team have written a Hello class.

class Hello
{
    public function hello()
    {
        echo "Hello","\n"
    }
}

If you want Hello’s functionality, you can extend it.

class Messenger extends Hello
{
}

$o = new Messenger;
$o->hello();

Pretty basic object oriented stuff. However, lets say someone on your team has also written a class Goodbye.

class Goodbye
{
    public function goodbye()
    {
        echo "Goodbye","\n";
    }
}

As it stands right now, there’s no way for you to incorporate Goodbye’s functionality into your Messenger class. That is, in PHP, a class can only have one parent class. Some languages have experimented with allowing a class to have many parents (with a feature called multiple inheritance), but multiple inheritance creates a slew of ambiguities and has a bad reputation as it complicates simpler single class inheritance.

When PHP’s core team considered this programming problem for PHP 5.4, instead of multiple inheritance they developed a language feature called “traits”.

What’s a Trait

Traits are PHP’s take on the multiple inheritance problem. Think of a trait as something in-between a class and an interface. You can define a trait in PHP with the following

trait Hello
{
}

Similar to interfaces, you can define abstract methods on traits

trait Hello
{
    abstract public function sendMessage();
}

However, different from interfaces, you can also define concrete methods on traits

trait Hello
{
    abstract public function sendMessage();

    public function hello()
    {
        echo "Hello World","\n";
    }

}    

You may not directly instantiate traits. The following program

trait Hello
{
    abstract public function sendMessage();

    public function hello()
    {
        echo "Hello World","\n";
    }
}    

$o = new Hello;

will halt with the error.

Fatal error: Cannot instantiate trait Hello

So far, traits look a lot like abstract classes. However, a class cannot extend a trait. The following program

trait Hello
{
    abstract public function sendMessage();

    public function hello()
    {
        echo "Hello World","\n";
    }
}    

class Messenger extends Hello
{
}

$o = new Messenger

will halt with the error

Fatal error: Class Messenger cannot extend from trait

Instead, a class uses traits. Consider the following program.

trait Hello
{
    abstract public function sendMessage();

    public function hello()
    {
        echo "Hello World","\n";
    }
}    

class Messenger
{
    use Hello;
    public function sendMessage()
    {
        echo 'I am going to say hello',"\n";
        echo $this->hello(),"\n";
    }
}

$o = new Messenger;
$o->sendMessage();

This program will run with the following output

I am going to say hello
Hello World

Notice that our class definition contains a

use Hello;

This tells PHP we want our class Messenger to use the trait Hello. The use keyword here has nothing to do with PHP namespaces. When use is inside a class block, PHP knows it means use a trait — when use is outside a class block, PHP knows it means alias or import a namespace.

When a classes uses a trait, it has access to all the trait’s methods, just as though it had extended from it. Notice we’re calling the hello method

echo $this->hello(),"\n";

There’s no hello defined on Messenger. Instead, hello is defined on the trait.

Multiple Magic

So far the functionality we’ve described can all be accomplished without traits. That is, other than the use operator, this all looks like plain old class inheritance. That’s about to change. Consider the following program

trait Goodbye
{
    public function goodbye()
    {
        echo "Goodbye","\n";
    }    
}

trait Hello
{
    public function hello()
    {
        echo "Hello World","\n";
    }
}    


class Messenger
{
    use Hello, Goodbye;
    public function sendMessage()
    {
        echo 'You say goodbye',"\n";
        echo $this->goodbye();

        echo 'and I say hello',"\n";
        echo $this->hello(),"\n";
    }
}

$o = new Messenger;
$o->sendMessage();

Here we’ve defined two traits. One named Goodbye, the other named Hello. Then, in our Messenger class, we’ve used both

use Hello, Goodbye;

Run the above program, and you’ll get the following output

You say goodbye
Goodbye
and I say hello
Hello World

This is the point of traits. A client programmer can pull in any trait they want into their class to adopt the trait’s functionality.

While not as powerful as multiple inheritance (i.e. you need to write your code as traits in the first place), traits cordon off the ambiguities that arise from multiple inheritance into a separate system. This allows that separate system to develop advanced features for dealing with those ambiguities without complicating existing class syntax. If you’re interested in how these ambiguities are resolved, the PHP manual has a more in depth discussion of all the features of PHP’s traits.

With PHP 5.3 finally deprecated, you’ll start seeing traits used more often. In particular, there are parts of the Laravel framework that rely on traits for their functionality. Next time we’ll have a quick primer on Laravel’s MacroableTrait.

Originally published October 19, 2014

Laravel Facade Troubleshooting

Like this article? Frustrated by Magento? Then you’ll love Commerce Bug, the must have debugging extension for anyone using Magento. Whether you’re just starting out or you’re a seasoned pro, Commerce Bug will save you and your team hours everyday. Grab a copy and start working with Magento instead of against it.

Last time we described the PHP patterns used to implement Laravel’s facade feature, and started to describe some of the “gotchas” involved. We briefly discussed how a facade introduces a second type of singleton/shared service into the system, as well as the inherent problem of hijacking/borrowing a word that’s already in use to describe another object oriented programming pattern.

This week we’re going to continue that critique of facades, with particular attention paid to typical problems you may run into implementing your facades, or using facades in a system/application you’re not intimately familiar with.

Aliases and Global Classes

One problem you’ll run into with facades is the global namespace problem. The facades classes that ship with Laravel have a proper PHP namespace, but in order to provide dead simple access to the facade class, the system creates a globally available class aliases

Class Alias: DB
Actual Facade Class: Illuminate\Support\Facades\DB

These globally available aliases are generically named (DB, Redis, etc.), and can lead to namespace collisions — i.e. client developers create classes (typically model or migration classes) named the same as the aliases. This, in turn, can lead to confusing situations with misleading error messages.

This is more than a hypothetical ivory tower concern. Consider this stack exchange question. Here the user has some other class named Redis in the global namespace. Instead of loading the facade alias, PHP defers to their class. It’s not obvious that this is what PHP’s doing, and the user ends up confused and stuck.

The big trade off, as it always is with a global namespace, is ease of use. While Laravel’s system code uses namespaces, the system itself is biased towards giving client programmers the ability to ignore namespaces all together.

Consider that Laravel has global facade classes, an app/routes.php file included in the global namespace, an app/start/*.php files hierarchy for global startup tasks, and an autoloader and code generation tools that that ship loading and creating classes in the global namespace (from/in the app/commands, app/controllers, app/models, and app/database/seeds folders).

While this may make “modern” PHP developers cringe, it does remove the namespace learning curve barrier for people new to Laravel and new to programming. I suspect it’s one of the reasons Laravel’s such a popular framework.

One Abstraction Over the Line

Another tricky part of facades is the issue of meta-programming in general. Facades provide a great way for folks completely new to programming with a way to “do things”, but a programmer with a small bit of experience will see something like

DB::select

and assume there’s a class DB with a method select somewhere. In hiding the implementation details of the service container, facades also hide the actual class doing the work, which makes it harder for a developer to know what class actually implements a service.

Even as an experienced developer, I couldn’t tell you off the top of my head what the “DB” facade’s actual class is. Whenever I need to know this, I need to

  1. Open up the app configuration

  2. Look for the DB alias and the actual class

  3. Get the service identifier (db) from the facade class definition

  4. Use grep or ack to search through my source code to find how the service was bound and/or extended

  5. Jump to that class definition

  6. Then, sometimes, some extra searching if someone’s used App:extend to change a service definition

The same goes for all standard IDE and code completion/browsing tools — unless they’re configured to work specifically with Laravel, looking up method definitions can be a flow breaking event.

Facades make reading the client programmer’s intent easy, but at the expensive of making learning how the system works more difficult.

This might be a tenable situation if facades were the only abstraction to use static method calls. However, Laravel’s default ORM (Eloquent), also uses static method calls. Things like the following

Users::all(...);
Users::where(...)->get();

are a common site in any Laravel application. Neither all, nor where, are defined as static methods on an Eloquent model. Instead, Eloquent leverages the power of __callStatic to give model objects special features.

We’ll cover the specifics of this ORM magic in another article, but the takeaway here is, in Laravel static method call syntax (::) might mean calling a static method, might mean calling a facade, or might mean calling a magic ORM method. That is, as a programmer, you can’t just look at

Someclass::foo(...);

and know where to find the method foo.

In the end, Laravel’s meta-programming creates a weird, two plateau learning curve. Of all the modern PHP programming frameworks it’s the easiest to get started with. However, that ease comes at the cost of a very steep learning curve when you’re ready to start digging into how the framework does its job.

In future articles article we’ll dive deep on some of Laravel’s other magic methods, including the aforementioned Eloquent ORM and Laravel’s database manager service.

Originally published October 17, 2014