From 8c16891c6e7a4738d63788f4447614056ab5136e Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Tue, 27 Oct 2020 13:50:24 -0500 Subject: [PATCH] add isCallable helper --- src/Illuminate/Support/Reflector.php | 42 ++++++++++++++++++++++++++ tests/Support/SupportReflectorTest.php | 26 ++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/src/Illuminate/Support/Reflector.php b/src/Illuminate/Support/Reflector.php index fb597f5141f1..5490b0923ff2 100644 --- a/src/Illuminate/Support/Reflector.php +++ b/src/Illuminate/Support/Reflector.php @@ -3,10 +3,52 @@ namespace Illuminate\Support; use ReflectionClass; +use ReflectionMethod; use ReflectionNamedType; class Reflector { + /** + * This is a PHP 7.4 compatible implementation of is_callable. + * + * @param mixed $var + * @param bool $syntaxOnly + * @return bool + */ + public static function isCallable($var, $syntaxOnly = false) + { + if (! is_array($var)) { + return is_callable($var, $syntaxOnly); + } + + if (! isset($var[0]) && ! isset($var[1]) || + ! is_string($var[1])) { + return false; + } + + $class = is_object($var[0]) ? get_class($var[0]) : $var[0]; + + $method = $var[1]; + + if (! class_exists($class)) { + return false; + } + + if (method_exists($class, $method)) { + return (new ReflectionMethod($class, $method))->isPublic(); + } + + if (is_object($var[0]) && method_exists($class, '__call')) { + return (new ReflectionMethod($class, '__call'))->isPublic(); + } + + if (! is_object($var[0]) && method_exists($class, '__callStatic')) { + return (new ReflectionMethod($class, '__callStatic'))->isPublic(); + } + + return false; + } + /** * Get the class name of the given parameter's type, if possible. * diff --git a/tests/Support/SupportReflectorTest.php b/tests/Support/SupportReflectorTest.php index 55c4940f7543..8f5f394ca520 100644 --- a/tests/Support/SupportReflectorTest.php +++ b/tests/Support/SupportReflectorTest.php @@ -57,6 +57,16 @@ public function testUnionTypeName() $this->assertNull(Reflector::getParameterClassName($method->getParameters()[0])); } + + public function testIsCallable() + { + $this->assertTrue(Reflector::isCallable(function () {})); + $this->assertTrue(Reflector::isCallable([B::class, 'f'])); + $this->assertFalse(Reflector::isCallable([TestClassWithCall::class, 'f'])); + $this->assertTrue(Reflector::isCallable([new TestClassWithCall, 'f'])); + $this->assertTrue(Reflector::isCallable([TestClassWithCallStatic::class, 'f'])); + $this->assertFalse(Reflector::isCallable([new TestClassWithCallStatic, 'f'])); + } } class A @@ -82,3 +92,19 @@ public function f(A|Model $x) }' ); } + +class TestClassWithCall +{ + public function __call($method, $parameters) + { + + } +} + +class TestClassWithCallStatic +{ + public static function __callStatic($method, $parameters) + { + + } +}