问题
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:
- 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.
- 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