Я пытаюсь сохранить вызываемый/замыкающий объект как поле/свойство класса.
Моя первоначальная попытка была такой:
class MyClass
{
protected callable $modifier;
public function __construct(callable $modifier) {
$this->modifier = $modifier;
}
}
class SomeOtherClass
{
public static function func()
{
return ['a' => 'b'];
}
}
$test = new MyClass('SomeOtherClass::func');
Это вернуло ошибку:
$modifier не может иметь вызываемый тип
Затем, прочитав это в Интернете, я попробовал это, но безуспешно:
class MyClass
{
protected Closure $modifier;
public function __construct(callable $modifier) {
$this->modifier = Closure::fromCallable($modifier);
}
}
class SomeOtherClass
{
public static function func()
{
return ['a' => 'b'];
}
}
$test = new MyClass('SomeOtherClass::func');
Последний возвращает эту ошибку:
Аргумент №1 ($модификатор) должен иметь вызываемый тип, задана строка.
Тогда я попробовал это:
class MyClass
{
protected Closure $modifier;
public function __construct(callable $modifier) {
$this->modifier = Closure::fromCallable($modifier);
}
}
class SomeOtherClass
{
public static function func()
{
return ['a' => 'b'];
}
}
$test = new MyClass(['SomeOtherClass', 'func']); // <----- changed string to an array
Что возвращает эту ошибку:
Аргумент №1 (модификатор $) должен иметь вызываемый тип, задан массив.
Возможно ли это вообще сделать на PHP?
func
— это метод экземпляра, поэтому вы должны предоставить экземпляр.
@shingo Да, ты прав. Эта проблема была добавлена в пример кода случайно. Исходный код, который у меня есть, в порядке. Я обновлю вопрос.
Предполагая, что вы используете PHP 8.1 или выше (которые на момент написания статьи являются единственными поддерживаемыми версиями PHP), тогда вы можете использовать первоклассный вызываемый синтаксис, как представлено в 8.1.
Для этого вы используете имя функции, а затем (...)
в качестве аргумента для передачи.
В вашем случае вы можете сделать это:
class MyClass
{
protected Closure $modifier;
public function __construct(callable $modifier) {
$this->modifier = Closure::fromCallable($modifier);
}
public function runClosure()
{
var_dump(($this->modifier)());
}
}
class SomeOtherClass
{
public static function func()
{
return ['a' => 'b'];
}
}
$test = new MyClass(SomeOtherClass::func(...));
$test->runClosure();
Живая демонстрация: https://3v4l.org/nMmvq
Н.Б. В приведенном выше коде предполагается, что вы хотите использовать func()
как статический метод (как определено в примере кода). Если кто-то читает и хочет сделать что-то подобное, но с помощью метода экземпляра, вам потребуется небольшая корректировка следующим образом:
class MyClass
{
protected Closure $modifier;
public function __construct(callable $modifier) {
$this->modifier = Closure::fromCallable($modifier);
}
public function runClosure()
{
var_dump(($this->modifier)());
}
}
class SomeOtherClass
{
public function func()
{
return ['a' => 'b'];
}
}
$soc = new SomeOtherClass();
$test = new MyClass($soc->func(...));
$test->runClosure();
Живая демонстрация: https://3v4l.org/iEAiM
Просто для полноты картины. Проблема в коде ОП, скорее всего, была вызвана пространствами имен.
Вы должны использовать полное имя в вызываемом массиве/строке. Итак, вам нужно использовать [SomeOtherClass::class, 'func']
или ['\MyNamespace\SomeOtherClass', 'func']
.
Смотрите живую демонстрацию: https://3v4l.org/pnWYC
Интересный. Все становится слишком сложно понять, когда сообщение об ошибке неверно...
В этом случае сообщение об ошибке не совсем неверно. Только массив, который ссылается на существующий класс/метод, считается «вызываемым». Ему не удалось найти класс \SomeOtherClass
, поэтому значение считалось обычным массивом и не вызываемым.
Что касается
callable
как подсказки типа, это было явно запрещено для свойств, поскольку оно зависит от контекста.