Completed
Pull Request — master (#638)
by
unknown
01:59
created

PrettyPageHandler::__construct()   B

Complexity

Conditions 6
Paths 4

Size

Total Lines 39

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 7.3329

Importance

Changes 0
Metric Value
dl 0
loc 39
ccs 14
cts 21
cp 0.6667
rs 8.6737
c 0
b 0
f 0
cc 6
nc 4
nop 0
crap 7.3329
1
<?php
2
/**
3
 * Whoops - php errors for cool kids
4
 * @author Filipe Dobreira <http://github.com/filp>
5
 */
6
7
namespace Whoops\Handler;
8
9
use InvalidArgumentException;
10
use RuntimeException;
11
use Symfony\Component\VarDumper\Cloner\AbstractCloner;
12
use Symfony\Component\VarDumper\Cloner\VarCloner;
13
use UnexpectedValueException;
14
use Whoops\Exception\Formatter;
15
use Whoops\Util\Misc;
16
use Whoops\Util\TemplateHelper;
17
18
class PrettyPageHandler extends Handler
19
{
20
    const EDITOR_SUBLIME = "sublime";
21
    const EDITOR_TEXTMATE = "textmate";
22
    const EDITOR_EMACS = "emacs";
23
    const EDITOR_MACVIM = "macvim";
24
    const EDITOR_PHPSTORM = "phpstorm";
25
    const EDITOR_IDEA = "idea";
26
    const EDITOR_VSCODE = "vscode";
27
    const EDITOR_ATOM = "atom";
28
    const EDITOR_ESPRESSO = "espresso";
29
    const EDITOR_XDEBUG = "xdebug";
30
31
    /**
32
     * Search paths to be scanned for resources, in the reverse
33
     * order they're declared.
34
     *
35
     * @var array
36
     */
37
    private $searchPaths = [];
38
39
    /**
40
     * Fast lookup cache for known resource locations.
41
     *
42
     * @var array
43
     */
44
    private $resourceCache = [];
45
46
    /**
47
     * The name of the custom css file.
48
     *
49
     * @var string
50
     */
51
    private $customCss = null;
52
53
    /**
54
     * @var array[]
55
     */
56
    private $extraTables = [];
57
58
    /**
59
     * @var bool
60
     */
61
    private $handleUnconditionally = false;
62
63
    /**
64
     * @var string
65
     */
66
    private $pageTitle = "Whoops! There was an error.";
67
68
    /**
69
     * @var array[]
70
     */
71
    private $applicationPaths;
72
73
    /**
74
     * @var array[]
75
     */
76
    private $blacklist = [
77
        '_GET' => [],
78
        '_POST' => [],
79
        '_FILES' => [],
80
        '_COOKIE' => [],
81
        '_SESSION' => [],
82
        '_SERVER' => [],
83
        '_ENV' => [],
84
    ];
85
86
    /**
87
     * A string identifier for a known IDE/text editor, or a closure
88
     * that resolves a string that can be used to open a given file
89
     * in an editor. If the string contains the special substrings
90
     * %file or %line, they will be replaced with the correct data.
91
     *
92
     * @example
93
     *  "txmt://open?url=%file&line=%line"
94
     * @var mixed $editor
95
     */
96
    protected $editor;
97
98
    /**
99
     * A list of known editor strings
100
     * @var array
101
     */
102
    protected $editors = [
103
        "sublime"  => "subl://open?url=file://%file&line=%line",
104
        "textmate" => "txmt://open?url=file://%file&line=%line",
105
        "emacs"    => "emacs://open?url=file://%file&line=%line",
106
        "macvim"   => "mvim://open/?url=file://%file&line=%line",
107
        "phpstorm" => "phpstorm://open?file=%file&line=%line",
108
        "idea"     => "idea://open?file=%file&line=%line",
109
        "vscode"   => "vscode://file/%file:%line",
110
        "atom"     => "atom://core/open/file?filename=%file&line=%line",
111
        "espresso" => "x-espresso://open?filepath=%file&lines=%line",
112
    ];
113
114
    /**
115
     * @var TemplateHelper
116
     */
117
    private $templateHelper;
118
119
    /**
120
     * Constructor.
121
     */
122 1
    public function __construct()
123
    {
124 1
        if (ini_get('xdebug.file_link_format') || extension_loaded('xdebug')) {
125
            // Register editor using xdebug's file_link_format option.
126
            $this->editors['xdebug'] = function ($file, $line) {
127 1
                return str_replace(['%f', '%l'], [$file, $line], ini_get('xdebug.file_link_format'));
128
            };
129
130
            // If xdebug is available, use it as default editor.
131 1
            $this->editor = $this->editors['xdebug'];
132 1
        }
133
134
        // Add the default, local resource search path:
135 1
        $this->searchPaths[] = __DIR__ . "/../Resources";
136
137
        // blacklist php provided auth based values
138 1
        $this->blacklist('_SERVER', 'PHP_AUTH_PW');
139
140 1
        $this->templateHelper = new TemplateHelper();
141
142 1
        if (class_exists('Symfony\Component\VarDumper\Cloner\VarCloner')) {
143 1
            $cloner = new VarCloner();
144
            // Only dump object internals if a custom caster exists.
145
            $cloner->addCasters(['*' => function ($obj, $a, $stub, $isNested, $filter = 0) {
0 ignored issues
show
Unused Code introduced by
The parameter $isNested is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $filter is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
146
                $class = $stub->class;
147
                $classes = [$class => $class] + class_parents($class) + class_implements($class);
148
149
                foreach ($classes as $class) {
150
                    if (isset(AbstractCloner::$defaultCasters[$class])) {
151
                        return $a;
152
                    }
153
                }
154
155
                // Remove all internals
156
                return [];
157 1
            }]);
158 1
            $this->templateHelper->setCloner($cloner);
159 1
        }
160 1
    }
161
162
    /**
163
     * @return int|null
164
     */
165 1
    public function handle()
166
    {
167 1
        if (!$this->handleUnconditionally()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->handleUnconditionally() of type boolean|null is loosely compared to false; this is ambiguous if the boolean can be false. You might want to explicitly use !== null instead.

If an expression can have both false, and null as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.

$a = canBeFalseAndNull();

// Instead of
if ( ! $a) { }

// Better use one of the explicit versions:
if ($a !== null) { }
if ($a !== false) { }
if ($a !== null && $a !== false) { }
Loading history...
168
            // Check conditions for outputting HTML:
169
            // @todo: Make this more robust
170 1
            if (PHP_SAPI === 'cli') {
171
                // Help users who have been relying on an internal test value
172
                // fix their code to the proper method
173 1
                if (isset($_ENV['whoops-test'])) {
174
                    throw new \Exception(
175
                        'Use handleUnconditionally instead of whoops-test'
176
                        .' environment variable'
177
                    );
178
                }
179
180 1
                return Handler::DONE;
181
            }
182
        }
183
184
        $templateFile = $this->getResource("views/layout.html.php");
185
        $cssFile      = $this->getResource("css/whoops.base.css");
186
        $zeptoFile    = $this->getResource("js/zepto.min.js");
187
        $prettifyFile = $this->getResource("js/prettify.min.js");
188
        $clipboard    = $this->getResource("js/clipboard.min.js");
189
        $jsFile       = $this->getResource("js/whoops.base.js");
190
191
        if ($this->customCss) {
192
            $customCssFile = $this->getResource($this->customCss);
193
        }
194
195
        $inspector = $this->getInspector();
196
        $frames = $this->getExceptionFrames();
197
        $code = $this->getExceptionCode();
198
199
        // List of variables that will be passed to the layout template.
200
        $vars = [
201
            "page_title" => $this->getPageTitle(),
202
203
            // @todo: Asset compiler
204
            "stylesheet" => file_get_contents($cssFile),
205
            "zepto"      => file_get_contents($zeptoFile),
206
            "prettify"   => file_get_contents($prettifyFile),
207
            "clipboard"  => file_get_contents($clipboard),
208
            "javascript" => file_get_contents($jsFile),
209
210
            // Template paths:
211
            "header"                     => $this->getResource("views/header.html.php"),
212
            "header_outer"               => $this->getResource("views/header_outer.html.php"),
213
            "frame_list"                 => $this->getResource("views/frame_list.html.php"),
214
            "frames_description"         => $this->getResource("views/frames_description.html.php"),
215
            "frames_container"           => $this->getResource("views/frames_container.html.php"),
216
            "panel_details"              => $this->getResource("views/panel_details.html.php"),
217
            "panel_details_outer"        => $this->getResource("views/panel_details_outer.html.php"),
218
            "panel_left"                 => $this->getResource("views/panel_left.html.php"),
219
            "panel_left_outer"           => $this->getResource("views/panel_left_outer.html.php"),
220
            "frame_code"                 => $this->getResource("views/frame_code.html.php"),
221
            "env_details"                => $this->getResource("views/env_details.html.php"),
222
223
            "title"            => $this->getPageTitle(),
224
            "name"             => explode("\\", $inspector->getExceptionName()),
225
            "message"          => $inspector->getExceptionMessage(),
226
            "previousMessages" => $inspector->getPreviousExceptionMessages(),
227
            "docref_url"       => $inspector->getExceptionDocrefUrl(),
228
            "code"             => $code,
229
            "previousCodes"    => $inspector->getPreviousExceptionCodes(),
230
            "plain_exception"  => Formatter::formatExceptionPlain($inspector),
231
            "frames"           => $frames,
232
            "has_frames"       => !!count($frames),
233
            "handler"          => $this,
234
            "handlers"         => $this->getRun()->getHandlers(),
235
236
            "active_frames_tab" => count($frames) && $frames->offsetGet(0)->isApplication() ?  'application' : 'all',
0 ignored issues
show
Bug introduced by
The method isApplication cannot be called on $frames->offsetGet(0) (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
237
            "has_frames_tabs"   => $this->getApplicationPaths(),
238
239
            "tables"      => [
240
                "GET Data"              => $this->masked($_GET, '_GET'),
241
                "POST Data"             => $this->masked($_POST, '_POST'),
242
                "Files"                 => isset($_FILES) ? $this->masked($_FILES, '_FILES') : [],
243
                "Cookies"               => $this->masked($_COOKIE, '_COOKIE'),
244
                "Session"               => isset($_SESSION) ? $this->masked($_SESSION, '_SESSION') :  [],
245
                "Server/Request Data"   => $this->masked($_SERVER, '_SERVER'),
246
                "Environment Variables" => $this->masked($_ENV, '_ENV'),
247
            ],
248
        ];
249
250
        if (isset($customCssFile)) {
251
            $vars["stylesheet"] .= file_get_contents($customCssFile);
252
        }
253
254
        // Add extra entries list of data tables:
255
        // @todo: Consolidate addDataTable and addDataTableCallback
256
        $extraTables = array_map(function ($table) use ($inspector) {
257
            return $table instanceof \Closure ? $table($inspector) : $table;
258
        }, $this->getDataTables());
259
        $vars["tables"] = array_merge($extraTables, $vars["tables"]);
260
261
        $plainTextHandler = new PlainTextHandler();
262
        $plainTextHandler->setException($this->getException());
263
        $plainTextHandler->setInspector($this->getInspector());
264
        $vars["preface"] = "<!--\n\n\n" .  $this->templateHelper->escape($plainTextHandler->generateResponse()) . "\n\n\n\n\n\n\n\n\n\n\n-->";
265
266
        $this->templateHelper->setVariables($vars);
267
        $this->templateHelper->render($templateFile);
268
269
        return Handler::QUIT;
270
    }
271
272
    /**
273
     * Get the stack trace frames of the exception that is currently being handled.
274
     *
275
     * @return \Whoops\Exception\FrameCollection;
0 ignored issues
show
Documentation introduced by
The doc-type \Whoops\Exception\FrameCollection; could not be parsed: Expected "|" or "end of type", but got ";" at position 33. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
276
     */
277
    protected function getExceptionFrames()
278
    {
279
        $frames = $this->getInspector()->getFrames();
280
281
        if ($this->getApplicationPaths()) {
282
            foreach ($frames as $frame) {
283
                foreach ($this->getApplicationPaths() as $path) {
284
                    if (strpos($frame->getFile(), $path) === 0) {
285
                        $frame->setApplication(true);
286
                        break;
287
                    }
288
                }
289
            }
290
        }
291
292
        return $frames;
293
    }
294
295
    /**
296
     * Get the code of the exception that is currently being handled.
297
     *
298
     * @return string
299
     */
300
    protected function getExceptionCode()
301
    {
302
        $exception = $this->getException();
303
304
        $code = $exception->getCode();
305
        if ($exception instanceof \ErrorException) {
306
            // ErrorExceptions wrap the php-error types within the 'severity' property
307
            $code = Misc::translateErrorCode($exception->getSeverity());
308
        }
309
310
        return (string) $code;
311
    }
312
313
    /**
314
     * @return string
315
     */
316
    public function contentType()
317
    {
318
        return 'text/html';
319
    }
320
321
    /**
322
     * Adds an entry to the list of tables displayed in the template.
323
     * The expected data is a simple associative array. Any nested arrays
324
     * will be flattened with print_r
325
     * @param string $label
326
     * @param array  $data
327
     */
328 1
    public function addDataTable($label, array $data)
329
    {
330 1
        $this->extraTables[$label] = $data;
331 1
    }
332
333
    /**
334
     * Lazily adds an entry to the list of tables displayed in the table.
335
     * The supplied callback argument will be called when the error is rendered,
336
     * it should produce a simple associative array. Any nested arrays will
337
     * be flattened with print_r.
338
     *
339
     * @throws InvalidArgumentException If $callback is not callable
340
     * @param  string                   $label
341
     * @param  callable                 $callback Callable returning an associative array
342
     */
343 1
    public function addDataTableCallback($label, /* callable */ $callback)
344
    {
345 1
        if (!is_callable($callback)) {
346
            throw new InvalidArgumentException('Expecting callback argument to be callable');
347
        }
348
349 1
        $this->extraTables[$label] = function (\Whoops\Exception\Inspector $inspector = null) use ($callback) {
350
            try {
351 1
                $result = call_user_func($callback, $inspector);
352
353
                // Only return the result if it can be iterated over by foreach().
354 1
                return is_array($result) || $result instanceof \Traversable ? $result : [];
355
            } catch (\Exception $e) {
356
                // Don't allow failure to break the rendering of the original exception.
357
                return [];
358
            }
359
        };
360 1
    }
361
362
    /**
363
     * Returns all the extra data tables registered with this handler.
364
     * Optionally accepts a 'label' parameter, to only return the data
365
     * table under that label.
366
     * @param  string|null      $label
367
     * @return array[]|callable
368
     */
369 2
    public function getDataTables($label = null)
370
    {
371 2
        if ($label !== null) {
372 2
            return isset($this->extraTables[$label]) ?
373 2
                   $this->extraTables[$label] : [];
374
        }
375
376 2
        return $this->extraTables;
377
    }
378
379
    /**
380
     * Allows to disable all attempts to dynamically decide whether to
381
     * handle or return prematurely.
382
     * Set this to ensure that the handler will perform no matter what.
383
     * @param  bool|null $value
384
     * @return bool|null
385
     */
386 1
    public function handleUnconditionally($value = null)
387
    {
388 1
        if (func_num_args() == 0) {
389 1
            return $this->handleUnconditionally;
390
        }
391
392
        $this->handleUnconditionally = (bool) $value;
393
    }
394
395
    /**
396
     * Adds an editor resolver, identified by a string
397
     * name, and that may be a string path, or a callable
398
     * resolver. If the callable returns a string, it will
399
     * be set as the file reference's href attribute.
400
     *
401
     * @example
402
     *  $run->addEditor('macvim', "mvim://open?url=file://%file&line=%line")
403
     * @example
404
     *   $run->addEditor('remove-it', function($file, $line) {
405
     *       unlink($file);
406
     *       return "http://stackoverflow.com";
407
     *   });
408
     * @param string $identifier
409
     * @param string|callable $resolver
410
     */
411 1
    public function addEditor($identifier, $resolver)
412
    {
413 1
        $this->editors[$identifier] = $resolver;
414 1
    }
415
416
    /**
417
     * Set the editor to use to open referenced files, by a string
418
     * identifier, or a callable that will be executed for every
419
     * file reference, with a $file and $line argument, and should
420
     * return a string.
421
     *
422
     * @example
423
     *   $run->setEditor(function($file, $line) { return "file:///{$file}"; });
424
     * @example
425
     *   $run->setEditor('sublime');
426
     *
427
     * @throws InvalidArgumentException If invalid argument identifier provided
428
     * @param  string|callable          $editor
429
     */
430 4
    public function setEditor($editor)
431
    {
432 4
        if (!is_callable($editor) && !isset($this->editors[$editor])) {
433
            throw new InvalidArgumentException(
434
                "Unknown editor identifier: $editor. Known editors:" .
435
                implode(",", array_keys($this->editors))
436
            );
437
        }
438
439 4
        $this->editor = $editor;
440 4
    }
441
442
    /**
443
     * Given a string file path, and an integer file line,
444
     * executes the editor resolver and returns, if available,
445
     * a string that may be used as the href property for that
446
     * file reference.
447
     *
448
     * @throws InvalidArgumentException If editor resolver does not return a string
449
     * @param  string                   $filePath
450
     * @param  int                      $line
451
     * @return string|bool
452
     */
453 4
    public function getEditorHref($filePath, $line)
454
    {
455 4
        $editor = $this->getEditor($filePath, $line);
456
457 4
        if (empty($editor)) {
458 1
            return false;
459
        }
460
461
        // Check that the editor is a string, and replace the
462
        // %line and %file placeholders:
463 4
        if (!isset($editor['url']) || !is_string($editor['url'])) {
464
            throw new UnexpectedValueException(
465
                __METHOD__ . " should always resolve to a string or a valid editor array; got something else instead."
466
            );
467
        }
468
469 4
        $editor['url'] = str_replace("%line", rawurlencode($line), $editor['url']);
470 4
        $editor['url'] = str_replace("%file", rawurlencode($filePath), $editor['url']);
471
472 4
        return $editor['url'];
473
    }
474
475
    /**
476
     * Given a boolean if the editor link should
477
     * act as an Ajax request. The editor must be a
478
     * valid callable function/closure
479
     *
480
     * @throws UnexpectedValueException  If editor resolver does not return a boolean
481
     * @param  string                   $filePath
482
     * @param  int                      $line
483
     * @return bool
484
     */
485 1
    public function getEditorAjax($filePath, $line)
486
    {
487 1
        $editor = $this->getEditor($filePath, $line);
488
489
        // Check that the ajax is a bool
490 1
        if (!isset($editor['ajax']) || !is_bool($editor['ajax'])) {
491
            throw new UnexpectedValueException(
492
                __METHOD__ . " should always resolve to a bool; got something else instead."
493
            );
494
        }
495 1
        return $editor['ajax'];
496
    }
497
498
    /**
499
     * Given a boolean if the editor link should
500
     * act as an Ajax request. The editor must be a
501
     * valid callable function/closure
502
     *
503
     * @param  string $filePath
504
     * @param  int    $line
505
     * @return array
506
     */
507 1
    protected function getEditor($filePath, $line)
508
    {
509 1
        if (!$this->editor || (!is_string($this->editor) && !is_callable($this->editor))) {
510
            return [];
511
        }
512
513 1
        if (is_string($this->editor) && isset($this->editors[$this->editor]) && !is_callable($this->editors[$this->editor])) {
514
            return [
515
                'ajax' => false,
516
                'url' => $this->editors[$this->editor],
517
            ];
518
        }
519
520 1
        if (is_callable($this->editor) || (isset($this->editors[$this->editor]) && is_callable($this->editors[$this->editor]))) {
521 1
            if (is_callable($this->editor)) {
522
                $callback = call_user_func($this->editor, $filePath, $line);
523
            } else {
524 1
                $callback = call_user_func($this->editors[$this->editor], $filePath, $line);
525
            }
526
527 1
            if (empty($callback)) {
528
                return [];
529
            }
530
531 1
            if (is_string($callback)) {
532
                return [
533 1
                    'ajax' => false,
534 1
                    'url' => $callback,
535 1
                ];
536
            }
537
538
            return [
539
                'ajax' => isset($callback['ajax']) ? $callback['ajax'] : false,
540
                'url' => isset($callback['url']) ? $callback['url'] : $callback,
541
            ];
542
        }
543
544
        return [];
545
    }
546
547
    /**
548
     * @param  string $title
549
     * @return void
550
     */
551 1
    public function setPageTitle($title)
552
    {
553 1
        $this->pageTitle = (string) $title;
554 1
    }
555
556
    /**
557
     * @return string
558
     */
559 1
    public function getPageTitle()
560
    {
561 1
        return $this->pageTitle;
562
    }
563
564
    /**
565
     * Adds a path to the list of paths to be searched for
566
     * resources.
567
     *
568
     * @throws InvalidArgumentException If $path is not a valid directory
569
     *
570
     * @param  string $path
571
     * @return void
572
     */
573 2
    public function addResourcePath($path)
574
    {
575 2
        if (!is_dir($path)) {
576 1
            throw new InvalidArgumentException(
577 1
                "'$path' is not a valid directory"
578 1
            );
579
        }
580
581 1
        array_unshift($this->searchPaths, $path);
582 1
    }
583
584
    /**
585
     * Adds a custom css file to be loaded.
586
     *
587
     * @param  string $name
588
     * @return void
589
     */
590
    public function addCustomCss($name)
591
    {
592
        $this->customCss = $name;
593
    }
594
595
    /**
596
     * @return array
597
     */
598 1
    public function getResourcePaths()
599
    {
600 1
        return $this->searchPaths;
601
    }
602
603
    /**
604
     * Finds a resource, by its relative path, in all available search paths.
605
     * The search is performed starting at the last search path, and all the
606
     * way back to the first, enabling a cascading-type system of overrides
607
     * for all resources.
608
     *
609
     * @throws RuntimeException If resource cannot be found in any of the available paths
610
     *
611
     * @param  string $resource
612
     * @return string
613
     */
614
    protected function getResource($resource)
615
    {
616
        // If the resource was found before, we can speed things up
617
        // by caching its absolute, resolved path:
618
        if (isset($this->resourceCache[$resource])) {
619
            return $this->resourceCache[$resource];
620
        }
621
622
        // Search through available search paths, until we find the
623
        // resource we're after:
624
        foreach ($this->searchPaths as $path) {
625
            $fullPath = $path . "/$resource";
626
627
            if (is_file($fullPath)) {
628
                // Cache the result:
629
                $this->resourceCache[$resource] = $fullPath;
630
                return $fullPath;
631
            }
632
        }
633
634
        // If we got this far, nothing was found.
635
        throw new RuntimeException(
636
            "Could not find resource '$resource' in any resource paths."
637
            . "(searched: " . join(", ", $this->searchPaths). ")"
638
        );
639
    }
640
641
    /**
642
     * @deprecated
643
     *
644
     * @return string
645
     */
646
    public function getResourcesPath()
647
    {
648
        $allPaths = $this->getResourcePaths();
649
650
        // Compat: return only the first path added
651
        return end($allPaths) ?: null;
652
    }
653
654
    /**
655
     * @deprecated
656
     *
657
     * @param  string $resourcesPath
658
     * @return void
659
     */
660
    public function setResourcesPath($resourcesPath)
661
    {
662
        $this->addResourcePath($resourcesPath);
663
    }
664
665
    /**
666
     * Return the application paths.
667
     *
668
     * @return array
669
     */
670
    public function getApplicationPaths()
671
    {
672
        return $this->applicationPaths;
673
    }
674
675
    /**
676
     * Set the application paths.
677
     *
678
     * @param array $applicationPaths
679
     */
680
    public function setApplicationPaths($applicationPaths)
681
    {
682
        $this->applicationPaths = $applicationPaths;
683
    }
684
685
    /**
686
     * Set the application root path.
687
     *
688
     * @param string $applicationRootPath
689
     */
690
    public function setApplicationRootPath($applicationRootPath)
691
    {
692
        $this->templateHelper->setApplicationRootPath($applicationRootPath);
693
    }
694
695
    /**
696
     * blacklist a sensitive value within one of the superglobal arrays.
697
     *
698
     * @param $superGlobalName string the name of the superglobal array, e.g. '_GET'
699
     * @param $key string the key within the superglobal
700
     */
701 1
    public function blacklist($superGlobalName, $key)
702
    {
703 1
        $this->blacklist[$superGlobalName][] = $key;
704 1
    }
705
706
    /**
707
     * Checks all values within the given superGlobal array.
708
     * Blacklisted values will be replaced by a equal length string cointaining only '*' characters.
709
     *
710
     * We intentionally dont rely on $GLOBALS as it depends on 'auto_globals_jit' php.ini setting.
711
     *
712
     * @param $superGlobal array One of the superglobal arrays
713
     * @param $superGlobalName string the name of the superglobal array, e.g. '_GET'
714
     * @return array $values without sensitive data
715
     */
716
    private function masked(array $superGlobal, $superGlobalName)
717
    {
718
        $blacklisted = $this->blacklist[$superGlobalName];
719
720
        $values = $superGlobal;
721
        foreach ($blacklisted as $key) {
722
            if (isset($superGlobal[$key]) && is_string($superGlobal[$key])) {
723
                $values[$key] = str_repeat('*', strlen($superGlobal[$key]));
724
            }
725
        }
726
        return $values;
727
    }
728
}
729