Completed
Push — master ( 211a6a...987c6d )
by Rasmus
03:11
created

readable::trace()   F

Complexity

Conditions 16
Paths 323

Size

Total Lines 47
Code Lines 32

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 24
CRAP Score 18.0478

Importance

Changes 0
Metric Value
dl 0
loc 47
ccs 24
cts 30
cp 0.8
rs 3.7109
c 0
b 0
f 0
cc 16
eloc 32
nc 323
nop 2
crap 18.0478

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace mindplay;
4
5
use Closure;
6
use Error;
7
use ErrorException;
8
use Exception;
9
use ReflectionFunction;
10
11
/**
12
 * Pseudo-namespace for functions that generate human-readable string representations
13
 * of most types of PHP values.
14
 */
15
abstract class readable
16
{
17
    /**
18
     * @var int strings longer than this number of characters will be truncated when formatting string-values
19
     */
20
    public static $max_string_length = 120;
21
22
    /**
23
     * @var string[] map where PHP error severity code => constant name
24
     */
25
    public static $severity_names = [
26
        E_ERROR             => "E_ERROR",
27
        E_USER_ERROR        => "E_USER_ERROR",
28
        E_CORE_ERROR        => "E_CORE_ERROR",
29
        E_COMPILE_ERROR     => "E_COMPILE_ERROR",
30
        E_PARSE             => "E_PARSE",
31
        E_WARNING           => "E_WARNING",
32
        E_USER_WARNING      => "E_USER_WARNING",
33
        E_CORE_WARNING      => "E_CORE_WARNING",
34
        E_COMPILE_WARNING   => "E_COMPILE_WARNING",
35
        E_NOTICE            => "E_NOTICE",
36
        E_USER_NOTICE       => "E_USER_NOTICE",
37
        E_STRICT            => "E_STRICT",
38
        E_RECOVERABLE_ERROR => "E_RECOVERABLE_ERROR",
39
        E_DEPRECATED        => "E_DEPRECATED",
40
        E_USER_DEPRECATED   => "E_USER_DEPRECATED",
41
    ];
42
43
    /**
44
     * @param mixed $value any type of PHP value
45
     *
46
     * @return string readable representation of the given value
47
     */
48 1
    public static function value($value)
49
    {
50 1
        $type = is_array($value) && is_callable($value)
51 1
            ? "callable"
52 1
            : strtolower(gettype($value));
53
54
        switch ($type) {
55 1
            case "boolean":
56 1
                return $value ? "true" : "false";
57
58 1
            case "integer":
59 1
                return number_format($value, 0, "", "");
60
61 1
            case "double": // (for historical reasons "double" is returned in case of a float, and not simply "float")
62 1
                $formatted = sprintf("%.6g", $value);
63
64 1
                return $value == $formatted
65 1
                    ? "{$formatted}"
66 1
                    : "~{$formatted}";
67
68 1
            case "string":
69 1
                $string = strlen($value) > self::$max_string_length
70 1
                    ? substr($value, 0, self::$max_string_length) . "...[" . strlen($value) . "]"
71 1
                    : $value;
72
73 1
                return '"' . addslashes($string) . '"';
74
75 1
            case "array":
76 1
                return "[" . self::values($value) . "]";
77
78 1
            case "object":
79 1
                if ($value instanceof Closure) {
80 1
                    $reflection = new ReflectionFunction($value);
81
82 1
                    return "{Closure in " . $reflection->getFileName() . "({$reflection->getStartLine()})}";
83
                }
84
85 1
                return "{" . ($value instanceof \stdClass ? "object" : get_class($value)) . "}";
86
87 1
            case "resource":
88 1
                return "{" . get_resource_type($value) . "}";
89
90 1
            case "callable":
91 1
                return is_object($value[0])
92 1
                    ? '{' . get_class($value[0]) . "}->{$value[1]}()"
93 1
                    : "{$value[0]}::{$value[1]}()";
94
95 1
            case "null":
96 1
                return "null";
97
        }
98
99 1
        return "{{$type}}"; // "unknown type" and possibly unsupported (future) types
100
    }
101
102
    /**
103
     * @param array $array array containing any type of PHP values
104
     *
105
     * @return string comma-separated human-readable representation of the given values
106
     */
107 1
    public static function values(array $array)
108
    {
109 1
        $formatted = array_map(['mindplay\\readable', 'value'], $array);
110
111 1
        if (array_keys($array) !== range(0, count($array) - 1)) {
112 1
            foreach ($formatted as $name => $value) {
113 1
                $formatted[$name] = self::value($name) . " => {$value}";
114
            }
115
        }
116
117 1
        return implode(", ", $formatted);
118
    }
119
120
    /**
121
     * @param mixed $value any type of PHP value
122
     *
123
     * @return string human-readable type-name
124
     */
125 1
    public static function typeof($value)
126
    {
127 1
        $type = ! is_string($value) && ! is_object($value) && is_callable($value)
128 1
            ? "callable"
129 1
            : strtolower(gettype($value));
130
131
        switch ($type) {
132 1
            case "boolean":
133 1
                return "bool";
134 1
            case "integer":
135 1
                return "int";
136 1
            case "double":
137 1
                return "float";
138 1
            case "object":
139 1
                return $value instanceof \stdClass ? "object" : get_class($value);
140 1
            case "string":
141 1
            case "array":
142 1
            case "resource":
143 1
            case "callable":
144 1
            case "null":
145 1
                return $type;
146
        }
147
148 1
        return "unknown";
149
    }
150
151
    /**
152
     * @param mixed $callable
153
     *
154
     * @return string human-readable description of callback
155
     */
156 1
    public static function callback($callable)
157
    {
158 1
        if (is_string($callable) && is_callable($callable)) {
159 1
            return "{$callable}()";
160 1
        } elseif (is_object($callable) && method_exists($callable, "__invoke")) {
161 1
            return $callable instanceof Closure
162 1
                ? self::value($callable)
163 1
                : "{" . get_class($callable) . "}->__invoke()";
164
        }
165
166 1
        return self::value($callable);
167
    }
168
169
    /**
170
     * @param Exception|Error|int $error an Exception, Error (or one of the E_* error severity constants)
171
     *
172
     * @return string
173
     */
174 1
    public static function error($error)
175
    {
176 1
        if (is_int($error)) {
177 1
            return static::severity($error);
178
        }
179
180 1
        $type = get_class($error);
181
182 1
        if ($error instanceof ErrorException) {
183
            $severity = static::severity($error->getSeverity());
184
185
            $type = "{$type}: {$severity}";
186
        }
187
188 1
        if ($error instanceof Exception || $error instanceof Error) {
0 ignored issues
show
Bug introduced by
The class Error does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
189 1
            $message = $error->getMessage() ?: '{none}';
190
191 1
            $file = $error->getFile()
192 1
                ? $error->getFile() . "(" . $error->getLine() . ")"
193 1
                : "{no file}";
194
195 1
            return "{$type} with message: {$message} in {$file}";
196
        }
197
198
        return $type;
199
    }
200
201
    /**
202
     * @param int $severity one of the E_* error severity constants
203
     *
204
     * @return string
205
     */
206 1
    public static function severity($severity)
207
    {
208 1
        return isset(self::$severity_names[$severity])
209 1
            ? self::$severity_names[$severity]
210 1
            : "{unknown error-code}";
211
    }
212
213
    /**
214
     * @param array|Exception|Error $source      Exception, Error or stack-trace data as provided
215
     *                                           by Exception::getTrace() or debug_backtrace()
216
     * @param bool                  $with_params if TRUE, calls will be formatted with parameters
217
     *
218
     * @return string
219
     */
220 1
    public static function trace($source, $with_params = true)
221
    {
222 1
        if ($source instanceof Exception || $source instanceof Error) {
0 ignored issues
show
Bug introduced by
The class Error does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
223 1
            $trace = $source->getTrace();
224
        } elseif (is_array($source)) {
225
            $trace = $source;
226
        } else {
227
            return "{stack-trace unavailable}";
228
        }
229
230 1
        $formatted = [];
231
232 1
        foreach ($trace as $index => $entry) {
233 1
            $line = array_key_exists("line", $entry)
234 1
                ? ":" . $entry["line"]
235 1
                : "";
236
237 1
            $file = isset($entry["file"])
238 1
                ? $entry["file"]
239 1
                : "{no file}";
240
241 1
            $function = isset($entry["class"])
242 1
                ? $entry["class"] . @$entry["type"] . @$entry["function"]
243 1
                : @$entry["function"];
244
245 1
            if ($function === "require" || $function === "include") {
246
                // bypass argument formatting for include and require statements
247
                $args = isset($entry["args"]) && is_array($entry["args"])
248
                    ? reset($entry["args"])
249
                    : "";
250
            } else {
251 1
                $args = $with_params && isset($entry["args"]) && is_array($entry["args"])
252 1
                    ? static::values($entry["args"])
253 1
                    : "";
254
            }
255
256 1
            $call = $function
257 1
                ? "{$function}({$args})"
258 1
                : "";
259
260 1
            $depth = $index + 1;
261
262 1
            $formatted[] = sprintf("%6s", "{$depth}.") . " {$file}{$line} {$call}";
263
        }
264
265 1
        return implode("\n", $formatted);
266
    }
267
}
268