Autoloading helps us to organize our code in a clean fashion. This makes it easier to understand how all of the pieces fit together. In fact, I would go as far as to say that code organization is half the battle. With the introduction of Composer to the PHP community, we now have a way to handle code organization like never before. As we know, Composer is The Dependency Manager for PHP. In addition to dependency management, Composer also provides efficient autoloading for us. We’ll explore autoloading in this tutorial now.
In our dependency injection for beginners tutorial, we built up a suite of classes to help us win the FIFA Soccer world championship. We had a Country, some Players, a Team, and many supporting methods and properties to accomplish our goal. We actually constructed our little application in a very top-down procedural style. That is to say, we had one big PHP file that kind of looked like this.
- Class 1
- Class 2
- Class 3
- Client code / Implementation of classes.
This is perfect for learning and tutorial-style applications. We can very quickly view everything that is happening in one simple PHP file. If you would like to build a larger application, however, this approach will begin to fail pretty quickly. Maybe you have ten or fifteen classes to account for, and many lines of code in your client to implement. One PHP file is not going to cut it in this case. The convention is actually to create one class per PHP file. From there, your client can either include the needed classes or autoload them for use. Let’s try to break our prior application into a proper organization with autoloading.
The /root
directory
We can start with the root directory. In this directory, we will have our client code in the index.php
file, in addition to a source
directory which holds our classes. Inside the source
directory are the following three files.
/root/source/Country.php
<?php
class Country
{
protected $team;
protected $name;
public function __construct(Team $team, $name = 'USA')
{
$this->team = $team;
$this->name = $name;
}
public function recruit(Player $player)
{
$this->team->join($player);
}
}
/root/source/Player.php
<?php
class Player
{
protected $name;
public function __construct($name)
{
$this->name = $name;
}
}
/root/source/Team.php
<?php
class Team
{
protected $players = [];
public function __construct($players = [])
{
$this->players = $players;
}
public function join(Player $player)
{
$this->players[] = $player;
}
public function getplayers()
{
return $this->players;
}
}
/root/index.php
In our root directory, we also have our client code which looks like this.
<?php
$team = new Team;
$usa = new Country($team);
$player1 = new Player('Carli Lloyd');
$usa->recruit($player1);
$player2 = new Player('Morgan Brian');
$player3 = new Player('Ashlyn Harris');
$player4 = new Player('Tobin Heath');
$player5 = new Player('Alex Morgan');
$usa->recruit($player2);
$usa->recruit($player3);
$usa->recruit($player4);
$usa->recruit($player5);
echo '<pre>';
print_r($team->getplayers());
When we try to run our program, things are not looking good!
Fatal error: Class ‘Team’ not found in C:\wamp\www\root\index.php on line 3
include
the Needed Class Files
You might be thinking, well that is an easy fix. We just need to include the needed class files like so.
include('source/Country.php');
include('source/Player.php');
include('source/Team.php');
$team = new Team;
$usa = new Country($team);
$player1 = new Player('Carli Lloyd');
$usa->recruit($player1);
$player2 = new Player('Morgan Brian');
$player3 = new Player('Ashlyn Harris');
$player4 = new Player('Tobin Heath');
$player5 = new Player('Alex Morgan');
$usa->recruit($player2);
$usa->recruit($player3);
$usa->recruit($player4);
$usa->recruit($player5);
echo '<pre>';
print_r($team->getplayers());
When we run this, everything does work just fine.
There is a better way, however, and that is to make use of autoloading with Composer. Manually including files is not going to scale, and will lead to trouble down the line. Let’s see how to fix this with Composer.
Autoload With Composer
From here on out, we’ll assume you already have Composer installed. If you don’t, go ahead and install now before proceeding. With Composer installed, we can navigate to our project root using the terminal. In our case that is C:\wamp\www\root> If you’ve never used Composer before, simply type composer
to see what is available to us.
C:\wamp\www\root>composer ______ / ____/___ ____ ___ ____ ____ ________ _____ / / / __ \/ __ `__ \/ __ \/ __ \/ ___/ _ \/ ___/ / /___/ /_/ / / / / / / /_/ / /_/ (__ ) __/ / \____/\____/_/ /_/ /_/ .___/\____/____/\___/_/ /_/ Composer version 1.0-dev (39e6f51bef3a4b148d9f35a0ae7a082e9aed97f4) 2015-07-09 11:04:41 Usage: command [options] [arguments] Options: --help (-h) Display this help message --quiet (-q) Do not output any message --verbose (-v|vv|vvv) Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug --version (-V) Display this application version --ansi Force ANSI output --no-ansi Disable ANSI output --no-interaction (-n) Do not ask any interactive question --profile Display timing and memory usage information --working-dir (-d) If specified, use the given directory as working directory. Available commands: about Short information about Composer archive Create an archive of this composer package browse Opens the package's repository URL or homepage in your browser. clear-cache Clears composer's internal package cache. clearcache Clears composer's internal package cache. config Set config options create-project Create new project from a package into given directory. depends Shows which packages depend on the given package diagnose Diagnoses the system to identify common errors. dump-autoload Dumps the autoloader dumpautoload Dumps the autoloader global Allows running commands in the global composer dir ($COMPOSER_HOME). help Displays help for a command home Opens the package's repository URL or homepage in your browser. info Show information about packages init Creates a basic composer.json file in current directory. install Installs the project dependencies from the composer.lock file if present, or falls back on the composer.json. licenses Show information about licenses of dependencies list Lists commands remove Removes a package from the require or require-dev require Adds required packages to your composer.json and installs them run-script Run the scripts defined in composer.json. search Search for packages self-update Updates composer.phar to the latest version. selfupdate Updates composer.phar to the latest version. show Show information about packages status Show a list of locally modified packages suggests Show package suggestions update Updates your dependencies to the latest version according to composer.json, and updates the composer.lock file. validate Validates a composer.json
We need a composer.json
before anything else, so that init
command looks good to us. This command will take you through the process of creating a project using a composer.json file. For the purposes of this tutorial, it will create a lot you don’t need, so once you have your composer.json, simply edit it so you only have an empty file containing the { and } characters, then run composer install
which will generate the autoload.php file we need. In fact, a few things are now created in our project, let’s see.
/root
Note we now have a vendor
directory in our root
.
/root/vendor
As well as a newly created autoload.php file
/root/vendor/composer
And finally, some various supporting files for Composer.
Behold! The Autoload File!
<?php
// autoload.php @generated by Composer
require_once __DIR__ . '/composer' . '/autoload_real.php';
return ComposerAutoloaderInit8ba53b7977ed0d2d530d1c0d7714ddcf::getLoader();
Configure PSR4
We’re almost there. First, we need to configure the composer.json file for PSR4 autoloading.
{ "autoload": { "psr-4": { "Myapp\\": "source" } } }
Once you declare your namespace, you’ll need to run composer dump-autoload
in order to generate the proper autoloading files. Let’s see.
autoload_psr4.php
<?php
// autoload_psr4.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'Myapp\\' => array($baseDir . '/source'),
);
Include the Autoloader
Recall that the index.php
file lives in the root of our project. Note that in a prior step, we simply used three include statements to make use of the three classes we need to run our application. At this point, we are no longer using multiple include statements, but we will need to include the autoloader file just one time in our client code so that it will automatically load any class we need. In addition, we will add use
statements for the Team, Country, and Player classes. We can update our client code like so.
<?php
require('vendor/autoload.php');
use Myapp\Team;
use Myapp\Country;
use Myapp\Player;
$team = new Team;
$usa = new Country($team);
$player1 = new Player('Carli Lloyd');
$usa->recruit($player1);
$player2 = new Player('Morgan Brian');
$player3 = new Player('Ashlyn Harris');
$player4 = new Player('Tobin Heath');
$player5 = new Player('Alex Morgan');
$usa->recruit($player2);
$usa->recruit($player3);
$usa->recruit($player4);
$usa->recruit($player5);
echo '<pre>';
print_r($team->getplayers());
This is perfect, but when we try to run our application, we get an error of: Fatal error: Class ‘Myapp\Team’ not found in C:\wamp\www\root\index.php on line 9. This is because we had set a namespace for Myapp. If we declare a namespace via PSR4, we need to make sure to update our class files to make use of those namespaces.
Update Class Namespaces
This is super easy. All we have to do is add namespace Myapp;
to the top of our Class files.
/root/source/Country.php
<?php
namespace Myapp;
class Country
{
protected $team;
protected $name;
public function __construct(Team $team, $name = 'USA')
{
$this->team = $team;
$this->name = $name;
}
public function recruit(Player $player)
{
$this->team->join($player);
}
}
/root/source/Player.php
<?php
namespace Myapp;
class Player
{
protected $name;
public function __construct($name)
{
$this->name = $name;
}
}
/root/source/Team.php
<?php
namespace Myapp;
class Team
{
protected $players = [];
public function __construct($players = [])
{
$this->players = $players;
}
public function join(Player $player)
{
$this->players[] = $player;
}
public function getplayers()
{
return $this->players;
}
}
Now, when we run the client code, we are right back to our championship-winning ways.
Composer Autoloading Summary
Composer is the best thing in the world for PHP developers. In this tutorial, we took a look at just one aspect of what makes Composer so great, and that is its ability to generate robust autoloading components for your application. With this newfound skill, you can better organize and scale your applications no matter how many class dependencies they might have.