Search code examples
phpnamespacesautoloadspl

How to use roots classes in all namespaces automatically with spl_autoload?


It is possible to make accessible a Class within spl_autoload_register (automatically)?

For example, I using spl_autoload_register in index.php:

<?php
class Utils {
   public function example() {
      echo 'Hello word!';
   }
}

spl_autoload_register(function($class)
{
    $relative_class = strtolower(str_replace('\\', '/', $class));

    $file = './src/' . $relative_class . '.php';

    if (is_file($file)) {
        require_once $file;
    }
});

$user = new \Controllers\Foo\User;

This new \Controllers\Foo\User; autoload this file ./src/controllers/foo/user.php

user.php:

<?php
namespace Controllers/Foo;

class User
{
    public function foo() {
        //Something...
    }
}

If I need to use a Utils class I'll have to add new \Controllers\Foo\User in the file user.php like this:

public function foo() {
   \Utils::example();
}

or

<?php
namespace Controllers/Foo;

use \Utils as Utils;

class User
{
    public function foo() {
        Utils::example();
    }
}

It is possible to make accessible to Utils class within spl_autoload_register (automatically)? I would use without use \Utils as Utils; and without backslash (\Utils::).


Solution

  • There is no standard anything to do this besides using use ... as ... or "backslash" (\), however we can "cheat the PHP" using eval() within the spl_autoload_register() to extend the Utils class within the namespace.

    Only use this if really necessary, prefer to use "backslash" (\) or use \Utils as Utils

    Example (read comments in code):

    <?php
    class Utils {
       public static function example() {
          echo 'Hello World!';
       }
    }
    
    spl_autoload_register(function($class)
    {
        $relative_class = strtolower(str_replace('\\', '/', $class));
    
        $file = './src/' . $relative_class . '.php';
    
        if (is_file($file)) {
            $np = explode('\\', $class); //Dividi string
    
            //Check if class exists in namespace (prevent conflicts)
            if (class_exists(implode('::', $np)) === false) {
    
                //Remove "class name", use only "namespace"
                array_pop($np);
    
                //evaluate a namespace in eval (extends the Utils class)
                eval(
                    'namespace ' . implode('\\', $np) . ' {' . PHP_EOL .
                    'class Utils extends \Utils {}' . PHP_EOL .
                    '}'
                );
            }
    
            require_once $file;
        }
    });
    
    $user = new \Controllers\Foo\User;
    $user->foo(); //Show "Hello word!"
    

    I admit that it is an ugly hack and maybe I will not use, but it's still a hint yes.

    Note: use \Utils as Utils not work in eval