Home » Php » php – Relative namespaces and call_user_func()

php – Relative namespaces and call_user_func()

Posted by: admin July 12, 2020 Leave a comment

Questions:

Code speaks better than words:

namespaces.php:

<?php

namespace foo;

use foo\models;

class factory
{
    public static function create($name)
    {
        /*
         * Note 1: FQN works!
         * return call_user_func("\foo\models\$name::getInstance");
         *
         * Note 2: direct instantiation of relative namespaces works!
         * return models\test::getInstance();
         */

        // Dynamic instantiation of relative namespaces fails: class 'models\test' not found
        return call_user_func("models\$name::getInstance");
    }
}

namespace foo\models;

class test
{
    public static $instance;

    public static function getInstance()
    {
        if (!self::$instance) {
            self::$instance = new self;
        }

        return self::$instance;
    }

    public function __construct()
    {
        var_dump($this);
    }
}

namespace_test.php:

<?php

require_once 'namespaces.php';

foo\factory::create('test');

As commented, if I use the full-qualified name inside call_user_func() it works as expected, but if I use relative namespaces it says the class was not found – but direct instantiations works. Am I missing something or its weird by design?

How to&Answers:

You have to use the fully qualified classname in callbacks.

See Example #3 call_user_func() using namespace name

<?php

namespace Foobar;

class Foo {
    static public function test() {
        print "Hello world!\n";
    }
}

call_user_func(__NAMESPACE__ .'\Foo::test'); // As of PHP 5.3.0
call_user_func(array(__NAMESPACE__ .'\Foo', 'test')); // As of PHP 5.3.0

I believe this is because call_user_func is a function from the global scope, executing the callback from the global scope as well. In any case, see first sentence.

Also see the note aboveExample #2 Dynamically accessing namespaced elements which states

One must use the fully qualified name (class name with namespace prefix).

Answer:

In current versions of PHP, the way you have it is the way it is — when using a string to reference a classname, it needs to be fully qualified with it’s complete namespace. It’s not great, but that’s the way it is.

In the forthcoming PHP v5.5, they will include a feature to address this, by providing a new Classname::class syntax, which you can use instead of putting the FQN classname in a string.

For more info on this, please see the relevant PHP RFC page here: https://wiki.php.net/rfc/class_name_scalars

Your code would look something like this:

return call_user_func([models$name::class,"getInstance"]);

That may not be exact; I don’t have a copy of 5.5 to test with to confirm. But either way, the new syntax will make things a lot better for use cases like yours.