Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use composer psr-4 fallback correctly

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');
like image 261
Maksym Bodnar Avatar asked Feb 14 '18 17:02

Maksym Bodnar


1 Answers

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
like image 53
rob006 Avatar answered Oct 08 '22 09:10

rob006