Why do fully qualified names have to be used when dynamically assessing namespaced elements?

邮差的信 提交于 2020-03-25 17:57:21

问题


I have been trying to understand the chapter Namespaces and dynamic language features from php.net manual. Please consider the following code:

namespace Foo;

function strstr() {
    echo "My local ststr method called";
}

$a = 'strstr';
$a(); //This would call the global strstr method

As per the manual, I have to go $a = 'Foo\strstr', but I can't find a reason for this. Why can't php interpreter at runtime know that $a = 'strstr'; was defined in the context of namespace Foo, just like executing strstr() straightaway the interpreter remember the context is namespace Foo at runtime.

One reason I can think of is $a to be called on an event if a certain condition(like if user input received) is met, because then the function is just bound to the event without any contextual information.


回答1:


More from PHP docs on imports:

Importing is performed at compile-time, and so does not affect dynamic class, function or constant names.

I couldn't find the reason for this decision, but I'd imagine it is a combination of the following:

  1. Performance - resolving imports for dynamic names on each use could be costly. This can't be done during compilation due to the dynamic nature of PHP and its strings.
  2. Consistency and avoiding complexity - for dynamic names to work the way you describe, several issues would have to be resolved, like passing them to different scripts with different imports, serialization/deserialization etc.

It seems to me that treating all dynamic names as fully-qualified was the easiest solution. I also can't imagine a real-world use-case since most code can do without using dynamic names like that at all.




回答2:


Let's inspect the opcodes that are generated (using php -d opcache.opt_debug_level=0x10000 test.php):

The code:

<?php

namespace Foo;

function strstr() {
    echo "My local ststr method called";
}

$a = 'strstr';
$a();

strstr();

The opcodes:

$_main: ; (lines=7, args=0, vars=1, tmps=3)
    ; (before optimizer)
    ; /code/test.php:1-13
L0 (5):     NOP
L1 (9):     ASSIGN CV0($a) string("strstr")
L2 (10):    INIT_DYNAMIC_CALL 0 CV0($a)
L3 (10):    DO_FCALL
L4 (12):    INIT_NS_FCALL_BY_NAME 0 string("Foo\strstr")
L5 (12):    DO_FCALL
L6 (13):    RETURN int(1)

Foo\strstr: ; (lines=2, args=0, vars=0, tmps=0)
    ; (before optimizer)
    ; /code/test.php:5-7
L0 (6):     ECHO string("My local ststr method called")
L1 (7):     RETURN null

As you can see $a() is using the INIT_DYNAMIC_CALL opcode. When the passed function name is a string, it is treated as being fully qualified.

On the other hand strstr() uses the INIT_NS_FCALL_BY_NAME opcode, with the name resolved in the current namespace passed to it. Upon execution it will first look for that name (Foo\strstr), and when that does not exist it will fall back to the global namespace.




回答3:


The main reason is probably that it would make the result completely unpredictable. Value of variable can be passed as argument to another function/method in different namespace, which could pass it further to different namespace. If this value will be treated as relative name, it will mean completely different things depending on context. Take this example:

namespace A {
    function test() {
        echo __FUNCTION__, "\n";
    }

    function run($callback) {
        $callback();
    }
}


namespace B {
    function test() {
        echo __FUNCTION__, "\n";
    }

    function run($callback) {
        $callback();
    }
}

namespace {
    function test() {
        echo __FUNCTION__, "\n";
    }

    function run($callback) {
        $callback();
    }

    $callback = 'test';
    run($callback);
    A\run($callback);
    B\run($callback);

    $callback = 'A\test';
    run($callback);
    A\run($callback);
    B\run($callback);
}

Now callback is treated always in the same consistent way:

test
test
test
A\test
A\test
A\test

If you would interpret dynamic callback as relative name, you will get pure madness, where each call will give you different result:

test
A\test
B\test
A\test
A\A\test
B\A\test

Treating strings as FQN makes it much simpler and more predictable.



来源:https://stackoverflow.com/questions/60474786/why-do-fully-qualified-names-have-to-be-used-when-dynamically-assessing-namespac

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!