Pop quiz hotshot! You’ve put in a long day of programming. Your data models are complete, the prototype UI is done. You’re going to end the day creating a simple Laravel command. You run command:make
$ php artisan command:make PulsestormSomeCommand
You register your command
#File: app/start/artisan.php
Artisan::add(new PulsestormSomeCommand);
and then run artisan list
to make sure it’s registered
$ php artisan list
{"error":{ "type":"ErrorException",
"message":"Argument 1 passed to
Illuminate\\Console\\Application::add() must be an
instance of Symfony\\Component\\Console\\Command\\Command,
instance of PulsestormSomeCommand given, called in
...src\/Illuminate\/Support\/Facades\/Facade.php on line 208
and defined",
//...
What do you do?
Bad Dennis Hopper impersonation aside, this is a real thing that’s happened to me twice. The first time it was tremendously confounding and I ended up wasting a few hours solving the problem. The second time I managed to remember the root cause relatively quickly, but it was still a bizarre wtf moment, and it’s what led to this Laravel/Composer autoloader series.
Today we’re going to talk about how Laravel’s autoloaders interact with each other when they’re loading the basic Laravel MVC class types. We’ll start with a definition of those MVC types, then move on to discussing how each type is loaded. Then, we’ll wrap up with a few examples of how Laravel 4.2’s autoloader introduces ambiguities that can be hard to figure out if you’re new to the platform — including an answer for the scenario above.
As a reminder, this series covers the specifics of Laravel 4.2 — but the concepts should be applicable across versions.
Laravel’s MVC Types
Types, or type systems, are another area where 50+ years of computer science and business practices have led to an overloading of terms. A “type system” might refer to the native types available in your language. For example, PHP has integers, floats, arrays, objects, strings, etc. A type system might also refer to the classes and objects you yourself have built for dealing with a particular problem domain (Vehicle, Car, Pontiac, etc). Languages like ruby, where everything is an object, can be particularly confusing on this issue, as ruby only has one type (an object), but the ruby standard library has classes which allow you to create an integer object, string object, etc.
All in all, when a developer says “type system” what they mean will be heavily influenced by the system they’re working in, and what it means to their day to day working and thinking.
When we say MVC types, we’re referring to the high level objects, classes, and methodologies Laravel provides for developers building web applications. Laravel itself has an immeasurable number of types under the hood — there’s Manager
classes, Driver
classes, etc. Understanding those types is important, but we’re not interested in those today. The “MVC types” we’re going to talk about are the types you’ll use most if you’re building Laravel applications.
The Types
The six Laravel types we’re interested in are
- Controllers
- Models
- Artisan Commands
- Database Migrations
- Database Seeds
- Test Cases
Controllers
Controllers are the C
in MVC, and contain a entry point for any URL into your application. Laravel’s routing system is a little different than other MVC applications — your routes are setup with PHP callbacks in app/routes.php
, and often won’t include any controllers. However, for larger applications with many entry points it makes sense to start categorizing request types with controller classes.
You can find Laravel 4.2 controllers in the app/controllers
folder.
Models
Models are the M
in MVC. There’s two schools of thought on models — some people think they’re the classes that interact with your database/datastore — other people think they’re the classes that contain the “business or domain logic” for your application. Laravel has a folder for model classes, and provides the Eloquent ORM for talking to the database.
You can find Laravel 4.2 models in the app/models
folder
Artisan Commands
Laravel 4.2 ships with a system for creating command line programs. This system is named artisan
, and is based on the popular Symfony Console component. By creating your command line scripts and programs with artisan
, you don’t need to worry about code for parsing arguments, creating help files, etc., and you also improve discoverability of these commands for third parties using your program.
You can find Laravel 4.2 command classes in the app/commands
folder, and you’ll (typically) register commands in the app/start/artisan.php
file
Database Migrations
Laravel’s database migration system allows you to define a set of programmatic rules for modifying a database’s schema (adding tables, columns, etc.), and undoing those changes (“rolling back”). Migrations are critical for teams with multiple members, and also provide the ability to easily reinstall/rebuild the application on a different set of servers.
You can find Laravel 4.2 migration classes in the app/database/migrations
folder.
Database Seeds
If Database migrations are for managing a database schema — seeds are for managing the default data inside a database. Some developers scoff at the idea of requiring your application to have a certain set of data available in the database to function, but other developers do not. A seed object contains code for “seeding” a database with default data.
You can find Laravel 4.2 database seed classes in the app/database/seeds
folder.
Test Cases
While Laravel doesn’t have its own test system, A default Laravel 4.2 project does come configured to run phpunit
test cases inside a Laravel bootstrapped environment.
You can find Laravel 4.2’s phpunit
tests in the app/tests
folder.
Autoloading Laravel’s Six MVC Types
Now that we have basic definitions for Laravel’s six MVC types, we’re going to discuss how Laravel’s autoloader chain interacts with each of these types.
First up are controllers. In Laravel 4.2 you set up a controller with something like the following in your routes.php
file
Route::get('some-path/{id}', 'MySpecialController@showProfile');
and then define you controller in the following file
#File: app/controllers/MySpecialController.php
class MySpecialController extends BaseController
{
public function showProfile($id)
{
}
}
Laravel will automatically instantiate the controller for you. Laravel does nothing special to include
or require
the controller definition file. That is, it relies on the autoloader to automatically load the controller file for you.
As to which autoloader Laravel uses to load a controller class — that’s a trickier question. Since this is a Laravel MVC type, you might assume that it’s the Laravel autoloader that handles this. This is buoyed by the fact that Laravel’s app/start/global.php
file includes the controller path as one to autoload from
#File: app/start/global.php
ClassLoader::addDirectories(array(
//...
app_path().'/controllers',
//...
));
However, Laravel’s main autoloader is last on the autoloader stack
Illuminate\Foundation\AliasLoader::load
Composer\Autoload\ClassLoader::loadClass
Swift::autoload
Illuminate\Support\ClassLoader::load
This means the Composer autoloader (listed second) gets first crack at autoloading the controller class. If you take a look at a stock composer.json
in a Laravel 4.2 project, you’ll see something like this
//..
"classmap": [
"app/commands",
"app/controllers",
"app/models",
"app/database/migrations",
"app/database/seeds",
"app/tests/TestCase.php"
]
//..
That is, Laravel 4.2 ships using the classmap autoloader we discussed in our Composer Autoloader Features articles, and the classmap
autoloader is configured to scan the app/controllers
folder. If you have a Laravel project, try opening up the generated class map file at vendor/composer/autoload_classmap.php
, and I bet you’ll find your controller file(s) listed there.
So, this means we can say, definitively, that Laravel uses the Composer autoloader to load controller classes, right?
Wrong.
Consider the following use case — add the following to your app/routes.php
file
Route::get('testbed', 'PulsestormTestbedController@test');
and then create a controller file at
#File: app/controllers/PulsestormTestbedController.php
<?php class PulsestormTestbedController extends BaseController
{
public function test()
{
var_dump("I am loaded");
}
}
and load your URL.
http://laravel.example.com/index.php/testbed
You should see the text I am loaded
dumped to the browser.
That is, Laravel successfully loaded your controller class and instantiated an object — except that shouldn’t be possible. Since we never ran composer dumpautoloader
, the Composer autoloader doesn’t know about the new controller file.
Remember though, Laravel has four autoloaders
Illuminate\Foundation\AliasLoader::load
Composer\Autoload\ClassLoader::loadClass
Swift::autoload
Illuminate\Support\ClassLoader::load
After running Illuminate\Foundation\AliasLoader::load
, Composer\Autoload\ClassLoader::loadCLass
, and Swift::autoload
, Laravel finally runs its own, internal autoloader, Illuminate\Support\ClassLoader::load
. This autoloader actually loads the controller class.
So, to sum up how and where Laravel autoloads a controller class
A stock Laravel 4.2 system will use Composer classmap autoloader to load a controller’s class name. If the classmap autoloader fails to load a class, Laravel will fallback to using its own
Illuminate\Support\ClassLoader::load
autoloader.
This is, in my opinion, a bit of sloppy systems engineering. It introduces ambiguity as to which method will load a controller class/object — it also creates a situation where Laravel has a defined naming convention for its controller in Illuminate\Support\ClassLoader::load
, but has no naming convention for controllers loaded via the classmap autoloader, (remember, Composer’s classmap generator will run through every .php
and .hh
file in a directory looking for classes to add).
Laravel follows a similar pattern when loading a model class. That is, a stock Laravel composer.json
file ships with the app/models
folder included in the classmap
section
"classmap": [
//...
"app/models",
//... ]
This means the classmap
autoloader will handle every .php
or .hh
file in app/models
. However, this same “we haven’t run dumpautoload
yet” rules apply. If the classmap autoloader fails to load a Model
class, Laravel will fall back to Illuminate\Support\ClassLoader::load
autoloader.
While I may consider this sloppy systems engineering, I can easily see a case being made that it’s good product engineering. I’m not familiar enough with the history to say for sure, but I can imagine the Laravel core team wanting to add some sort of Composer autoloader support to be in line with current PHP standards, but not wanting to remove the traditional Laravel autoloader if existing users were still using it. Backwards compatibility may be a dirty word to some, but these sort of compromises are the heart of product engineering, and impossible to judge without the context of the original development cycle.
As we’ll see below though, this classmap vs. Laravel autoloader problem can create real problems for developers who stumble into it.
Generated Code
Next, we’re going to talk about autoloading artisan
command classes, database migrations, and database seed classes.
The first thing to know — the “Composer first, then Illuminate” situation we described above applies to database seed classes, and artisan commands. Again, if we look at composer.json
"classmap": [
"app/commands",
//...
"app/database/seeds",
//...
]
We see both the app/commands
folder and app/database/seeds
folders are part of the Composer classmap autoloader. If we then look at the default folders the Illuminate autoloader will look through
ClassLoader::addDirectories(array(
app_path().'/commands',
//...
app_path().'/database/seeds',
//...
));
We see both the app/commands
folder and app/database/seeds
folder.
Confusingly though, the Composer classmap/Illuminate loader fallback does not apply to database migrations. Autoloading Laravel’s migrations is handled strictly by the classmap autoloader — you won’t find the migrations folders added to the Illuminate ClassLoader. This is most likely due to the special naming of migration files (YYYY_MM_DD_name.php
), which means the Illuminate autoloader has no chance of loading these class files.
There’s another wrinkle to commands and migrations (but not seeds), and that’s how a working Laravel developer creates them. While you’ll typically create models and controllers by hand, when it comes to migrations, seeds, and commands you’ll be using code generation.
That is, when you want to add a migration, you use artisan
‘s migrate:make
command
$ php artisan migrate:make add_some_table_to_database
Created Migration: 2015_02_06_164452_add_some_table_to_database
Generating optimized class loader
Notice the second line of output (Generating optimized class loader). Behind the scenes artisan
is running composer dumpautoloader
for you, and this regenerates the classmap autoloader files. i.e., your migration class file in 2015_02_06_164452_add_some_table_to_database.php
is ready to be autoloaded.
There’s also a command for generating new artisan
commands
$ php artisan command:make MyCommandClassName
Command created successfully.
Curiously, despite knowing about the new class, Laravel doesn’t automatically run composer dumpautoload
for you in this situation — perhaps because the core artisan
developers know the Illuminate class loader will take care of this.
Test Cases
The last Laravel MVC type we’ll talk about are your test classes. Tests are a little different, because they don’t run in a pure Laravel bootstrapped environment. Instead, tests run in a phpunit
bootstrap environment, and this phpunit
environment bootstraps a Laravel environment.
This may seem a pedantic distinction — but it has important consequences. For example, your test classes in app/tests
? Laravel does not autoload these. In fact, your test classes aren’t autoloaded at all. PHPUnit manually include
s these files (for reasons both historic and practical). If you’re curious where this happens in PHPUnit’s systems code, take a look at this method
#File: vendor/phpunit/phpunit/src/Util/Fileloader.php
public static function load($filename)
{
$oldVariableNames = array_keys(get_defined_vars());
include_once $filename;
$newVariables = get_defined_vars();
$newVariableNames = array_diff(
array_keys($newVariables), $oldVariableNames
);
foreach ($newVariableNames as $variableName) {
if ($variableName != 'oldVariableNames') {
$GLOBALS[$variableName] = $newVariables[$variableName];
}
}
return $filename;
}
So why are we mentioning test classes if there’s no autoloader? While PHPUnit autoloads individual test classes, it does not load any base test classes you may have. Laravel itself ships with a base test class named TestCase
#File: app/tests/TestCase.php
class TestCase extends Illuminate\Foundation\Testing\TestCase {
/**
* Creates the application.
*
* @return \Symfony\Component\HttpKernel\HttpKernelInterface
*/
public function createApplication()
{
$unitTesting = true;
$testEnvironment = 'testing';
return require __DIR__.'/../../bootstrap/start.php';
}
}
It’s this base test case that bootstraps the Laravel testing environment.
So, if PHPUnit doesn’t load this class, what does? Take one last gander at Laravel’s stock composer.json
"classmap": [
//...
"app/tests/TestCase.php"
]
Here Laravel has explicitly added this base test case to the classmap autoloader.
If you wanted to create your own base tests classes, you’d do the same. That is, you’d add the file to composer.json
"classmap": [
//...
"app/tests/MyBaseTestCase.php"
]
And then run the dumpautoload
command
$ composer dumpautoload
Swift Autoloader
There’s one last class type to talk about, and it’s not a Laravel MVC class — it’s any class that’s a part of the Swift Mailer library.
#File: vendor/swiftmailer/swiftmailer/lib/classes/Swift.php
public static function autoload($class)
{
// Don't interfere with other autoloaders
if (0 !== strpos($class, 'Swift_')) {
return;
}
$path = dirname(__FILE__).'/'.str_replace('_', '/', $class).'.php';
if (!file_exists($path)) {
return;
}
require $path;
//...
}
There’s a small chance this legacy autoloader might cause class name conflicts in your program. For example, try running the following code in a Laravel bootstrapped environment (i.e. a Laravel Route
callback or controller action)
$object = new Swift_Message;
$r = new ReflectionObject($object);
var_dump($r->getFilename());
This bit of code creates a Swift_Message
object, and then uses PHP’s reflection class to find the class definition. In a working system, it will var_dump
the following file path.
string '/path/to/laravel/login/vendor/swiftmailer/swiftmailer/lib/classes/Swift/Message.php'
Now, crete the following class file
#File: app/models/Swift_Message.php
<?php class Swift_Message
{
}
and run the Composer dumpautoload
command
$ composer dumpautoload
If you re-run the reflection code above, you’ll see the following
string '/path/to/laravel/app/models/Swift_Message.php'
That is, PHP is now loading the class we created instead of the native SwiftMailer Swift_Message
class. This happens because of the autoloader stack order
Illuminate\Foundation\AliasLoader::load
Composer\Autoload\ClassLoader::loadClass
Swift::autoload
Illuminate\Support\ClassLoader::load
The Composer\Autoload\ClassLoader::loadClass
autoloader wins out over Swift::autoload
because PHP calls Composer\Autoload\ClassLoader::loadClass
first.
Now, it’s unlikely you’ll run into this problem yourself, you’d need to be naming your classes with a Swift
prefix and not following the standard naming conventions for model classes. However, it is theoretically possible, and something you’ll want to watch out for.
Framework vs. Application
Another area you’ll want to be careful with is conflicts between your application classes and framework classes.
For example, in PHP, there’s nothing to stop you from defining a class in Laravel’s Illuminate namespace.
#File: app/models/Illuminate/View/Test.php
<?php namespace Illuminate\View;
class Test
{
}
With the above in place, give the following reflection snippet a try
$reflection = new ReflectionClass('Illuminate\View\Test');
var_dump($reflection->getFilename());
PHP will print out the path to the class, letting us know we’ve correctly defined it.
string '/path/to/laravel/app/models/Illuminate/View/Test.php' (length=88)
Next, we’re going to create a Illuminate\View\View
class
#File: app/models/Illuminate/View/Test.php
<?php namespace Illuminate\View;
class View
{
}
If we run this one through our reflection snippet
$reflection = new ReflectionClass('Illuminate\View\View');
var_dump($reflection->getFilename());
we’ll see that PHP does not return a path to our class — instead, it returns the path to the real Illuminate\View\View
class in the Laravel framework library.
string '/path/to/login/vendor/laravel/framework/src/Illuminate/View/View.php' (length=106)
So far so good — this is reasonable behavior to expect from a framework. However, without adding any extra code, run the dumpautoload
command,
$ composer dumpautoload
and try running the reflection snippet again
string '/path/to/app/models/Illuminate/View/View.php' (length=88)
This time PHP returns the path to the Illuminate\View\View
class we defined. That’s because the Composer classmap autoloader wins out over the PSR-0
autoloader defined in the framework composer.json
Other PHP frameworks promote this sort of behavior as a way to selectively replace core system/framework classes — if you’re a Magento developer you’re probably familiar with code pool overrides. While you may be tempted to take advantage of this behavior for a quick fix to an application you don’t understand, without an implicit promise from the framework developer to maintain this behavior, I’d treat this as a side effect to be wary of rather than a feature to rely on.
The Answer to this Week’s Puzzler
So, with all tha said, have you figured out the pop quiz question we led off with?
The answer is, in part, pilot error. What I left out was that, immediately prior to running command:make
, I had absentmindedly run the following
$ php artisan migrate:make PulsestormSomeCommand
Created Migration: 2015_02_08_153827_PulsestormSomeCommand
Generating optimized class loader
Now, this was obviously a dumb thing to do — but what this did was create a PulsestormSomeCommand
class in the migrations folder.
#File: app/database/migrations/2015_02_08_153827_PulsestormSomeCommand.php
//...
class PulsestormSomeCommand extends Migration {
//...
}
Then, when I realized I’d accidentally run the wrong command, I ran the correct one
$ php artisan command:make PulsestormSomeCommand
Command created successfully.
And Laravel created a command class in the app/commands
folder
#File: app/commands/PulsestormSomeCommand.php
//...
class PulsestormSomeCommand extends Command {
//...
}
So, what I had now was a system with two classes named PulsestormSomeCommand
in the global namespace. Laravel itself still served pages fine — because Laravel loads neither the command or migration classes during normal system execution.
However, when I tried to run php artisan
, PHP ended up instantiating the PulsestormSomeCommand
migration class. With that context, the error message
Argument 1 passed to Illuminate\Console\Application::add() must be an instance of Symfony\Component\Console\Command\Command, instance of PulsestormSomeCommand given, called in
makes a lot more sense. Laravel’s telling us it can’t use our migration class as a command class.
Transitionary Period
This brings us, at long last, to the end of Laravel’s autoloader architecture. Beyond the utility of being able to debug autoload related bugs and day-to-day blocking issues, Laravel’s autoloader offers an interesting perspective on the the tradeoffs of systems engineering vs. product engineering.
The autoloader architecture in Laravel 4.2, while solid, does show signs of being in transition. Next time we’ll tie a bow on our series with a look at the changes to this architecture in the recently released Laravel 5.