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

10,481

Solution 1

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?

Solution 2

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;
Share:
10,481
Dave Hollingworth
Author by

Dave Hollingworth

Web application developer and IT trainer. I like to keep things simple :-)

Updated on August 01, 2022

Comments

  • Dave Hollingworth
    Dave Hollingworth over 1 year

    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?

  • Dave Hollingworth
    Dave Hollingworth about 8 years
    Nice explanation, thanks. I was under the mistaken impression that psr-4 had to have matching folders and namespaces. As for separate packages for Core and App, good point, although they're just examples I picked for this question ;-)
  • humble_wolf
    humble_wolf over 6 years
    One question : Why PSR-4 requires both name-space and directory, why aren't they happy with namespace only ?
  • Sven
    Sven over 6 years
    @AmarjeetChaudhary You have full freedom to define the starting directory for PSR-4 structures to your liking, however you HAVE to provide a directory somehow because how would you otherwise know where to start looking? Defining the root directory of a project is no sensible default because that's where meta data like README, composer.json, .gitignore usually is stored. Defining any other directory as a default also bears the question how to agree on a name. src seems to be a common selection for many projects, but it's not forced on everybody.
  • humble_wolf
    humble_wolf over 6 years
    @sven I may be lacking some fundamental concepts but as per this guy (phpenthusiast.com/blog/how-to-autoload-with-composer) , what we are doing in composer.json file is, dumping some php file paths into composer's autoload.php , why can't we identify those paths via namespace only ?
  • Sven
    Sven over 6 years
    @AmarjeetChaudhary That guy is doing it exactly like I do it too, with regards to defining the autoloading. However he is claiming that "you have to import namespaces" (section "How to autoload the PSR-4 way?" letter "e"), which is wrong - you don't have to, you may. Importing namespaces has nothing to do with autoloading, so Composer is not affected. Importing is like telling PHP "Look, I think there's a class named Foo\Bar, and I will only write Bar in this file" and PHP will expand your Bar to Foo\Bar - and only if this is really used as a class, PHP will trigger the autoloading.
  • cprn
    cprn over 5 years
    I assume it's bad performance-wise but only for composer, right? Or does the application instance using autoload.php files generated by composer suffer performance hit as well?
  • Christian
    Christian almost 2 years
    @cprn this is a bit late but, if you do not dump the autoloader (which is default behaviour), than it will affect application performance as well. Generally speaking, it's also bad practice because if you have 2+ packages defining the catchall namespace (a) you need to search for the source class in both packages and (b) they can actually conflict if they both define the exact classes(+namespace) and (c) following previous point, one class would shadow the other.
  • cprn
    cprn almost 2 years
    @Christian I was just making sure I understand correctly that after dumping the autoloader all class lookups are done in no more than O(n) time complexity.
  • Christian
    Christian almost 2 years
    @cprn it depends... there are sort of 3 options: (1) no dumping, lookups only (2) dumping but with lookups dump-autoload (3) dumping and no lookups dump-autoload --optimize. Theoretically, (3) is the best for performance. In practice, if you have a large class that you rarely use and just a few other small classes, then (1) could be better.