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:
- 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.
- The
namespace
mentioned inpsr-4
section incomposer.json
was not matching thenamespace
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 likeFoo\\Bar\\Baz
a namespace prefixFoo\\
pointing to a directorysrc/
means that the autoloader will look for a file namedsrc/Bar/Baz.php
and include it if present.
Related Topics
Php: Inserting Values from the Form into MySQL
Get the Latest File Addition in a Directory
Utf-8 Encoded HTML Pages Show Questions Marks Instead of Characters
How to Create Codeigniter Batch Insert Array
PHP Emitting 500 on Errors - Where Is This Documented
Laravel Orderby Relationship Count
Select Outgoing Ip for Curl Request
Seamless Way to Check If User Likes Page
PHP Foreach Loop Through Multidimensional Array
How to Call Codeigniter Controller Function from View
Unexpected Character in Input: '\' (Ascii=92) State=1
Fatal Error: Using $This When Not in Object Context
Laravel Request::All() Should Not Be Called Statically