Subversion-Projekte lars-tiefland.laravel_shop

Revision

Revision 148 | Zur aktuellen Revision | Details | Vergleich mit vorheriger | 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
    {
688 lars 307
        $frames = $this->getInspector()->getFrames($this->getRun()->getFrameFilters());
148 lars 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
     *
357
     * @return static
358
     */
359
    public function addDataTable($label, array $data)
360
    {
361
        $this->extraTables[$label] = $data;
362
        return $this;
363
    }
364
 
365
    /**
366
     * Lazily adds an entry to the list of tables displayed in the table.
367
     *
368
     * The supplied callback argument will be called when the error is
369
     * rendered, it should produce a simple associative array. Any nested
370
     * arrays will be flattened with `print_r`.
371
     *
372
     * @param string   $label
373
     * @param callable $callback Callable returning an associative array
374
     *
375
     * @throws InvalidArgumentException If $callback is not callable
376
     *
377
     * @return static
378
     */
379
    public function addDataTableCallback($label, /* callable */ $callback)
380
    {
381
        if (!is_callable($callback)) {
382
            throw new InvalidArgumentException('Expecting callback argument to be callable');
383
        }
384
 
688 lars 385
        $this->extraTables[$label] = function (\Whoops\Inspector\InspectorInterface $inspector = null) use ($callback) {
148 lars 386
            try {
387
                $result = call_user_func($callback, $inspector);
388
 
389
                // Only return the result if it can be iterated over by foreach().
390
                return is_array($result) || $result instanceof \Traversable ? $result : [];
391
            } catch (\Exception $e) {
392
                // Don't allow failure to break the rendering of the original exception.
393
                return [];
394
            }
395
        };
396
 
397
        return $this;
398
    }
399
 
400
    /**
401
     * Returns all the extra data tables registered with this handler.
402
     *
403
     * Optionally accepts a 'label' parameter, to only return the data table
404
     * under that label.
405
     *
406
     * @param string|null $label
407
     *
408
     * @return array[]|callable
409
     */
410
    public function getDataTables($label = null)
411
    {
412
        if ($label !== null) {
413
            return isset($this->extraTables[$label]) ?
414
                   $this->extraTables[$label] : [];
415
        }
416
 
417
        return $this->extraTables;
418
    }
419
 
420
    /**
421
     * Set whether to handle unconditionally.
422
     *
423
     * Allows to disable all attempts to dynamically decide whether to handle
424
     * or return prematurely. Set this to ensure that the handler will perform,
425
     * no matter what.
426
     *
427
     * @param bool|null $value
428
     *
429
     * @return bool|static
430
     */
431
    public function handleUnconditionally($value = null)
432
    {
433
        if (func_num_args() == 0) {
434
            return $this->handleUnconditionally;
435
        }
436
 
437
        $this->handleUnconditionally = (bool) $value;
438
        return $this;
439
    }
440
 
441
    /**
442
     * Adds an editor resolver.
443
     *
444
     * Either a string, or a closure that resolves a string, that can be used
445
     * to open a given file in an editor. If the string contains the special
446
     * substrings %file or %line, they will be replaced with the correct data.
447
     *
448
     * @example
449
     *  $run->addEditor('macvim', "mvim://open?url=file://%file&line=%line")
450
     * @example
451
     *   $run->addEditor('remove-it', function($file, $line) {
452
     *       unlink($file);
453
     *       return "http://stackoverflow.com";
454
     *   });
455
     *
456
     * @param string          $identifier
457
     * @param string|callable $resolver
458
     *
459
     * @return static
460
     */
461
    public function addEditor($identifier, $resolver)
462
    {
463
        $this->editors[$identifier] = $resolver;
464
        return $this;
465
    }
466
 
467
    /**
468
     * Set the editor to use to open referenced files.
469
     *
470
     * Pass either the name of a configured editor, or a closure that directly
471
     * resolves an editor string.
472
     *
473
     * @example
474
     *   $run->setEditor(function($file, $line) { return "file:///{$file}"; });
475
     * @example
476
     *   $run->setEditor('sublime');
477
     *
478
     * @param string|callable $editor
479
     *
480
     * @throws InvalidArgumentException If invalid argument identifier provided
481
     *
482
     * @return static
483
     */
484
    public function setEditor($editor)
485
    {
486
        if (!is_callable($editor) && !isset($this->editors[$editor])) {
487
            throw new InvalidArgumentException(
488
                "Unknown editor identifier: $editor. Known editors:" .
489
                implode(",", array_keys($this->editors))
490
            );
491
        }
492
 
493
        $this->editor = $editor;
494
        return $this;
495
    }
496
 
497
    /**
498
     * Get the editor href for a given file and line, if available.
499
     *
500
     * @param string $filePath
501
     * @param int    $line
502
     *
503
     * @throws InvalidArgumentException If editor resolver does not return a string
504
     *
505
     * @return string|bool
506
     */
507
    public function getEditorHref($filePath, $line)
508
    {
509
        $editor = $this->getEditor($filePath, $line);
510
 
511
        if (empty($editor)) {
512
            return false;
513
        }
514
 
515
        // Check that the editor is a string, and replace the
516
        // %line and %file placeholders:
517
        if (!isset($editor['url']) || !is_string($editor['url'])) {
518
            throw new UnexpectedValueException(
519
                __METHOD__ . " should always resolve to a string or a valid editor array; got something else instead."
520
            );
521
        }
522
 
523
        $editor['url'] = str_replace("%line", rawurlencode($line), $editor['url']);
524
        $editor['url'] = str_replace("%file", rawurlencode($filePath), $editor['url']);
525
 
526
        return $editor['url'];
527
    }
528
 
529
    /**
530
     * Determine if the editor link should act as an Ajax request.
531
     *
532
     * @param string $filePath
533
     * @param int    $line
534
     *
535
     * @throws UnexpectedValueException If editor resolver does not return a boolean
536
     *
537
     * @return bool
538
     */
539
    public function getEditorAjax($filePath, $line)
540
    {
541
        $editor = $this->getEditor($filePath, $line);
542
 
543
        // Check that the ajax is a bool
544
        if (!isset($editor['ajax']) || !is_bool($editor['ajax'])) {
545
            throw new UnexpectedValueException(
546
                __METHOD__ . " should always resolve to a bool; got something else instead."
547
            );
548
        }
549
        return $editor['ajax'];
550
    }
551
 
552
    /**
553
     * Determines both the editor and if ajax should be used.
554
     *
555
     * @param string $filePath
556
     * @param int    $line
557
     *
558
     * @return array
559
     */
560
    protected function getEditor($filePath, $line)
561
    {
562
        if (!$this->editor || (!is_string($this->editor) && !is_callable($this->editor))) {
563
            return [];
564
        }
565
 
566
        if (is_string($this->editor) && isset($this->editors[$this->editor]) && !is_callable($this->editors[$this->editor])) {
567
            return [
568
                'ajax' => false,
569
                'url' => $this->editors[$this->editor],
570
            ];
571
        }
572
 
573
        if (is_callable($this->editor) || (isset($this->editors[$this->editor]) && is_callable($this->editors[$this->editor]))) {
574
            if (is_callable($this->editor)) {
575
                $callback = call_user_func($this->editor, $filePath, $line);
576
            } else {
577
                $callback = call_user_func($this->editors[$this->editor], $filePath, $line);
578
            }
579
 
580
            if (empty($callback)) {
581
                return [];
582
            }
583
 
584
            if (is_string($callback)) {
585
                return [
586
                    'ajax' => false,
587
                    'url' => $callback,
588
                ];
589
            }
590
 
591
            return [
592
                'ajax' => isset($callback['ajax']) ? $callback['ajax'] : false,
593
                'url' => isset($callback['url']) ? $callback['url'] : $callback,
594
            ];
595
        }
596
 
597
        return [];
598
    }
599
 
600
    /**
601
     * Set the page title.
602
     *
603
     * @param string $title
604
     *
605
     * @return static
606
     */
607
    public function setPageTitle($title)
608
    {
609
        $this->pageTitle = (string) $title;
610
        return $this;
611
    }
612
 
613
    /**
614
     * Get the page title.
615
     *
616
     * @return string
617
     */
618
    public function getPageTitle()
619
    {
620
        return $this->pageTitle;
621
    }
622
 
623
    /**
624
     * Adds a path to the list of paths to be searched for resources.
625
     *
626
     * @param string $path
627
     *
628
     * @throws InvalidArgumentException If $path is not a valid directory
629
     *
630
     * @return static
631
     */
632
    public function addResourcePath($path)
633
    {
634
        if (!is_dir($path)) {
635
            throw new InvalidArgumentException(
636
                "'$path' is not a valid directory"
637
            );
638
        }
639
 
640
        array_unshift($this->searchPaths, $path);
641
        return $this;
642
    }
643
 
644
    /**
645
     * Adds a custom css file to be loaded.
646
     *
647
     * @param string|null $name
648
     *
649
     * @return static
650
     */
651
    public function addCustomCss($name)
652
    {
653
        $this->customCss = $name;
654
        return $this;
655
    }
656
 
657
    /**
658
     * Adds a custom js file to be loaded.
659
     *
660
     * @param string|null $name
661
     *
662
     * @return static
663
     */
664
    public function addCustomJs($name)
665
    {
666
        $this->customJs = $name;
667
        return $this;
668
    }
669
 
670
    /**
671
     * @return array
672
     */
673
    public function getResourcePaths()
674
    {
675
        return $this->searchPaths;
676
    }
677
 
678
    /**
679
     * Finds a resource, by its relative path, in all available search paths.
680
     *
681
     * The search is performed starting at the last search path, and all the
682
     * way back to the first, enabling a cascading-type system of overrides for
683
     * all resources.
684
     *
685
     * @param string $resource
686
     *
687
     * @throws RuntimeException If resource cannot be found in any of the available paths
688
     *
689
     * @return string
690
     */
691
    protected function getResource($resource)
692
    {
693
        // If the resource was found before, we can speed things up
694
        // by caching its absolute, resolved path:
695
        if (isset($this->resourceCache[$resource])) {
696
            return $this->resourceCache[$resource];
697
        }
698
 
699
        // Search through available search paths, until we find the
700
        // resource we're after:
701
        foreach ($this->searchPaths as $path) {
702
            $fullPath = $path . "/$resource";
703
 
704
            if (is_file($fullPath)) {
705
                // Cache the result:
706
                $this->resourceCache[$resource] = $fullPath;
707
                return $fullPath;
708
            }
709
        }
710
 
711
        // If we got this far, nothing was found.
712
        throw new RuntimeException(
713
            "Could not find resource '$resource' in any resource paths."
714
            . "(searched: " . join(", ", $this->searchPaths). ")"
715
        );
716
    }
717
 
718
    /**
719
     * @deprecated
720
     *
721
     * @return string
722
     */
723
    public function getResourcesPath()
724
    {
725
        $allPaths = $this->getResourcePaths();
726
 
727
        // Compat: return only the first path added
728
        return end($allPaths) ?: null;
729
    }
730
 
731
    /**
732
     * @deprecated
733
     *
734
     * @param string $resourcesPath
735
     *
736
     * @return static
737
     */
738
    public function setResourcesPath($resourcesPath)
739
    {
740
        $this->addResourcePath($resourcesPath);
741
        return $this;
742
    }
743
 
744
    /**
745
     * Return the application paths.
746
     *
747
     * @return array
748
     */
749
    public function getApplicationPaths()
750
    {
751
        return $this->applicationPaths;
752
    }
753
 
754
    /**
755
     * Set the application paths.
688 lars 756
     *
148 lars 757
     * @return void
758
     */
688 lars 759
    public function setApplicationPaths(array $applicationPaths)
148 lars 760
    {
761
        $this->applicationPaths = $applicationPaths;
762
    }
763
 
764
    /**
765
     * Set the application root path.
766
     *
767
     * @param string $applicationRootPath
768
     *
769
     * @return void
770
     */
771
    public function setApplicationRootPath($applicationRootPath)
772
    {
773
        $this->templateHelper->setApplicationRootPath($applicationRootPath);
774
    }
775
 
776
    /**
777
     * blacklist a sensitive value within one of the superglobal arrays.
778
     * Alias for the hideSuperglobalKey method.
779
     *
780
     * @param string $superGlobalName The name of the superglobal array, e.g. '_GET'
781
     * @param string $key             The key within the superglobal
782
     * @see hideSuperglobalKey
783
     *
784
     * @return static
785
     */
786
    public function blacklist($superGlobalName, $key)
787
    {
788
        $this->blacklist[$superGlobalName][] = $key;
789
        return $this;
790
    }
791
 
792
    /**
793
     * Hide a sensitive value within one of the superglobal arrays.
794
     *
795
     * @param string $superGlobalName The name of the superglobal array, e.g. '_GET'
796
     * @param string $key             The key within the superglobal
797
     * @return static
798
     */
799
    public function hideSuperglobalKey($superGlobalName, $key)
800
    {
801
        return $this->blacklist($superGlobalName, $key);
802
    }
803
 
804
    /**
805
     * Checks all values within the given superGlobal array.
806
     *
807
     * Blacklisted values will be replaced by a equal length string containing
808
     * only '*' characters for string values.
809
     * Non-string values will be replaced with a fixed asterisk count.
810
     * We intentionally dont rely on $GLOBALS as it depends on the 'auto_globals_jit' php.ini setting.
811
     *
812
     * @param array|\ArrayAccess  $superGlobal     One of the superglobal arrays
813
     * @param string $superGlobalName The name of the superglobal array, e.g. '_GET'
814
     *
815
     * @return array $values without sensitive data
816
     */
817
    private function masked($superGlobal, $superGlobalName)
818
    {
819
        $blacklisted = $this->blacklist[$superGlobalName];
820
 
821
        $values = $superGlobal;
822
 
823
        foreach ($blacklisted as $key) {
824
            if (isset($superGlobal[$key])) {
825
                $values[$key] = str_repeat('*', is_string($superGlobal[$key]) ? strlen($superGlobal[$key]) : 3);
826
            }
827
        }
828
 
829
        return $values;
830
    }
831
}