How to Create a Psr-4 Autoloader for My Project

How to create a PSR-4 autoloader for my project?

If you are using composer, you do not create the autoloader but let composer do its job and create it for you.

The only thing you need to do is create the appropriate configuration on composer.json and execute composer dump-autoload.

E.g.:

{
"autoload": {
"psr-4": {"App\\": "src/"}
}
}

By doing the above, if you have a file structure like this

├── src/
│ ├── Controller/
│ ├── Model/
│ ├── View/
│ └── Kernel.php
├── public/
│ └── index.php
└── vendor/

After executing composer dump-autoload the autoloader will be generated on vendor/autoload.php.

All your classes should be nested inside the App namespace, and you should put only one class per file.

E.g.:

<?php /* src/Controller/Home.php */

namespace App\Controller;

class Home { /* implementation */ }

And you need only to include the autoloader in your entry-point script (e.g. index.php).

<?php

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

Which will allow you to simply load your classes directly from anywhere after this point, like this:

use App\Controller\Home;

$homeController = new Home();

This is explained at the docs, here.

PSR-4 autoloader issue

You need to include the Composer autoload file otherwise your application doesn't know what classes exist. It is a file created by composer when you install the dependencies, a lot of frameworks that use Composer will include this file for you automatically, but if you are not using a framework you will need to include the file yourself.

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

Where you need to put it will depend on your application but its best to load as early as possible, if you have a bootstrap file then that would be the place to put it.

You can read about it here

How to use the PSR-4 autoload in my /Classes/ folder?

There are mainly two ways to get it working: The first option is to use an absolute server path (starting with a '/'), to set the base directory for your classes in your autoload function:

spl_autoload_register(function ($class) {

$prefix = 'Classes\\';
$base_dir = '/var/www/html/my_project/src/'; // your classes folder
$len = strlen($prefix);
if (strncmp($prefix, $class, $len) !== 0) {
return;
}
$relative_class = substr($class, $len);
$file = $base_dir . str_replace('\\', '/', $relative_class) . '.php';
if (file_exists($file)) {
require $file;
}
});

The better solution is, as @Félix suggested, to stick with the __DIR__ constant to keep things relative to your project folder. Absolute paths are brittle between deployments on different servers. __DIR__ refers to the directory of the file it is used in; in this case it is where you register the autoload function. Starting from this directory, you can navigate to the classes base directory, for example $base_dir = __DIR__ . '/../../src/;

Don't forget to namespace your classes:

namespace Classes;

class Foo
{
public function test()
{
echo 'Hurray';
}
}

Then use classes like this:

$foo = new Classes\Foo();
$foo->test();

PSR4 auto load without composer

You have to read the composer and load the classes yourself for each namespaces defined into the composer.json.

Here is how :

function loadPackage($dir)
{
$composer = json_decode(file_get_contents("$dir/composer.json"), 1);
$namespaces = $composer['autoload']['psr-4'];

// Foreach namespace specified in the composer, load the given classes
foreach ($namespaces as $namespace => $classpaths) {
if (!is_array($classpaths)) {
$classpaths = array($classpaths);
}
spl_autoload_register(function ($classname) use ($namespace, $classpaths, $dir) {
// Check if the namespace matches the class we are looking for
if (preg_match("#^".preg_quote($namespace)."#", $classname)) {
// Remove the namespace from the file path since it's psr4
$classname = str_replace($namespace, "", $classname);
$filename = preg_replace("#\\\\#", "/", $classname).".php";
foreach ($classpaths as $classpath) {
$fullpath = $dir."/".$classpath."/$filename";
if (file_exists($fullpath)) {
include_once $fullpath;
}
}
}
});
}
}

loadPackage(__DIR__."/vendor/project");

new CompanyName\PackageName\Test();

Of course, I don't know the classes you have in the PackageName.
The /vendor/project is where your external library was cloned or downloaded. This is where you have the composer.json file.

Note: this works only for psr4 autoload.

EDIT : Adding support for multiple classpaths for one namespace

EDIT2 : I created a Github repo to handle this code, if anyone wants to improve it.

Custom autoloader for project vs Composer PSR-4

The solution to not force relative path depth is to use the autoloader returned by Composer like this:

$loader = require 'vendor/autoload.php';
$loader->add('', __DIR__ . '/app/');

Composer PSR-4 autoload is not working after trying multiple methods and tutorials

While testing the given code, the problem was resolved by renaming index.php within src to App.php which relates to class name in the file. Also used "xenframe\\hello\\": "src/" in psr-4 section in composer.json.

Also its worth mentioning that while composer was unable to autoload required class, due to PSR-4 non compliance, no error was reported by composer.

The problems were:

  1. The filename of class was not properly compliant with PSR-4 specifications. From section 2.3.3

The terminating class name corresponds to a file name ending in .php. The file name MUST match the case of the terminating class name.


  1. The namespace mentioned in psr-4 section in composer.json was not matching the namespace used in class file. From Composer's PSR-4 Schema

Under the psr-4 key you define a mapping from namespaces to paths, relative to the package root. When autoloading a class like Foo\\Bar\\Baz a namespace prefix Foo\\ pointing to a directory src/ means that the autoloader will look for a file named src/Bar/Baz.php and include it if present.



Related Topics



Leave a reply



Submit