Laboratory of PSR-4: Autoloading Standard.
This repository is a standalone part of a larger project: PHP.lab — a curated knowledge base and laboratory for PHP engineering.
Usage
To run the example application with Docker use command:
docker compose up -d
After creating the Docker container the Composer dependencies have to be defined and installed:
docker compose exec application composer require --dev squizlabs/php_codesniffer --dev phpunit/phpunit \
&& docker compose exec application composer install
Tom make PHP Code Sniffer commands easily accessible run:
docker compose exec application bash -c "
ln -s /code/vendor/bin/phpcs /usr/local/bin/phpcs;
ln -s /code/vendor/bin/phpcbf /usr/local/bin/phpcbf;
ln -s /code/vendor/bin/phpunit /usr/local/bin/phpunit;
"
To run PHP Code Sniffer use command:
docker compose exec application /var/www/vendor/bin/phpcs
or, if the shortcut has been created:
docker compose exec application phpcs
To run PHP Unit use command:
docker compose exec application /var/www/vendor/bin/phpunit
or, if the shortcut has been created:
docker compose exec application phpunit
To update Composer dependencies use command (should be done before the command below):
docker compose exec application composer update
To login into the Docker container use command:
docker exec -it psr-4-example-app /bin/bash
License
This project is licensed under the GPL-3.0 - see LICENSE.
Official documentation
PHP-FIG PSR-4 Official documentation
What are PSRs
PSR stands for PHP Standard Recommendation.
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.
The term “class” refers to classes, interfaces, traits, and other similar structures.
A fully qualified class name has the following form:
\<NamespaceName>(\<SubNamespaceNames>)*\<ClassName>
Fully qualified class name meets the following conditions:
SomeVendornamespace SomeVendor;
class SomeClass {}
class SomeClass {}
namespace SomeVendor;
class SomeClass {}
namespace SomeVendor\SomeName;
class SomeClass {}
namespace SomeVendor\Some\More\Names;
class SomeClass {}
SomeClass is the terminating class namenamespace SomeVendor\SomeName;
class SomeClass {}
$someObject = new \SomeVendor\SomeName\SomeClass();
\SomeVendor\SomeName) is never enoughnamespace SomeVendor\SomeName;
class SomeClass {}
$someObject = new \SomeVendor\SomeName;
In PSR-0, underscores were converted to directory separators.
In PSR-4, they are just ordinary characters.
namespace SomeVendor\SomePackage;
class Some_Class {} // \SomeVendor\SomePackage\Some_Class
class Other_Underscored_Class {} // \SomeVendor\SomePackage\Other_Underscored_Class
All of these are syntactically valid class names under PSR-4:
class restclient {} // all lower case - fine
class RESTCLIENT {} // all upper case - fine
class RESTClient {} // mixed case (pascal/camel with uppercase acronym) - fine
class restClient {} // camelCase - fine
class RestClient {} // PascalCase - fine (and conventional)
Note:
class RESTClient {} and class RestClient {} are PSR-1/PSR-12 compliantclass RestClient {} is both PSR-1/PSR-12 and PER 3.0 compliantnamespace SomeVendor\SomePackage;
class SomeClass {}
$someObject = new \SomeVendor\SomePackage\SomeClass();
namespace SomeVendor\SomePackage;
class SomeClass {}
$someObject = new \somevendor\somepackage\someclass();
When loading a file that corresponds to a fully qualified class name:
Composer example configuration in composer.json file
"autoload": {
"psr-4": {
"SomeVendor\\" : "src/",
"SomeVendor\\SomePackage\\" : "src/",
"SomeVendor\\OtherPackage\\" : [
"src/otherpackage",
"lib/otherpackage"
],
"OtherVendor\\AnotherPackage\\" : "packages/anotherpackage/src/"
}
},
"autoload-dev": {
"psr-4": {
"SomeVendor\\SomePackage\\" : "tests/"
}
}
Imortant:
src/ and tests/unit_tests/, the dev entry wins during development, which could hide bugs.Result rules:
Namespace prefix → Base directories
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
SomeVendor\ → src/
SomeVendor\SomePackage\ → src/
→ tests/
SomeVendor\OtherPackage\ → src/otherpackage/
→ lib/otherpackage/
OtherVendor\AnotherPackage\ → packages/anotherpackage/src/
Result examples:
Namespace prefix → Base directories
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
SomeVendor\AnyClass → src/AnyClass.php
SomeVendor\SomePackage\SomeClass → src/SomeClass.php (checked first)
→ tests/SomeClass.php (chacked if still not found)
SomeVendor\OtherPackage\SomeClass → src/otherpackage/SomeClass.php (checked first)
→ lib/otherpackage/SomeClass.php (chacked if still not found)
OtherVendor\AnotherPackage\SomeClass → packages/anotherpackage/src/SomeClass.php
Composer example configuration in composer.json file
"autoload": {
"psr-4": {
"SomeVendor\\SomePackage\\" : "src/",
"SomeVendor\\OtherPackage\\" : [
"src/otherpackage",
"lib/otherpackage"
],
"OtherVendor\\AnotherPackage\\" : "packages/anotherpackage/src/"
}
},
Result rules:
Namespace prefix → Base directories
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
SomeVendor\SomePackage\ → src/
SomeVendor\OtherPackage\ → src/otherpackage/
→ lib/otherpackage/
OtherVendor\AnotherPackage\ → packages/anotherpackage/src/
Result examples:
Namespace prefix → Base directories
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
SomeVendor\SomePackage\SomeClass → src/SomeClass.php
SomeVendor\SomePackage\SomeName\SomeClass → src/SomeName/SomeClass.php
SomeVendor\SomePackage\SomeName\OtherName\SomeClass → src/SomeName/OtherName/SomeClass.php
SomeVendor\OtherPackage\SomeClass → src/otherpackage/SomeClass.php (checked first)
→ lib/otherpackage/SomeClass.php (chacked if still not found)
SomeVendor\OtherPackage\SomeName\SomeClass → src/otherpackage/SomeName/SomeClass.php (checked first)
→ lib/otherpackage/SomeName/SomeClass.php (checked if still not found)
OtherVendor\AnotherPackage\SomeClass → packages/anotherpackage/src/SomeClass.php
OtherVendor\AnotherPackage\SomeName\SomeClass → packages/anotherpackage/src/SomeName/SomeClass.php
OtherVendor\AnotherPackage\SomeNam\OtherName\SomeClass → packages/anotherpackage/src/SomeName/OtherName/SomeClass.php
.php. The file name MUST match the case of the terminating class name.
🔗Composer example configuration in composer.json file
"autoload": {
"psr-4": {
"SomeVendor\\SomePackage\\" : "src/",
}
},
Result rules:
Namespace prefix → Base directories
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
SomeVendor\SomePackage\ → src/
Result examples:
Namespace prefix → Base directories
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
SomeVendor\SomePackage\SomeClass → src/SomeClass.php
SomeVendor\SomePackage\SomeName\SomeClass → src/SomeName/SomeClass.php
SomeVendor\SomePackage\SomeName\OtherName\SomeClass → src/SomeName/OtherName/SomeClass.php
Composer example configuration in composer.json file
"autoload": {
"psr-4": {
"SomeVendor\\" : "src/",
"SomeVendor\\SomePackage\\" : "src/",
"SomeVendor\\OtherPackage\\" : [
"src/otherpackage",
"lib/otherpackage"
],
"OtherVendor\\AnotherPackage\\" : "packages/anotherpackage/src/"
}
},
"autoload-dev": {
"psr-4": {
"SomeVendor\\SomePackage\\" : "tests/unit_tests/"
}
}
Result rules:
Namespace prefix → Base directories
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
SomeVendor\ → src/
SomeVendor\SomePackage\ → src/
→ tests/
SomeVendor\OtherPackage\ → src/otherpackage/
→ lib/otherpackage/
OtherVendor\AnotherPackage\ → packages/anotherpackage/src/
Result examples:
Namespace prefix → Base directories
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
SomeVendor\AnyClass → src/AnyClass.php
SomeVendor\SomeName\SomeClass → src/SomeName/SomeClass.php
SomeVendor\SomePackage\SomeClass → src/SomeClass.php (checked first)
→ tests/SomeClass.php (chacked if still not found)
SomeVendor\SomePackage\SomeName\SomeClass → src/SomeName/SomeClass.php (checked first)
→ tests/SomeName/SomeClass.php (chacked if still not found)
SomeVendor\SomePackage\SomeName\OtherName\SomeClass → src/SomeName/OtherName/SomeClass.php (checked first)
→ tests/SomeName/OtherName/SomeClass.php (chacked if still not found)
SomeVendor\OtherPackage\SomeClass → src/otherpackage/SomeClass.php (checked first)
→ lib/otherpackage/SomeClass.php (chacked if still not found)
SomeVendor\OtherPackage\SomeName\SomeClass → src/otherpackage/SomeName/SomeClass.php (checked first)
→ lib/otherpackage/SomeName/SomeClass.php (checked if still not found)
OtherVendor\AnotherPackage\SomeClass → packages/anotherpackage/src/SomeClass.php
OtherVendor\AnotherPackage\SomeName\SomeClass → packages/anotherpackage/src/SomeName/SomeClass.php
OtherVendor\AnotherPackage\SomeNam\OtherName\SomeClass → packages/anotherpackage/src/SomeName/OtherName/SomeClass.php
Autoloader implementations MUST NOT throw exceptions, MUST NOT raise errors of any level, and SHOULD NOT return a value.
The table below shows the corresponding file path for a given fully qualified class name, namespace prefix, and base directory.
| 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 |
\Aura\Web\Response\Status |
Aura\Web |
/path/to/aura-web/src/ |
/path/to/aura-web/src/Response/Status.php |
\Symfony\Core\Request |
Symfony\Core |
./vendor/Symfony/Core/ |
./vendor/Symfony/Core/Request.php |
\Zend\Acl |
Zend |
/usr/includes/Zend/ |
/usr/includes/Zend/Acl.php |
For example implementations of autoloaders conforming to the specification, please see the examples file. Example implementations MUST NOT be regarded as part of the specification and MAY change at any time.