Never require() anything in PHP again

Posted by

In my last article I hinted at a way to setup HHVM so you never need to require() a single file ever again at the top of your PHP code. (This can also be done using PHP 7 without HHVM but with some caveats).

I got a lot of comments asking how to achieve this.

There’s a very easy way to get this to work for your web code or even your php scripts. It requires two fairly simple steps…

 

1. You must use Composer in your project.

Autoloading is a system in PHP that allows you to write a function that tells the PHP runtime where to find the files that contain any symbol you want to use at runtime.

You must set this up once at the entry point where your code runs, and after that any and all code can happily run without needing to ever require() a single file ever again.

It is generally fairly tedious to manage a manual function to do the autoloading for you. You have to also remember to update the file any time you add a new class or file, which is painful.

But fortunately, Composer can take care of writing this function and keeping it updated on your behalf!

PHP 7

First your project must use composer. Composer is a package management system for PHP and can be found here: https://getcomposer.org/

If you are using PHP 7 and composer, you can add your own code paths to be included as part of the autoload that composer already generates for libraries you are using. This will crawl your code and add appropriate mappings from the classes you wrote to their respective files.

This is covered in the Composer docs here.

The key is to add a list of namespaces or folders to be included in your composer.json, for example:

{
    "autoload": {
        "classmap": ["src/", "lib/", "Something.php"]
    }
}

The autoloader now can be setup by the following code (adjust the path correctly depending on where your script is relative to the vendor/ directory):

require_once(__DIR__.'/vendor/autoload.php');

HHVM

If you want to go even further and happen to be using HHVM in your project (and you should), you can get autoload to work for all top level symbols: Classes, functions outside of classes and even constants outside of classes.

Create a hh_autoload.json file in the root directory of your project and put the following json in it:

{
  "roots": [ "php/"]
}

 

Then run the following:

composer require hhvm
composer require facebook/hhvm-autoload

This assumes all your php code is under php/ . If it’s in a different directory like “src/” then put that in instead. You can add more than one directory by adding more values inside the square brackets (comma-separated / inside double quotes).

 

At this point assuming there were no errors, you will get a generated file in vendor/hh_autoload.php which contains the code to run to setup autoload for ALL symbols in your project.

 

By adding the following require code, your entire code can now be autoloaded with no further require calls:

 

require_once(__DIR__.'/vendor/hh_autoload.php');

Now any time you add a new top-level symbol (class, function, constant), you must run the following command to crawl your code again and update the autoload file (regardless of whether you use HHVM or PHP 7):

 

composer dump-autoload

 
This takes care of the first part.

 

Now in order to never have to require() in every possible entry-point file, we need to ensure all web requests flow through a single script...

 

2.All your requests need to go through a single router script.

 

The goal of this is to have every single web request, no matter the URL, come to a single PHP script.

The PHP script should then “route” the request to the appropriate controller class (or whatever code you have to render your page).

 

If you are using controller classes (you should, it’s a neat way of organizing out your code), you can use a package called “fastRoute” to generate efficient code for parsing out your routes.

 

Run the following command in your project root:

composer require nikic/fast-route

Then you can use the default example provided on the github repo for fastRoute to setup a basic router:

https://github.com/nikic/FastRoute

 

The only change from the example is to replace the require line with the appropriate require line instead and .

 

require_once(__DIR__.'/vendor/hh_autoload.php');  // HHVM

require_once(__DIR__.'/vendor/autoload.php');  // PHP 7

 

(This assumes your router script is in the root of the project. If not, adjust the above paths as needed)

 

At this point you still need to tell your HTTP server to route all requests through index.php in order for this to work.

I use nginx so the following worked for my setup in my /etc/nginx/sites-enabled/default file:

 

location / {
  try_files $uri $uri/ /index.php;
}

 

This makes nginx first try the actual uri file first, then default back to index.php.

 

At this point if you setup a test controller on a URL like /test, everything should work fine and get routed properly through the router, which will automatically setup the autoload code for you beforehand.

 

Voila, you never need to write a single require() call again!

One comment

Leave a Reply