Проверьте, может ли вызываемый объект получить класс в качестве параметра в php - PullRequest
0 голосов
/ 06 сентября 2018

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

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

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

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

Ответы [ 2 ]

0 голосов
/ 06 сентября 2018

Если вы хотите, чтобы можно было отразить любой вызываемый тип, вам нужно заключить логику в небольшую функцию. В зависимости от того, есть ли у вас массив, имя функции или анонимная функция, вам нужно создать либо 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

0 голосов
/ 06 сентября 2018

Вы можете с помощью ReflectionParameter :: getType :

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

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

echo $reflectionType1;

выход:

Foo

...