Autoload PSR-4 vs. Files (Composer) [duplicate] - php

I've added the following to my composer.json file. This works fine but I have a long list of sub-namespaces (eg. Apple, Orange, Lemon, Pear, Banana... etc) that I want to include.
1) Do I have to indicate each sub-namespace or is there a shortcut eg. "Pure\\*": "pure"
composer.json:
"autoload": {
"psr-4": {
"Pure\\": "pure",
"Pure\\Apple\\": "pure/src/Pure/Apple",
"Pure\\Orange\\": "pure/src/Pure/Orange",
"Pure\\Lemon\\": "pure/src/Pure/Lemon"
}
}
2) Is it better to include a custom autoload file instead:
composer.json:
"autoload": {
"files": [
"pure/src/Pure/autoload.php"
]
}
autoload.php:
spl_autoload_register(function ($class) {
//etc...
}

Do I have to indicate each sub-namespace or is there a shortcut
When declaring the autoloading, you should use the longest possible or reasonable prefix.
If this example package is the only one you are ever creating, and it is the only one using Pure as the namespace, go with that if the number of longer prefixes in several subdirectories is too high. However, this assumes that any other package in the world that you are using should avoid doing the same thing with the same namespace.
Composer will be able to search for sub namespaces in all available directories, i.e. if you have two packages, and one says Pure is to be found in pure/src/Pure, and the other says Pure is in code/stuff, Composer will try to find a class Pure\Something\Class in one of these directories first, then has to try the second one if it does not find it. Composer will remember whether the pure/src/Pure/Something directory exists and avoid looking for anything starting with Pure\Something there if a second class in that namespace has to be loaded.
But from a code organization standpoint, one package should only ever provide a defined set of namespaces, and no other package should provide classes in the same namespace. You could accidentially add the same class into two packages and get interesting problems that may be hard to debug if the two files are different.
Is it better to include a custom autoload file instead:
No, avoid it at all cost. You won't get any benefit from this because the file always has to be loaded, and it is duplicate code that occupies some memory - you already have the autoloader from Composer. If you have code that does not conform to PSR-4 or PSR-0, you can use a classmap. For new code: Use PSR-4 only!
Also, your custom autoloader cannot be optimized by Composer. Although any optimization should be measured for effectiveness (read my detailed answer on this: Why use a PSR-0 or PSR-4 autoload in composer if classmap is actually faster?), using your own autoloader will entirely prevent your code from being optimized if it would be beneficial.

Related

composer dump-autoload -o skips all my classes

I just upgraded to Composer 2.0 which has apparently made some modifications to how it parses classes...
composer dump-autoload -o or the alias composer du -o basically dumps a lot of lines in my console with text like:
Class MyProject\SubCategory\CoolClass located in C:/udvikling/MyProjectRoot/src/MyProjectClasses\MyProject\SubCategory\CoolClass.php does not comply with psr-4 autoloading standard. Skipping
After reading Composer psr-4 autoload issue i ensured that all names that contribute to my namespaces and classpaths are capitalized. i.e. use myproject/helperclass is now use MyProject/HelperClass
My C:/udvikling/MyProjectRoot/src/composer.json includes the following
"autoload": {
"psr-4": {
"MyProject\\": "MyProjectClasses/"
}
}
I made one go away by adding a leading backslash to the classname:
<?php
namespace \MyProject\Core;
class Timer {
//...
but... according to php.net
Fully qualified names (i.e. names starting with a backslash) are not allowed in namespace declarations, because such constructs are interpreted as relative namespace expressions.
So this seems to not be the right choice... (my IDE also gave me an angry squiggly line under it...)
Besides going back to another version of composer, how do I fix this?
File structure:
C:\udvikling\MyProjectRoot -- the root of the project
C:\udvikling\MyProjectRoot\src --the php sources
C:\udvikling\MyProjectRoot\src\MyProjectClasses -- my PHP classes
C:\udvikling\MyProjectRoot\src\vendor -- my vendor stuff for composer
C:\udvikling\MyProjectRoot\src\composer.json - my composer config
I went and did some extra reading.
Apparently one of the goals of psr-4 is to reduce the folder depth.
The “src” and “tests” directories have to include vendor and package directory names. >This is an artifact of PSR-0 compliance.
Many find this structure to be deeper and more repetitive than necessary. This proposal suggests that an additional or superseding PSR would be useful [...]
It’s difficult to implement package-oriented autoloading via an extension or amendment to PSR-0, because PSR-0 does not allow for an intercessory path between any portions of the class name.
So basically, the issue was that inside my MyProjectClasses directory, for the class MyProject\Core\Timer I had it in a similar directory structure:
C:\udvikling\MyProjectRoot\src\MyProjectClasses\MyProject\Core\Timer.php.
This had to be altered to that the base package was not included: C:\udvikling\MyProjectRoot\src\MyProjectClasses\Core\Timer.php

composer with files not using psr0 and psr4 autoloading (symfony project)

I have a problem with composer auto loader. Currently working on a application which was developed about 10 years back. The folder structure of 2 libraries which are currently used in the project do not comply the psr0 and psr4 auto loading rules.
The structure after the composer install looks as follows
Example 1
Folder path: /vendor/AppBook/ORS/class/model/Country
Filename: class.Country.php
PHP Class name: Country
Example 2
Folder path: /vendor/AppBook/ORS/class/model/Country
Filename: class.CountryCollection.php
PHP Class name: CountryCollection
Please advise what should I do in order that composer auto loader can detect these files.
Thank you in advance
From the docs:
You can use the classmap generation support to define autoloading for all libraries that do not follow PSR-0/4. To configure this you specify all directories or files to search for classes.
Example:
{
"autoload": {
"classmap": ["src/", "lib/", "Something.php"]
}
}
You can still add composer.json to your legacy libraries and define classmap autoloading type for them.
You can rename these files to make them compatible to PSR-4 (unlikely because that requires using namespaces - in 10 years old code?) or PSR-0. Additionally, you have to remove any explicit loading of these files via include, include_once, require or require_once because the file names changed.
PHP will autoload these classes by their class name. This will possibly run into issues if the case sensitivity in the class name is not respected everywhere. Example:
class UpperCase {}
$a = new upperCase();
The autoloading would try to find a file ending with upperCase.php, which will not match the PSR-0 required UpperCase.php, so the code will fail. However, this will work, making the situation not better:
class UpperCase {}
$b = new UpperCase();
$a = new upperCase();
The reason is that PHP treats class names case insensitive, so once a class is loaded, you can use any case in it's name. It is only the first occurrence in your code path that has to match. The problem is to be sure where this really is, so essentially it has to be correct everywhere.
Yes, the classmap feature is the easier way. But you'd still want to remove include/require calls to optimize the performance a bit, so you have to touch the code anyway. And despite it's age, it has to be maintained - so why not do it fully, switching to a well-known autoloading standard. It will help you in the long run when you have to maintain PSR-0/4 compatible classes and this old code in parallel.

Autoload My Classes with Sub-Namespaces (Composer.json)

I've added the following to my composer.json file. This works fine but I have a long list of sub-namespaces (eg. Apple, Orange, Lemon, Pear, Banana... etc) that I want to include.
1) Do I have to indicate each sub-namespace or is there a shortcut eg. "Pure\\*": "pure"
composer.json:
"autoload": {
"psr-4": {
"Pure\\": "pure",
"Pure\\Apple\\": "pure/src/Pure/Apple",
"Pure\\Orange\\": "pure/src/Pure/Orange",
"Pure\\Lemon\\": "pure/src/Pure/Lemon"
}
}
2) Is it better to include a custom autoload file instead:
composer.json:
"autoload": {
"files": [
"pure/src/Pure/autoload.php"
]
}
autoload.php:
spl_autoload_register(function ($class) {
//etc...
}
Do I have to indicate each sub-namespace or is there a shortcut
When declaring the autoloading, you should use the longest possible or reasonable prefix.
If this example package is the only one you are ever creating, and it is the only one using Pure as the namespace, go with that if the number of longer prefixes in several subdirectories is too high. However, this assumes that any other package in the world that you are using should avoid doing the same thing with the same namespace.
Composer will be able to search for sub namespaces in all available directories, i.e. if you have two packages, and one says Pure is to be found in pure/src/Pure, and the other says Pure is in code/stuff, Composer will try to find a class Pure\Something\Class in one of these directories first, then has to try the second one if it does not find it. Composer will remember whether the pure/src/Pure/Something directory exists and avoid looking for anything starting with Pure\Something there if a second class in that namespace has to be loaded.
But from a code organization standpoint, one package should only ever provide a defined set of namespaces, and no other package should provide classes in the same namespace. You could accidentially add the same class into two packages and get interesting problems that may be hard to debug if the two files are different.
Is it better to include a custom autoload file instead:
No, avoid it at all cost. You won't get any benefit from this because the file always has to be loaded, and it is duplicate code that occupies some memory - you already have the autoloader from Composer. If you have code that does not conform to PSR-4 or PSR-0, you can use a classmap. For new code: Use PSR-4 only!
Also, your custom autoloader cannot be optimized by Composer. Although any optimization should be measured for effectiveness (read my detailed answer on this: Why use a PSR-0 or PSR-4 autoload in composer if classmap is actually faster?), using your own autoloader will entirely prevent your code from being optimized if it would be beneficial.

composer custom autoloader without adding autoload in composer.json

I'm refering to
https://getcomposer.org/doc/01-basic-usage.md#autoloading
Probably all of the composer-users will add this into the composer.json to make it load custom namespaces:
{
"autoload": {
"psr-4": {"Acme\\": "src/"}
}
}
In my case my folder structure is completely the same as the namespaces, so \hello\world would refer to a class inside \hello\world. So, is there a way to tell composer he should search inside the directory with the same names like the namespaces, without telling it inside the composer.json? It's unnecessary expense!
No, you can't do that.
If you want to use long paths resembling the exact namespace structure of your class, you can always use PSR-0 instead, which forces you to use these long directory names. But note that this has some slight drawbacks - traversing longer paths takes slightly more time.
You can do it with a custom autoloader, since PHP can handle multiple autoloaders!

Own project via composer

I've got some libraries loaded through composer, and I'm wondering if it's possible to add my own library in the /vendor map, and then to have the composer autoloader load it? The structure would be something like /vendor/mylibrary/ and then a namespace mylibrary.
Would this be possible? Also would it be possible to add a different map to the composer autoloader? Like for example /app/src/ and then to have it load all the classes in that folder? Or do I have to make my own loader for that?
Thanks
Reading the composer documentation:
You can even add your own code to the autoloader by adding an autoload field to composer.json.
{
"autoload": {
"psr-0": {"Acme": "src/"}
}
}
Composer will register a PSR-0 autoloader for the Acme namespace.
You define a mapping from namespaces to directories. The src directory would be in your project root, on the same level as vendor directory is. An example filename would be src/Acme/Foo.php containing an Acme\Foo class.
After adding the autoload field, you have to re-run install to
re-generate the vendor/autoload.php file.
So basically, you just need to follow PSR-0, and tell composer where to find your library, by adding that line to your composer.json
Yes.You can achieve it. Configure your composer.json file as following:
{
"autoload": {
"classmap": [ "classes" ]
}
Here classes is the name of the directory where you have all your application related classes.Vendor related class should be auto detected as well. Just add the following line to achieve both at the same time:
require 'vendor/autoload.php';
And you can use the namesapce to reference your class like the following:
use classes\Model\Article;
Yes, of course it is possible to add own libraries and you should feel highly encouraged to do so. If your library is available publicly, you can simply register it at packagist.org. If not, it's a bit more complicated, but not impossible.
If your project does not follow the PSR-0 standard, composer will create a classmap for you. A custom autoloader is not supported.
I'd recommend you to read the (really excellent) documentation about all this and come back, if you're running into problems.
http://getcomposer.org/doc/

Categories