0

I have tried to write the following code but cannot figure out why it will not find the class in spl_autoload_register() with a namespace?

Error I get is:

Warning: require_once(src/test\StringHelper.php): failed to open stream: No such file or directory

Autoloader.php file:

<?php

spl_autoload_register(function($classname){
    require_once "src/$classname.php"; // NOT WORKING DYNAMICALLY

//    require_once "src/StringHelper.php"; // WORKING WHEN HARD CODED
});

$stringHelper1 = new test\StringHelper(); // Class with namespace defined
echo $stringHelper1->hello() . PHP_EOL; // returns text

StringHelper.php inside src folder:

<?php namespace test;

class StringHelper{

    function hello(){
        echo "hello from string helper";
    }
}

I am also using XAMPP if this make a difference.

sam67
  • 61
  • 2
  • 12
  • 2
    Because `$classname` is the [fully qualified name](https://www.php.net/manual/en/language.namespaces.rules.php) which includes the namespace. You need to strip out the classname if you just need that – apokryfos Apr 02 '21 at 14:05
  • @mario sorry I tried this before when testing with a "/" but still no luck and so removed and forgot to update the error message. – sam67 Apr 02 '21 at 14:06
  • @apokryfos please can you post a code example for me to see? thanks – sam67 Apr 02 '21 at 14:11
  • https://stackoverflow.com/questions/19901850/how-do-i-get-an-objects-unqualified-short-class-name – Jeto Apr 02 '21 at 14:12
  • @Jeto - That answer is about getting the name from a class instance, not a string (which this would give them). The accepted answer even uses the reflection to get the name. – M. Eriksson Apr 02 '21 at 14:44
  • I would recommend reading up on [PSR-4](https://www.php-fig.org/psr/psr-4/) and use one of the many autoloaders for it. You could then also use composer to handle the autoloading for you. – M. Eriksson Apr 02 '21 at 14:46
  • @MagnusEriksson There are many answers to that question, many of which go through the class name first. 100% agreed on using a proper PSR-4 autoloader though (but I'm guessing OP is deliberately experimenting with custom ones). – Jeto Apr 02 '21 at 14:48

1 Answers1

3

As already pointed out in the comments, you'll need to strip everything besides the class name, like so:

$classname = substr($classname, strrpos($classname, "\\") + 1);

Within the context of your autoloading function:

spl_autoload_register(function($classname){

    $classname = substr($classname, strrpos($classname, "\\") + 1);
    require_once "src/{$classname}.php"; 
});

Let's take this a step further by making use of the fact that an autoload function always receives the qualified namespace as opposed to, for example, the relative namespace:

<?php

namespace Acme;

$foo = new \Acme\Foo(); // Fully qualified namespace 
$foo = new Acme\Foo();  // Qualified namespace
$foo = new Foo();       // Relative namespace

In all three instances, our autoload function is always given Acme\Foo as argument. With this in mind, it's fairly easy to implement an autoloader strategy that maps a namespace and any sub-namespaces to a file system path - especially if we include the top-level namespace (Acme, in this case) in the filesystem hierarchy.

For example, given these two classes within some project of ours...

<?php

namespace Acme;

class Foo {}

Foo.php

<?php

namespace Acme\Bar;

class Bar {}

Bar.php

...within this file system layout...

my-project
`-- library
    `-- Acme
        |-- Bar
        |   `-- Bar.php
        `-- Foo.php

...we could implement a simple mapping between a namespaced class and its physical location like so:

<?php

namespace Acme;

const LIBRARY_DIR = __DIR__.'/lib'; // Where our classes reside

/**
 * Autoload classes within the current namespace
 */
spl_autoload_register(function($qualified_class_name) {

    $filepath = str_replace(

        '\\', // Replace all namespace separators...
        '/',  // ...with their file system equivalents
        LIBRARY_DIR."/{$qualified_class_name}.php"
    );

    if (is_file($filepath)) {

        require_once $filepath;
    }
});

new Foo();
new Bar\Bar();

Also note that you can register multiple autoloading functions, for example, to handle different top-level namespaces in different physical locations. In a real-word project, though, you might want to get yourself acquainted with Composer's autoloading mechanism:

At some point, you might also want to have a look into PHP's autoloading specification:

nosurs
  • 680
  • 6
  • 13