In this post we will code a custom autoloading solution in Core PHP from scratch and will also take a look at utilizing composer for autoloading in Core PHP.

If you have used a framework like laravel, you may wonder how it autoloads classes without including a source php file of that class.

In core PHP, you have to include the source file of the class in order to use it somewhere in another file.

For example:

<?php
// UserModel.php

class UserModel
{

}

And to use this UserModel class you have to include this file before using UserModel class.

<?php
// using UserModel.php in some file

require('path/to/UserModel.php');

$user = new UserModel();

But if you are using some framework like Laravel, You don't have to include the source file of the class in order to use it.

For example in Laravel, to use the user model in the controller you just have to declare the namespace of the model using the keyword "use".

example:

<?php
namespace App\Http\Controllers;

use App\Http\Controllers\Controller;
use App\Models\User;

class UserController extends Controller
{

  public function show($id)
   {
      return view('user.profile', [
        'user' => User::findOrFail($id)
      ]);
  }
}

See the above beautiful code snippet of the laravel controller, Is it not elegant as Laravel promises? Have you noticed? there is no statement to include base Controller and model User. But both of the classes have been used in the code example. If you have used Laravel you know it will work, if you have not used it, Trust me! This is how it works, Yeah without including source files, you can use classes from all over the Laravel, anywhere in the Laravel context.

Is it possible to use class without including a source file? The answer is NO. Then how does a framework like Laravel do it? you will see later in this post. Let's first understand how autoloading works with core php.

If you are doing a Core PHP project, PHP already has a default autoloader spl_autoload() for you, You just need to activate it.

Before starting with the coding part, I want you to create a separate folder for each of the following examples.

Example of using default autoloader

<?php
// index.php

// directory that contains your classes
define('CLASS_DIR', 'class/');

// Add your class dir to include path
set_include_path(get_include_path() . PATH_SEPARATOR . CLASS_DIR);

// Use default autoload implementation
spl_autoload_register();


$t = new Test();

echo $t->somefunc();

Now create a Test class with method somefunc() in the class folder for testing purposes.

<?php
// class/test.php

class Test
{
  function somefunc()
  {
    return "I'm test method of the Test class.";
  }
}

Run with php cli and the output should be:

$ php index.php
I'm the test method of the Test class.

You can find all the code snippets used in this post in attachments at the end of the post. This code snippet is in folder "php-inbuilt-autoloader-1".

You can also configure suffix or extension, see the below example

<?php
// index.php

// directory that contains your classes
define('CLASS_DIR', 'class/');

// add your class dir to include path
set_include_path(get_include_path() . PATH_SEPARATOR . CLASS_DIR);

// use default autoload implementation
spl_autoload_register();

// autoloader will look for files with <CLASS_NAME>.class.php extension in the class folder
spl_autoload_extensions('.class.php');

$t = new Test();

echo $t->somefunc();
<?php
// class/test.class.php

class Test
{
  function somefunc()
  {
    return "I'm test method of the Test class sufixed with .class.php.";
  }
}

You can find all the code snippets used in this post in attachments at the end of the post. This code snippet is in folder "php-inbuilt-autoloader-2".

Default autoloader also plays well with namespaces. It will auto map namespace to source file path.

For example, create a vendor folder in the class directory. Now in the vendor folder create Package Folder and in Package folder create a file test.class.php.

Directory Structure: class/Vendor/Package/test.class.php

  • source root folder
  • class
    • Vendor
    • Package
      • test.class.php
<?php
// class/Vendor/Package/test.class.php

namespace Vendor\Package;

class Test
{
  function somefunc()
  {
    return "I'm test method of the Vendor\Package\Test class suffixed with .class.php.";
  }
}
<?php

// directory that contains your classes
define('CLASS_DIR', 'class/');

// add your class dir to include path
set_include_path(get_include_path() . PATH_SEPARATOR . CLASS_DIR);

// use default autoload implementation
spl_autoload_register();

// autoloader will look for files with <CLASS_NAME>.class.php extension in the class folder
spl_autoload_extensions('.class.php');

$t = new Vendor\Package\Test();

echo $t->somefunc();

Output:

$ php index.php 
I'm testing the Vendor\Package\Test class suffixed with .class.php.

You can find all the code snippets used in this post in attachments at the end of the post. This code snippet is in folder "php-inbuilt-autoloader-3".

PHP also allows you to register your own custom autoloader using spl_autoload_register() that is used to locate and include the source file of the class.

Let's see how we can code custom solutions to auto include files.

Create file test.php under directory vendor/Bitnbytes/Package, class file path should be vendor/Bitnbytes/Package/Test.php.

<?php
// vendor/Bitnbytes/Package/Test.php

namespace Bitnbytes\Package;

class Test
{
  function somefunc()
  {
    return "I'm somefunc() from Bitnbytes package class Test.";
  }
}

Register a custom autoloader to load Test class from our custom package.

<?php
// index.php

define('CLASS_DIR', 'vendor/');

spl_autoload_register(function ($class) {
  // replace namespace with directory separator
  $file = CLASS_DIR . str_replace('\\', DIRECTORY_SEPARATOR, $class) . '.php';

  // if the file exists, require it
  if (file_exists($file)) {
    require $file;
  }
});


$t = new Bitnbytes\Package\Test();
echo $t->somefunc();

Output:

$ php index.php
I'm somefunc() from Bitnbytes package class Test.

You can find all the code snippets used in this post in attachments at the end of the post. This code snippet is in the folder "php-custom-autoloader-1".

You may ask that the given code snippets only allow you to autoload from one base directory, but laravel autoload classes from many different directories. Of Course you can also do that by registering several autoloaders or either with one also.

Let me show you first with separate autoloaders for each directory.

Create files with appropriate directories

  • app/Controllers/SomeController.php
  • app/Models/User.php
  • vendor/Bitnbytes/Package/Test.php
  • index.php
<?php
// app/Controllers/SomeController.php

namespace App\Controllers;

// will be autoloaded by app directory loader
use App\Models\User;
// will be autoloaded by vendor directory loader
use Bitnbytes\Package\Test;

class SomeController
{
  function test()
  {

    // Why PHP_EOL? so that each output should be in separate line

    $t = new Test();
    echo $t->somefunc() . PHP_EOL;

    $t = new User();
    echo $t->test() . PHP_EOL;
    echo "I'm test method from SomeController class.";
  }
}
<?php
// app/Models/User.php

namespace App\Models;

class User
{
  function test()
  {
    return "I'm test method from User class.";
  }
}
<?php
// vendor/Bitnbytes/Package/Test.php

namespace Bitnbytes\Package;

class Test
{
  function somefunc()
  {
    return "I'm somefunc() from Bitnbytes package class Test.";
  }
}
<?php
// index.php

define('VENDOR_DIR', 'vendor/');
define('APP_DIR', 'app/');

// vendor directory loader
spl_autoload_register(function ($class) {
  // replace namespace with directory separator
  $file = VENDOR_DIR . str_replace('\\', DIRECTORY_SEPARATOR, $class) . '.php';

  // if the file exists, require it
  if (file_exists($file)) {
    require $file;
  }
});

// app directory loader
spl_autoload_register(function ($class) {
  // why double slash? for escaping purpose
  $prefix = 'App\\';

  // does the class use the App prefix
  $len = strlen($prefix);
  if (strncmp($prefix, $class, $len) !== 0) {
    // no, return;
    return;
  }

  $relativeClass = substr($class, $len);

  // replace namespace with directory separator
  $file = APP_DIR . str_replace('\\', DIRECTORY_SEPARATOR, $relativeClass) . '.php';

  // if the file exists, require it
  if (file_exists($file)) {
    require $file;
  }
});


$c = new App\Controllers\SomeController();
$c->test();

Now run the index.php with php cli and output should be:

$ php index.php 
I'm somefunc() from Bitnbytes package class Test.
I'm test method from User class.
I'm test method from SomeController class.

You can find all the code snippets used in this post in attachments at the end of the post. This code snippet is in folder "php-custom-autoloader-2".

How can this be done with just one autoloader?

Just modify your index.php

<?php
// index.php

define('VENDOR_DIR', 'vendor/');
define('APP_DIR', 'app/');

spl_autoload_register(function ($class) {
  // app directory loader

  // why double slash? for escaping purpose
  $prefix = 'App\\';

  // does the class use the App prefix
  $len = strlen($prefix);
  if (strncmp($prefix, $class, $len) !== 0) {
    // look in vendor directory

    // replace namespace with directory separator
    $file = VENDOR_DIR . str_replace('\\', DIRECTORY_SEPARATOR, $class) . '.php';

    // if the file exists, require it
    if (file_exists($file)) {
      require $file;
    }

    // nothing found, return;
    return;
  }

  $relativeClass = substr($class, $len);

  // replace namespace with directory separator
  $file = APP_DIR . str_replace('\\', DIRECTORY_SEPARATOR, $relativeClass) . '.php';

  // if the file exists, require it
  if (file_exists($file)) {
    require $file;
  }
});


$c = new App\Controllers\SomeController();
$c->test();

You can find all the code snippets used in this post in attachments at the end of the post. This code snippet is in the folder "php-custom-autoloader-3".

When you headed to this post regarding autoloading, composer may come to your mind. Some of you may be expecting this post to be about autoloading with composer. If you are that someone, Let's cover the autoloading with the composer too.

What is Composer?
Composer is the application level tool for the dependency management in PHP, like npm in Node.js. Composer also allows you to declare directories for autoloading, that will omit include statements from your code and make it look cleaner.

You can download composer by clicking this link and follow installing instructions given on https://getcomposer.org/doc/00-intro.md.

If you're struggling to set up composer, just download composer.phar by clicking here and copy it to your working directory.

Create composer.json file, use command php composer.phar init or php composer init, depending on installation.

php composer.phar init

Provide the information asked by composer:

  • Package name (/) [lenovo/php-custom-autoloader-4]: bitnbytes/phpcomposer
  • Description []: this is to demonstrate how to use composer in core php project
  • Ignore below by just clicking enter
  • Author [Harcharan Singh bitnbytesio@gmail.com, n to skip]:
  • Minimum Stability []:
  • Package Type (e.g. library, project, metapackage, composer-plugin) []: project
  • License []: UNLICENSED
  • Would you like to define your dependencies (require) interactively [yes]? no
  • Would you like to define your dev dependencies (require-dev) interactively [yes]? no

Or just create a new composer.json file in working directory and copy the below snippet

{
    "name": "bitnbytes/phpcomposer",
    "description": "this is to demonstrate how to use composer in core php project",
    "type": "project",
    "license": "UNLICENSED",
    "authors": [
        {
            "name": "Harcharan Singh",
            "email": "bitnbytesio@gmail.com"
        }
    ],
    "require": {}
}

Create below directory structure:

  • app/Controllers/SomeController.php
  • app/Models/User.php

Note: make sure to delete the vendor directory if you are using the same directory that we have used in previous examples. I recommend you create a new directory.

Code snippet for the index.php

<?php
// index.php
$loader = include_once(__DIR__ . '/vendor/autoload.php');

$loader->addPsr4('App\\', 'app');

$c = new App\Controllers\SomeController();
$c->test();

Use below code snippet for Our SomeController.php

<?php
// app/Controllers/SomeController.php

namespace App\Controllers;

// will be autoloaded by app directory loader
use App\Models\User;


class SomeController
{
  function test()
  {

    // Why PHP_EOL? so that each output should be in separate line

    $t = new User();
    echo $t->test() . PHP_EOL;
    echo "I'm test method from SomeController class.";
  }
}

Our User model file User.php should look like:

<?php
// app/Models/User.php

namespace App\Models;

class User
{
  function test()
  {
    return "I'm test method from User class.";
  }
}

Now run the index.php file with php cli using command php index.php.

$ php index.php 
I'm test method from User class.
I'm test method from SomeController class.

Using composer.json to register namespaces for the project

Modify composer.json, we just added autoload section and the rest of the json will remain the same.

{
    "name": "bitnbytes/phpcomposer",
    "description": "this is to demonstrate how to use composer in core php project",
    "type": "project",
    "license": "UNLICENSED",
    "authors": [
        {
            "name": "Harcharan Singh",
            "email": "bitnbytesio@gmail.com"
        }
    ],
    "require": {},
    "autoload": {
        "psr-4": {
            "App\\": "app"
        }
    }
}

After modifying the composer.json use the command: php composer.phar dump-autoload or php composer dump-autoload, again depends on your installation.

I'm using:

php composer.phar dump-autoload

Create a new file use-composer-json.php

<?php
// use-composer-json.php
include_once(__DIR__ . '/vendor/autoload.php');

$c = new App\Controllers\SomeController();
$c->test();

Now run the use-composer-json.php file with php cli using command php use-composer-json.php

Output:

$ php use-composer-json.php
I'm test method from User class.
I'm test method from SomeController class.

You can find all the code snippets used in this post in attachments at the end of the post. This code snippet is in the folder "php-custom-autoloader-4".

PSR-4 Autoloading Standard

PSR-4 is a PHP Standard Recommendation defined by the PHP-FIG (Framework Interop Group). It defines how class names should map to file paths, so that any compliant autoloader can find and load a class automatically.

The key rules of PSR-4:

  • A fully qualified class name like Vendor\Package\ClassName maps to the file vendor/package/ClassName.php
  • The namespace prefix maps to a base directory
  • Sub-namespaces map to sub-directories within that base directory
  • The class name maps exactly to the filename (case-sensitive)

For example, with the mapping App\\app/:

| Class | File | |---|---| | App\Controllers\UserController | app/Controllers/UserController.php | | App\Models\User | app/Models/User.php |

All major PHP frameworks — Laravel, Symfony, Slim — use PSR-4. Composer implements PSR-4 out of the box, which is why you don't need to write your own autoloader when using Composer.

Classmap Autoloading with Composer

Classmap autoloading tells Composer to scan specified directories, find all PHP files, and build a static map of class names to file paths. Unlike PSR-4, it does not require any specific naming convention — it works with any class structure.

Add a classmap key to your composer.json:

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

After updating composer.json, run:

php composer.phar dump-autoload

Composer scans all .php files in the listed directories and generates the map in vendor/composer/autoload_classmap.php. This is useful for legacy codebases that don't follow PSR-4.

Files Autoloading with Composer

PSR-4 and classmap only handle classes. If you have PHP files that contain global helper functions (not classes), use the files autoloading type. These files are loaded on every request automatically.

{
    "autoload": {
        "psr-4": {
            "App\\": "app"
        },
        "files": [
            "src/helpers.php"
        ]
    }
}

For example, src/helpers.php might contain:

<?php
// src/helpers.php

function formatDate($date) {
    return date('Y-m-d', strtotime($date));
}

function slugify($text) {
    return strtolower(trim(preg_replace('/[^A-Za-z0-9-]+/', '-', $text)));
}

After updating composer.json, run dump-autoload and these functions will be available everywhere in your project — exactly how Laravel's helpers.php works.

Hope now you have understood how autoloading works in PHP, you can code your own autoloading solution for the core PHP project, you have also understood how frameworks like Laravel do it using Composer.

For latest updates: join me on Telegram

Have any questions or suggestions? Share them in the comments.