What Are the Differences Between PSR-0 and PSR-4? - php

Recently I've read about namespaces and how they are beneficial. I'm currently creating a project in Laravel and trying to move from class map autoloading to namespacing. However, I can't seem to grasp what the actual difference is between PSR-0 and PSR-4.
Some resources that I've read are...
Battle of the Autoloaders
Laracasts PSR-4 autoloading
PSR-0
PSR-4
What I understand:
PSR-4 does not convert underscores to directory separators
Certain specific rules of composer cause the directory structure to become complex which in turn makes PSR-0 namespacing verbose and thus PSR-4 was created
Examples explaining the difference would be appreciated.

They are very similar so it is not surprising that it's a bit confusing. The summary is that PSR-0 had some backwards compatibility features for PEAR-style classnames that PSR-4 dropped, as such it only supports namespaced code. On top of that PSR-4 does not force you to have the whole namespace as a directory structure, but only the part following the anchor point.
For example if you define that the Acme\Foo\ namespace is anchored in src/, with PSR-0 it means it will look for Acme\Foo\Bar in src/Acme/Foo/Bar.php while in PSR-4 it will look for it in src/Bar.php, allowing for shorter directory structures. On the other hand some prefer to have the full directory structure to clearly see what is in which namespace, so you can also say that Acme\Foo\ is in src/Acme/Foo with PSR-4 which will gives you the equivalent of the PSR-0 behavior described above.
Long story short for new projects and for most intents and purposes, you can use PSR-4 and forget all about PSR-0.

Here are the major differences,
1. For example if you define that the Acme\Foo\ namespace is anchored in src/,
with PSR-0 it means it will look for Acme\Foo\Bar in src/Acme/Foo/Bar.php
while in PSR-4 it will look for Acme\Foo\Bar in src/Bar.php(where Bar class is).
2. PSR-4 does not convert underscores to directory separators
3. You would prefer using PSR-4 with namespaces
4. PSR-0 will not work even if the class name is different from file name, like considering above example:
Acme\Foo\Bar ---> src/Acme/Foo/Bar.php (for Bar class) will work
Acme\Foo\Bar ---> src/Acme/Foo/Bar2.php (for Bar class) will not work

PSR-4 is something like 'relative path', PSR-0, 'absolute path'.
e.g.
config:
'App\Controller' => 'dir/'
PSR-0 autoload:
App\Controller\IndexController --> dir/App/Controller/IndexController.php
PSR-4 autoload:
App\Controller\IndexController --> dir/IndexController.php
And there are some more difference in details between PSR-0 and PSR-4, see here: http://www.php-fig.org/psr/psr-4/

Namespace/ folder convention.
Classes should be stored in folders according to their namespaces.
In general, you will create an src/ directory in your root folder, sitting at the same level as vendor/, and add your projects there. Below is an example of the folder structure:
.
+-- src
|
+-- Book
| +-- History
| | +-- UnitedStates.php - namespace Book\History;
+-- Vehicle
| +-- Air
| | +-- Wings
| | | +-- Airplane.php - namespace Vehicle\Air\Wings;
| +-- Road
| | +-- Car.php - namespace Vehicle\Road;
+-- tests
+-- test.php
+-- vendor
Difference between psr-0 and psr-4
psr-0
It is deprecated. Looking at vendor/composer/autoload_namespaces.php file you can see the namespaces and the directories that they are mapped to.
composer.json
"autoload": {
"psr-0": {
"Book\\": "src/",
"Vehicle\\": "src/"
}
}
Looking for Book\History\UnitedStates in src/Book/History/UnitedStates.php
Looking for Vehicle\Air\Wings\Airplane in src/Vehicle/Air/Wings/Airplane.php
psr-4
Looking at vendor/composer/autoload_psr4.php file you can see the namespaces and the directories that they are mapped to.
composer.json
"autoload": {
"psr-4": {
"Book\\": "src/",
"Vehicle\\": "src/"
}
}
Looking for Book\History\UnitedStates in src/History/UnitedStates.php
Looking for Vehicle\Air\Wings\Airplane in src/Air/Wings/Airplane.php
composer.json
"autoload": {
"psr-4": {
"Book\\": "src/Book/",
"Vehicle\\": "src/Vehicle/"
}
}
Looking for Book\History\UnitedStates src/Book/History/UnitedStates.php
Looking for Vehicle\Air\Wings\Airplane in src/Vehicle/Air/Wings/Airplane.php

Even when I tried but Composer is a mess. Sadly, it's the only alternative.of the market.
Why is a mess?.
The Composer's autocomplete works fine if you are in control of the code. However, if you are importing a different project, you find yourself with lots of styles and ways to create folders. For example, some projects are /company/src/class.php while others are company/class.php and others are company/src/class/class.php
I created a library that solves it:
https://github.com/EFTEC/AutoLoadOne (it's free, MIT).
It generates an autoinclude by scanning all the classes of a folder, so it works in every case (psr-0 psr-4, classes without namespace, file with multiple classes..
edit: And again, downvoted without any reason. ;-)

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

Automatically including file in package when included as a dependency in Composer

I have a package that must automatically load a non-namespaced PHP file when the package is included in an application.
Below is my general directory structure
packages/
+-- PackageA/
+-- Entities/
+-- Mappers/
+-- Services/
+-- composer.json
+-- constants.php
apps/appA/
+-- vendors/
+-- autoload/
+-- composer.json
apps/appB/
+-- vendors/
+-- composer.json
I've followed the directions here to use a path repository in making PackageA a dependency for appA/. This part works smoothly.
Some files within PackageA require access to constants, mostly filepaths. This is what "constants.php" is for, and these values are defined procedurally:
<?php
define('XML_REPO_PATH', __DIR__ . '/../blah/xml/');
// --etc--
I originally thought to use the 'files' autoloading mechanism in "packages/PackageA/composer.json" with:
{
...
"autoload": {
"psr-4": { ... }
"files": ["constants.php"]
}
}
However, this is not requiring constants.php when PackageA is included as a dependency in appA. To fix this, instead of putting "files": [...] in "packages/PackageA/composer.json", I put the following in the autoload section of "app/appsA/composer.json":
"files": ["vendors/packages/PackageA/constants.php"]
This isn't very desirable because every application using PackageA would need this. I would think that the nature of composer would allow me to make sure that files within PackageA have access to (i.e., are meant to include) certain procedural code, like in the case of config constants. Is there a way to do this?
Don't use Composer's files autoloading to include configuration files or files with constants. Please think about the performance impact for all other libraries. A file in the files section is loaded on every invocation of your script, regardless, if you are using PackageA or not. Also think about possible clashes of constant names, due to non namespaced constant usage. files autoloading is only! meant to be used for legacy code that cannot otherwise be made working. You should avoid using it.
because in php < 5.6 i can't concatenate class constants with other constants like __DIR__
The main problem is not the concatenation, but that the constants file isn't a class. Autoloading won't work here, because Composer's Autolader loads classes only.
So, one solution could be to introduce an empty class for Constants, but add the side-effects on top.
Then namespace it under your vendor\PackageA umbrella.
This enables you to add use vendor\PackageA\Constants; in other classes,
in order to trigger the autoloading, right?
You are including an empty class, but when the file is autoloaded, the defines are happening as side-effects. A good IDE will place an error flag on this file, because it causes side-effects. It's still ugly, because other developers don't know where the defines come from, when they simply include a class - but better than using autoloading files section.
composer.json
"autoload": {
"psr-4": { "\Vendor\PackageA\\" : "./src/packages/PackageA/" }
}
constants.php
<?php
namespace Vendor\PackageA;
class Constants
{
// #todo PHP 5.6 namespaced class constants
}
// global side effect: constant definition
define('XML_REPO_PATH', __DIR__ . '/../blah/xml/');
// etc..
The best-practice is probably to use a Configuration class with a constructor that accepts a configuration object or array for the configuration of your package.
This decouple package and application from hard-coded global configurations.
Basically, configuration injection (App environment into Package, Package configures itself based on that context).

Why specify the namespace when using psr-4 autoloading with Composer?

I'm a little confused with how I should be using psr-4 autoloading in Composer. Let's say I've got a folder structure like this:
/
|- Core/
| - Router.php
|- App/
| - Models
| User.php
|- composer.json
Basically, in the project root: composer.json; a Core folder containing a Router php class; an App folder containing a Models folder that contains a User class.
The Router class looks like this:
<?php
namespace Core;
class Router {
}
and the Users class looks like this:
<?php
namespace App\Models;
class User {
}
So I can autoload these classes using the Composer psr-4 autoloader, I can do this in composer.json:
{
"autoload": {
"psr-4": {
"Core\\": "Core",
"App\\Models\\": "App/Models"
}
}
}
So I can then use the classes without requiring them (after running composer dump-autoload) like this:
$router = new Core\Router();
$user = new App\Models\User();
which works with no problems.
However, I can also do this in composer.json:
{
"autoload": {
"psr-4": {
"": ""
}
}
}
which, according to the documentation is a fallback directory where any namespace can be, relative to the root. So by having this "empty" entry in the composer autoloader, which I believe says "starting in the root, look in any directory for a class in any namespace", I can autoload any of my classes if I follow the correct folder naming / namespace structure.
So my question is, why would I do the former if the latter works and is much simpler? Is it a performance thing? Or is there another reason?
Why shouldn't you always do "psr-4": {"": ""}?
Reason 1: It cost performance. The definition says that for EVERY class that needs autoloading, Composer should look into the root directory. These classes are not only the ones in your package, but ALL other classes as well.
Composer tries to optimizes this effort a bit by remembering fruitless searches, but this only pays if you load another class with the same prefix.
Reason 2: The essence of PSR-4 is that you don't have to have the whole namespace path mapped to a directory path. Assuming that you have a package which deals with a very specific group of classes like \Vendor\Template\Escaping\Output\*, and nothing else (having small packages makes it easier to reuse them without adding too much code), you can have them in src/Vendor/Template/Escaping/Output/AnyClass.php and define
"psr-4": {
"\\Vendor\\Template\\Escaping\\Output\\": "src/Vendor/Template/Escaping/Output/"
}
You can also put the class into src/AnyClass.php and define
"psr-4": {
"\\Vendor\\Template\\Escaping\\Output\\": "src/"
}
And this shortens the directory path significantly, marginally improving speed (I think - have no figures though), but mostly improving developing the thing due to less opening of empty folders.
Having both a Core namespace and an App namespace in the same package makes me suspicious: Why isn't there one package for each of these?
Usually you only have one folder for your own project when using composer. Then you only need to specify one namespace.
Consider that you would rearrange your file structure to
/
|- lib/
| - Core/
| - Router.php
| - App/
| - Models
| User.php
|- composer.json
and change your composer.json to
{
"autoload": {
"psr-4": {
"MyApp\\": "lib/"
}
}
}
then you only have one specified namespace and you dont need to add any further namespaces. You can call your classes like this:
$router = new \MyApp\Core\Router;
$user = new \MyApp\App\Models\User;
or like this:
namespace MyApp;
$router = new Core\Router;
$user = new App\Models\User;
PSR-4 is a standard that translates namespaces INTO physical directories.

Autoloading and PSR-0/PSR-4

I, like many others it seems, am having trouble getting my head around when and how to use auto-loading. I think I understand the concept of composer and PSR-0/PSR-4 and directory structure that this requires. But if I'm building my own project using my own MVC framework
Should I now put all my class files in a src folder inside the vendor folder
Do I then edit the composer autoload file?
Or do I still keep the original structure and just use my own autoloader?
-project
-app
-core /Main.php
-controllers /Controller.php
-models /User.php
/index.php
Since composer comes with its own autoloader which will load all dependenies that I may want to include with my project and if I'm not going to make my website into a distributed project do I even need my own namespacing? Why don't I just stick with includes/requires?
And finally, if I do adopt namespacing using a closure like this
function __autoload($class){
require $class .'.php';
});
do I need to require the autoload.php file in all pages where I load my classes as I do with the old include/require of the past. Is the above file correct? I think the namespace would be
<?php
namespace app\core; //for Main.php
namespace app\controllers; //for Controller.php
use app\controllers\Controller; //if I call the class
At the beginning of your main file (maybe /var/www/dist/index.php) you just include the Composer or whatever autoloader you use.
<?php
if ( file_exists( __DIR__.'/path/to/vendor/autoload.php' ) )
require __DIR__.'/path/to/vendor/autoload.php';
Then you can either add shortcuts to the classes that you use in a file by making use of the use statement
use MyNamespace\Controller\Index,
MyNamespace\Service\FooService;
use Zend\Foo\Bar;
use Symfony\Baz\Biz;
use Etc\Etc\Etc;
// Refers to \Zend\Foo\Bar
$bar = new Bar;
or just use the full path when instantiating a class
$bar = new \Zend\Foo\Bar;
To add your own namespace, just add it to your composer.json file
"autoload" : {
"psr-4" : {
"MyNamespace\\" : "src/"
}
}
and open your command line interface/terminal/console and add the namespace to the autoloader
# local install of Composer in your project
php composer.php dump-autoload
# or global install and `composer` is in your $PATH
composer dump-autoload
There is a rule:
One class per file
And if your project really is (PSR-0 deprecated or) PSR-4 compatible, then you don't use more than one namespace in one file. A class name gets (in the PSR-4 FIG standard) defined as
A fully qualified class name has the following form:
\<NamespaceName>(\<SubNamespaceNames>)*\<ClassName>
The contiguous sub-namespace names after the "namespace prefix" [note: \ is the "root") correspond to a subdirectory within a "base directory", in which the namespace separators represent directory separators.
with the following example:
+------------------------------+------------------+------------------------+---------------------------------------+
| Fully Qualified Class Name | Namespace Prefix | Base Directory | Resulting File Path |
+------------------------------+------------------+------------------------+---------------------------------------+
| \Acme\Log\Writer\File_Writer | Acme\Log\Writer | ./acme-log-writer/lib/ | ./acme-log-writer/lib/File_Writer.php |
+------------------------------+------------------+------------------------+---------------------------------------+

Composer package autoloading

I can't get my library to work. The directory structure is lib-name/src and inside theres a main dir and a tests dir, how do I tell composer to load from the /lib-name/src/main folder?
Link to my library github https://github.com/gerardorn/catalogo
There are 3 ways to map your classes for autloading with composer.
PSR-0
The recommended way is PSR-0 compatible. This protocol describes the directory structure of your library. Each namespace need to be a directory. Classes with underscores are separated as well (PEAR style).
In your case, the class Catalogable has a namespace gerardorn\catalogo. To be PSR-0 compatible the directory structure is:
- src
- main
- gerardorn
- catalogo
- Catalogable.php
In your composer.json you should put the following:
"autoload" : {
"psr-0" : {"gerardorn" : "src/main"}
}
Classmap
Alternatively you could use classmap. Classes are searched for in the directory regardless of the namespace.
"autoload": {
"classmap": ["src/main"]
}
Files
The 3th method files doesn't apply in your case.
PHPUnit
Note that PHPUnit is needed to test your library, but not to run your library. Therefor you shouldn't put that up as a required library.
It's good that you're writing unit tests. You should sign up for Travis CI. It will run your PHPUnit tests each time that you push to GitHub and warn you (by e-mail) if something got broken.
Your directory structure should be PSR-0 compatible.
i.e. your file Catalogable.php has a namespace gerardorn\catalogo, so your directory structure for this has to match the following.
- src
- main
- gerardorn
- catalogo

Categories