PHP ArrayAccess

This week’s a quick remedial primer on PHP’s ArrayAccess interface. We’ll be back to Laravel next time, and promise you’ll see why we needed the primer.

First, some background and context. PHP has a native language type called an array. This isn’t a C array, where each element points to a specific memory location. Instead, a PHP array is an implementation of an ordered map. You can add any variable to an array. If you want to treat an array as an ordered list, you can add items with the following syntax

$array = [];
$array[]  = 'abc';
$array[]  = 123;
$array[2] = 'science';    

You can also treat an array as a hash-map/dictionary

$array = [];
$array['foo'] = 'bar';
$array['baz'] = 42;    

PHP also has a native language type called an object. To create an object, a user programmer must first define a class with the class keyword

class Foo
{
}

and then use the new keyword to create an object from the class

$object = new Foo;

PHP also includes a built in class called stdClass. A user programmer can use this class to create stand-alone objects without defining a class.

$object = new stdClass;

Finally, more modern versions of php include a shortcut for creating stdClass objects

$object = {};

Array vs. Object

Objects and arrays are different things in PHP, although they share some functionality. You can use an array as a dictionary like data structure

$array = [];
$array['foo'] = 'bar';
$array['baz-bar'] = 'science';

echo $array['foo'];

You can also use an object as a dictionary like data structure

$object = {};
$object->foo = 'bar';
$object->{'baz-bar'} = 'science'

echo $object->foo;

The only difference is the syntax. Arrays use the bracket characters [], while objects use the “arrow” operator (->). If you try to use array brackets to access the values on on object, PHP will issue a fatal error

Fatal error: Cannot use object of type stdClass as array …

So far, most of this is common knowledge to working PHP programmers. What’s less commonly known is PHP allows end-user-programmers to define what should happen when a programmer tries to access object properties with array syntax. Put another way, you can give your objects the ability to work with array brackets ($o[])

Array Access

PHP ships with a number of built in classes and interfaces. These classes and interfaces allow a PHP developer to add features to the language that would normally be reserved for a PHP core developer. The one we’re interested in today is ArrayAccess.

If you want to give your object the ability to act like an array, all you need to do is implement the built-in ArrayAccess interface. Give the following simple program a try

<?php
class Foo implements ArrayAccess
{
}

$object = new Foo;
$object['foo'] = 'bar';        

echo $object['foo'],"\n";

In our class declaration statement we’ve included implements ArrayAccess. If we run this program, we’ll see the following error.

PHP Fatal error: Class Foo contains 4 abstract methods and must therefore be declared abstract or implement the remaining methods (ArrayAccess::offsetExists, ArrayAccess::offsetGet, ArrayAccess::offsetSet, …) in

Simply implementing an interface isn’t enough — we actually need to write the methods that are part of the interface synopsis. For a user defined interface, this would mean looking at the interface definition — for a built-in interface, we need to rely on the PHP manual. If you look at the manual, you’ll see the interface is defined as

ArrayAccess {
    /* Methods */
    abstract public boolean offsetExists ( mixed $offset )
    abstract public mixed offsetGet ( mixed $offset )
    abstract public void offsetSet ( mixed $offset , mixed $value )
    abstract public void offsetUnset ( mixed $offset )
}

That is, we need to define the 4 methods ArrayAccess::offsetExists, ArrayAccess::offsetGet, ArrayAccess::offsetSet, ArrayAccess::offsetUnset. Unfortunately, the help stops there. Interfaces are great for telling you what method you need to define, but less great at telling you what each method is supposed to do.

Let’s do a little detective work. Create the following short program, and then run it (either in your browser or via the command line)

<?php
class Foo implements ArrayAccess
{
    public function offsetExists ($offset)
    {
        echo __METHOD__, "\n";
    }

    public function offsetGet ($offset)
    {
        echo __METHOD__, "\n";    
    }

    public function offsetSet ($offset, $value)
    {
        echo __METHOD__, "\n";    
    }

    public function offsetUnset ($offset)
    {
        echo __METHOD__, "\n";
    }

}

$object = new Foo;
$object['foo'] = 'bar';

This program defines the class Foo and implements each of the ArrayAccess interface’s abstract methods. Then, it instantiates a Foo object, and tries to set an array property. We’ve also programed each method to output its name whenever the program calls the method.

If you run the above program, you’ll see output something like this

Foo::offsetSet

Now try getting a property.

$object = new Foo;
$object['foo'] = 'bar';
$bar = $object['foo'];

echo "The value we fetched: " . $bar,"\n";

The above will output something like the following

Foo::offsetSet
Foo::offsetGet
The value we fetched: 

What did this teach us? When we tried to set a value via array access ($object['foo'] = 'bar';), behind the scenes PHP called our class’s offsetSet method. When we tried to get a value via array access ($bar = $object['foo'];), behind the scenes PHP called offsetGet. However, no value was actually set or gotten from the object (The value we fetched:).

That’s how the PHP ArrayAccess interface works. It has some behind the scenes magic that will call a method on your class when you perform an array operation on your object, but it’s still up to you to implement that access. If you try something like this

<?php
class Foo implements ArrayAccess
{
    protected $_data=array();
    public function offsetExists ($offset)
    {
        return array_key_exists($offset, $this->_data);
    }

    public function offsetGet ($offset)
    {
        return $this->_data[$offset];
    }

    public function offsetSet ($offset, $value)
    {
        $this->_data[$offset] = $value;
    }

    public function offsetUnset ($offset)
    {
        unset($this->_data[$offset]);
    }

}

$object = new Foo;
$object['foo'] = 'bar';
$bar = $object['foo'];

echo "The value we fetched: " . $bar,"\n";

You’ll have much better results.

The value we fetched: bar

While you initial reaction may be annoyance — “Why doesn’t PHP just implement this for me” — by giving you the responsibility of implementing your own ArrayAccess rules, the PHP core developers have given you (or the framework developers you rely on) tremendous power to create new behavior for your own objects.

Originally published September 15, 2014

Laravel Objects

Laravel is already a well documented system. The quick-start guide guide has all the information a developer needs to start building applications with Laravel. What’s less well documented, and more interesting to me, is documentation of Laravel’s implementation. The PHP community has a pretty good handle on how to use MVC systems, but less attention is paid to the code that makes these systems run.

Today we’re going to cover the basics of Laravel’s global application object, and how to instantiate “Laravel objects” like an internals developer. A lot of this code will end up being the sort of thing you’d never write day-to-day with Laravel, but understanding the internals will help you debug core system code, understand module implementations, and be a better overall PHP developer ready to handle whatever system eventually replaces Laravel.

Getting Started

The first thing we’ll want to do is create a simple route where we’ll put our code. Open up app/routes.php and add the following to the bottom of the file.

#File app/routes.php
Route::get('tutorial', function(){        
    return '<p>Done</p>';
});

Next, load the following URL in your browser.

http://laravel.example.com/tutorial

You should see the text Done. Next, replace your code with the following.

#File app/routes.php
Route::get('tutorial', function(){        
    $app = app();
    var_dump(get_class($app));
});

If you reload the page, you should see the following text

string 'Illuminate\Foundation\Application' (length=33)

The global app() function returns the Laravel application object. There are other ways to access the Application object, but for now we’re going to keep things simple and use the app() function.

For the remainder of this article we’ll put all our code in this tutorial route.

Laravel Objects

A Laravel object is just a PHP object, but a PHP object that’s been instantiated via the Application object’s make method. You could also call the make method the make factory, or the make factory method.

If you’re more a learn by doing person, consider the following. In plain old PHP, you can instantiate a simple object with code like this

#File: app/routes.php
Route::get('tutorial', function(){       
    $object = new stdClass;
    $object->foo = 'bar';

    var_dump($object);    
});

In Laravel, you instantiate an object with code that’s slightly different.

#File: app/routes.php
Route::get('tutorial', function(){       
    $app = app();
    $laravel_object = $app->make('stdClass');
    $laravel_object->foo = 'bar';
    var_dump($laravel_object);        
});

That is, you call the Illuminate\Foundation\Application object’s make method, and pass in the name of the class. In both cases, the above code will output something like this

object(stdClass)[34]
  public 'foo' => string 'bar' (length=3)

You may be thinking something like

Why bother with the Laravel make method if the results are the same?

We’ll cover that more below — this is one of those catch 22’s in programming where it’s necessary to get a little experience using the system before you can understand why you’d want to use the system.

The make method will work with any PHP class. For example, if you create a simple Laravel model class with the following contents in app/models/Hello.php

#File: app/models/Hello.php
<?php
class Hello
{
}

You can instantiate it via PHP with the following

#File: app/routes.php
Route::get('tutorial', function(){       
    $object = new Hello;
    var_dump($object);        
});

To create a Laravel object using the Hello class, you’d do the following.

#File: app/routes.php
Route::get('tutorial', function(){       
    $app = app();
    $hello = $app->make('Hello');
    var_dump($hello);        
});

Here we’ve used make to instantiate the class Hello. This will work with any PHP class that Laravel can autoload. For example, if we wanted to instantiate the Illuminate/Hashing/BcryptHasher class, we’d do it like this

#File: app/routes.php
Route::get('tutorial', function(){     
    $app = app();    
    $bcrypt = $app->make('Illuminate\Hashing\BcryptHasher');
    var_dump($bcrypt);
});

An important warning here — make does not inherit the scope of the PHP file where you call make — you’ll need to use the full namespace path.

The make factory also supports constructor parameters. For example, consider the following Hello class

#File: app/models/Hello.php
<?php
class Hello
{
    public function __construct($one='default1', $two='default2')
    {
        echo "First  Param: $one","\n<br>\n";
        echo "Second Param: $two","\n<br>\n";
        echo "\n<br>\n";
    }
}

Here we’ve added a __construct method to our Hello class. If you’re super new to PHP, PHP calls a class’s __construct method whenever you instantiate an object. If we ran the following code,

#File: app/routes.php
Route::get('tutorial', function(){       
    $o = new Hello;

    $o = new Hello('changed1','changed1');
});

We’ll get output something like this

First Param: default1 
Second Param: default2 

First Param: changed1 
Second Param: changed1 

That is, instantiating the object without parameters results in the class using the default values for the constructor parameters. If we use parameters when instantiating our object, the class uses the parameters.

So, now consider the same scenario with make. The following code

#File: app/routes.php
Route::get('tutorial', function(){       
    $app = app();
    $app->make('Hello');        
});

will output

First Param: default1 
Second Param: default2 

So that’s the default values used — but how do we tell Laravel we want to use constructor parameters? It’s not obvious, but it’s easy to remember once you’ve learned it — just use make’s second parameter. The following

#File: app/routes.php
Route::get('tutorial', function(){       
    $app = app();
    $app->make('Hello', array('laravel1','laravel2'));
});

will output

First Param: laravel1 
Second Param: laravel2 

That is, Laravel’s second argument accepts an array of parameters to pass to the constructor. Since Laravel is PHP 5.4+ only, we could also write that as

$app->make('Hello', ['laravel1','laravel2']);

Here we’re using the new-in-5.4 [] language construct to create an array. The [] syntax may seem like a small thing, but it’s a good tool for improving the readability of code that uses a lot of arrays.

Why use Laravel Objects?

So that’s the basics of Laravel object instantiation. The question that’s sure to be bouncing around the back of your head is — “Why replace PHP’s new keyword with a factory method if the objects are the same”?

The benefit of using a factory method to instantiate an object is that you give yourself the ability to monitor, control, and change how objects are instantiated in your PHP system. The behavior of PHP’s objects are hardwired into the language. You create an object, the constructor’s called, etc. There’s no chance to change how any of that works.

If you’re a low level C/C++ programmer you could probably write a PHP module to change the behavior of PHP’s object instantiation, but that’s a order of magnitude more work to get right, and low level C/C++ wizards are hard to come by.

By creating a system to replace PHP’s instantiating of objects, the Laravel systems programmers have given themselves the ability to change the behavior of object instantiation, and give their objects super powers. The only caveat is they need to convince you, the PHP client programmer, to use their factory pattern to instantiate objects.

We’ll talk more about that convincing in a future article, but you’re probably more interested in the super powers we mentioned. What sort of super powers do Laravel objects have? Many of them are related to the Application object’s “container and binding” features, which we’ll also cover in a future article. There are, however, a few super powers we can talk about today.

Class Aliasing

Laravel’s implemented a class aliasing feature for their objects. Other names for this functionality include duck-typing, monkey patching, or inversion of control. If you’re a Magento programmer, you’re used to calling it class rewrites. If you’re a Drupal developer the concept is “Pluggable”.

Regardless of what you call it, Laravel allows you to swap out class definitions at runtime. For example, consider the following code from earlier.

#File: app/models/Hello.php
<?php
class Hello
{
}

#File: app/routes.php
Route::get('tutorial', function(){       
    $app = app();
    $hello = $app->make('Hello');
    var_dump($hello);        
});

Here we instantiated an object Hello. Let’s say we want to replace Hello with a new object, Welcome. First we’ll need to create our new class. To do so, add the following code to the app/models/Welcome.php file.

#File: app/models/Welcome.php
<?php
class Welcome extends Hello
{
}    

Here we’ve defined a new class Welcome, and had it extend our original class Hello. This means Welcome will behave exactly the same as Hello. Then, at the end of Laravel’s app/start/global.php file, add the following code. (If you’re not familiar with global.php, read up on the Laravel request lifecycle)

#File: app/start/global.php
$app = app();
$alias = $app->alias('Welcome', 'Hello');

Here we’ve used the Application object’s alias method to tell Laravel whenever it instantiates a Hello object, it should use the Welcome class. Try running the following code

#File: app/routes.php
Route::get('tutorial', function(){       
    $app = app();
    $hello = $app->make('Hello');
    var_dump($hello);        
});

And you’ll see PHP will dump a Welcome object instead of a Hello object.

object(Welcome)[141]

Once an alias is setup, we can start adding new methods to the Welcome class, extending the system’s functionality without needing to replace the original Hello class. This is a powerful feature, and if you’ve ever tried setting up a class rewrite in a system like Magento, you can see the immediate advantage of Laravel’s simpler syntax.

Instantiation Events

Another advantage of using make is the Application object’s instantiation events, or as Laravel calls them — “resolving callbacks”. That is, a PHP callback which fires whenever an object is made. (i.e. when resolving the object)

Try adding the following to the end of you app/start/global.php file

#File: app/start/global.php
$app = app();
$app->resolving('Welcome', function($object, $app){
    echo "I just instantiated a " . get_class($object) . "\n<br>\n";
});

and then reload the page from above. You should see the following content in your browser.

I just instantiated a Welcome

What we did here was tell Laravel that anytime a Welcome object is instantiated, we should call the anonymous function passed in as the second argument to resolving

function($object, $app){
    echo "I just instantiated a " . get_class($object) . "\n<br>\n";
}

Laravel makes heavy use f anonymous functions as callbacks. If you’re not familiar with anonymous functions in programming, this old Joel on Software article is still the best primer I’ve seen on the subject.

Using the resolving feature, we can track when Laravel instantiates certain objects, and also change the object that’s passed into the callback. This powerful feature enables to you do anything you’d like to all objects of a certain type without needing to modify a single line of existing code.

The application also has a “global” resolving callback. This allows you to specify a callback/anonymous function for Laravel to call whenever it makes an object. If you want to give it a try, replace the above code in app/start/global.php with the following

#File: app/start/global.php
$app = app();
$app->resolvingAny(function($object, $application){
    //we'll explain this conditional in a minute. 
    if(!is_object($object))
    {
        return;
    }
    echo "I just instantiated a " . get_class($object) . " object \n<br>\n";
});

Reload your page, and you should see something like the following.

I just instantiated a Illuminate\Routing\Router object 
I just instantiated a Welcome object 

Laravel called the resolvingAny callback/listener you specified when it instantiated a Illuminate\Routing\Router object, as well as our Welcome object. If we had set this callback up earlier in the bootstrap process we would have seen more of Laravel’s internal objects. Laravel will send anything created through make to the resolvingAny callback.

This brings us to our final point for today. You’re probably wondering about that conditional guard clause.

if(!is_object($object))
{
    return;
}

Let’s replace that the with some more debugging code

#File: app/start/global.php
$app = app();
$app->resolvingAny(function($object, $application){
    //we'll explain this conditional in a minute. 
    if(!is_object($object))
    {
        echo "What's inside \$object: [" , $object, "]\n<br>\n";
    }
});

If you reload with the above in place, you’ll see something like the following

What's inside $object: [local] 

For some reason the resolvingAny method has intercepted something that’s not an object. Instead of an object, the parameter contains the string local. Why has a method meant to listen for instantiated objects received a string?

That’s a larger question we’ll start answering next time. Today we’ve concentrated on the object instantiating features of the Application object, but that’s only one small part of the Application object’s responsibilities in Laravel. Next time we’ll cover the Application object’s container features, what binding objects into the container means, and why a method meant for objects might sometimes receive a string.

Originally published September 10, 2014

Laravel’s Custom Error Handler

Laravel’s error handling is one of its most noticeable developer facing features. Like many modern PHP frameworks, Laravel ships with its error reporting set to E_ALL, and strives for Notice free PHP code. Additionally, Laravel includes the Whoops framework, which creates readable stack traces that pinpoint the exact line a PHP error came from.

Where Laravel really distinguishes itself is the steps it takes, on a framework level, to make sure its error handling accounts for all PHP errors. PHP has uncaught exceptions, fatal errors, caught exceptions, and non-fatal errors — making sure you catch every possible “bad” code situation is harder than it sounds, and it’s a credit to the Laravel core team that they’ve managed to thread this needle.

This article with explore Laravel’s systems level error handling code, as well as the myriad ways PHP makes robust error reporting difficult to accomplish.

Bootstrap

Laravel, like most modern PHP frameworks, is built around the idea of a single global application object. Laravel redirects all requests to a central index.php file. If you omit the comments, this file is pretty sparse

#File: index.php
require __DIR__.'/../bootstrap/autoload.php';
$app = require_once __DIR__.'/../bootstrap/start.php';
$app->run();

There’s a line to pull it Laravel’s custom autoloading rules, a line to pull in an application bootstrap start.php file, and a call to the application’s run method. The bootstrap start file is where the Laravel core instantiates the application file, and then returns the application object

#File: bootstrap/start.php
$app = new Illuminate\Foundation\Application;
//...
return $app;    

If you’re used to a more class based PHP framework, you may be surprised to learn you can return a value from a PHP include or require statement.

It’s a good idea to become familiar with everything going on in this file, but the line we’re interested in today is here

#File: bootstrap/start.php
$framework = $app['path.base'].
                 '/vendor/laravel/framework/src';

require $framework.'/Illuminate/Foundation/start.php';

Here Laravel pulls in another file named start.php. Why the second bootstrap? You’ll notice this second file exists in the composer vendor hierarchy — that is, it’s a file distributed with the core packages that make up the Laravel framework. The bootstrap/start.php file, on the other hand, lives under the root project folder. Once Laravel creates its initial application, this first bootstrap file is the responsibility of the application owner/developer. That is, once created, composer or Laravel will never update it again.

Having the first start.php perform a require on the second start.php ensures the application developer can take advantage of future improvements to the framework without needing to merge a file that’s part of their application.

Like the first start.php, there’s a lot of important things going on in this second Illuminate foundation start.php file. The lines we’re interested in are these two.

#File: vendor/laravel/framework/src/Illuminate/Foundation/start.php
$app->startExceptionHandling();

if ($env != 'testing') ini_set('display_errors', 'Off');

The startExceptionHandling method on the application object is where Laravel setups up its custom error handlers. You’ll notice it’s using the global $app object — this is the same object setup in the previous include file. Remember — unless a PHP includes/require file starts with a namespace declaration, it exists in its parent’s scope, and in this case that’s PHP’s global scope.

Before we dive into the startExceptionHandling method, make note of the second line.

#File: vendor/laravel/framework/src/Illuminate/Foundation/start.php
if ($env != 'testing') ini_set('display_errors', 'Off');

This is where Laravel turns off displaying errors during a test run. It’s also an example of the Laravel core developers showing a strong opinion, and using the more-concise-but-prone-to-errors single line bracket-less conditional statement.

A less opinionated developer might write the above like like this

if ($env != 'testing') 
{
    ini_set('display_errors', 'Off');
}

We point it out here mainly so you’ll be aware it’s a common site in the Laravel code base, so adjust your code smell sensors accordingly.

The Laravel Application Container

If we take a look at the application object’s startExceptionHandling method,

#File: vendor/laravel/framework/src/Illuminate/Foundation/Application.php
public function startExceptionHandling()
{
    $this['exception']->register($this->environment());

    $this['exception']->setDebug($this['config']['app.debug']);
}

we see the actual logic is handled by the object in the exception prop— except, wait? How is $this['exception'] even working? Shouldn’t that throw a Cannot use object as array error? The reason this works in Laravel’s Application object extends a base Laravel container class, and the container class implements the PHP ArrayAccess interface.

#File: vendor/laravel/framework/src/Illuminate/Container/Container.php
//...
class Container implements ArrayAccess {
}

If you’re not familiar with it, implementing the ArrayAccess interface allows you to programtically control what happens when you use array syntax ($foo[2]) with a PHP object.

Covering this in full is beyond the scope of this article, but when you see $this['someprop'] in the application object, Laravel’s accessing a service object. Don’t worry if you don’t understand Laravel’s services — that’s a topic for another day. For the purposes of this article, all you need to know is $this['exception'] contains an object who’s class is Illuminate\Exception\Handler, and that’s where we’ll find the PHP code used to setup Laravel’s error handling.

Configuring PHP’s Error Handling

Let’s take the two lines of PHP above

#File: vendor/laravel/framework/src/Illuminate/Foundation/Application.php
$this['exception']->register($this->environment());
$this['exception']->setDebug($this['config']['app.debug']);

and consider the second line first. The setDebug method

#File: vendor/laravel/framework/src/Illuminate/Exception/Handler.php
public function setDebug($debug)
{
    $this->debug = $debug;
}

is just a simple setter method that allows the exception service to know the value of the app.debug configuration field. If you’ve ever wondered why changing this value at runtime doesn’t seem to affect the error display, this is why. Once Laravel sets the debug property, it never references the configuration for this value again.

Next up is the method we’re really interested in: register

#File: vendor/laravel/framework/src/Illuminate/Exception/Handler.php

public function register($environment)
{
    $this->registerErrorHandler();

    $this->registerExceptionHandler();

    if ($environment != 'testing') $this->registerShutdownHandler();
}

Here we see Laravel’s calling three different methods — registerErrorHandler, registerExceptionHandler, and registerShutdownHandler. For folks new to the platform, PHP has both an error system and an exception system. Exceptions were introduced to the language in PHP 5, but the error system was left in place for backwards compatibility reasons.

If we take a look at the definitions for each of these methods.

#File: vendor/laravel/framework/src/Illuminate/Exception/Handler.php
protected function registerErrorHandler()
{
    set_error_handler(array($this, 'handleError'));
}

protected function registerExceptionHandler()
{
    set_exception_handler(array($this, 'handleUncaughtException'));
}

protected function registerShutdownHandler()
{
    register_shutdown_function(array($this, 'handleShutdown'));
}

We can see they’re using the native PHP functions to register callback methods. A PHP callback is a pseudo type that PHP knows how to invoke as a method or function. A callback can be a string (invoked as a function), a first-class-function/closure (invoked as itself), or, as above, an array where the first item is an object, and the second item is an object method.

If you didn’t follow all that, what it means is when we say

set_error_handler(array($this, 'handleError'));

We’re telling PHP to call $this->handleError() as the custom error handler. Similarly, when we say

set_exception_handler(array($this, 'handleUncaughtException'));

We’re telling PHP to call $this->handleUncaughtException() as the custom exception handler. The same is true for $this->handleShutdown and the register shutdown function. Don’t worry if you’re not familiar with the shutdown function — we’ll be talking about it more below.

Tracing an Error

So that’s the exception and error handler set, but what actually happens when your Laravel application issues an error or doesn’t catch an exception?

First, let’s take a look at the error handler

#File: vendor/laravel/framework/src/Illuminate/Exception/Handler.php
public function handleError($level, $message, $file = '', $line = 0, $context = array())
{
    if (error_reporting() & $level)
    {
        throw new ErrorException($message, 0, $level, $file, $line);
    }
}

Here we see that Laravel turns all errors into a thrown ErrorException exception. Actually, that’s a Illuminate\Exception\ErrorException object (since we’re in Illuminate/Exception/Handler.php, and the namespace is Illuminate\Exception). This means almost every error and warning in PHP will also be routed to the Exception handler (unless, of course, application code does a try/catch for their PHP errors).

We say “almost every” because, as you can see above, the exception throwing code is wrapped in a conditional. Look carefully at that conditional

error_reporting() & $level

It’d be easy to read that as “If error_reporting() AND $level are true, but that’s not what’s going on. That’s a single & operator — which is one of PHP’s [bitwise operators] (http://php.net/manual/en/language.operators.bitwise.php). If you’re not familiar with bitwise operators and how they relate to PHP’s error handling, I’ve written extensively on this in my Survey of PHP error handling article.

For our purposes, know that if (error_reporting() & $level) can be translated into english as “If the error that just happened is an error PHP would report”. This means Laravel’s error handler will obey your custom error levels (see the shutdown handler section below for a caveat to this). It’s also worth mentioning that back up in the Illuminate Foundation Bootstrap start.php, Laravel sets the error handling to -1

#File: vendor/laravel/framework/src/Illuminate/Foundation/start.php
/*
|--------------------------------------------------------------------------
| Set PHP Error Reporting Options
|--------------------------------------------------------------------------
|
| Here we will set the strictest error reporting options, and also turn
| off PHP's error reporting, since all errors will be handled by the
| framework and we don't want any output leaking back to the user.
|
*/

error_reporting(-1);

An error reporting level of -1 is a special shortcut that means show all PHP errors. It’s functionally equivalent to error_reportin(E_ALL), although the reason -1 works is, again, based on PHP’s bitwise error codes.

So, in short, the Laravel error handler will throw an ErrorException whenever PHP issues a reportable error. This means our next stop is the uncaught exception handler.

Tracing an Exception

If we take a look at the exception handling callback, we see

#File: vendor/laravel/framework/src/Illuminate/Exception/Handler.php
public function handleUncaughtException($exception)
{
    $this->handleException($exception)->send();
}

That line might be more clearly written as

#File: vendor/laravel/framework/src/Illuminate/Exception/Handler.php
$this->handleException($exception)
->send();    

Or even

#File: vendor/laravel/framework/src/Illuminate/Exception/Handler.php
$response = $this->handleException($exception);
$response->send();

That is, the exception handler calls another method (handleException), and then calls send on the object returned by the handleException method. Before we hop over to the handleException method, we’ll let you know this call returns a Laravel response object, and the send method will send output back to the browser/user.

With that in mind, here’s handleException

#File: vendor/laravel/framework/src/Illuminate/Exception/Handler.php
public function handleException($exception)
{
    $response = $this->callCustomHandlers($exception);

    // If one of the custom error handlers returned a response, we will send that
    // response back to the client after preparing it. This allows a specific
    // type of exceptions to handled by a Closure giving great flexibility.
    if ( ! is_null($response))
    {
        return $this->prepareResponse($response);
    }

    // If no response was sent by this custom exception handler, we will call the
    // default exception displayer for the current application context and let
    // it show the exception to the user / developer based on the situation.
    return $this->displayException($exception);
}

The first two lines

#File: vendor/laravel/framework/src/Illuminate/Exception/Handler.php
$response = $this->callCustomHandlers($exception);

if ( ! is_null($response))
{
    return $this->prepareResponse($response);
}

reveal an important feature of the Laravel application object. Laravel’s application object has a pushError method. This method allows you to register callbacks to handle specific Exception types yourself. Give the following code a try in your application

app()->pushError(function($exception, $status_code, $is_this_error_from_the_console){
    var_dump(__FILE__ . '::' . __LINE__);
    //var_dump(func_get_args());        
    return 'I am the exception handler now.';
    //return null;  #return null if you don't want to handle it
});    
throw new Exception('Look at me');

We’re not going to dive into the implementation of this feature, except to say callCustomHandlers is the method that goes through and calls all the custom errors pushed onto the application, and the call to $this->prepareResponse($response) takes the value returned by the custom error handler and turns it into a Laravel response object.

Assuming there’s no custom error handler, the next journey for our exception is into the displayException method.

#File: vendor/laravel/framework/src/Illuminate/Exception/Handler.php
$this->displayException($exception);

This method is responsible for displaying the exception. Well, actually, if we look at its definition

#File: vendor/laravel/framework/src/Illuminate/Exception/Handler.php
protected function displayException($exception)
{
    $displayer = $this->debug ? $this->debugDisplayer : $this->plainDisplayer;

    return $displayer->display($exception);
}

we see its responsible for calling display on the Exception handler’s “displayer” object. The first line

#File: vendor/laravel/framework/src/Illuminate/Exception/Handler.php
$displayer = $this->debug ? $this->debugDisplayer : $this->plainDisplayer;

chooses which displayer object the exception handler should use. If the debug property is set on the exception handler, the exception handler will use the debugDisplayer. If not, it uses the plainDisplayer. Once Laravel chooses the displayer, it calls its display method (passing in the exception).

return $displayer->display($exception);

In english, this means if you’re running Laravel with the debug configuration set to true, you’ll get the super fancy “Whoops” exception handler

but if you’re running with the debug configuration set to false, you’ll get the stock Laravel production debug message.

It’s beyond the scope of this article, but if you’re interested in how Laravel sets up the debug property, as well as the debugDisplayer and plainDisplayer, take a look in the register method of the Exception service provider

vendor/laravel/framework/src/Illuminate/Exception/ExceptionServiceProvider.php

Also beyond the scope of this article is how the Whoops exception handler “does its magic”. If you’re curious in tracing that our yourself, by default the debugDisplayer’s class is Illuminate\Exception\WhoopsDisplayer, defined in the following file

vendor/laravel/framework/src/Illuminate/Exception/WhoopsDisplayer.php

All that said, we will take a quick look at how the production/plain displayer works, since it’s relatively simple

#File: vendor/laravel/framework/src/Illuminate/Exception/PlainDisplayer.php
class PlainDisplayer implements ExceptionDisplayerInterface {
    public function display(Exception $exception)
    {
        $status = $exception instanceof HttpExceptionInterface ? $exception->getStatusCode() : 500;

        $headers = $exception instanceof HttpExceptionInterface ? $exception->getHeaders() : array();

        return new Response(file_get_contents(__DIR__.'/resources/plain.html'), $status, $headers);
    }

}

As you can see above, Laravel will load the HTML contents of the following file as a string

./vendor/laravel/framework/src/Illuminate/Exception/resources/plain.html

and use this file’s contents to generate a Laravel response object. It’s important to note that this plain.html file exists as part of a vendor package — which means any change you make to it will be overridden when you update the framework. This means an application developer has no way to customize the display of the PlainDisplayer.

Fortunately, Laravel provides a method (via the App facade) to add a custom error handler.

App::error(function(){
    //if this isn't production, bail
    if(App::environment() !== 'production')
    {
        return;
    }
    return '<p>Any valid Laravel Response here</p>';
});

This is very similar to the pushError method, with one important difference. The pushError method adds an error handler to the end of the error handling queue. The more common error method will add your new error as the first error handler. This gives you the opportunity to preempt any other error handlers registered in your system.

So — that’s the exception handler traced out. Through this process Laravel creates and sends a response object for any uncaught exception. Any error message that would normally display an error is turned into an exception. This handles a proverbial 99% of all error cases in PHP — but there’s one last thing we need to consider — and that’s the shutdown handler.

Tracing the Shutdown Handler

You’ll remember back up in the register method, there was a final line where Laravel setup a shutdown handler.

#File: vendor/laravel/framework/src/Illuminate/Exception/Handler.php

if ($environment != 'testing') $this->registerShutdownHandler();
//...

protected function registerShutdownHandler()
{
    register_shutdown_function(array($this, 'handleShutdown'));
}

A shutdown callback isn’t specifically for handling errors. Instead, it’s a callback that fires when a PHP page/program has finished its execution. However, if we take a look at handleShutdown, we’ll see Laravel’s using it for error/exception handling

#File: vendor/laravel/framework/src/Illuminate/Exception/Handler.php
public function handleShutdown()
{
    $error = error_get_last();

    // If an error has occurred that has not been displayed, we will create a fatal
    // error exception instance and pass it into the regular exception handling
    // code so it can be displayed back out to the developer for information.
    if ( ! is_null($error))
    {
        extract($error);

        if ( ! $this->isFatal($type)) return;

        $this->handleException(new FatalError($message, $type, 0, $file, $line))->send();
    }
}

The shutdown function uses PHP’s error_get_last function to fetch an array of information about the last error. A null response indicates there hasn’t been an error — if that’s the case handeShutdown finishes without taking additional action. However, if error information is returned, and the error type is one of PHP’s fatal errors

#File: vendor/laravel/framework/src/Illuminate/Exception/Handler.php
protected function isFatal($type)
{
    return in_array($type, array(E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE));
}

then Laravel will instantiate a FatalError object, and hand this object off the the handleException method, same as an uncaught exception from the exception handler. (The variables in the function above are defined by the call to extract — required reading if you’re not familiar with extract).

So — why is this here? Let’s go back and consider the error handler

#File: vendor/laravel/framework/src/Illuminate/Exception/Handler.php
public function handleError($level, $message, $file = '', $line = 0, $context = array())
{
    if (error_reporting() & $level)
    {
        throw new ErrorException($message, 0, $level, $file, $line);
    }
}

You’ll recall this method throws an exception for any error that PHP would normally display — but what happens if your program produces a fatal error, but your error reporting is set to not report an error on that level? In this scenario the handleError method wouldn’t throw an exception — and PHP would do no error reporting. Since the error is fatal this halts PHP’s page rendering with no notice. Some PHP developers refer to this as the “white screen of death”. That’s far from ideal, so Laravel has the shutdown function there to catch any fatal errors.

Put another way, Laravel attempts to obey the system’s error_reporting level unless it’s a fatal error, in which case Laravel attempts to handle the error. I don’t have enough history with the platform to know why this behavior is implemented in a shutdown function instead of in the error handler itself, but my guess would be the shutdown function came as an enhancement/bug-fix later in the framework’s life.

The shutdown function also serves another purpose — there’s a few not-well-documented situations where PHP will raise an error, but not call any custom error handling functions. Some of these are impossible to report on due to the nature of the error (PHP itself seg-faults), but if, for any reason, Laravel’s error and exception handlers fail to catch an error, this shutdown handler is there to grab it.

One negative consequence of this behavior is it’s impossible to turn off Laravel exception handling in the framework without a wholesale redefinition of the application object. That’s because once a shutdown handler is registered, the PHP API provides no way to cancel or change it.

Wrap Up

As you can see, thanks to years of legacy behavior, doing something seemingly simple like controlling how PHP handles all its errors can be a herculean task. While Laravel’s approach isn’t perfect, it is some of the best I’ve seen from a framework, and their use and promotion of whoopos is a clear win for developers. If you’re thinking of creating a new PHP framework, you could do a lot worse than following the lead set by the Laravel core team.

Originally published September 1, 2014