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

nette / utils / 22290136219

23 Feb 2026 01:47AM UTC coverage: 93.125% (-0.003%) from 93.128%
22290136219

push

github

dg
added CLAUDE.md

2086 of 2240 relevant lines covered (93.13%)

0.93 hits per line

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

95.74
/src/Utils/Callback.php
1
<?php declare(strict_types=1);
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
namespace Nette\Utils;
9

10
use Nette;
11
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;
12

13

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

21
        /**
22
         * Invokes internal PHP function with own error handler.
23
         * @param  callable-string  $function
24
         * @param  list<mixed>  $args
25
         * @param  callable(string, int): (bool|void|null)  $onError
26
         */
27
        public static function invokeSafe(string $function, array $args, callable $onError): mixed
1✔
28
        {
29
                $prev = set_error_handler(function (int $severity, string $message, string $file, int $line) 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 = (string) preg_replace("#^$function\\(.*?\\): #", '', $msg);
1✔
35
                                if ($onError($msg, $severity) !== false) {
1✔
36
                                        return true;
1✔
37
                                }
38
                        }
39

40
                        return $prev ? $prev(...func_get_args()) !== false : 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(mixed $callable): \ReflectionMethod|\ReflectionFunction
1✔
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
                        assert($callable instanceof \Closure || is_string($callable));
105
                        return new \ReflectionFunction($callable);
1✔
106
                }
107
        }
108

109

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

118

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

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

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

136
                } else {
137
                        return $r->name;
1✔
138
                }
139
        }
140
}
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