Deep Dive into The Laravel Artisan file

obasekietinosa

Etinosa Obaseki

Posted on June 3, 2020

Deep Dive into The Laravel Artisan file

Introduction

The popular PHP framework, Laravel comes with a CLI tool, artisan that offers a lot of the functionality that makes the framework so useful.

From preinstalled system commands like $ php artisan migrate to any custom commands you write, you've used artisan.

In this article, we'll explore every line of the artisan file and figure out what they do and how it's all tied together.

The artisan File

#!/usr/bin/env php
<?php
 
define('LARAVEL_START', microtime(true));
 
/*
|--------------------------------------------------------------------------
| Register The Auto Loader
|--------------------------------------------------------------------------
|
| Composer provides a convenient, automatically generated class loader
| for our application. We just need to utilize it! We'll require it
| into the script here so that we do not have to worry about the
| loading of any our classes "manually". Feels great to relax.
|
*/
 
require __DIR__.'/vendor/autoload.php';
 
$app = require_once __DIR__.'/bootstrap/app.php';
 
/*
|--------------------------------------------------------------------------
| Run The Artisan Application
|--------------------------------------------------------------------------
|
| When we run the console application, the current CLI command will be
| executed in this console and the response sent back to a terminal
| or another output device for the developers. Here goes nothing!
|
*/
 
$kernel = $app->make(Illuminate\Contracts\Console\Kernel::class);
 
$status = $kernel->handle(
    $input = new Symfony\Component\Console\Input\ArgvInput,
    new Symfony\Component\Console\Output\ConsoleOutput
);
 
/*
|--------------------------------------------------------------------------
| Shutdown The Application
|--------------------------------------------------------------------------
|
| Once Artisan has finished running, we will fire off the shutdown events
| so that any final work may be done by the application before we shut
| down the process. This is the last thing to happen to the request.
|
*/
 
$kernel->terminate($input, $status);
 
exit($status);

Line by Line Analysis

Line 1

#!/usr/bin/env php 

This line is something called a shebang. A shebang tells the command line where to look for the interpreter for a particular script.

For example, when writing bash scripts it is common to see a shebang such as #!/bin/bash at the top which points to the bash shell that will execute the script.

The shebang in this bash example uses an absolute path to the bash executable while the one from the artisan file uses a logical path. This UNIX Stack Exchange Question features some interesting views on the subject.

It only works on UNIX-like systems, so Mac OS X or Linux but not Windows (although, if you use git bash it should work).

In the artisan case, our shebang uses the env command to find the first php executable in the user's path, wherever it may be.

Shebangs mostly allow you run scripts in the command line without first calling the interpreter. For example, running artisan commands without prepending php to it.

You can set this up by first making the artisan script executable via chmod +x artisan and then running ./artisan <your-command>

Take note of the dot in front of the artisan. That tells the shell that you want to run that as a command.

The next line is just a regular opening php tag, so we'll skip ahead to line 4.

Line 4

define('LARAVEL_START', microtime(true));

This line creates a PHP constant LARAVEL_START and assigns it the value of microtime(true)

According to the PHP Documentation, "microtime() returns the current Unix timestamp with microseconds.". It returns a string value by default, but passing the optional $get_as_float parameter as true returns a float instead.

So, where does this get used in the framework and what is it for? Well, I ran a quick search on the laravel/framework repo and didn't find any references to the constant.

My best guess, is that it's used to test how fast the framework starts up or serves request. You can compare the LARAVEL_START value to the value of microtime(true) when your request is completed.

I found this Stack Overflow question and answer that seems to support my guess. Although, it's asking about the variable in index.php (the application entrypoint for web requests just as artisan is the entrypoint for console "requests").

The next few lines we'll look at feature the first docblock in the artisan file.

Lines 6 - 16

/*

|--------------------------------------------------------------------------

| Register The Auto Loader

|--------------------------------------------------------------------------

|

| Composer provides a convenient, automatically generated class loader

| for our application. We just need to utilize it! We'll require it

| into the script here so that we do not have to worry about the

| loading of any our classes "manually". Feels great to relax.

|

*/

Here we have a docblock describing the composer-provided autoloader and why Laravel uses it.

A fun fact about Taylor Otwell and Laravel's docblock is that (for multiline comments) each line is exactly 3 characters less than the preceding. Go ahead and check for yourself here!

And, interestingly, according to this tweet, he did them by hand.

https://twitter.com/taylorotwell/status/563929477265121280

The next line we'll consider focuses on including the autoloader.

Line 18

require __DIR__.'/vendor/autoload.php';

This require directive loads in the autoload.php file that is included in the vendor directory after successfully running $ composer install

To learn more about how Composer works especially in the context of Laravel I would recommend this excellent article by Alan Storm on the subject.

Next, we'll turn our attention to Line 20

Line 20

$app = require_once __DIR__.'/bootstrap/app.php';

This line creates and initializes the $app variable with the contents of /bootstrap/app.php

app.php initializes a new instance of the Laravel application which is basically the IoC container and returns that value from the script.

Thus, the $app variable contains an instance of our Laravel application and we can basically load and use any parts of the application we want.

For example, using the make() method to resolve a class' dependencies and create an instance of it.

This is exactly the same as what we can do with the app() helper method that we use within our code.

Next, more comment shenanigans!

Lines 22 - 31

/*

|--------------------------------------------------------------------------

| Run The Artisan Application

|--------------------------------------------------------------------------

|

| When we run the console application, the current CLI command will be

| executed in this console and the response sent back to a terminal

| or another output device for the developers. Here goes nothing!

|

*/

Here we have another docblock. Once again, with each line 3 characters less than the preceding.

Doing something like this, especially if it's by hand, would have to be difficult.

I remember watching an episode on Laracasts where Jeffrey Way says it's just another example of how much care and detail Taylor Orwell puts into the framework. I'm inclined to agree.

This block talks about the app lifecycle from the console command to output along with a cheeky "here goes nothing!".

Next we'll use the $app variable from above on Line 33.

Line 33

$kernel = $app->make(Illuminate\Contracts\Console\Kernel::class);

So, here we initialize another variable, $kernel, using the app container to resolve Kernel class via the make() method.

Of course, it's not a concrete class we are passing into the make() method but an interface. This is a great example of the SOLID principles at work in Laravel, particularly the Liskov Substitution Principle and Dependency Inversion.

It allows us to easily switch out the concrete kernel class that is called if we need to by simply changing the binding of the interface to the concrete class.

Typically, within our own application code, we will register such bindings within a service provider class as recommended by the Laravel Documentation on the matter, but in the artisan file, such a binding would happen too late.

So, instead the binding is done within the /bootstrap/app.php. This file binds the different Kernels for console (which is used by artisan) and web as well as the Exception Handler.

$app->singleton(

    Illuminate\Contracts\Http\Kernel::class,

    App\Http\Kernel::class

);

 

$app->singleton(

    Illuminate\Contracts\Console\Kernel::class,

    App\Console\Kernel::class

);

 

$app->singleton(

    Illuminate\Contracts\Debug\ExceptionHandler::class,

    App\Exceptions\Handler::class

);

As we see from the above, the Kernel contract resolves to App\Console\Kernel.

With the instance of Kernel loaded into the variable, we are now set to run the actual artisan command.

The next few lines we'll consider handle this.

Lines 35-38

$status = $kernel->handle(

    $input = new Symfony\Component\Console\Input\ArgvInput,

    new Symfony\Component\Console\Output\ConsoleOutput

);

Here we assign the output of the $kernel->handle() method into the $status variable.

If we visit the App\Console\Kernel class, we'll find that it inherits from Illuminate\Foundation\Console\Kernel which is where the handle() method resides.

The method takes two arguments - $input and $output - runs some bootstrapping to load the providers and other required services before creating an instance of the artisan application and calling it's run($input, $output) method.

The method returns the status code of the action and it is this value that eventually gets passed into our $status variable in the artisan file.

The input and output variables we passed to the run() method must be instances of Symfony\Component\Console\Input\InputInterface & Symfony\Component\Console\Output\OutputInterface respectively. This is another example of the two SOLID principles we mentioned earlier. Being able to easily switch out the concrete implementations makes it easier for us to use a different input and output for testing purposes or if our application needs are different.

Up next, we encounter another docblock.

Lines 40 - 49

/*

|--------------------------------------------------------------------------

| Shutdown The Application

|--------------------------------------------------------------------------

|

| Once Artisan has finished running, we will fire off the shutdown events

| so that any final work may be done by the application before we shut

| down the process. This is the last thing to happen to the request.

|

*/

You know the drill by know. Another docblock that leaves me wondering how much time must have gone into phrasing the sentences.

This one talks about the shutdown process for the command.

Line 51

$kernel->terminate($input, $status);

To begin the shutdown process, we make a call to the terminate() method on the $kernel instance. Once again, this method is defined on Illuminate\Foundation\Console\Kernel and simply calls the terminate() method on the app instance.

The terminate() method takes in two arguments but appears to do nothing with them.

And now, the final line.

Line 53

exit($status);

According to the PHP Docs, this method terminates the execution of a script. It goes on to say the following about the optional $status argument:

If status is a string, this function prints the status just before exiting.

"If status is an integer, that value will be used as the exit status and not printed. Exit statuses should be in the range 0 to 254, the exit status 255 is reserved by PHP and shall not be used. The status 0 is used to terminate the program successfully."

Conclusion

In exploring the artisan file, I have regained an appreciation for Laravel's architecture.

The entrypoint for web requests (index.php) will feature many of the same structures and ideas.

💖 💪 🙅 🚩
obasekietinosa
Etinosa Obaseki

Posted on June 3, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related