Psr-4 : namespace and spl autoload - php

Okay, as I browse around there are lots of posts and answers on the line. To my understanding
psr-0: SPL autoload only
psr-4: SPL autoload + namespace
Most of the answers contain LONG methods. psr-4 should not be HARD to implement since it purposes is to simplify file structure yet remaining its own advantage.
I have a structure like this project\view\main.php
in main.php
namespace project\view;
class main {
.......
}
out of the project folder which is my root directory, i have a index.php
spl_autoload_register( function ($ClassName) {
require $ClassName . '.php';
});
$main = new project\view\main();
Question: Am I doing it right with psr-4 or am I still missing something from the document?

From the specification PSR-0[1] is deprecated (since 2014-10-21) and is replaced by PSR-4
From PSR-4 documentation:
This PSR describes a specification for autoloading classes from file
paths. It is fully interoperable, and can be used in addition to any
other autoloading specification, including PSR-0. This PSR also
describes where to place files that will be autoloaded according to
the specification.
http://www.php-fig.org/psr/psr-4/
If you want the full rationnale for the specification change you can consult the PSR-4 meta document.
If you want to see working example, you can search at the same location
My ultimate recommandation would be to take a look at composer and let it handle it. You would only have to include the automatically generated autoload.php file to have fully functionnal autoloading.

Related

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 PSR-4 vs. Files (Composer) [duplicate]

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.

PSR-4 directory structure and namespacing for a set of functions?

I have a set of PHP functions that I find useful. I want to create a PSR-4 compliant repository for them, but the guides I have found (1,2,3) seem to talk only about classes for autoloading.
For instance, my files are as follows, with one function per file:
my_cool_function1.php
my_cool_function2.php
... etc.
How can I create a PSR-4 compliant library from them?
The reason you're not able to find any documentation for PSR-4 autoloading files which aren't classes, that's because as the specification states - it's designed for autoloading classes.
Taken directly from the official specs:
This PSR describes a specification for autoloading classes from file paths. It is fully interoperable, and can be used in addition to any other autoloading specification, including PSR-0. This PSR also describes where to place files that will be autoloaded according to the specification.
More specifically;
The term "class" refers to classes, interfaces, traits, and other similar structures.
A file with functions isn't really a similar structure.
To autoload those files, you'll need to autoload using files:
"autoload": {
"files": [
"src/my_cool_function1.php",
"src/my_cool_function2.php"
],
"psr-4": {
"SomeNamespace\\": "src/YourNamespace/"
}
}
You'll notice from this, that the psr-4 spec maps (usually) to a namespace.
Don't forget you can use static functions in classes so that PSR-4 will load them
class MyClass {
public static my_cool_function1() {}
}
Then you can invoke them as just a normal function using the colon operator:
MyClass::my_cool_function1() {}

Composer, PSR-4 Autoloading Iteration

I am currently running Slim and Twig, my folder structure is as follow
/application/modules
In my composer.json I have
"autoload": {
"psr-4": {
"Application\\": "application/modules/",
"Core\\": "application/",
"Middleware\\": "application/slim/middleware/"
}
}
My issue is is that in Application\modules\ I have a directory for each module. Now my question is, is it possible to make composer iterate through the sub directories when using PSR-4 autoload?
I see an issue with your PSR-4 declaration.
You shouldn't place "Core" classes inside a folder that has subfolders for other namespaces.
PSR-4 autoloading in Composer works like this: If the fully qualified class name of the class to load matches a prefix declared in any of the PSR-4 definitions, then the prefix mentioned in the prefix is removed from the class name, and the remaining class name is mapped to a filename and being searched.
If you have classes named Application\ in the folder application/modules, and you have classes named Core\ in the folder application, then technically Composer might find files that match a class name like Core\modules\Whatever, although these file will contain a class Application\Whatever instead.
I recommend moving all the Core classes into their own folder and point to this in the PSR-4 declaration.
The problem with your original question is that you omit an important information: What is the class and file structure for your modules?
Composer's autoloader will happily resolve any class that starts with the namespace prefix Application, remove that prefix from the classname, convert the remainders into a path name and search for that file in application/modules/. Given that you have a module class Application\MyModule\Foobar, it will be searched in application/modules/MyModule/Foobar.php. Why? Because the prefix Application will be removed to allow for shorter path names in PSR-4. (Using PSR-0 would mean you have to have a folder for every level of namespace in the classname.)
Note that it is recommended for optimum performance to make the prefix for namespaces as long as possible, because Composer allows to have more than one directory for any given prefix, but then has to search for the correct file in more than one directory. Searching for files is time consuming!

Composer ClassLoader Namespaces Trouble

I'm trying to use the Composer ClassLoader.
I'm trying to load up some CodeIgniter Libraries with PSR namespaces.
In my index.php I have:
$loader = include_once ROOTPATH . 'vendor/autoload.php';
$loader->add('CLI', ROOTPATH . 'application/libraries/CLI/');
$loader->register();
A simplified example of my folder structure is:
libaries/
CLI/
Tree/
Parser.php - namespace CLI\Tree;
Settings.php - namespace CLI;
Am I correct in assuming that Parser.php and Settings.php would be autoloaded? As I understood the documentation example it looks into sub-folders.
I want to avoid having to do the following:
$loader->addClassMap([
'CLI\\Settings' => ROOTPATH . 'application/libraries/CLI/Settings.php',
'CLI\\Tree\\Parser' => ROOTPATH . 'application/libraries/CLI/Tree/Parser.php',
]);
$loader->register();
Why don't you simply add the autoloading of your own code to the composer.json file you already have? That way Composer will create the autoloading file also for your own classes, you would be able to include your current project in another project without having to worry about autoloading (may be unlikely, but:), and you learn how to declare autoloading if you want to create your own modules.
From your code I guess this would work:
"autoload": {
"psr-0": {
"CLI": "application/libraries"
}
}
Explanation: CLI is the prefix of the classes that could possibly found in the path. Make this as long as possible if you are using a lot of CLI classes, and only some are found in that path. Otherwise Composer will search a class in more than one directory, which is sort of bad for performance.
application/libraries is the prefix path where the PSR-0 style classes are located. PSR-0 dictates that a class named X_Y_Z or X\Y\Z is located in the path X/Y/Z.php, so the class prefix you were giving is NOT included in the prefix path you tell Composer to search for.
The prefix path is relative to the location of composer.json.
You could also use PSR-4. That would allow to remove empty directory structures, but will work only with namespaces:
"autoload": {
"psr-4": {
"CLI\\": "application/libraries/CLI"
}
}
Two important differences: The class prefix must end with a backslash (and because this is JSON, the backslash has to be escaped, so double backslash).
Second: The class prefix will get removed from the path that is getting created from the classname. So a class W\X\Y\Z with the class prefix W\X\ will only create Y\Z.php as the path to the class and add the path prefix to it.
I added "CLI" to your path to show that PSR-4 would work, but that directory is not really needed in terms of PSR-4 - if it is empty, you could move files one level up.

Categories