Вызываемый объект PHP как член объекта

У меня есть класс Logger который, между прочим, имеет метод Log,
Как Log является наиболее распространенным использованием Logger Например, я подключен __invoke звонить Log

Другой класс "Сайт" содержит член "Журнал", экземпляр Logger.

Почему это работает:

$Log = $this->Log;  
$Log("Message");  

Но не это:

$this->Log("Message");

Первый завершается с ошибкой "PHP Fatal error: вызов неопределенного метода Site::Log()"
Это ограничение реализации вызываемого объекта или я что-то неправильно понимаю?

5 ответов

Решение

По тем же причинам, по которым вы не можете сделать это:

$value = $this->getArray()["key"];

или даже это

$value = getArray()["key"];

Потому что синтаксис PHP не очень хорошо работает.

К сожалению, это (все еще) ограничение PHP, но имеет смысл, когда вы думаете об этом, поскольку класс может содержать свойства и методы, которые совместно используют имена. Например:

<?php
class Test {
    public $log;

    public function __construct() {
        $this->log = function() {
            echo 'In Test::log property';
        };
    }

    public function log() {
        echo 'In Test::log() method';
    }
}

$test = new Test;
$test->log(); // In Test::log() method
call_user_func($test->log); // In Test::log property
?>

Если бы PHP разрешил синтаксис по вашему желанию, какая функция была бы вызвана? К сожалению, это только оставляет нас с call_user_func[_array]() (или копирование $this->log в другую переменную и вызывая это).

Однако было бы неплохо, если бы следующий синтаксис был приемлемым:

<?php
{$test->log}();
?>

Но, увы, это не так.

Это может работать:

${this->Log}("Message");

Но, может быть, проще и лучше использовать полный вызов? Кажется, нет способа получить то, что вы хотите работать в одной строке.

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

с php7.4 для меня работает следующий код

      ($this->Log)("Message");

Спасибо за ваш вопрос и за все ответы! Поделюсь своим опытом, вдруг кому-нибудь пригодится по этой теме.

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

В других языках я использовал его по-другому, а в PHP сначала написал так:

      const DEBUG_LOGGING = true;
$logdbg = DEBUG_LOGGING ?
    function(...$args) {
        foreach ($args as $arg) {
            echo var_export($arg, true), PHP_EOL;
        }
    } :
    null;

но тогда это будет в глобальной области:

      //this would work:
$logdbg && $logdbg($var1, $var2);


function test() {
  //some code
  
  //this wouldn't work:
  $logdbg && $logdbg($var3, $var4);
  
  //it would have to be:
  global $logdbg; $logdbg && $logdbg($var3, $var4);
  
  //other code
}

и я не хотел добавлять глобальные переменные, которые нельзя было бы поместить в пространство имен. Итак, проверив, что может быть в пространстве имен, я определил это внутри класса:

      const DEBUG_LOGGING = true;
class Dbg {
    public static $log = null;
    public static function init() {
        if (DEBUG_LOGGING) {
            self::$log = function(...$args) {
                foreach ($args as $arg) {
                    echo var_export($arg, true), PHP_EOL;
                }
            };
        }
    }
}
Dbg::init();

Dbg::$log && Dbg::$log('outside function', $var1);

function test() {
  //some code
  
    Dbg::$log && Dbg::$log('inside function', $var2, $ar3);
  
  //other code
}
test();

но он выдал мне то же самое «неинициализированное» предупреждение, о котором говорится в этой теме, и не сработало!

Благодаря этой теме и рекомендации Норберта Вагнера я попробовал использовать круглые скобки, и это сработало! Мне не нужно было добавлять его при логической проверке, только при вызове, и теперь код выглядит так и работает:

      //the only difference with the previous snippet
Dbg::$log && (Dbg::$log)('outside function', $var1);

Dbg::$log && (Dbg::$log)('inside function', $var2, $ar3);
Другие вопросы по тегам