Completed
Pull Request — master (#612)
by Barry vd.
01:42
created

PrettyPageHandler::matchesPattern()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

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