Difference between PSR-4 and classmap autoloading? - composer-php

In regards to Laravel, I got a question about Composer autoloading i.e. the difference between "psr-4" and "classmap" autoloading.
1 difference that I know is PSR-4 does not need repeated dumpautoload for every changes or addition of new files whereas classmap needs dumpautoload for every change in existing files containing classes or addition of new file in specified directory.

PSR-4 standard requires of you a strict filesystem structure based on namespaces. Say you have an app in src directory with App namespace, then all sub-namespaces will mirror subdirectories and class names will be the same as file names without the .php extension.
{
"autoload": {
"psr-4": { "App\\": "src/" }
}
}
src/
Foo/
Bar.php <---- App\Foo\Bar class
Baz.php <---- App\Baz class
The autoloader then "knows" where to look for the class of a certain fully qualified name and therefore doesn't require the dump-autoload command to sniff files for classes.
Performance issues are then solved with composer dump-autoload --optimize-autoloader flag, or -o, which will generate class map a similar way the classmap autoloading does.
On the other hand, classmap autoloading does not require you to have almost any certain file or directory structure, it will recursively go through .php and .inc files in specified directories and files and sniff for classes in them.
{
"autoload": {
"classmap": ["src/", "lib/", "Something.php"]
}
}
Those classes are then added to a list (cached in a PHP file in vendor/composer directory) which is used for autoloading.
Any new class then must be added to that list by running composer dump-autoload command.

Related

PSR-4 autoloading not working for class declared from parent directory

I have the following declaration for PSR-4 autoloading in my composer.json file:
"autoload": {
"psr-4": {
"Application\\": "../source/",
"ACME\\": "source/"
}
}
My directory structure is as follows:
acme-testbed/
framework/
source/
Interfaces/
Helpers/
I18N.php
Helpers/
I18N.php
composer.json
source/
Interfaces/
Modules/
Frontend.php
Modules/
Frontend.php
This way, I can do things like \ACME\Helpers\I18N::getLanguages() or \Application\Modules\Frontend::setLanguage("es") (I would really use aliases to do just I18N::getLanguages() or Frontend::setLanguage("es") but it's just for illustrative purposes).
The problem I have is my application finds every single class under the ACME namespace but not the Application one. As you can see, every declaration for the ACME namespace is inside the framework folder (same as the composer.json file) but every declaration of the Application namespace lies within the application root, which is its parent directory.
How can I adjust the composer.json file so I can use both namespaced declarations?
EDIT #1: just realized I shown the wrong folder structure (there's no Application folder for the Application namespace declarations). My bad :)
If your namespaces match directories names, you should define autoload rules like this:
"autoload": {
"psr-4": {
"Application\\": "../source/Application/",
"ACME\\": "source/"
}
}
In your case Application namespace is in acme-testbed/source/Application, so you need to point it directly.

Why did composer install oauth2-client with different directory names and files

I am new to composer and I used it to install the oauth2-client. I think I am having some sort of misunderstanding about how this is supposed to work.
From thephpleague github page I installed from the command line using
composer require league/oauth2-client
This added files to /usr/local/bin/vendor/league/oauth2-client.
The file structure looks the same as it does on github, except I don't have all the same files.
And the php in the files is looking for files in \League\OAuth2, so I am getting errors that it can't find included files, because I don't have that directory.
Did I do it wrong, or am I just not getting something?
The backslash is the PHP namespace separator, not the directory separator.
In the composer.json for oauth2 from TheLeague, this is the autoload directive:
"autoload": {
"psr-4": {
"League\\OAuth2\\Client\\": "src/"
}
},
It says that the code inside of src directory is in the League\OAuth2\Client namespace.
Composer follows PSR-4 with regards to namespacing and autoloading, so check that out if you want to know what goes on.
UPDATE:
If you've installed other League extensions, like oauth2-facebook, it will install itself into the same src directory - because of the autoload directive in composer.json.
Why?
Well, because of the namespace, you will find 'Facebook' in the League\OAuth2\Client\Provider namespace.
Because of PSR-4, this means that they need to go into the same directory, even though they are different packages.
That is the reason why you'll see Facebook.php in src/Providers directory. Check the oauth2-facebook repository
You probably have required oauth2-facebook and oauth2-google, or one of your other required packages requires it. It rarely just add themselves. :)

Composer private package issue

I have created a private composer package in packagist.com but when I use
composer require command to fetch it. My package coming under vendor folder which is on root.
But I want it to be in app/code folder. Is there any parameter for composer.json where I can set app/code, so it will come under app/code/.
Yes there is. According to the composer documentation, if your project uses the PSR-4 specification for loading classes, then adding the snippet below to the composer.json file would work.
NB: You will need to change ProjectNameSpace to the base namespace for your project.
...
"autoload": {
"psr-4": {
"ProjectNameSpace\\": "app/code"
}
},
...
Theoretically. You can't composer will always place the code in vendor directory. The code is external and therefore can be updated only by composer. It must not be in app/code as only code you modify for the project should be in app/code.
If you wish to make a Magento project, you should have the fallowing files in the versioning tool.
app/*
composer.json
composer.lock
.htaccess
index.php
The other files will be handled by composer.
But if you really need to do it, but I don't see any reason to do so, then you could use a post-update & post-install script to move the code. But it's a very bad idea.

psr-0 autoloading with composer not wotking, but it works perfectly with psr-4 autoloading

I tried to autoload a file with PSR-0 ,but it is not auto loading that file. I tried the same file with PSR-4 auto loading. With PSR-4 it worked perfectly. Is there any difference in folder structure needed for PSR-0?
I couldn't make PSR-0 working even if keep the folder structure mentioned in What is the difference between PSR-0 and PSR-4?
Here is my folder structure.
Test
--Package
--Test.php
I have in Test.php:
<?php
namespace Test\Package;
class Test
{
public function __construct()
{
echo "In Test class";
}
}
and composer.json looks like
{
"autoload": {
"psr-0": {
"Test\\Package\\": "Test/Package/"
}
}
}
Counter-intuitively, the composer documentation on PSR-0 includes a partial path making it seem that PSR-0 requires a path to the package in order to load classes. In reality, PSR-0 constructs the path based on the package, so it only needs a path specified if the code lives inside a folder like src/ or lib/ that is not part of the namespace path. If the namespace based directory structure starts in the same directory as composer.json, then no path is required.
Assuming a directory structure as specified in the question, there's several ways to load this class using composer.
PSR-0
{
"autoload": {
"psr-0": { "Test\\Package\\": "" }
}
}
Note that even though the code lives in Test/Package/, this folder is not specified in PSR-0.
PSR-4
For PSR-4 autoloading, the path to the package source must appear in the composer.json file.
{
"autoload": {
"psr-4": { "Test\\Package\\": "Test/Package/" }
}
}
Classmap
When the requirement exists to load classes which aren't organized into the typical namespace folder tree, it is also possible to simply specify a list of folders in which to search for classes using the classmap array.
{
"autoload": {
"classmap": [ "Test/Package/" ]
}
}
In general, however, using PSR-0 or PSR-4 will provide an easier experience, as the classmap approach requires every folder to be separately specified.

Why in laravel do you need to specify classes in composer.json

Why in laravel do you to specify classes in composer.json? I thought composer is a program that manages your project's dependencies, yet it's also used to map your controller classes for instance. Why is that?
You don't need to specify your classes in composer.json. Sure you can do that if you want but for most of the cases there is no need to do so.
Let's take a look at the autoload section of Laravels default composer.json
"autoload": {
"classmap": [
"app/commands",
"app/controllers",
"app/models",
"app/database/migrations",
"app/database/seeds",
"app/tests/TestCase.php"
]
},
The classmap basically means autoload everything that's specified here. If it's a directory (like "app/controllers") it will load all classes within the folder. recursively.
So just because you want to move your controllers to subdirectories in app/controllers doesn't mean you have to change anything in composer.json
You have to do one thing though, run composer dump-autoload. You see, composer creates a file where it stores the classes and the actual file, that contains the class. You can find this file at vendor/composer/autoload_classmap.php.
The entries look like:
'IndexController' => $baseDir . '/app/controllers/IndexController.php
If you now move IndexController.php to app/controllers/foo the application will still try to include it from app/controllers until you run composer dump-autoload which will regenerate autoload_classmap.php.

Resources