ExceptionHandler::getContent()   B
last analyzed

Complexity

Conditions 10
Paths 62

Size

Total Lines 80
Code Lines 54

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 10
eloc 54
c 1
b 0
f 0
nc 62
nop 1
dl 0
loc 80
rs 7.1369

How to fix   Long Method    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
/**
4
 * Lenevor Framework
5
 *
6
 * LICENSE
7
 *
8
 * This source file is subject to the new BSD license that is bundled
9
 * with this package in the file license.md.
10
 * It is also available through the world-wide-web at this URL:
11
 * https://lenevor.com/license
12
 * If you did not receive a copy of the license and are unable to
13
 * obtain it through the world-wide-web, please send an email
14
 * to [email protected] so we can send you a copy immediately.
15
 *
16
 * @package     Lenevor
17
 * @subpackage  Base
18
 * @link        https://lenevor.com
19
 * @copyright   Copyright (c) 2019 - 2021 Alexander Campo <[email protected]>
20
 * @license     https://opensource.org/licenses/BSD-3-Clause New BSD license or see https://lenevor.com/license or see /license.md
21
 */
22
23
namespace Syscodes\Debug;
24
25
use Exception;
26
use Throwable;
27
use Syscodes\Debug\FatalExceptions\FlattenException;
28
use Syscodes\Debug\FatalExceptions\OutOfMemoryException;
29
30
/**
31
 * A generic ErrorHandler for the PHP engine.
32
 * 
33
 * @author Alexander Campo <[email protected]>
34
 */
35
class ExceptionHandler
36
{
37
    /**
38
     * Gets caught buffer of memory.
39
     * 
40
     * @var mixed $caughtBuffer
41
     */
42
    protected $caughtBuffer;
43
44
    /**
45
     * Gets caught lenght of buffer.
46
     * 
47
     * @var int $caughtLength
48
     */
49
    protected $caughtLength;
50
51
    /**
52
     * Gets the charset. By default UTF-8.
53
     * 
54
     * @var string $charset
55
     */
56
    protected $charset;
57
58
    /**
59
     * Gets activation of debugging.
60
     * 
61
     * @var bool $debug
62
     */
63
    protected $debug;
64
65
    /**
66
     * Gets the file link format.
67
     * 
68
     * @var string $fileLinkFormat
69
     */
70
    protected $fileLinkFormat;
71
72
    /**
73
     * Gets an error handler.
74
     * 
75
     * @var string $handler
76
     */
77
    protected $handler;
78
79
    /**
80
     * Register the exception handler.
81
     * 
82
     * @param  bool  $debug
83
     * @param  string|null  $charset
84
     * @param  string|null  $fileLinkformat
85
     * 
86
     * @return static
87
     */
88
    public static function register($debug = true, $charset = null, $fileLinkFormat = null)
89
    {
90
        $handler = new static($debug, $charset, $fileLinkFormat);
91
92
        set_exception_handler([$handler, 'handle']);
93
94
        return $handler;
95
    }
96
97
    /**
98
     * Constructor. Initialize the ExceptionHandler instance.
99
     * 
100
     * @param  bool  $debug
101
     * @param  string|null  $charset
102
     * @param  string|null  $fileLinkformat
103
     * 
104
     * @return void
105
     */
106
    public function __construct(bool $debug = true, string $charset = null, $fileLinkFormat = null)
107
    {
108
        $this->debug          = $debug;
109
        $this->charset        = $charset ?: ini_get('default_charset') ?: 'UTF-8'; 
110
        $this->fileLinkFormat = $fileLinkFormat;
111
    }
112
113
    /**
114
     * Sets a user exception handler.
115
     * 
116
     * @param  \Callable  $handler
0 ignored issues
show
Bug introduced by
The type Callable was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
117
     * 
118
     * @return \Callable|null
119
     */
120
    public function setHandler(Callable $handler)
121
    {
122
        $oldHandler    = $this->handler;
123
        $this->handler = $handler;
0 ignored issues
show
Documentation Bug introduced by
It seems like $handler of type callable is incompatible with the declared type string of property $handler.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
124
125
        return $oldHandler;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $oldHandler returns the type string which is incompatible with the documented return type Callable|null.
Loading history...
126
    }
127
128
    /**
129
     * Sets the format for links to source files.
130
     * 
131
     * @param  string|FileLinkFormatter  $fileLinkFormat
0 ignored issues
show
Bug introduced by
The type Syscodes\Debug\FileLinkFormatter was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
132
     * 
133
     * @return string
134
     */
135
    public function setFileLinkFormat($fileLinkFormat)
136
    {
137
        $oldHandler    = $this->handler;
138
        $this->handler = $handler;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $handler seems to be never defined.
Loading history...
139
140
        return $oldHandler;
141
    }
142
143
    /**
144
     * Sends a response for the given Exception.
145
     * 
146
     * How does it work:
147
     * First, the exception is handled by system exception handler, then by the user exception handler.
148
     * The latter has priority and any exit from the first one is canceled.
149
     * 
150
     * @param  \Exception  $exception
151
     * 
152
     * @return void
153
     */
154
    public function handler(Exception $exception)
155
    {
156
        if ($this->handler === null && $exception instanceof OutOfMemoryException) {
157
            $this->sendPhpResponse($exception);
158
        }
159
160
        $caughtLength = $this->caughtLength = 0;
161
162
        ob_start(function ($buffer) {
163
            $this->caughtBuffer = $buffer;
164
165
            return '';            
166
        });
167
168
        $this->sendPhpResponse($exception);
169
170
        if (isset($this->caughtBuffer[0])) {
171
            ob_start(function ($buffer) {
172
                if ($this->caughtLength) {
173
                    $cleanBuffer = substr_replace($buffer, '', 0, $this->caughtLength);
174
175
                    if (isset($cleanBuffer[0])) {
176
                        $buffer = $cleanBuffer;
177
                    }
178
                }
179
180
                return $buffer;
181
182
            });
183
184
            echo $this->caughtBuffer;
185
            // Return the length of the output buffer.
186
            $caughtLength = ob_get_length();
187
        }
188
189
        $this->caughtBuffer = null;
190
191
        try {
192
            ($this->handler)($exception);
193
            $caughtLength = $this->caughtLength;
0 ignored issues
show
Unused Code introduced by
The assignment to $caughtLength is dead and can be removed.
Loading history...
194
        } catch (Exception $e) {
195
            if ( ! $caughtLength) {
196
                throw $e;
197
            }
198
        }
199
    }
200
201
    /**
202
     * Sends the error associated with the given Exception as a plain PHP response. 
203
     * 
204
     * This method uses plain php functions as header and echo to generate the output 
205
     * response.
206
     * 
207
     * @param \Exception|\Syscodes\Debug\FlattenExceptions\FlattenException  $exception An \Exception or \FlattenException instance
0 ignored issues
show
Bug introduced by
The type Syscodes\Debug\FlattenExceptions\FlattenException was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
208
     * 
209
     * @return string  The HTML content as a string 
210
     */
211
    public function sendPhpResponse($exception)
212
    {
213
        if ( ! $exception instanceof FlattenException) {
214
            $exception = FlattenException::make($exception);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $exception is correct as Syscodes\Debug\FatalExce...ption::make($exception) targeting Syscodes\Debug\FatalExce...lattenException::make() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
215
        }
216
217
        if ( ! headers_sent()) {
218
            header(sprintf('HTTP/1.0 %s', $exception->getStatusCode()));
0 ignored issues
show
Bug introduced by
Are you sure the usage of $exception->getStatusCode() targeting Syscodes\Debug\FatalExce...eption::getStatusCode() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
Bug introduced by
$exception->getStatusCode() of type void is incompatible with the type double|integer|string expected by parameter $values of sprintf(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

218
            header(sprintf('HTTP/1.0 %s', /** @scrutinizer ignore-type */ $exception->getStatusCode()));
Loading history...
219
220
            foreach ($exception->getHeaders() as $name => $value) {
0 ignored issues
show
Bug introduced by
Are you sure the usage of $exception->getHeaders() targeting Syscodes\Debug\FatalExce...Exception::getHeaders() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
Bug introduced by
The expression $exception->getHeaders() of type void is not traversable.
Loading history...
221
                header($name.':'.$value, false);
222
            }
223
224
            header('Content-Type: text/html; charset='.$this->charset);
225
        }
226
227
        echo $this->design($this->getContent($exception), $this->getStylesheet());
228
    }
229
230
    /**
231
     * Gets the full HTML content associated with the given exception.
232
     * 
233
     * @param  \Exception|\Syscodes\Debug\FlattenExceptions\FlattenException  $exception  An \Exception or \FlattenException instance
234
     * 
235
     * @return string  The HTML content as a string 
236
     */
237
    public function getHtmlResponse($exception)
238
    {
239
        if ( ! $exception instanceof FlattenException) {
240
            $exception = FlattenException::make($exception);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $exception is correct as Syscodes\Debug\FatalExce...ption::make($exception) targeting Syscodes\Debug\FatalExce...lattenException::make() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
241
        }
242
243
        echo $this->design($this->getContent($exception), $this->getStylesheet());
244
    }
245
246
    /**
247
     * Layout HTML for gets the content and style css.
248
     * 
249
     * @param  string  $content
250
     * @param  string  $styleCss
251
     * 
252
     * @return string
253
     */
254
    private function design($content, $styleCss)
255
    {
256
        return <<<EOF
257
<!DOCTYPE html>
258
<html>
259
    <head>
260
        <meta charset="{$this->charset}" />
261
        <meta name="robots" content="noindex">    
262
        <meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1">
263
        <style>
264
            $styleCss
265
        </style>
266
    </head>
267
    <body>
268
        $content
269
    </body>
270
</html>
271
EOF;
272
    }
273
274
    /**
275
     * Gets the HTML content associated with the given exception.
276
     * 
277
     * @param  \Syscodes\Debug\FlattenExceptions\FlattenException  $exception
278
     * 
279
     * @return string  The HTML content as a string
280
     */
281
    public function getContent(FlattenException $exception)
282
    {
283
        switch ($exception->getStatusCode()) {
0 ignored issues
show
Bug introduced by
Are you sure the usage of $exception->getStatusCode() targeting Syscodes\Debug\FatalExce...eption::getStatusCode() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
284
            case 404:
285
                $title = 'Sorry, the page you are looking to could not be found';
286
                break;
287
            default:
288
                $title = 'Whoops, looks like something went wrong';
289
        }
290
291
        if ( ! $this->debug) {
292
            return <<<EOF
293
                <div class="container">
294
                    <h1>$title</h1>
295
                </div>
296
EOF;
297
        }
298
299
        $content = '';
300
301
        try {
302
            $count = count($exception->getAllPrevious());
303
            $total = $count + 1;
304
305
            foreach ($exception->toArray() as $position => $e) {
306
                $index   = $count - $position + 1;
307
                $class   = $this->formatClass($e['class']);
308
                $message = nl2br($this->escapeHtml($e['message']));
309
                $content .= sprintf(<<<'EOF'
310
                    <div class="trace">
311
                        <table>
312
                            <tr class="trace-head"><th> 
313
                                    <h3 class="trace-class">
314
                                        <span class="text-muted">(%d/%d)</span>
315
                                        <span class="exception_title">%s</span>
316
                                    </h3>
317
                                    <p class="break-long-words trace-message">%s</p>
318
                            </th></tr>
319
EOF
320
                    , $index, $total, $class, $message);
321
322
                foreach ($e['trace'] as $trace) {
323
                    $content .= '<tr><td>';
324
325
                    if ($trace['function']) {
326
                        $content .= sprintf('from <span class="trace-class">%s</span><span class="trace-type">%s</span><span class="trace-method">%s</span>(<span class="trace-arguments">%s</span>)', $this->formatClass($trace['class']), $trace['type'], $trace['function'], $this->formatArgs($trace['args']));
327
                    }
328
329
                    if (isset($trace['file']) && isset($trace['line'])) {
330
                        $content .= $this->formatPath($trace['file'], $trace['line']);
331
                    }
332
                    
333
                    $content .= "</td></tr>\n";
334
                }
335
336
                $content .= "</table>\n</div>\n";
337
            }
338
        } catch (Exception $e) {
339
            if ($this->debug) {
340
                $e     = FlattenException::make($e);
0 ignored issues
show
Bug introduced by
Are you sure the assignment to $e is correct as Syscodes\Debug\FatalExce...ttenException::make($e) targeting Syscodes\Debug\FatalExce...lattenException::make() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
341
                $title = sprintf('Exception thrown when handling an exception: (%s: %s)',
342
                    $e->getClass(),
343
                    $this->escapeHtml($e->getMessage())
344
                );
345
            } else {
346
                $title = 'Whoops, looks like something went wrong';
347
            }
348
        }
349
350
        return <<<EOF
351
            <div class="exception">
352
                <div class="container">
353
                    <div class="exception-wrapper">
354
                        <h1 class="break-long-words exception-message">$title</h1>
355
                    </div>
356
                </div>
357
            </div>
358
359
            <div class="container">
360
                $content
361
            </div>
362
EOF;
363
    }
364
365
    public function getStylesheet()
366
    {
367
        if ( ! $this->debug) {
368
            return <<<'EOF'
369
                body { background-color: #fff; color: #222; font: 16px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; overflow: hidden; }
370
                .container { display: flex; align-items: center; justify-content: center; height: 100vh; width: 100vw; }
371
                h1 { color: #586d7e; font-size: 2em; text-shadow: none; word-break: break-all; word-break: break-word; }
372
EOF;
373
        }
374
375
        return <<<'EOF'
376
            body { background-color: #F9F9F9; color: #222; font: 14px/1.4 Helvetica, Arial, sans-serif; margin: 0; padding-bottom: 45px; }
377
378
            a { cursor: pointer; text-decoration: none; }
379
            a:hover { text-decoration: underline; }
380
            abbr[title] { border-bottom: none; cursor: help; text-decoration: none; }
381
382
            code, pre { font: 13px/1.5 Consolas, Monaco, Menlo, "Ubuntu Mono", "Liberation Mono", monospace; }
383
384
            table, tr, th, td { background: #FFF; border-collapse: collapse; vertical-align: top; }
385
            table { background: #FFF; border: 1px solid #E0E0E0; box-shadow: 0px 0px 1px rgba(128, 128, 128, .2); margin: 1em 0; width: 100%; }
386
            table th, table td { border: solid #E0E0E0; border-width: 1px 0; padding: 8px 10px; }
387
            table th { background-color: #E0E0E0; font-weight: bold; text-align: left; }
388
389
            .hidden-xs-down { display: none; }
390
            .block { display: block; }
391
            .break-long-words { -ms-word-break: break-all; word-break: break-all; word-break: break-word; -webkit-hyphens: auto; -moz-hyphens: auto; hyphens: auto; }
392
            .text-muted { color: #999; }
393
394
            .container { max-width: 1024px; margin: 0 auto; padding: 0 15px; }
395
            .container::after { content: ""; display: table; clear: both; }
396
397
            .exception { background: #B0413E; border-bottom: 2px solid rgba(0, 0, 0, 0.1); border-top: 1px solid rgba(0, 0, 0, .3); flex: 0 0 auto; margin-bottom: 30px; }
398
399
            .exception-wrapper { display: flex; align-items: center; min-height: 70px; }
400
            .exception-message { flex-grow: 1; padding: 30px 0; text-shadow: none; }
401
            .exception-message, .exception-message a { color: #FFF; font-size: 21px; font-weight: 400; margin: 0; }
402
            .exception-message.long { font-size: 18px; }
403
            .exception-message a { border-bottom: 1px solid rgba(255, 255, 255, 0.5); font-size: inherit; text-decoration: none; }
404
            .exception-message a:hover { border-bottom-color: #ffffff; }
405
406
            .exception-illustration { flex-basis: 111px; flex-shrink: 0; height: 66px; margin-left: 15px; opacity: .7; }
407
408
            .trace + .trace { margin-top: 30px; }
409
            .trace-head .trace-class { color: #222; font-size: 18px; font-weight: bold; line-height: 1.3; margin: 0; position: relative; }
410
411
            .trace-message { font-size: 14px; font-weight: normal; margin: .5em 0 0; }
412
413
            .trace-file, .trace-file a { color: #222; margin-top: 3px; font-size: 13px; }
414
            .trace-class { color: #B0413E; }
415
            .trace-type { padding: 0 2px; }
416
            .trace-method { color: #B0413E; font-weight: bold; }
417
            .trace-args { color: #777; font-weight: normal; padding-left: 2px; }
418
419
            @media (min-width: 575px) {
420
                .hidden-xs-down { display: initial; }
421
            }
422
EOF;
423
    }
424
425
    /**
426
     * Gets the format class where the exception.
427
     * 
428
     * @param  string  $class
429
     * 
430
     * @return string
431
     */
432
    private function formatClass($class)
433
    {
434
        $parts = explode('\\', $class);
435
436
        return sprintf('<abbr title="%s">%s</abbr>', $class, array_pop($parts));
437
    }
438
439
    /**
440
     * Gets the path file with you line code.
441
     * 
442
     * @param  string  $path
443
     * @param  int  $line
444
     * 
445
     * @return string
446
     */
447
    private function formatPath($path, $line)
448
    {
449
        $file = $this->escapeHtml(preg_match('#[^/\\\\]*+$#', $path, $file) ? $file[0] : $path);
450
        $frmt = $this->fileLinkFormat ?: ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format');
451
452
        if ( ! $frmt) {
453
            return sprintf('<span class="block trace-file">in <span title="%s%3$s"><strong>%s</strong>%s</span></span>', 
454
                $this->escapeHtml($path),
455
                $file,
456
                0 < $line ? ' line '.$line : ''
457
            );
458
        }
459
460
        if (is_string($frmt)) {
461
            $index  = strpos($f = $frmt, '&', max(strrpos($f, '%f'), strrpos($f, '%l')) ?: strlen($f));
462
            $frmt = [substr($f, 0, $index)] + preg_split('/&([^>]++)>/', substr($f, $index), -1, PREG_SPLIT_DELIM_CAPTURE);
463
464
            for ($index = 1; isset($frmt[$index]); ++$index) {
465
                if (strpos($path, $k = $frmt[$index++])) {
466
                    $path = substr_replace($path, $frmt[$index], 0, strlen($k));
0 ignored issues
show
Unused Code introduced by
The assignment to $path is dead and can be removed.
Loading history...
467
                    break;
468
                }
469
            }
470
471
            $data = strstr($frmt[0], ['%f' => $file, '%l' => $line]);
0 ignored issues
show
Bug introduced by
array('%f' => $file, '%l' => $line) of type array<string,integer|string> is incompatible with the type string expected by parameter $needle of strstr(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

471
            $data = strstr($frmt[0], /** @scrutinizer ignore-type */ ['%f' => $file, '%l' => $line]);
Loading history...
472
        } else {
473
            try {
474
                $data = $frmt->format($file, $line);
475
            } catch (Exception $e) {
476
                return sprintf('<span class="block trace-file-path">in <span title="%s%3$s"><strong>%s</strong>%s</span></span>', 
477
                        $this->escapeHtml($path), $file, 0 < $line ? ' line '.$line : '');
478
            }
479
        }
480
481
        return sprintf('<span class="block trace-file">in <a href="%s" title="Go to source"><b>%s</b>%s</a></span>', 
482
                $this->escapeHtml($data), $file, $line > 0 ? ' line '.$line : '');
483
    }
484
485
    /**
486
     * Formats an array as a string.
487
     * 
488
     * @param  array  $args
489
     * 
490
     * @return string
491
     */
492
    private function formatArgs(array $args)
493
    {
494
        $result = [];
495
496
        foreach ($args as $key => $value) {
497
            if ($value[0] === 'object') {
498
                $formatValue = sprintf('<em>(Object)</em>%s', $this->formatClass($value[1]));
499
            } elseif ($value[0] === 'array') {
500
                $formatValue = sprintf('<em>(array)</em>%s', is_array($value[1]) ? $this->formatArgs($value[1]) : $value[1]);
501
            } elseif ($value[0] === 'null') {
502
                $formatValue = '<em>Null</em>';
503
            } elseif ($value[0] === 'boolean') {
504
                $formatValue = '<em>'.strtolower(var_export($value[1], true)).'</em>';
505
            } elseif ($value[0] === 'resource') {
506
                $formatValue = '<em>resource</em>';
507
            } else {
508
                $formatValue = str_replace("\n", '', $this->escapeHtml(var_export($value[1], true)));
509
            }
510
511
            $result[] = is_int($key) ? $formatValue : sprintf("'%s' => %s", $this->escapeHtml($key), $formatValue);
512
        }
513
514
        return implode(', ', $result);
515
    }
516
517
    /**
518
     * Gets HTML-encode as a string.
519
     * 
520
     * @param  string  $string
521
     * 
522
     * @return string
523
     */
524
    private function escapeHtml($string)
525
    {
526
        return htmlspecialchars($string, ENT_COMPAT | ENT_SUBSTITUTE, $this->charset);
527
    }
528
}