типы — Проверьте, может ли вызываемый объект получить класс в качестве параметра в переполнении стека.

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

На данный момент я делаю что-то вроде

try {
$f($foo);
} catch (\TypeError $e) {
throw new \InvalidArgumentException('The provided function can not evaluate inputs of this type');
}

Есть ли способ проверить это БЕЗ фактического вызова вызываемого? Может быть, с отражением или какой-то другой темной магией?

0

Решение

Если вы хотите, чтобы можно было отразить любой вызываемый тип, вам нужно заключить логику в небольшую функцию. В зависимости от того, есть ли у вас массив, имя функции или анонимная функция, вам нужно создать ReflectionFunction или же ReflectionMethod. К счастью, оба они расширяются ReflectionFunctionAbstract, таким образом, мы можем напечатать возвращаемое значение.

function reflectCallable($arg): ReflectionFunctionAbstract {
if (is_array($arg)) {
$ref = new ReflectionMethod(...$arg);
} elseif (is_callable($arg)) {
$ref = new ReflectionFunction($arg);
}

return $ref;
}

Это вернет вам соответствующий объект для вашего вызываемого значения, который вы затем сможете использовать для получения параметров и действовать соответственно:

function definedFunc(Foo $foo) {}
$callable = function(Foo $foo) {};
class Bar { public function baz(Foo $foo) {} }

foreach (['definedFunc', $callable, ['Bar', 'baz']] as $callable) {
$reflected = reflectCallable($callable);

if ((string) $reflected->getParameters()[0]->getType() === 'Foo') {
echo 'Callable takes Foo', PHP_EOL;
}
}

Увидеть https://3v4l.org/c5vmM

Обратите внимание, что это не делает никакой обработки ошибок — вы, вероятно, получите предупреждения / уведомления, если вызываемый объект не принимает никаких параметров или первый параметр не имеет типа. Также требуется PHP 7+, но, надеюсь, это не проблема.

В настоящее время он не поддерживает объекты, которые реализуют __invoke или статические вызовы, определенные как "Foo::bar", но их не будет слишком сложно добавить в случае необходимости. Я только что нашел что-то очень похожее в источнике Twig, который выполняет более тщательную работу: https://github.com/twigphp/Twig/blob/2.x/lib/Twig/Node/Expression/Call.php#L262

1

Другие решения

Вы можете с ReflectionParameter :: GetType:

$f = function(Foo $foo) {};

$reflectionFunc = new ReflectionFunction($f);
$reflectionParams = $reflectionFunc->getParameters();
$reflectionType1 = $reflectionParams[0]->getType();

echo $reflectionType1;

выход:

Foo

1