• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nette / utils / 20848143243

09 Jan 2026 09:58AM UTC coverage: 93.264%. Remained the same
20848143243

push

github

dg
added CLAUDE.md

2063 of 2212 relevant lines covered (93.26%)

0.93 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.65
/src/Utils/Callback.php
1
<?php
2

3
/**
4
 * This file is part of the Nette Framework (https://nette.org)
5
 * Copyright (c) 2004 David Grudl (https://davidgrudl.com)
6
 */
7

8
declare(strict_types=1);
9

10
namespace Nette\Utils;
11

12
use Nette;
13
use function explode, func_get_args, ini_get, is_array, is_callable, is_object, is_string, preg_replace, restore_error_handler, set_error_handler, sprintf, str_contains, str_ends_with;
14

15

16
/**
17
 * PHP callable tools.
18
 */
19
final class Callback
20
{
21
        use Nette\StaticClass;
22

23
        /**
24
         * Invokes internal PHP function with own error handler.
25
         * @param  array<mixed>  $args
26
         */
27
        public static function invokeSafe(string $function, array $args, callable $onError): mixed
1✔
28
        {
29
                $prev = set_error_handler(function ($severity, $message, $file) use ($onError, &$prev, $function): ?bool {
1✔
30
                        if ($file === __FILE__) {
1✔
31
                                $msg = ini_get('html_errors')
1✔
32
                                        ? Html::htmlToText($message)
33
                                        : $message;
1✔
34
                                $msg = preg_replace("#^$function\\(.*?\\): #", '', $msg);
1✔
35
                                if ($onError($msg, $severity) !== false) {
1✔
36
                                        return null;
1✔
37
                                }
38
                        }
39

40
                        return $prev ? $prev(...func_get_args()) : false;
1✔
41
                });
1✔
42

43
                try {
44
                        return $function(...$args);
1✔
45
                } finally {
×
46
                        restore_error_handler();
1✔
47
                }
48
        }
49

50

51
        /**
52
         * Checks that $callable is valid PHP callback. Otherwise throws exception. If the $syntax is set to true, only verifies
53
         * that $callable has a valid structure to be used as a callback, but does not verify if the class or method actually exists.
54
         * @return callable
55
         * @throws Nette\InvalidArgumentException
56
         */
57
        public static function check(mixed $callable, bool $syntax = false): mixed
1✔
58
        {
59
                if (!is_callable($callable, $syntax)) {
1✔
60
                        throw new Nette\InvalidArgumentException(
1✔
61
                                $syntax
1✔
62
                                ? 'Given value is not a callable type.'
1✔
63
                                : sprintf("Callback '%s' is not callable.", self::toString($callable)),
1✔
64
                        );
65
                }
66

67
                return $callable;
1✔
68
        }
69

70

71
        /**
72
         * Converts PHP callback to textual form. Class or method may not exists.
73
         */
74
        public static function toString(mixed $callable): string
1✔
75
        {
76
                if ($callable instanceof \Closure) {
1✔
77
                        $inner = self::unwrap($callable);
1✔
78
                        return '{closure' . ($inner instanceof \Closure ? '}' : ' ' . self::toString($inner) . '}');
1✔
79
                } else {
80
                        is_callable(is_object($callable) ? [$callable, '__invoke'] : $callable, true, $textual);
1✔
81
                        return $textual;
1✔
82
                }
83
        }
84

85

86
        /**
87
         * Returns reflection for method or function used in PHP callback.
88
         * @param  callable  $callable  type check is escalated to ReflectionException
89
         * @throws \ReflectionException  if callback is not valid
90
         */
91
        public static function toReflection($callable): \ReflectionMethod|\ReflectionFunction
92
        {
93
                if ($callable instanceof \Closure) {
1✔
94
                        $callable = self::unwrap($callable);
1✔
95
                }
96

97
                if (is_string($callable) && str_contains($callable, '::')) {
1✔
98
                        return new ReflectionMethod(...explode('::', $callable, 2));
1✔
99
                } elseif (is_array($callable)) {
1✔
100
                        return new ReflectionMethod($callable[0], $callable[1]);
1✔
101
                } elseif (is_object($callable) && !$callable instanceof \Closure) {
1✔
102
                        return new ReflectionMethod($callable, '__invoke');
1✔
103
                } else {
104
                        return new \ReflectionFunction($callable);
1✔
105
                }
106
        }
107

108

109
        /**
110
         * Checks whether PHP callback is function or static method.
111
         */
112
        public static function isStatic(callable $callable): bool
113
        {
114
                return is_string(is_array($callable) ? $callable[0] : $callable);
×
115
        }
116

117

118
        /**
119
         * Unwraps closure created by Closure::fromCallable().
120
         */
121
        public static function unwrap(\Closure $closure): callable|array
1✔
122
        {
123
                $r = new \ReflectionFunction($closure);
1✔
124
                $class = $r->getClosureScopeClass()?->name;
1✔
125
                if (str_ends_with($r->name, '}')) {
1✔
126
                        return $closure;
1✔
127

128
                } elseif (($obj = $r->getClosureThis()) && $obj::class === $class) {
1✔
129
                        return [$obj, $r->name];
1✔
130

131
                } elseif ($class) {
1✔
132
                        return [$class, $r->name];
1✔
133

134
                } else {
135
                        return $r->name;
1✔
136
                }
137
        }
138
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc