Working well with a Laravel codebase requires solid understanding of how composer autoloading and class namespaces work.
In this article we will cover PSR-4 class autoloading through php composer and the related concept of class namespaces.
By the end of the article you should be able to read and understand any php codebase that uses autoloading.
Project directory setup
Create a new php project directory named autoload
and move into the project directory:
mkdir autoload
cd autoload
Create the basic php project structure:
mkdir app
touch index.php
touch composer.json
The index.php file is our application bootstrap file that starts the execution of our php code.
Autoload setup
Add the following to the composer.json
file:
{
"autoload": {
"psr-4": {
"App\\": "app/"
}
}
}
The json above maps the App
root namespace to the app
root directory in our project.
Any php class files in the app
directory will have the App
namespace.
With the autoload map in place we can run the composer dump-autoload
command to generate the vendor/autoload.php
file:
composer dump-autoload -o
Display the project directory structure with the tree
command:
tree
The result:
.
├── app
├── composer.json
├── index.php
└── vendor
├── autoload.php
└── composer
├── ClassLoader.php
├── LICENSE
├── autoload_classmap.php
├── autoload_namespaces.php
├── autoload_psr4.php
├── autoload_real.php
└── autoload_static.php
We can see that the composer dump-autoload
command created the vendor
directory and the vendor/autoload.php
file.
This vendor/autoload.php
file will be used to autoload any classes added to the app
directory or one of its subdirectories.
Using the autoload file
The final step of the autoloading setup is to use the generated the autoload.php
file in our index.php
bootstrap file.
Add the following to the index.php
file:
<?php
require_once __DIR__ . "/vendor/autoload.php";
// TODO add PHP code here
That is all that is needed to start using auto loaded php classes under the app
directory.
Auto loaded class example
First create a class file named Greeting.php
directly in the app
directory:
touch app/Greeting.php
All auto loaded class file names must begin with an uppercase letter.
Add the following to the new app/Greeting.php
file:
<?php
namespace App;
class Greeting
{
public function sayHello()
{
return "Hello";
}
}
The autoloading configuration in the composer.json
file maps the root App
namespace to the root app
directory.
Therefore the Greeting
class that is directly inside the app
directory will be in the App
namespace as specified by the namespace App
declaration at the top of the file.
The namespace declaration in the class file must match the PSR-4 autoloading configuration in the composer.json
file.
Using the Greeting class
Lets now use the class in our index.php file by changing the content of the index.php
file to:
<?php
require_once __DIR__ . "/vendor/autoload.php";
echo (new \App\Greeting)->sayHello();
echo (new \App\Greeting)->sayHello();
This example uses two separate instances of the fully namespace qualified class name (FQN) \App\Greeting
to print the text Hello
twice.
Run the index.php
bootstrap file using the php
command:
php index.php
You should see the output:
HelloHello
Alternatively instead of typing the FQN
of the class each time, we can use the namespace of the class using the use <namespace>\<class>
statement:
<?php
require_once __DIR__ . "/vendor/autoload.php";
use App\Greeting;
echo (new Greeting)->sayHello();
echo (new Greeting)->sayHello();
Using one class from another
We can also use one class from within another class.
First create a second class file named Audience.php
also directly in the app
directory:
touch app/Audience.php
Add the following to the app/Audience.php
file:
<?php
namespace App;
class Audience
{
public function toWorld()
{
return "World";
}
}
The namespace of this class is also App
since it is directly in the app
directory.
Now modify the content of the app/Greeting.php
file to use the Audience
class:
<?php
namespace App;
class Greeting
{
public function sayHello()
{
return "Hello" . (new Audience)->toWorld();
}
}
Note we did not have to use the FQN
of the Audience
class nor add the use App\Audience
statement, since both the Greeting
and Audience
classes are in the same namespace.
Run the bootstrap file again:
php index.php
We should see the output:
HelloWorldHelloWorld
Using the Audience class from index.php
If we wanted to use the Audience
class directly from our index.php
file, it would be no different than how we used the Greeting
class.
<?php
require_once __DIR__ . "/vendor/autoload.php";
use App\Greeting;
use App\Audience;
echo (new Greeting)->sayHello();
echo (new Audience)->toWorld();
Moving the Audience class to a different namespace
Create a new Helpers subdirectory under the app
directory:
mkdir -p app/Helpers
Note that the first letter of the subdirectory name Helpers
must be uppercase.
Delete the app/Audience.php
file and instead re-create it in the app/Helpers
directory:
rm app/Audience.php
touch app/Helpers/Audience.php
Add the following to app/Helpers/Audience.php
file:
<?php
namespace App\Helpers;
class Audience
{
public function toWorld()
{
return "World";
}
}
The only difference from before is that the namespace declaration has changed to App\Helpers
.
With autoloading, the path of the namespace declaration in the class file, starts with the root namespace App
which is mapped to the root app
directory, and follows the path of the class file directory.
So since Greeting.php
is in the root app
directory, the namespace declaration in the file is App
and since Audience.php
is in the app/Helpers
directory, the namespace declaration in the file is App\Helpers
.
In order for the casing of the directory names in the directory path and the namespace path declarations to match, the first letter of the subdirectory name of all subdirectories under the root directory app
must be uppercase.
Using the modified Audience class from the Greeting class
To use the new Audience
class from the Greeting
class, modify the app/Greeting.php
file:
<?php
namespace App;
use App\Helpers\Audience;
class Greeting
{
public function sayHello()
{
return "Hello" . (new Audience)->toWorld();
}
}
Since the Audience
class is now in different namespace, we added the use App\Helpers\Audience
statement to be able to reference it from the Greeting
class.
Using the modified Audience class from index.php
To use the modified Audience
class directly from the index.php
file, all we need to do is to use the new namespace of the Audience
class:
<?php
require_once __DIR__ . "/vendor/autoload.php";
use App\Greeting;
use App\Helpers\Audience;
echo (new Greeting)->sayHello();
echo (new Audience)->toWorld();
Moving the index.php file to a subdirectory
Web applications usually bootstrap using an index.php
file that is in a subdirectory named public
of the root application directory.
So we will move our index.php
file into a subdirectory named public
to see how we can use autoloading when it is in a subdirectory.
First create a subdirectory named public
in the project root directory.
mkdir public
Delete the index.php
file then create a new one inside the public directory:
rm index.php
touch public/index.php
Add the following to the public/index.php
bootstrap file:
<?php
require_once __DIR__ . "/../vendor/autoload.php";
use App\Greeting;
use App\Helpers\Audience;
echo (new Greeting)->sayHello();
echo (new Audience)->toWorld();
Note that the only thing that changed is the path to the autoload.php
file.
Since the index.php
file is in a subdirectory, we have to traverse to the parent directory before going down the composer generated vendor
directory path.
To run the index.php
file we now have to use the subdirectory path:
php public/index.php
A more robust index.php file
Modify the public/index.php
file to the following:
<?php
declare(strict_types=1);
require __DIR__ . '/../vendor/autoload.php';
ini_set('display_errors', '1');
//Default setting for php 8+
ini_set('display_startup_errors', '1');
//Default setting for php 8+
error_reporting(E_ALL);
use App\Greeting;
use App\Helpers\Audience;
try {
echo (new Greeting)->sayHello();
echo (new Audience)->toWorld();
} catch (Exception $e) {
echo $e->getMessage();
}
The new code configures some php.ini
file settings and adds some error handling.
There can be more than one root namespace
Generally most php applications or composer packages have one root autoloading directory and hence one root namespace.
However you can have multiple root autoloading directories using composer.json.
In addition, a root namespace can be mapped to a root directory that is a subdirectory.
The example below illustrates this.
Add a new root data/db
directory that is a subdirectory:
mkdir -p data/db
Map a new root namespace to the subdirectory:
{
"autoload": {
"psr-4": {
"App\\": "app/",
"Database\\": "data/db/"
}
}
}
We have added an additional root namespace named Database
in the example. This namespace is mapped to the root directory data/db
that is a subdirectory.
The root namespace App
maps to the app
directory and the root namespace Database
maps to the data/db
directory.
With this new autoload configuration, the namespace App\Helpers
maps to the app/Helpers
directory and the namespace Database\Migrations
for example maps to a data/db/Migrations
directory.
Whenever we add a new namespace mapping to composer.json we must run composer dump-autoload again to update the vendor/autoload.php
file.
composer dump-autoload -o
Conclusion
We created the most basic php example to teach the concept of PSR-4 autoloading using composer and the related concept of namespaces.
Using the namespace of a class, you can reference that class from any other class in any other namespace or you can reference it from the index.php
bootstrap file.
Also classes from any package used by your application are referenced using namespaces that are autoloaded by the package.
A php or Laravel package autoloads the classes of the package using the autoload mapping in the composer.json file of the package.
When a package is required in our application, all its autoloaded classes will be available to the application.
That is the power of composer autoloading.
With this basic understanding, you can read and understand any php code or create your own autoloaded classes.