Completed
Pull Request — master (#44)
by Phil
03:17
created

QueryStringParserTrait::filterQueryParams()   C

Complexity

Conditions 9
Paths 9

Size

Total Lines 22
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 9.0117

Importance

Changes 0
Metric Value
dl 0
loc 22
ccs 18
cts 19
cp 0.9474
rs 6.412
c 0
b 0
f 0
cc 9
eloc 19
nc 9
nop 2
crap 9.0117
1
<?php
2
3
namespace Percy\Http;
4
5
use InvalidArgumentException;
6
7
trait QueryStringParserTrait
8
{
9
    /**
10
     * @var integer
11
     */
12
    protected $param = 0;
13
14
    /**
15
     * Parse HTTP query string and return array representation
16
     * to be attached to a database query.
17
     *
18
     * @param string $query
19
     *
20
     * @return array
21
     */
22 16
    public function parseQueryString($query)
23
    {
24 16
        if (empty($query)) {
25 2
            return [];
26
        }
27
28 15
        parse_str($query, $split);
29
30 15
        $query = [];
31
32 15
        while (list($key, $value) = each($split)) {
33 15
            $mapped = call_user_func_array([$this, 'filterQueryParams'], [$key, $value]);
34 12
            if ($mapped !== false) {
35 11
                $query[$key] = $mapped;
36 11
            }
37 12
        }
38
39 12
        return $query;
40
    }
41
42
    /**
43
     * Map the parsed query string in to correct array structure.
44
     *
45
     * @param string $key
46
     * @param mixed  $value
47
     *
48
     * @return array|boolean
49
     */
50 15
    protected function filterQueryParams($key, $value)
51
    {
52
        switch ($key) {
53 15
            case 'limit':
54 15
            case 'offset':
55 1
                return (int) $value;
56 15
            case 'sort':
57 5
                return $this->parseSort($value);
58 14
            case 'filter':
59 10
                return $this->parseFilters((array) $value);
60 6
            case 'has':
61 1
                return explode(',', $value);
62 5
            case 'include':
63
                return $this->parseInclude(explode(',', $value));
64 5
            case 'search':
65 4
                return $this->parseSearch($value);
66 4
            case 'minscore':
67 3
                return (float) $value;
68 1
            default:
69 1
                return false;
70 1
        }
71
    }
72
73
    /**
74
     * Map sorts to a usable format.
75
     *
76
     * @param string $value
77
     *
78
     * @return array
79
     */
80 5
    protected function parseSort($value)
81
    {
82 5
        $map   = [];
83 5
        $sorts = explode(',', $value);
84
85 5
        foreach ($sorts as $sort) {
86 5
            $sort      = explode('|', $sort);
87 5
            $direction = (count($sort) > 1) ? $sort[1] : 'asc';
88
89 5
            if (in_array($sort[0], ['rand', 'random'])) {
90 2
                return 'RAND()';
91
            }
92
93 4
            $map[] = [
94 4
                'field'     => $sort[0],
95
                'direction' => $direction
96 4
            ];
97 4
        }
98
99 4
        return $map;
100
    }
101
102
    /**
103
     * Map search to a usable format.
104
     *
105
     * @param string $value
106
     *
107
     * @return array
108
     */
109 4
    protected function parseSearch($value)
110
    {
111 4
        $search = explode('|', $value);
112
113 4
        if (count($search) !== 2) {
114 1
            throw new InvalidArgumentException(
115
                'Malformed query string, search format should be (search=field|term) or (search=field1,field2|term)'
116 1
            );
117
        }
118
119
        return [
120 3
            'fields' => $search[0],
121 3
            'term'   => $search[1]
122 3
        ];
123
    }
124
125
    /**
126
     * Map filters in to useable array.
127
     *
128
     * @param array $filters
129
     *
130
     * @return array
131
     */
132 10
    protected function parseFilters(array $filters)
133
    {
134 10
        $mapped = [];
135
136 10
        $this->param = 0;
137
138 10
        foreach ($filters as $filter) {
139 10
            $mapped[] = $this->parseFilter($filter);
140 8
        }
141
142 8
        return $mapped;
143
    }
144
145
    /**
146
     * Parse an individual filter.
147
     *
148
     * @param string $filter
149
     *
150
     * @return array
151
     */
152 10
    protected function parseFilter($filter)
153
    {
154 10
        $filter = explode('|', $filter);
155
156 10
        if (count($filter) !== 3) {
157 1
            throw new InvalidArgumentException(
158
                'Malformed query string, filter format should be (filter[]=field|delimiter|value)'
159 1
            );
160
        }
161
162 9
        $filter = array_combine(['field', 'delimiter', 'value'], $filter);
163
164 9
        $filter['binding']   = str_replace('.', '_', $filter['field']) . '_' . $this->param++;
165 9
        $filter['delimiter'] = strtolower($filter['delimiter']);
166 9
        $filter['delimiter'] = html_entity_decode($filter['delimiter']);
167
168 9
        if (! in_array($filter['delimiter'], [
169 9
            '=', '!=', '<>', '<=', '>=', '<', '>', 'in', 'not in', 'like', 'not like'
170 9
        ])) {
171 2
            throw new InvalidArgumentException(sprintf('(%s) is not an accepted delimiter', $filter['delimiter']));
172
        }
173
174 8
        return $filter;
175
    }
176
177
    /**
178
     * Map includes in to useable array.
179
     *
180
     * @param array $filters
0 ignored issues
show
Bug introduced by
There is no parameter named $filters. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
181
     *
182
     * @return array
183
     */
184
    protected function parseInclude(array $includes)
185
    {
186
        $mapped = [];
187
188
        foreach ($includes as $include) {
189
            $parts = explode(';', $include);
190
            $key   = array_shift($parts);
191
192
            if (empty($parts)) {
193
                $mapped[$key] = [];
194
                continue;
195
            }
196
197
            $mapped[$key]['filter'] = [];
198
            $mapped[$key]['limit']  = null;
199
200
            foreach ($parts as $part) {
201
                $split = explode('|', $part);
202
203
                switch (count($split)) {
204
                    case 2:
205
                        $mapped[$key]['limit'] = $split[1];
206
                    break;
207
                    case 3:
208
                        $mapped[$key]['filter'][] = $this->parseFilter($part);
209
                    break;
210
                    default:
211
                        throw new InvalidArgumentException('Include formatted incorrectly.');
212
                    break;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
213
                }
214
            }
215
        }
216
217
        return $mapped;
218
    }
219
}
220