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.
Related
I'm a bit confused because I'm programming a plugin for WordPress by using composer as it's the real way to go.
So I've created a composer file inside my plugin and some other stuff. In the composer file I've added my namespace for autoloading:
"autoload": {
"psr-4": {
"Johnny\\Lolkick\\": [
"includes/classes/"
]
}
}
Inside my classes folder I've created now a class with the name class-main.php. I've decided to take this name because of the WordPress naming conventions:
https://make.wordpress.org/core/handbook/best-practices/coding-standards/php/#naming-conventions
The class by itself was named class Main {. Inside my base plugin file I've created now a new instance of my class which failed. After changing the file name to Main.php it worked.
So in result the WordPress naming convention broke the autoloading of composer. I want to know now how do you handle this problem? How should I keep the naming convention by using composer?
Since your code base is not compatible with PSR-4 autoloading, a psr-4 mapping inside your composer.json's autoload section won't work, as you noticed.
I'd say you have two choices here:
First one would be to use classmap instead:
{
"autoload": {
"classmap": ["includes/classes/"]
}
}
This would simply parse all the files recursively within that folder and map the classes to their names, no matter what naming scheme you're following.
Second one would be to build your own autoloader, and use files to have it loaded automatically:
{
"autoload": {
"files": ["includes/autoloader.php"]
}
}
That autoloader would have to define what should happen (which class should be loaded, or not) when referring to a given class name.
In both cases, don't forget to run composer dump-autoload afterward.
In my default symfony4 structure I want to add lib folder, where I have additional classes. So something like this:
-bin
-config
-lib
- Importer.php
...(other files with classes)
-public
-src
- Controller
- TestController.php
- Entity
- Form
...
...
But I cannot figure out how to later use my files (i.e.: Importer.php).
Let's say Importer.php has a single class Importer() inside. If I try to use it from TestController.php I get:
Attempted to load class "Importer" from namespace "lib". Did you
forget a "use" statement for another namespace?
TestController.php has
use Importer;
specified on top (autodetected by PhpStorm). I also tried adding namespace in my Importer.php file, for example:
namespace lib;
and then in TestController:
use lib\Importer;
But it produces the same result.
Lastly after reading about services, I tried adding the file to config/services.yaml
lib\:
resource: '../lib/Importer.php'
Which gives the same result...
What to do, how to live?
First of all read about php namespaces.
Next read about the psr-4 standart.
Select a prefix for your folder, let's say Lib. Make sure that all files in the lib folder has a properly namespace. E.g. Importer class must be stored in the lib\Importer.php and must have the namespace Lib;, Items\Item class must be stored in the lib\Items\Item.php and must have the namespace Lib\Items\Item; and so on.
Your files are ready. Just need to inform Symfony about them.
Symfony uses composer's autoloader, so check composer's autoload section. Than add new folder for autoloading in composer.json:
"autoload": {
"psr-4": {
"App\\": "src/",
"Lib\\": "lib/"
}
},
It says that all classes in lib folder have their own separate files and Lib prefix in their namespace and other part of namespace is similar to directories structure.
Next you need to clear autoloader's cache. Run in console:
composer dump-autoload
And finally you can use your class:
use Lib\Importer;
$importer = new Importer;
Also you can add your files to autowire.
I am wanting to structure my laravel app in a way that all of my code is under the src directory. My project structure would look something like the below. How would I do this where I can still call Route::get('accounting/item/{id}','AccountingItemController#getId')
I am wanting to avoid adding every module under src to the ClassLoader. Is there a way to tell the class loader to load all sub-directories under the parent directory src?
app
app/src
app/src/accounting
app/src/accounting/controllers
app/src/accounting/models
app/src/accounting/repos
app/src/accounting/interfaces
app/src/job
app/src/job/controllers
app/src/job/models
app/src/job/repos
app/src/job/interfaces
Yes, it's called PSR-0.
You should namespace all of your code. Typically you'll have a vendor name that you'll use a the top level namespace. Your application structure should then look something like this.
app/src/Vendor/Accounting/Controllers
app/src/Vendor/Job/Controllers
Your controllers will then be namespaced accordingly.
namespace Vendor\Accounting\Controllers;
And when using them in routes.
Route::get('accounting/item/{id}','Vendor\Accounting\Controllers\ItemController#getId');
Lastly, you can register your namespace with Composer in your composer.json.
"autoload": {
"psr-0": {
"Vendor": "app/src"
}
}
Of course, if you don't want that top level Vendor namespace you can remove it, but you'll need to register each component as PSR-0.
"autoload": {
"psr-0": {
"Accounting": "app/src",
"Job": "app/src",
}
}
Once done, run composer dump-autoload once and you should be able to add new controllers, models, libraries, etc. Just make sure the directory structure aligns with the namespacing of each file.
Do you have composer installed? You should use this:
composer dump-autoload
But you can could add directories to the Laravel's classloader. Check the reference here: http://laravel.com/api/class-Illuminate.Support.ClassLoader.html
I play around with Silex, PHP micro-framework. At the moment I try to load my own classes but I have no luck doing it. Maybe somebody could explain me a little how does loading in Silex works?
My project structure looks like this:
app/
vendor/
web/
tests/
bootstrap.php
composer.json
composer.lock
Let's say I want to load a class Controller\User (namespace here) from /app/MainController.php.
How can I do that? I've browsed some articles (loading via Composer or Symfony's UniversalClassLoader), followed some instructions, and still it does not work.
If someone could give me a hand with it please, I would appreciate it.
I assume you load your Silex classes in bootstrap.php like that:
// bootstrap.php
require_once __DIR__.'/vendor/autoload.php';
If so replace this code with the following:
// bootstrap.php
$loader = require __DIR__.'/vendor/autoload.php';
$loader->add(YOUR_NAMESPACE, DIRECTORY_OF_THE_NAMESPACE);
Example:
// bootstrap.php
$loader = require __DIR__.'/vendor/autoload.php';
$loader->add('Tutorial', __DIR__.'/src');
You can add multiple namespaces if you like, just call the add method for every namespace.
In src/ you would store your class files. For every namespace create a folder which contains your class files for this namespace.
The file should have the same name as the class.
MyNamespace\MyClass => src/MyNamespace/MyClass.php
MyNamespace\SubNamespace\SubClass => src/MyNamespace/SubNamespace/SubClass.php
In every class file you have to set the associated namespace in the first line.
// src/Tutorial/DemoController.php
namespace Tutorial;
class DemoController{
// class definition goes here..
}
You have now access to your classes in every file which includes bootstrap.php.
In /app/MainController.php you can now access your own class like this:
// app/MainController.php
use Tutorial\DemoController;
$foo = new DemoController();
This solution worked for me. Hope it works for you.
I was also looking for the same thing, since Silex documentation still has the registerNamespace function that was removed.
Found a really nice answer here
In Short, all you have to do, is add a "psr-0" in "autoload" section in the composer.json
Example composer.json:
{
"require": {
"silex/silex": "~1.3",
"doctrine/dbal": "~2.2"
},
"autoload": {
"psr-0": {
"MyApp": "src/"
}
}
}
Make sure to update the Composer Autoloader ("composer install" or "composer update")
As of October, 2014 the best way to autoload your own classes into silex, is using PSR-4 (instead of PSR-0) autoloading through Composer using composer.json file in your application root:
{
"require": {
"silex/silex": "~2.0",
},
"autoload": {
"psr-4": {"Vendor\\Namespace\\": "path/to/src/"}
}
}
After editing your composer.json you need to do a:
composer update
on your project root directory, using terminal.
I've started a new project, where I use Composer to handle some dependencies, as well as their auto-loading.
I only keep the composer.json file in the VCS, instead of the entire vendor directory, so I don't want to start adding my code in there.
How should I handle my own project specific code, so that it auto loads as well?
This is actually very simple. Excluding vendors directory from your repository is the right approach. Your code should be stored in a separate place (like src).
Use the autoload property to make that composer recognizes your namespace(s):
{
"autoload": {
"psr-4": {
"Acme\\": "src/"
}
}
}
Assuming you have class names following the psr-4 standard, it should work. Below some example of class names and their locations on the file system:
Acme\Command\HelloCommand -> src/Command/HelloCommand.php
Acme\Form\Type\EmployeeType -> src/Form/Type/EmployeeType.php
Remember to define a namespace for each class. Here's an example of Acme\Command\HelloCommand:
<?php
namespace Acme\Command;
class HelloCommand
{
}
Don't forget to include the autoloader in your PHP controllers:
<?php
require 'vendor/autoload.php';
Read more on PSR-4 standard on PHP Framework Interoperability Group.
Note that if you edit composer.json, you need to either run install, update or dump-autoload to refresh the autoloader class paths.