Completed
Pull Request — master (#41)
by Vladimir
02:31
created

WhereFilter::compareNullValues()   D

Complexity

Conditions 10
Paths 6

Size

Total Lines 26
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 10

Importance

Changes 0
Metric Value
cc 10
eloc 11
nc 6
nop 4
dl 0
loc 26
ccs 17
cts 17
cp 1
crap 10
rs 4.8196
c 0
b 0
f 0

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 2017 Vladimir Jimenez
5
 * @license   https://github.com/allejo/stakx/blob/master/LICENSE.md MIT
6
 */
7
8
namespace allejo\stakx\Twig;
9
10
use allejo\stakx\Object\FrontMatterObject;
11
use allejo\stakx\Object\JailObject;
12
use Twig_Error_Syntax;
13
14
/**
15
 * Where Twig Filter.
16
 *
17
 * This Twig filter introduces the `where` filter
18
 *
19
 * Usage:
20
 *   <array> | where(<key>, <comparison>, <value>)
21
 *
22
 * Available comparisons:
23
 *   - ==  Ensure the values are equal and are the same data type
24
 *   - !=  Ensure the values are not equal; returns false if the values are the same but different data types
25
 *   - >   Greater than
26
 *   - >=  Greater than or equal to
27
 *   - <   Less than
28
 *   - <=  Less than or equal to
29
 *   - ~=  Check if a string or array contains the <value>; case-sensitive
30
 *   - _=  Check if a string or array contains the <value>; case-insensitive
31
 *   - /=  Compare the <value> with a regular expression
32
 */
33
class WhereFilter
34
{
35
    /**
36
     * @param array|\ArrayAccess[] $array      The elements to filter through
37
     * @param string               $key        The key value in an associative array or FrontMatter
38
     * @param string               $comparison The actual comparison symbols being used
39
     * @param mixed                $value      The value we're searching for
40
     *
41
     * @return array
42
     */
43 39
    public function __invoke($array, $key, $comparison, $value)
44
    {
45 39
        $results = array();
46 39
        $this->search_r($array, $key, $comparison, $value, $results);
47
48 38
        return $results;
49
    }
50
51
    /**
52
     * @return \Twig_SimpleFilter
53
     */
54 4
    public static function get()
55
    {
56 4
        return new \Twig_SimpleFilter('where', new self());
57
    }
58
59
    /**
60
     * Recursive searching calling our comparison.
61
     *
62
     * @param array|\ArrayAccess[] $array      The elements to filter through
63
     * @param string               $key        The key value in an associative array or FrontMatter
64
     * @param string               $comparison The actual comparison symbols being used
65
     * @param string               $value      The value we're searching for
66
     * @param array                $results    The reference to where to keep the filtered elements
67
     */
68 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...
69
    {
70 39
        if (!is_array($array) && !($array instanceof \ArrayAccess))
71 39
        {
72 19
            return;
73
        }
74
75 39
        if ($this->compare($array, $key, $comparison, $value))
76 39
        {
77 30
            $results[] = $array;
78 30
        }
79
80 39
        foreach ($array as $subarray)
81
        {
82 39
            $this->search_r($subarray, $key, $comparison, $value, $results);
83 38
        }
84 38
    }
85
86
    /**
87
     * The logic for determining if an element matches the filter.
88
     *
89
     * @param array|\ArrayAccess[] $array      The elements to filter through
90
     * @param string               $key        The key value in an associative array or FrontMatter
91
     * @param string               $comparison The actual comparison symbols being used
92
     * @param mixed                $value      The value we're searching for
93
     *
94
     * @throws Twig_Error_Syntax
95
     *
96
     * @return bool
97
     */
98 39
    private function compare($array, $key, $comparison, $value)
99
    {
100 39
        if ($this->compareNullValues($array, $key, $comparison, $value))
101 39
        {
102 2
            return true;
103
        }
104
105 39
        if (!isset($array[$key]))
106 39
        {
107 39
            return false;
108
        }
109
110 38
        return $this->comparisonSymbol($array[$key], $comparison, $value);
111
    }
112
113
    /**
114
     * If the comparison is == or !=, then special behavior is defined for null values.
115
     *
116
     * @param array|\ArrayAccess[] $array      The elements to filter through
117
     * @param string               $key        The key value in an associative array or FrontMatter
118
     * @param string               $comparison The actual comparison symbols being used
119
     * @param mixed                $value      The value we're searching for
120
     *
121
     * @return bool
122
     */
123 39
    private function compareNullValues($array, $key, $comparison, $value)
124
    {
125 39
        if ($comparison != '==' && $comparison != '!=')
126 39
        {
127 25
            return false;
128
        }
129
130 14
        if (!($array instanceof JailObject))
131 14
        {
132 14
            return false;
133
        }
134
135 4
        if ($array->coreInstanceOf(FrontMatterObject::class) && !isset($array[$key]))
136 4
        {
137 4
            if ($comparison == '==' && is_null($value))
138 4
            {
139 1
                return true;
140
            }
141 3
            if ($comparison == '!=' && !is_null($value))
142 3
            {
143 1
                return true;
144
            }
145 2
        }
146
147 4
        return false;
148
    }
149
150 38
    private function comparisonSymbol($lhs, $comparison, $rhs)
151
    {
152
        switch ($comparison)
153
        {
154 38
            case '==':
155 9
                return ($lhs === $rhs);
156
157 29
            case '!=':
158 4
                return ($lhs !== $rhs);
159
160 25
            case '>':
161 3
                return ($lhs > $rhs);
162
163 22
            case '>=':
164 4
                return ($lhs >= $rhs);
165
166 18
            case '<':
167 3
                return ($lhs < $rhs);
168
169 15
            case '<=':
170 3
                return ($lhs <= $rhs);
171
172 12
            case '~=':
173 7
                return $this->contains($lhs, $rhs);
174
175 5
            case '_=':
176 3
                return $this->containsCaseInsensitive($lhs, $rhs);
177
178 2
            case '/=':
179 1
                return $this->regexMatches($lhs, $rhs);
180
181 1
            default:
182 1
                throw new Twig_Error_Syntax("Invalid where comparison ({$comparison})");
183 1
        }
184
    }
185
186 7
    private function contains($haystack, $needle)
187
    {
188
        return
189 7
            (is_array($haystack) && in_array($needle, $haystack)) ||
190 7
            (is_string($haystack) && strpos($haystack, $needle) !== false);
191
    }
192
193 3
    private function containsCaseInsensitive($haystack, $needle)
194
    {
195 3
        if (is_array($haystack))
196 3
        {
197 1
            $downCase = array_combine(array_map('strtolower', $haystack), $haystack);
198
199 1
            return isset($downCase[strtolower($needle)]);
200
        }
201
202 2
        return is_string($haystack) && strpos(strtolower($haystack), strtolower($needle)) !== false;
203
    }
204
205 1
    private function regexMatches($haystack, $regex)
206
    {
207 1
        return preg_match($regex, $haystack) === 1;
208
    }
209
}
210