Subversion-Projekte lars-tiefland.laravel_shop

Revision

Revision 688 | Zur aktuellen Revision | Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 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
    const EDITOR_NETBEANS = "netbeans";
31
 
32
    /**
33
     * Search paths to be scanned for resources.
34
     *
35
     * Stored in the reverse order they're declared.
36
     *
37
     * @var array
38
     */
39
    private $searchPaths = [];
40
 
41
    /**
42
     * Fast lookup cache for known resource locations.
43
     *
44
     * @var array
45
     */
46
    private $resourceCache = [];
47
 
48
    /**
49
     * The name of the custom css file.
50
     *
51
     * @var string|null
52
     */
53
    private $customCss = null;
54
 
55
    /**
56
     * The name of the custom js file.
57
     *
58
     * @var string|null
59
     */
60
    private $customJs = null;
61
 
62
    /**
63
     * @var array[]
64
     */
65
    private $extraTables = [];
66
 
67
    /**
68
     * @var bool
69
     */
70
    private $handleUnconditionally = false;
71
 
72
    /**
73
     * @var string
74
     */
75
    private $pageTitle = "Whoops! There was an error.";
76
 
77
    /**
78
     * @var array[]
79
     */
80
    private $applicationPaths;
81
 
82
    /**
83
     * @var array[]
84
     */
85
    private $blacklist = [
86
        '_GET' => [],
87
        '_POST' => [],
88
        '_FILES' => [],
89
        '_COOKIE' => [],
90
        '_SESSION' => [],
91
        '_SERVER' => [],
92
        '_ENV' => [],
93
    ];
94
 
95
    /**
96
     * An identifier for a known IDE/text editor.
97
     *
98
     * Either a string, or a calalble that resolves a string, that can be used
99
     * to open a given file in an editor. If the string contains the special
100
     * substrings %file or %line, they will be replaced with the correct data.
101
     *
102
     * @example
103
     *   "txmt://open?url=%file&line=%line"
104
     *
105
     * @var callable|string $editor
106
     */
107
    protected $editor;
108
 
109
    /**
110
     * A list of known editor strings.
111
     *
112
     * @var array
113
     */
114
    protected $editors = [
115
        "sublime"  => "subl://open?url=file://%file&line=%line",
116
        "textmate" => "txmt://open?url=file://%file&line=%line",
117
        "emacs"    => "emacs://open?url=file://%file&line=%line",
118
        "macvim"   => "mvim://open/?url=file://%file&line=%line",
119
        "phpstorm" => "phpstorm://open?file=%file&line=%line",
120
        "idea"     => "idea://open?file=%file&line=%line",
121
        "vscode"   => "vscode://file/%file:%line",
122
        "atom"     => "atom://core/open/file?filename=%file&line=%line",
123
        "espresso" => "x-espresso://open?filepath=%file&lines=%line",
124
        "netbeans" => "netbeans://open/?f=%file:%line",
125
    ];
126
 
127
    /**
128
     * @var TemplateHelper
129
     */
130
    protected $templateHelper;
131
 
132
    /**
133
     * Constructor.
134
     *
135
     * @return void
136
     */
137
    public function __construct()
138
    {
139
        if (ini_get('xdebug.file_link_format') || get_cfg_var('xdebug.file_link_format')) {
140
            // Register editor using xdebug's file_link_format option.
141
            $this->editors['xdebug'] = function ($file, $line) {
142
                return str_replace(['%f', '%l'], [$file, $line], ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format'));
143
            };
144
 
145
            // If xdebug is available, use it as default editor.
146
            $this->setEditor('xdebug');
147
        }
148
 
149
        // Add the default, local resource search path:
150
        $this->searchPaths[] = __DIR__ . "/../Resources";
151
 
152
        // blacklist php provided auth based values
153
        $this->blacklist('_SERVER', 'PHP_AUTH_PW');
154
 
155
        $this->templateHelper = new TemplateHelper();
156
 
157
        if (class_exists('Symfony\Component\VarDumper\Cloner\VarCloner')) {
158
            $cloner = new VarCloner();
159
            // Only dump object internals if a custom caster exists for performance reasons
160
            // https://github.com/filp/whoops/pull/404
161
            $cloner->addCasters(['*' => function ($obj, $a, $stub, $isNested, $filter = 0) {
162
                $class = $stub->class;
163
                $classes = [$class => $class] + class_parents($obj) + class_implements($obj);
164
 
165
                foreach ($classes as $class) {
166
                    if (isset(AbstractCloner::$defaultCasters[$class])) {
167
                        return $a;
168
                    }
169
                }
170
 
171
                // Remove all internals
172
                return [];
173
            }]);
174
            $this->templateHelper->setCloner($cloner);
175
        }
176
    }
177
 
178
    /**
179
     * @return int|null
180
     *
181
     * @throws \Exception
182
     */
183
    public function handle()
184
    {
185
        if (!$this->handleUnconditionally()) {
186
            // Check conditions for outputting HTML:
187
            // @todo: Make this more robust
188
            if (PHP_SAPI === 'cli') {
189
                // Help users who have been relying on an internal test value
190
                // fix their code to the proper method
191
                if (isset($_ENV['whoops-test'])) {
192
                    throw new \Exception(
193
                        'Use handleUnconditionally instead of whoops-test'
194
                        .' environment variable'
195
                    );
196
                }
197
 
198
                return Handler::DONE;
199
            }
200
        }
201
 
202
        $templateFile = $this->getResource("views/layout.html.php");
203
        $cssFile      = $this->getResource("css/whoops.base.css");
204
        $zeptoFile    = $this->getResource("js/zepto.min.js");
205
        $prismJs = $this->getResource("js/prism.js");
206
        $prismCss = $this->getResource("css/prism.css");
207
        $clipboard    = $this->getResource("js/clipboard.min.js");
208
        $jsFile       = $this->getResource("js/whoops.base.js");
209
 
210
        if ($this->customCss) {
211
            $customCssFile = $this->getResource($this->customCss);
212
        }
213
 
214
        if ($this->customJs) {
215
            $customJsFile = $this->getResource($this->customJs);
216
        }
217
 
218
        $inspector = $this->getInspector();
219
        $frames = $this->getExceptionFrames();
220
        $code = $this->getExceptionCode();
221
 
222
        // List of variables that will be passed to the layout template.
223
        $vars = [
224
            "page_title" => $this->getPageTitle(),
225
 
226
            // @todo: Asset compiler
227
            "stylesheet" => file_get_contents($cssFile),
228
            "zepto"      => file_get_contents($zeptoFile),
229
            "prismJs"   => file_get_contents($prismJs),
230
            "prismCss"   => file_get_contents($prismCss),
231
            "clipboard"  => file_get_contents($clipboard),
232
            "javascript" => file_get_contents($jsFile),
233
 
234
            // Template paths:
235
            "header"                     => $this->getResource("views/header.html.php"),
236
            "header_outer"               => $this->getResource("views/header_outer.html.php"),
237
            "frame_list"                 => $this->getResource("views/frame_list.html.php"),
238
            "frames_description"         => $this->getResource("views/frames_description.html.php"),
239
            "frames_container"           => $this->getResource("views/frames_container.html.php"),
240
            "panel_details"              => $this->getResource("views/panel_details.html.php"),
241
            "panel_details_outer"        => $this->getResource("views/panel_details_outer.html.php"),
242
            "panel_left"                 => $this->getResource("views/panel_left.html.php"),
243
            "panel_left_outer"           => $this->getResource("views/panel_left_outer.html.php"),
244
            "frame_code"                 => $this->getResource("views/frame_code.html.php"),
245
            "env_details"                => $this->getResource("views/env_details.html.php"),
246
 
247
            "title"            => $this->getPageTitle(),
248
            "name"             => explode("\\", $inspector->getExceptionName()),
249
            "message"          => $inspector->getExceptionMessage(),
250
            "previousMessages" => $inspector->getPreviousExceptionMessages(),
251
            "docref_url"       => $inspector->getExceptionDocrefUrl(),
252
            "code"             => $code,
253
            "previousCodes"    => $inspector->getPreviousExceptionCodes(),
254
            "plain_exception"  => Formatter::formatExceptionPlain($inspector),
255
            "frames"           => $frames,
256
            "has_frames"       => !!count($frames),
257
            "handler"          => $this,
258
            "handlers"         => $this->getRun()->getHandlers(),
259
 
260
            "active_frames_tab" => count($frames) && $frames->offsetGet(0)->isApplication() ?  'application' : 'all',
261
            "has_frames_tabs"   => $this->getApplicationPaths(),
262
 
263
            "tables"      => [
264
                "GET Data"              => $this->masked($_GET, '_GET'),
265
                "POST Data"             => $this->masked($_POST, '_POST'),
266
                "Files"                 => isset($_FILES) ? $this->masked($_FILES, '_FILES') : [],
267
                "Cookies"               => $this->masked($_COOKIE, '_COOKIE'),
268
                "Session"               => isset($_SESSION) ? $this->masked($_SESSION, '_SESSION') :  [],
269
                "Server/Request Data"   => $this->masked($_SERVER, '_SERVER'),
270
                "Environment Variables" => $this->masked($_ENV, '_ENV'),
271
            ],
272
        ];
273
 
274
        if (isset($customCssFile)) {
275
            $vars["stylesheet"] .= file_get_contents($customCssFile);
276
        }
277
 
278
        if (isset($customJsFile)) {
279
            $vars["javascript"] .= file_get_contents($customJsFile);
280
        }
281
 
282
        // Add extra entries list of data tables:
283
        // @todo: Consolidate addDataTable and addDataTableCallback
284
        $extraTables = array_map(function ($table) use ($inspector) {
285
            return $table instanceof \Closure ? $table($inspector) : $table;
286
        }, $this->getDataTables());
287
        $vars["tables"] = array_merge($extraTables, $vars["tables"]);
288
 
289
        $plainTextHandler = new PlainTextHandler();
290
        $plainTextHandler->setException($this->getException());
291
        $plainTextHandler->setInspector($this->getInspector());
292
        $vars["preface"] = "<!--\n\n\n" .  $this->templateHelper->escape($plainTextHandler->generateResponse()) . "\n\n\n\n\n\n\n\n\n\n\n-->";
293
 
294
        $this->templateHelper->setVariables($vars);
295
        $this->templateHelper->render($templateFile);
296
 
297
        return Handler::QUIT;
298
    }
299
 
300
    /**
301
     * Get the stack trace frames of the exception currently being handled.
302
     *
303
     * @return \Whoops\Exception\FrameCollection
304
     */
305
    protected function getExceptionFrames()
306
    {
307
        $frames = $this->getInspector()->getFrames();
308
 
309
        if ($this->getApplicationPaths()) {
310
            foreach ($frames as $frame) {
311
                foreach ($this->getApplicationPaths() as $path) {
312
                    if (strpos($frame->getFile(), $path) === 0) {
313
                        $frame->setApplication(true);
314
                        break;
315
                    }
316
                }
317
            }
318
        }
319
 
320
        return $frames;
321
    }
322
 
323
    /**
324
     * Get the code of the exception currently being handled.
325
     *
326
     * @return string
327
     */
328
    protected function getExceptionCode()
329
    {
330
        $exception = $this->getException();
331
 
332
        $code = $exception->getCode();
333
        if ($exception instanceof \ErrorException) {
334
            // ErrorExceptions wrap the php-error types within the 'severity' property
335
            $code = Misc::translateErrorCode($exception->getSeverity());
336
        }
337
 
338
        return (string) $code;
339
    }
340
 
341
    /**
342
     * @return string
343
     */
344
    public function contentType()
345
    {
346
        return 'text/html';
347
    }
348
 
349
    /**
350
     * Adds an entry to the list of tables displayed in the template.
351
     *
352
     * The expected data is a simple associative array. Any nested arrays
353
     * will be flattened with `print_r`.
354
     *
355
     * @param string $label
356
     * @param array  $data
357
     *
358
     * @return static
359
     */
360
    public function addDataTable($label, array $data)
361
    {
362
        $this->extraTables[$label] = $data;
363
        return $this;
364
    }
365
 
366
    /**
367
     * Lazily adds an entry to the list of tables displayed in the table.
368
     *
369
     * The supplied callback argument will be called when the error is
370
     * rendered, it should produce a simple associative array. Any nested
371
     * arrays will be flattened with `print_r`.
372
     *
373
     * @param string   $label
374
     * @param callable $callback Callable returning an associative array
375
     *
376
     * @throws InvalidArgumentException If $callback is not callable
377
     *
378
     * @return static
379
     */
380
    public function addDataTableCallback($label, /* callable */ $callback)
381
    {
382
        if (!is_callable($callback)) {
383
            throw new InvalidArgumentException('Expecting callback argument to be callable');
384
        }
385
 
386
        $this->extraTables[$label] = function (\Whoops\Exception\Inspector $inspector = null) use ($callback) {
387
            try {
388
                $result = call_user_func($callback, $inspector);
389
 
390
                // Only return the result if it can be iterated over by foreach().
391
                return is_array($result) || $result instanceof \Traversable ? $result : [];
392
            } catch (\Exception $e) {
393
                // Don't allow failure to break the rendering of the original exception.
394
                return [];
395
            }
396
        };
397
 
398
        return $this;
399
    }
400
 
401
    /**
402
     * Returns all the extra data tables registered with this handler.
403
     *
404
     * Optionally accepts a 'label' parameter, to only return the data table
405
     * under that label.
406
     *
407
     * @param string|null $label
408
     *
409
     * @return array[]|callable
410
     */
411
    public function getDataTables($label = null)
412
    {
413
        if ($label !== null) {
414
            return isset($this->extraTables[$label]) ?
415
                   $this->extraTables[$label] : [];
416
        }
417
 
418
        return $this->extraTables;
419
    }
420
 
421
    /**
422
     * Set whether to handle unconditionally.
423
     *
424
     * Allows to disable all attempts to dynamically decide whether to handle
425
     * or return prematurely. Set this to ensure that the handler will perform,
426
     * no matter what.
427
     *
428
     * @param bool|null $value
429
     *
430
     * @return bool|static
431
     */
432
    public function handleUnconditionally($value = null)
433
    {
434
        if (func_num_args() == 0) {
435
            return $this->handleUnconditionally;
436
        }
437
 
438
        $this->handleUnconditionally = (bool) $value;
439
        return $this;
440
    }
441
 
442
    /**
443
     * Adds an editor resolver.
444
     *
445
     * Either a string, or a closure that resolves a string, that can be used
446
     * to open a given file in an editor. If the string contains the special
447
     * substrings %file or %line, they will be replaced with the correct data.
448
     *
449
     * @example
450
     *  $run->addEditor('macvim', "mvim://open?url=file://%file&line=%line")
451
     * @example
452
     *   $run->addEditor('remove-it', function($file, $line) {
453
     *       unlink($file);
454
     *       return "http://stackoverflow.com";
455
     *   });
456
     *
457
     * @param string          $identifier
458
     * @param string|callable $resolver
459
     *
460
     * @return static
461
     */
462
    public function addEditor($identifier, $resolver)
463
    {
464
        $this->editors[$identifier] = $resolver;
465
        return $this;
466
    }
467
 
468
    /**
469
     * Set the editor to use to open referenced files.
470
     *
471
     * Pass either the name of a configured editor, or a closure that directly
472
     * resolves an editor string.
473
     *
474
     * @example
475
     *   $run->setEditor(function($file, $line) { return "file:///{$file}"; });
476
     * @example
477
     *   $run->setEditor('sublime');
478
     *
479
     * @param string|callable $editor
480
     *
481
     * @throws InvalidArgumentException If invalid argument identifier provided
482
     *
483
     * @return static
484
     */
485
    public function setEditor($editor)
486
    {
487
        if (!is_callable($editor) && !isset($this->editors[$editor])) {
488
            throw new InvalidArgumentException(
489
                "Unknown editor identifier: $editor. Known editors:" .
490
                implode(",", array_keys($this->editors))
491
            );
492
        }
493
 
494
        $this->editor = $editor;
495
        return $this;
496
    }
497
 
498
    /**
499
     * Get the editor href for a given file and line, if available.
500
     *
501
     * @param string $filePath
502
     * @param int    $line
503
     *
504
     * @throws InvalidArgumentException If editor resolver does not return a string
505
     *
506
     * @return string|bool
507
     */
508
    public function getEditorHref($filePath, $line)
509
    {
510
        $editor = $this->getEditor($filePath, $line);
511
 
512
        if (empty($editor)) {
513
            return false;
514
        }
515
 
516
        // Check that the editor is a string, and replace the
517
        // %line and %file placeholders:
518
        if (!isset($editor['url']) || !is_string($editor['url'])) {
519
            throw new UnexpectedValueException(
520
                __METHOD__ . " should always resolve to a string or a valid editor array; got something else instead."
521
            );
522
        }
523
 
524
        $editor['url'] = str_replace("%line", rawurlencode($line), $editor['url']);
525
        $editor['url'] = str_replace("%file", rawurlencode($filePath), $editor['url']);
526
 
527
        return $editor['url'];
528
    }
529
 
530
    /**
531
     * Determine if the editor link should act as an Ajax request.
532
     *
533
     * @param string $filePath
534
     * @param int    $line
535
     *
536
     * @throws UnexpectedValueException If editor resolver does not return a boolean
537
     *
538
     * @return bool
539
     */
540
    public function getEditorAjax($filePath, $line)
541
    {
542
        $editor = $this->getEditor($filePath, $line);
543
 
544
        // Check that the ajax is a bool
545
        if (!isset($editor['ajax']) || !is_bool($editor['ajax'])) {
546
            throw new UnexpectedValueException(
547
                __METHOD__ . " should always resolve to a bool; got something else instead."
548
            );
549
        }
550
        return $editor['ajax'];
551
    }
552
 
553
    /**
554
     * Determines both the editor and if ajax should be used.
555
     *
556
     * @param string $filePath
557
     * @param int    $line
558
     *
559
     * @return array
560
     */
561
    protected function getEditor($filePath, $line)
562
    {
563
        if (!$this->editor || (!is_string($this->editor) && !is_callable($this->editor))) {
564
            return [];
565
        }
566
 
567
        if (is_string($this->editor) && isset($this->editors[$this->editor]) && !is_callable($this->editors[$this->editor])) {
568
            return [
569
                'ajax' => false,
570
                'url' => $this->editors[$this->editor],
571
            ];
572
        }
573
 
574
        if (is_callable($this->editor) || (isset($this->editors[$this->editor]) && is_callable($this->editors[$this->editor]))) {
575
            if (is_callable($this->editor)) {
576
                $callback = call_user_func($this->editor, $filePath, $line);
577
            } else {
578
                $callback = call_user_func($this->editors[$this->editor], $filePath, $line);
579
            }
580
 
581
            if (empty($callback)) {
582
                return [];
583
            }
584
 
585
            if (is_string($callback)) {
586
                return [
587
                    'ajax' => false,
588
                    'url' => $callback,
589
                ];
590
            }
591
 
592
            return [
593
                'ajax' => isset($callback['ajax']) ? $callback['ajax'] : false,
594
                'url' => isset($callback['url']) ? $callback['url'] : $callback,
595
            ];
596
        }
597
 
598
        return [];
599
    }
600
 
601
    /**
602
     * Set the page title.
603
     *
604
     * @param string $title
605
     *
606
     * @return static
607
     */
608
    public function setPageTitle($title)
609
    {
610
        $this->pageTitle = (string) $title;
611
        return $this;
612
    }
613
 
614
    /**
615
     * Get the page title.
616
     *
617
     * @return string
618
     */
619
    public function getPageTitle()
620
    {
621
        return $this->pageTitle;
622
    }
623
 
624
    /**
625
     * Adds a path to the list of paths to be searched for resources.
626
     *
627
     * @param string $path
628
     *
629
     * @throws InvalidArgumentException If $path is not a valid directory
630
     *
631
     * @return static
632
     */
633
    public function addResourcePath($path)
634
    {
635
        if (!is_dir($path)) {
636
            throw new InvalidArgumentException(
637
                "'$path' is not a valid directory"
638
            );
639
        }
640
 
641
        array_unshift($this->searchPaths, $path);
642
        return $this;
643
    }
644
 
645
    /**
646
     * Adds a custom css file to be loaded.
647
     *
648
     * @param string|null $name
649
     *
650
     * @return static
651
     */
652
    public function addCustomCss($name)
653
    {
654
        $this->customCss = $name;
655
        return $this;
656
    }
657
 
658
    /**
659
     * Adds a custom js file to be loaded.
660
     *
661
     * @param string|null $name
662
     *
663
     * @return static
664
     */
665
    public function addCustomJs($name)
666
    {
667
        $this->customJs = $name;
668
        return $this;
669
    }
670
 
671
    /**
672
     * @return array
673
     */
674
    public function getResourcePaths()
675
    {
676
        return $this->searchPaths;
677
    }
678
 
679
    /**
680
     * Finds a resource, by its relative path, in all available search paths.
681
     *
682
     * The search is performed starting at the last search path, and all the
683
     * way back to the first, enabling a cascading-type system of overrides for
684
     * all resources.
685
     *
686
     * @param string $resource
687
     *
688
     * @throws RuntimeException If resource cannot be found in any of the available paths
689
     *
690
     * @return string
691
     */
692
    protected function getResource($resource)
693
    {
694
        // If the resource was found before, we can speed things up
695
        // by caching its absolute, resolved path:
696
        if (isset($this->resourceCache[$resource])) {
697
            return $this->resourceCache[$resource];
698
        }
699
 
700
        // Search through available search paths, until we find the
701
        // resource we're after:
702
        foreach ($this->searchPaths as $path) {
703
            $fullPath = $path . "/$resource";
704
 
705
            if (is_file($fullPath)) {
706
                // Cache the result:
707
                $this->resourceCache[$resource] = $fullPath;
708
                return $fullPath;
709
            }
710
        }
711
 
712
        // If we got this far, nothing was found.
713
        throw new RuntimeException(
714
            "Could not find resource '$resource' in any resource paths."
715
            . "(searched: " . join(", ", $this->searchPaths). ")"
716
        );
717
    }
718
 
719
    /**
720
     * @deprecated
721
     *
722
     * @return string
723
     */
724
    public function getResourcesPath()
725
    {
726
        $allPaths = $this->getResourcePaths();
727
 
728
        // Compat: return only the first path added
729
        return end($allPaths) ?: null;
730
    }
731
 
732
    /**
733
     * @deprecated
734
     *
735
     * @param string $resourcesPath
736
     *
737
     * @return static
738
     */
739
    public function setResourcesPath($resourcesPath)
740
    {
741
        $this->addResourcePath($resourcesPath);
742
        return $this;
743
    }
744
 
745
    /**
746
     * Return the application paths.
747
     *
748
     * @return array
749
     */
750
    public function getApplicationPaths()
751
    {
752
        return $this->applicationPaths;
753
    }
754
 
755
    /**
756
     * Set the application paths.
757
     *
758
     * @param array $applicationPaths
759
     *
760
     * @return void
761
     */
762
    public function setApplicationPaths($applicationPaths)
763
    {
764
        $this->applicationPaths = $applicationPaths;
765
    }
766
 
767
    /**
768
     * Set the application root path.
769
     *
770
     * @param string $applicationRootPath
771
     *
772
     * @return void
773
     */
774
    public function setApplicationRootPath($applicationRootPath)
775
    {
776
        $this->templateHelper->setApplicationRootPath($applicationRootPath);
777
    }
778
 
779
    /**
780
     * blacklist a sensitive value within one of the superglobal arrays.
781
     * Alias for the hideSuperglobalKey method.
782
     *
783
     * @param string $superGlobalName The name of the superglobal array, e.g. '_GET'
784
     * @param string $key             The key within the superglobal
785
     * @see hideSuperglobalKey
786
     *
787
     * @return static
788
     */
789
    public function blacklist($superGlobalName, $key)
790
    {
791
        $this->blacklist[$superGlobalName][] = $key;
792
        return $this;
793
    }
794
 
795
    /**
796
     * Hide a sensitive value within one of the superglobal arrays.
797
     *
798
     * @param string $superGlobalName The name of the superglobal array, e.g. '_GET'
799
     * @param string $key             The key within the superglobal
800
     * @return static
801
     */
802
    public function hideSuperglobalKey($superGlobalName, $key)
803
    {
804
        return $this->blacklist($superGlobalName, $key);
805
    }
806
 
807
    /**
808
     * Checks all values within the given superGlobal array.
809
     *
810
     * Blacklisted values will be replaced by a equal length string containing
811
     * only '*' characters for string values.
812
     * Non-string values will be replaced with a fixed asterisk count.
813
     * We intentionally dont rely on $GLOBALS as it depends on the 'auto_globals_jit' php.ini setting.
814
     *
815
     * @param array|\ArrayAccess  $superGlobal     One of the superglobal arrays
816
     * @param string $superGlobalName The name of the superglobal array, e.g. '_GET'
817
     *
818
     * @return array $values without sensitive data
819
     */
820
    private function masked($superGlobal, $superGlobalName)
821
    {
822
        $blacklisted = $this->blacklist[$superGlobalName];
823
 
824
        $values = $superGlobal;
825
 
826
        foreach ($blacklisted as $key) {
827
            if (isset($superGlobal[$key])) {
828
                $values[$key] = str_repeat('*', is_string($superGlobal[$key]) ? strlen($superGlobal[$key]) : 3);
829
            }
830
        }
831
 
832
        return $values;
833
    }
834
}