Subversion-Projekte lars-tiefland.laravel_shop

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php declare(strict_types=1);
2
/*
3
 * This file is part of sebastian/exporter.
4
 *
5
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
namespace SebastianBergmann\Exporter;
11
 
12
use function bin2hex;
13
use function count;
14
use function function_exists;
15
use function get_class;
16
use function get_resource_type;
17
use function gettype;
18
use function implode;
19
use function ini_get;
20
use function ini_set;
21
use function is_array;
22
use function is_float;
23
use function is_object;
24
use function is_resource;
25
use function is_string;
26
use function mb_strlen;
27
use function mb_substr;
28
use function preg_match;
29
use function spl_object_hash;
30
use function sprintf;
31
use function str_repeat;
32
use function str_replace;
33
use function strlen;
34
use function substr;
35
use function var_export;
36
use SebastianBergmann\RecursionContext\Context;
37
use SplObjectStorage;
38
 
39
/**
40
 * A nifty utility for visualizing PHP variables.
41
 *
42
 * <code>
43
 * <?php
44
 * use SebastianBergmann\Exporter\Exporter;
45
 *
46
 * $exporter = new Exporter;
47
 * print $exporter->export(new Exception);
48
 * </code>
49
 */
50
class Exporter
51
{
52
    /**
53
     * Exports a value as a string.
54
     *
55
     * The output of this method is similar to the output of print_r(), but
56
     * improved in various aspects:
57
     *
58
     *  - NULL is rendered as "null" (instead of "")
59
     *  - TRUE is rendered as "true" (instead of "1")
60
     *  - FALSE is rendered as "false" (instead of "")
61
     *  - Strings are always quoted with single quotes
62
     *  - Carriage returns and newlines are normalized to \n
63
     *  - Recursion and repeated rendering is treated properly
64
     *
65
     * @param int $indentation The indentation level of the 2nd+ line
66
     *
67
     * @return string
68
     */
69
    public function export($value, $indentation = 0)
70
    {
71
        return $this->recursiveExport($value, $indentation);
72
    }
73
 
74
    /**
75
     * @param array<mixed> $data
76
     * @param Context      $context
77
     *
78
     * @return string
79
     */
80
    public function shortenedRecursiveExport(&$data, Context $context = null)
81
    {
82
        $result   = [];
83
        $exporter = new self();
84
 
85
        if (!$context) {
86
            $context = new Context;
87
        }
88
 
89
        $array = $data;
90
        $context->add($data);
91
 
92
        foreach ($array as $key => $value) {
93
            if (is_array($value)) {
94
                if ($context->contains($data[$key]) !== false) {
95
                    $result[] = '*RECURSION*';
96
                } else {
97
                    $result[] = sprintf(
98
                        'array(%s)',
99
                        $this->shortenedRecursiveExport($data[$key], $context)
100
                    );
101
                }
102
            } else {
103
                $result[] = $exporter->shortenedExport($value);
104
            }
105
        }
106
 
107
        return implode(', ', $result);
108
    }
109
 
110
    /**
111
     * Exports a value into a single-line string.
112
     *
113
     * The output of this method is similar to the output of
114
     * SebastianBergmann\Exporter\Exporter::export().
115
     *
116
     * Newlines are replaced by the visible string '\n'.
117
     * Contents of arrays and objects (if any) are replaced by '...'.
118
     *
119
     * @return string
120
     *
121
     * @see    SebastianBergmann\Exporter\Exporter::export
122
     */
123
    public function shortenedExport($value)
124
    {
125
        if (is_string($value)) {
126
            $string = str_replace("\n", '', $this->export($value));
127
 
128
            if (function_exists('mb_strlen')) {
129
                if (mb_strlen($string) > 40) {
130
                    $string = mb_substr($string, 0, 30) . '...' . mb_substr($string, -7);
131
                }
132
            } else {
133
                if (strlen($string) > 40) {
134
                    $string = substr($string, 0, 30) . '...' . substr($string, -7);
135
                }
136
            }
137
 
138
            return $string;
139
        }
140
 
141
        if (is_object($value)) {
142
            return sprintf(
143
                '%s Object (%s)',
144
                get_class($value),
145
                count($this->toArray($value)) > 0 ? '...' : ''
146
            );
147
        }
148
 
149
        if (is_array($value)) {
150
            return sprintf(
151
                'Array (%s)',
152
                count($value) > 0 ? '...' : ''
153
            );
154
        }
155
 
156
        return $this->export($value);
157
    }
158
 
159
    /**
160
     * Converts an object to an array containing all of its private, protected
161
     * and public properties.
162
     *
163
     * @return array
164
     */
165
    public function toArray($value)
166
    {
167
        if (!is_object($value)) {
168
            return (array) $value;
169
        }
170
 
171
        $array = [];
172
 
173
        foreach ((array) $value as $key => $val) {
174
            // Exception traces commonly reference hundreds to thousands of
175
            // objects currently loaded in memory. Including them in the result
176
            // has a severe negative performance impact.
177
            if ("\0Error\0trace" === $key || "\0Exception\0trace" === $key) {
178
                continue;
179
            }
180
 
181
            // properties are transformed to keys in the following way:
182
            // private   $property => "\0Classname\0property"
183
            // protected $property => "\0*\0property"
184
            // public    $property => "property"
185
            if (preg_match('/^\0.+\0(.+)$/', (string) $key, $matches)) {
186
                $key = $matches[1];
187
            }
188
 
189
            // See https://github.com/php/php-src/commit/5721132
190
            if ($key === "\0gcdata") {
191
                continue;
192
            }
193
 
194
            $array[$key] = $val;
195
        }
196
 
197
        // Some internal classes like SplObjectStorage don't work with the
198
        // above (fast) mechanism nor with reflection in Zend.
199
        // Format the output similarly to print_r() in this case
200
        if ($value instanceof SplObjectStorage) {
201
            foreach ($value as $key => $val) {
202
                $array[spl_object_hash($val)] = [
203
                    'obj' => $val,
204
                    'inf' => $value->getInfo(),
205
                ];
206
            }
207
        }
208
 
209
        return $array;
210
    }
211
 
212
    /**
213
     * Recursive implementation of export.
214
     *
215
     * @param mixed                                       $value       The value to export
216
     * @param int                                         $indentation The indentation level of the 2nd+ line
217
     * @param \SebastianBergmann\RecursionContext\Context $processed   Previously processed objects
218
     *
219
     * @return string
220
     *
221
     * @see    SebastianBergmann\Exporter\Exporter::export
222
     */
223
    protected function recursiveExport(&$value, $indentation, $processed = null)
224
    {
225
        if ($value === null) {
226
            return 'null';
227
        }
228
 
229
        if ($value === true) {
230
            return 'true';
231
        }
232
 
233
        if ($value === false) {
234
            return 'false';
235
        }
236
 
237
        if (is_float($value)) {
238
            $precisionBackup = ini_get('precision');
239
 
240
            ini_set('precision', '-1');
241
 
242
            try {
243
                $valueStr = (string) $value;
244
 
245
                if ((string) (int) $value === $valueStr) {
246
                    return $valueStr . '.0';
247
                }
248
 
249
                return $valueStr;
250
            } finally {
251
                ini_set('precision', $precisionBackup);
252
            }
253
        }
254
 
255
        if (gettype($value) === 'resource (closed)') {
256
            return 'resource (closed)';
257
        }
258
 
259
        if (is_resource($value)) {
260
            return sprintf(
261
                'resource(%d) of type (%s)',
262
                $value,
263
                get_resource_type($value)
264
            );
265
        }
266
 
267
        if (is_string($value)) {
268
            // Match for most non printable chars somewhat taking multibyte chars into account
269
            if (preg_match('/[^\x09-\x0d\x1b\x20-\xff]/', $value)) {
270
                return 'Binary String: 0x' . bin2hex($value);
271
            }
272
 
273
            return "'" .
274
            str_replace(
275
                '<lf>',
276
                "\n",
277
                str_replace(
278
                    ["\r\n", "\n\r", "\r", "\n"],
279
                    ['\r\n<lf>', '\n\r<lf>', '\r<lf>', '\n<lf>'],
280
                    $value
281
                )
282
            ) .
283
            "'";
284
        }
285
 
286
        $whitespace = str_repeat(' ', 4 * $indentation);
287
 
288
        if (!$processed) {
289
            $processed = new Context;
290
        }
291
 
292
        if (is_array($value)) {
293
            if (($key = $processed->contains($value)) !== false) {
294
                return 'Array &' . $key;
295
            }
296
 
297
            $array  = $value;
298
            $key    = $processed->add($value);
299
            $values = '';
300
 
301
            if (count($array) > 0) {
302
                foreach ($array as $k => $v) {
303
                    $values .= sprintf(
304
                        '%s    %s => %s' . "\n",
305
                        $whitespace,
306
                        $this->recursiveExport($k, $indentation),
307
                        $this->recursiveExport($value[$k], $indentation + 1, $processed)
308
                    );
309
                }
310
 
311
                $values = "\n" . $values . $whitespace;
312
            }
313
 
314
            return sprintf('Array &%s (%s)', $key, $values);
315
        }
316
 
317
        if (is_object($value)) {
318
            $class = get_class($value);
319
 
320
            if ($hash = $processed->contains($value)) {
321
                return sprintf('%s Object &%s', $class, $hash);
322
            }
323
 
324
            $hash   = $processed->add($value);
325
            $values = '';
326
            $array  = $this->toArray($value);
327
 
328
            if (count($array) > 0) {
329
                foreach ($array as $k => $v) {
330
                    $values .= sprintf(
331
                        '%s    %s => %s' . "\n",
332
                        $whitespace,
333
                        $this->recursiveExport($k, $indentation),
334
                        $this->recursiveExport($v, $indentation + 1, $processed)
335
                    );
336
                }
337
 
338
                $values = "\n" . $values . $whitespace;
339
            }
340
 
341
            return sprintf('%s Object &%s (%s)', $class, $hash, $values);
342
        }
343
 
344
        return var_export($value, true);
345
    }
346
}