7

I have one directory that is going to keep all "helper" classes and functions. Let's call the directory helpers.

I want to configure PSR-4 fallback directory to point to this helpers directory:

    "autoload": {
          "psr-4": {
                "": "helpers/"
           }
     }

From Composer documentation:

... fallback directory where any namespace will be looked for.

So my understanding is that if my files/classes in that directory have PSR-4 compliant names my application should be able to find them there.

Now I created a file helpers/Logger.php with class Logger

What namespace should be for this class in order to 1) comply with PSR-4 and 2) just work?

I have tried

namespace Logger;

And load class as

$logger = new Logger();

But I receive error Class Logger not found

Deeper dive into composer code (loadClass() method) showed me that it actually finds and includes the file helpers/Logger.php, but the class still cannot be found for some reason.

According to PSR-4 namespace should be something like this:

namespace Helpers;

And class should be called like this:

$logger = new Helpers\Logger();

I have Class Helpers\Logger not found, but in addition the file helpers/Logger.php is not even included.

The logic inside Composer's loadClass() method for fallback is following:

    // PSR-4 lookup
    $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;

   ........

    // PSR-4 fallback dirs
    foreach ($this->fallbackDirsPsr4 as $dir) {
        if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
            return $file;
        }
    }

So it actually tries to match the file name against the fully qualified class name.

So it seems that I am missing something in my understanding of PSR-4.

But what exactly?

Edit

To check it all I run a simple file from project root, all other libraries that are configured via composer are loaded correctly (for example, Pimple is working fine):

<?php

require_once __DIR__ . '/vendor/autoload.php';
$app = new \Pimple\Container();

/** Register logger */
$app['logger'] = function ($c) {
    return new Helpers\Logger();
};

$app['logger']->info('test');

2 Answers2

3

This fallback works as a definition of directory for global namespace. So yes, it may be used for autoloading any class in any namespace, but you still need to follow PSR-4 rules for them. Directory structure should represent namespaces structure. If you have rules like:

"autoload": {
      "psr-4": {
            "": "helpers/"
       }
 }

your Helpers\Logger class should be in helpers/Helpers/Logger.php, because in this way Composer will resolve class file path from autoloading rules.

helpers/Helpers/Logger.php
   ^       ^      ^
   |       |      |
   |       |    Class name part
   |       |            
   |     Namespace part
   |
 Static prefix for global namespace
rob006
  • 21,383
  • 5
  • 53
  • 74
  • 1
    Thanks for the reply, but I am still confused - is it possible to avoid this duplicate **Helpers** part? So the class `Logger` is in `helpers/Logger.php`? What namespace should be set for this class? – Maksym Bodnar May 08 '18 at 17:08
  • 1
    @MaksymBodnar Just don't use fallback, you probably don't need it. Define regular autoloading rules for namespace as `"Helpers\\": "helpers/"` and put your logger class into `helpers/Logger.php`. – rob006 May 08 '18 at 17:14
0

PSR4 is working case-sensitive, so if you place a class in the folder helpers and the class itself uses the namespace Helpers, that won't work.

Nico Haase
  • 11,420
  • 35
  • 43
  • 69
  • According to PSR-4 (as I understand it) full path to the class consists of the prefix (which is an empty string in my case), base directory (which is `helpers/` in my case) and namespace that translates to the file name/class name (which is `Logger`). So it should find just `Logger`. – Maksym Bodnar Feb 15 '18 at 17:19
  • Ah, okay, then the autoloader might look for `Helpers\Logger` in the file `helpers/Helpers/Logger.php` - but you might find that on your own if you run your script with XDebug enabled to see where exactly the file should be located – Nico Haase Feb 15 '18 at 22:16
  • 1
    I think I get this. I am just curious why it is not possible to have `helpers/Logger.php` with `namespace Logger`? Or this is not PSR-4 compliant? – Maksym Bodnar Feb 16 '18 at 07:30
  • Why shouldn't that be possible? Just put that properly in your `composer.json` such that it can map the namepace `Logger` to the folder `helpers` ;) But this introduces much more configuration than you need if you just match namespaces with the proper folders – Nico Haase Feb 16 '18 at 08:07
  • Well, I wanted to use fallback feature - so I have a directory (`helpers`) where I can put all my _helpers_ classes and don't have to add every single one to composer autoload explicitly. But this seems to be impossible. – Maksym Bodnar Feb 16 '18 at 18:11
  • No, it is possible, but you have to use the proper namespace – Nico Haase Feb 16 '18 at 21:32
  • Could you give me a hint/advise, please? Because I have tried different combinations and it doesn't work unless I directly specify the prefix in autoload/psr-4 section. – Maksym Bodnar Feb 21 '18 at 22:24
  • What about placing the files at the position they belong to, just like I suggested multiple times and just like PSR4 tells you? – Nico Haase Feb 22 '18 at 05:55
  • I have already done that, just wanted to figure out what is the meaning of this fallback feature or how it is supposed to work. – Maksym Bodnar Feb 22 '18 at 16:46