Completed
Push — master ( 74371a...6da8c0 )
by Vladimir
02:21
created

WhereFilter::comparisonSymbol()   D

Complexity

Conditions 10
Paths 10

Size

Total Lines 35
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 20
CRAP Score 10

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 35
ccs 20
cts 20
cp 1
rs 4.8196
cc 10
eloc 22
nc 10
nop 3
crap 10

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/**
4
 * @copyright 2018 Vladimir Jimenez
5
 * @license   https://github.com/allejo/stakx/blob/master/LICENSE.md MIT
6
 */
7
8
namespace allejo\stakx\Templating\Twig\Extension;
9
10
use Twig_Error_Syntax;
11
12
/**
13
 * Where Twig Filter.
14
 *
15
 * This Twig filter introduces the `where` filter
16
 *
17
 * Usage:
18
 *   <array> | where(<key>, <comparison>, <value>)
19
 *
20
 * Available comparisons:
21
 *   - ==  Ensure the values are equal and are the same data type
22
 *   - !=  Ensure the values are not equal; returns false if the values are the same but different data types
23
 *   - >   Greater than
24
 *   - >=  Greater than or equal to
25
 *   - <   Less than
26
 *   - <=  Less than or equal to
27
 *   - ~=  Check if a string or array contains the <value>; case-sensitive
28
 *   - _=  Check if a string or array contains the <value>; case-insensitive
29
 *   - /=  Compare the <value> with a regular expression
30
 */
31
class WhereFilter extends AbstractTwigExtension implements TwigFilterInterface
32
{
33
    /**
34
     * @param array|\ArrayAccess[] $array      The elements to filter through
35
     * @param string               $key        The key value in an associative array or FrontMatter
36
     * @param string               $comparison The actual comparison symbols being used
37
     * @param mixed                $value      The value we're searching for
38
     *
39
     * @return array
40
     */
41 39
    public function __invoke($array, $key, $comparison, $value)
42
    {
43 39
        $results = array();
44 39
        $this->search_r($array, $key, $comparison, $value, $results);
45
46 38
        return $results;
47
    }
48
49
    /**
50
     * @return \Twig_SimpleFilter
51
     */
52 1
    public static function get()
53
    {
54 1
        return new \Twig_SimpleFilter('where', new self());
55
    }
56
57
    /**
58
     * Recursive searching calling our comparison.
59
     *
60
     * @param array|\ArrayAccess[] $array      The elements to filter through
61
     * @param string               $key        The key value in an associative array or FrontMatter
62
     * @param string               $comparison The actual comparison symbols being used
63
     * @param string               $value      The value we're searching for
64
     * @param array                $results    The reference to where to keep the filtered elements
65
     */
66 39
    private function search_r($array, $key, $comparison, $value, &$results)
0 ignored issues
show
Coding Style introduced by
This method is not in camel caps format.

This check looks for method names that are not written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection seeker becomes databaseConnectionSeeker.

Loading history...
67
    {
68 39
        if (!is_array($array) && !($array instanceof \ArrayAccess))
69
        {
70 38
            return;
71
        }
72
73 39
        if ($this->compare($array, $key, $comparison, $value))
74
        {
75 30
            $results[] = $array;
76
        }
77
78 39
        foreach ($array as $subarray)
79
        {
80 39
            $this->search_r($subarray, $key, $comparison, $value, $results);
81
        }
82 38
    }
83
84
    /**
85
     * The logic for determining if an element matches the filter.
86
     *
87
     * @param array|\ArrayAccess[] $array      The elements to filter through
88
     * @param string               $key        The key value in an associative array or FrontMatter
89
     * @param string               $comparison The actual comparison symbols being used
90
     * @param string               $value      The value we're searching for
91
     *
92
     * @throws Twig_Error_Syntax
93
     *
94
     * @return bool
95
     */
96 39
    private function compare($array, $key, $comparison, $value)
97
    {
98 39
        if ($this->compareNullValues($array, $key, $comparison, $value))
99
        {
100 2
            return true;
101
        }
102
103 39
        if (!isset($array[$key]))
104
        {
105 39
            return false;
106
        }
107
108 38
        return $this->comparisonSymbol($array[$key], $comparison, $value);
109
    }
110
111
    /**
112
     * If the comparison is == or !=, then special behavior is defined for null values.
113
     *
114
     * @param array|\ArrayAccess[] $array      The elements to filter through
115
     * @param string               $key        The key value in an associative array or FrontMatter
116
     * @param string               $comparison The actual comparison symbols being used
117
     * @param mixed                $value      The value we're searching for
118
     *
119
     * @return bool
120
     */
121 39
    private function compareNullValues($array, $key, $comparison, $value)
122
    {
123 39
        if ($comparison != '==' && $comparison != '!=')
124
        {
125 25
            return false;
126
        }
127
128 14
        if (!($array instanceof \ArrayAccess))
129
        {
130 14
            return false;
131
        }
132
133 5
        if (!isset($array[$key]))
134
        {
135 4
            if ($comparison == '==' && is_null($value))
136
            {
137 1
                return true;
138
            }
139 3
            if ($comparison == '!=' && !is_null($value))
140
            {
141 1
                return true;
142
            }
143
        }
144
145 5
        return false;
146
    }
147
148
    /**
149
     * @param mixed  $lhs
150
     * @param string $comparison
151
     * @param mixed  $rhs
152
     *
153
     * @throws Twig_Error_Syntax
154
     *
155
     * @return bool
156
     */
157 38
    private function comparisonSymbol($lhs, $comparison, $rhs)
158
    {
159
        switch ($comparison)
160
        {
161 38
            case '==':
162 9
                return ($lhs === $rhs);
163
164 29
            case '!=':
165 4
                return ($lhs !== $rhs);
166
167 25
            case '>':
168 3
                return ($lhs > $rhs);
169
170 22
            case '>=':
171 4
                return ($lhs >= $rhs);
172
173 18
            case '<':
174 3
                return ($lhs < $rhs);
175
176 15
            case '<=':
177 3
                return ($lhs <= $rhs);
178
179 12
            case '~=':
180 7
                return $this->contains($lhs, $rhs);
181
182 5
            case '_=':
183 3
                return $this->containsCaseInsensitive($lhs, $rhs);
184
185 2
            case '/=':
186 1
                return $this->regexMatches($lhs, $rhs);
187
188
            default:
189 1
                throw new Twig_Error_Syntax("Invalid where comparison ({$comparison})");
190
        }
191
    }
192
193 7
    private function contains($haystack, $needle)
194
    {
195
        return
196 7
            (is_array($haystack) && in_array($needle, $haystack)) ||
197 7
            (is_string($haystack) && strpos($haystack, $needle) !== false);
198
    }
199
200 3
    private function containsCaseInsensitive($haystack, $needle)
201
    {
202 3
        if (is_array($haystack))
203
        {
204 1
            $downCase = array_combine(array_map('strtolower', $haystack), $haystack);
205
206 1
            return isset($downCase[strtolower($needle)]);
207
        }
208
209 2
        return is_string($haystack) && strpos(strtolower($haystack), strtolower($needle)) !== false;
210
    }
211
212 1
    private function regexMatches($haystack, $regex)
213
    {
214 1
        return preg_match($regex, $haystack) === 1;
215
    }
216
}
217