PHP – аргумент №3 (модификатор $) должен иметь вызываемый тип, задана строка

Я пытаюсь сохранить вызываемый/замыкающий объект как поле/свойство класса.

Моя первоначальная попытка была такой:

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?

Что касается callable как подсказки типа, это было явно запрещено для свойств, поскольку оно зависит от контекста.

Chris Haas 03.09.2024 16:15
func — это метод экземпляра, поэтому вы должны предоставить экземпляр.
shingo 03.09.2024 16:17

@shingo Да, ты прав. Эта проблема была добавлена ​​в пример кода случайно. Исходный код, который у меня есть, в порядке. Я обновлю вопрос.

user2173353 03.09.2024 16:23
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
1
3
50
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Предполагая, что вы используете 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

Интересный. Все становится слишком сложно понять, когда сообщение об ошибке неверно...

user2173353 03.09.2024 17:55

В этом случае сообщение об ошибке не совсем неверно. Только массив, который ссылается на существующий класс/метод, считается «вызываемым». Ему не удалось найти класс \SomeOtherClass, поэтому значение считалось обычным массивом и не вызываемым.

Michal Hynčica 03.09.2024 18:07

Другие вопросы по теме