3v4l.org

run code in 300+ PHP versions simultaneously
<?php trait CouldCheckMethods { private static array $couldBeCalled = []; public static function check(callable $method): void { if (!self::could($method)) { throw new BadMethodCallException( sprintf( '%s cannot be called. To call that method, method should have attribute `%s`', self::callableToString($method), static::class ) ); } } public static function could(callable $method): bool { $method = self::callableToString($method); if (!array_key_exists($method, self::$couldBeCalled)) { self::$couldBeCalled[$method] = self::checkMethod($method); } return self::$couldBeCalled[$method]; } private static function checkMethod(string $method): bool { try { if (PHP_MINOR_VERSION >= 3) { // This is because since 8.4 calling new RefMet() with 1 parametr is deprecated $reflection = ReflectionMethod::createFromMethodName($method); } else { // but createFromMethodName is not available before 8.3 $reflection = new ReflectionMethod($method); } } catch (ReflectionException) { return false; } return (bool) $reflection->getAttributes(static::class); } private static function callableToString(callable $method): string { if (is_array($method)) { return sprintf('%s::%s', is_object($method[0]) ? get_class($method[0]) : $method[0], $method[1]); } if ($method instanceof Closure) { return 'Closure'; } if (is_string($method)) { return $method; } if (is_object($method) && method_exists($method, '__invoke')) { return sprintf('%s::__invoke', $method::class); } throw new InvalidArgumentException('Unsupported callable type'); } } #[Attribute(Attribute::TARGET_METHOD)] class CouldCallStatically { use CouldCheckMethods; } #[Attribute(Attribute::TARGET_METHOD)] class CouldCallNonStatically { use CouldCheckMethods; } class MyClass { #[CouldCallNonStatically] #[CouldCallStatically] protected static function send(string $send) { var_dump([ 'We made it', $send ]); } #[CouldCallStatically] protected static function onlyWorkStatically(string $send) { var_dump([ 'Work only static', $send ]); } protected function stillProtected() { var_dump('This will failed, because of missing attribute'); } public function __call(string $name, array $arguments) { CouldCallNonStatically::check([$this, $name]); return $this::$name(...$arguments); } public static function __callStatic(string $name, array $arguments) { CouldCallStatically::check([self::class, $name]); return static::$name(...$arguments); } } $obj = new MyClass(); $obj->send('Non static call'); MyClass::send('Static call'); try { MyClass::onlyWorkStatically('Try only static call'); $obj->onlyWorkStatically('Oh that will failed'); } catch (Throwable $e) { var_dump($e->getMessage()); } try { $obj->stillProtected('Oh that will failed'); } catch (Throwable $e) { var_dump($e->getMessage()); } try { MyClass::stillProtected('Oh that will failed'); } catch (Throwable $e) { var_dump($e->getMessage()); }

preferences:
30.11 ms | 406 KiB | 5 Q