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

nette / utils / 20431395313

22 Dec 2025 12:06PM UTC coverage: 93.164% (+71.8%) from 21.324%
20431395313

push

github

dg
Html::addText() accepts int|null for back compatibility [Closes #332][Closes #333]

1 of 1 new or added line in 1 file covered. (100.0%)

140 existing lines in 15 files now uncovered.

2058 of 2209 relevant lines covered (93.16%)

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
         */
26
        public static function invokeSafe(string $function, array $args, callable $onError): mixed
1✔
27
        {
28
                $prev = set_error_handler(function ($severity, $message, $file) use ($onError, &$prev, $function): ?bool {
1✔
29
                        if ($file === __FILE__) {
1✔
30
                                $msg = ini_get('html_errors')
1✔
31
                                        ? Html::htmlToText($message)
32
                                        : $message;
1✔
33
                                $msg = preg_replace("#^$function\\(.*?\\): #", '', $msg);
1✔
34
                                if ($onError($msg, $severity) !== false) {
1✔
35
                                        return null;
1✔
36
                                }
37
                        }
38

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

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

49

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

66
                return $callable;
1✔
67
        }
68

69

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

84

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

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

107

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

116

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

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

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

133
                } else {
134
                        return $r->name;
1✔
135
                }
136
        }
137
}
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

© 2025 Coveralls, Inc