Passed
Push — master ( 420731...189c3f )
by Sebastian
02:15
created

ConvertHelper_QueryParser   A

Complexity

Total Complexity 8

Size/Duplication

Total Lines 86
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 30
c 1
b 0
f 0
dl 0
loc 86
rs 10
wmc 8

2 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 2 1
B parse() 0 79 7
1
<?php
2
/**
3
 * File containing the {@see AppUtils\ConvertHelper_QueryParser} class.
4
 *
5
 * @package Application Utils
6
 * @subpackage ConvertHelper
7
 * @see AppUtils\ConvertHelper_QueryParser
8
 */
9
10
declare(strict_types=1);
11
12
namespace AppUtils;
13
14
/**
15
 * Query parser that works as a drop-in for the native
16
 * PHP function parse_str, and which overcomes this function's
17
 * limitations.
18
 *
19
 * @package Application Utils
20
 * @subpackage ConvertHelper
21
 * @see https://www.php.net/manual/en/function.parse-str.php
22
 */
23
class ConvertHelper_QueryParser
24
{
25
    public function __construct()
26
    {
27
        
28
    }
29
    
30
    public function parse(string $queryString) : array
31
    {
32
        // allow HTML entities notation
33
        $queryString = str_replace('&amp;', '&', $queryString);
34
        
35
        $paramNames = array();
36
        
37
        // extract parameter names from the query string
38
        $result = array();
39
        preg_match_all('/&?([^&]+)=.*/sixU', $queryString, $result, PREG_PATTERN_ORDER);
40
        if(isset($result[1])) {
41
            $paramNames = $result[1];
42
        }
43
        
44
        // to avoid iterating over the param names, we simply concatenate it
45
        $search = implode('', $paramNames);
46
        
47
        // store whether we need to adjust any of the names:
48
        // this is true if we find dots or spaces in any of them.
49
        $fixRequired = stristr($search, '.') || stristr($search, ' ');
50
        
51
        unset($search);
52
        
53
        $table = array();
54
        
55
        // A fix is required: replace all parameter names with placeholders,
56
        // which do not conflict with parse_str and which will be restored
57
        // with the actual parameter names after the parsing.
58
        //
59
        // It is necessary to do this even before the parsing, to resolve
60
        // possible naming conflicts like having both parameters "foo.bar"
61
        // and "foo_bar" in the query string: since "foo.bar" would be converted
62
        // to "foo_bar", one of the two would be replaced.
63
        if($fixRequired)
64
        {
65
            $counter = 1;
66
            $placeholders = array();
67
            foreach($paramNames as $paramName)
68
            {
69
                // create a unique placeholder name
70
                $placeholder = '__PLACEHOLDER'.$counter.'__';
71
                
72
                // store the placeholder name to replace later
73
                $table[$placeholder] = $paramName;
74
                
75
                // add the placeholder to replace in the query string before parsing
76
                $placeholders[$paramName.'='] = $placeholder.'=';
77
                
78
                $counter++;
79
            }
80
            
81
            // next challenge: replacing the parameter names by placeholders
82
            // safely. We sort the list by longest name first, to avoid shorter
83
            // parameter names being replaced first that can be part of longer ones.
84
            uksort($placeholders, function($a, $b) {
85
                return strlen($b) - strlen($a);
86
            });
87
                
88
            // replace all instances with the placeholder
89
            $queryString = str_replace(array_keys($placeholders), array_values($placeholders), $queryString);
90
        }
91
        
92
        // parse the query string natively
93
        $parsed = array();
94
        parse_str($queryString, $parsed);
95
        
96
        // do any of the parameter names need to be fixed?
97
        if(!$fixRequired) {
98
            return $parsed;
99
        }
100
        
101
        $keep = array();
102
        
103
        foreach($parsed as $name => $value)
104
        {
105
            $keep[$table[$name]] = $value;
106
        }
107
        
108
        return $keep;
109
    }
110
}
111