Почему при динамической оценке элементов пространства имен необходимо использовать полностью определенные имена? - PullRequest
0 голосов
/ 01 марта 2020

Я пытался понять главу Пространства имен и динамические c особенности языка из php. net руководства. Пожалуйста, рассмотрите следующий код:

namespace Foo;

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

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

Согласно инструкции, мне нужно go $a = 'Foo\strstr', но я не могу найти причину для этого. Почему интерпретатор php во время выполнения не может знать, что $a = 'strstr'; был определен в контексте namespace Foo, точно так же как при выполнении strstr() сразу же интерпретатор помнит, что контекст namespace Foo во время выполнения.

Одна из причин, по которой я могу придумать, - это $a для вызова события, если выполняется определенное условие (например, если пользовательский ввод получен), потому что тогда функция просто привязывается к событию без какой-либо контекстной информации.

Ответы [ 3 ]

2 голосов
/ 04 марта 2020

Давайте проверим сгенерированные коды операций (используя php -d opcache.opt_debug_level=0x10000 test.php):

Код:

<?php

namespace Foo;

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

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

strstr();

Коды операций:

$_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

Как вы можете см. $a() использует код операции INIT_DYNAMIC_CALL. Если переданное имя функции является строкой, оно считается полностью определенным.

С другой стороны, strstr() использует код операции INIT_NS_FCALL_BY_NAME, а имя, разрешенное в текущем пространстве имен, передается ему. После выполнения он сначала ищет это имя (Foo\strstr), а когда его не существует, он возвращается к глобальному пространству имен.

2 голосов
/ 01 марта 2020

Больше от PHP документов при импорте:

Импорт выполняется во время компиляции и поэтому не влияет на имена классов, функций или констант Dynami c .

Я не смог найти причину этого решения, но я бы предположил, что это комбинация следующего:

  1. Производительность - разрешение импорта для динамических c имен на каждое использование может быть дорогостоящим. Это не может быть сделано во время компиляции из-за динамической c природы PHP и его строк.
  2. Согласованность и избежание сложности - для имен Dynami c работают так, как вы описываете, несколько проблем должно быть решено, как передача их в различные сценарии с различным импортом, сериализация / десериализация и т. д. c.

Мне кажется, что обработка всех динамических c имен как полностью квалифицированных была самое простое решение Я также не могу представить себе реальный вариант использования, так как большая часть кода может обойтись без использования таких динамических c имен.

1 голос
/ 08 марта 2020

Основная причина, вероятно, состоит в том, что это сделает результат совершенно непредсказуемым. Значение переменной может быть передано в качестве аргумента другой функции / методу в другом пространстве имен, которое может передавать его дальше в другое пространство имен. Если это значение будет рассматриваться как относительное имя, это будет означать совершенно разные вещи в зависимости от контекста. Возьмите этот пример :

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);
}

Теперь обратный вызов обрабатывается всегда одинаковым образом:

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

Если вы интерпретируете динамический c обратный вызов как относительный имя, вы получите чистое безумие, где каждый вызов даст вам разные результаты:

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

Обработка строк как FQN делает его намного проще и более предсказуемым.

...