Completed
Push — master ( a2a4ba...d14277 )
by Vladimir
09:15
created

WhereFilter::containsCaseInsensitive()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 5
nc 3
nop 2
dl 0
loc 11
ccs 6
cts 6
cp 1
crap 3
rs 9.4285
c 0
b 0
f 0
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
 * @package allejo\stakx\Twig
34
 */
35
class WhereFilter
36
{
37
    /**
38
     * @param  array|\ArrayAccess[] $array      The elements to filter through
39
     * @param  string               $key        The key value in an associative array or FrontMatter
40
     * @param  string               $comparison The actual comparison symbols being used
41
     * @param  mixed                $value      The value we're searching for
42
     *
43
     * @return array
44
     */
45 39
    public function __invoke ($array, $key, $comparison, $value)
46
    {
47 39
        $results = array();
48 39
        $this->search_r($array, $key, $comparison, $value, $results);
49
50 38
        return $results;
51
    }
52
53
    /**
54
     * @return \Twig_SimpleFilter
55
     */
56 2
    public static function get ()
57
    {
58 2
        return new \Twig_SimpleFilter('where', new self());
59
    }
60
61
    /**
62
     * Recursive searching calling our comparison
63
     *
64
     * @param array|\ArrayAccess[] $array      The elements to filter through
65
     * @param string               $key        The key value in an associative array or FrontMatter
66
     * @param string               $comparison The actual comparison symbols being used
67
     * @param string               $value      The value we're searching for
68
     * @param array                $results    The reference to where to keep the filtered elements
69
     */
70 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...
71
    {
72 39
        if (!is_array($array) && !($array instanceof \ArrayAccess))
73 39
        {
74 19
            return;
75
        }
76
77 39
        if ($this->compare($array, $key, $comparison, $value))
78 39
        {
79 30
            $results[] = $array;
80 30
        }
81
82 39
        foreach ($array as $subarray)
83
        {
84 39
            $this->search_r($subarray, $key, $comparison, $value, $results);
85 38
        }
86 38
    }
87
88
    /**
89
     * The logic for determining if an element matches the filter
90
     *
91
     * @param  array|\ArrayAccess[] $array      The elements to filter through
92
     * @param  string               $key        The key value in an associative array or FrontMatter
93
     * @param  string               $comparison The actual comparison symbols being used
94
     * @param  mixed                $value      The value we're searching for
95
     *
96
     * @return bool
97
     *
98
     * @throws Twig_Error_Syntax
99
     */
100 39
    private function compare ($array, $key, $comparison, $value)
101
    {
102 39
        if ($this->compareNullValues($array, $key, $comparison, $value))
103 39
        {
104 2
            return true;
105
        }
106
107 39
        if (!isset($array[$key]))
108 39
        {
109 39
            return false;
110
        }
111
112 38
        return $this->comparisonSymbol($array[$key], $comparison, $value);
113
    }
114
115
    /**
116
     * If the comparison is == or !=, then special behavior is defined for null values
117
     *
118
     * @param  array|\ArrayAccess[] $array      The elements to filter through
119
     * @param  string               $key        The key value in an associative array or FrontMatter
120
     * @param  string               $comparison The actual comparison symbols being used
121
     * @param  mixed                $value      The value we're searching for
122
     *
123
     * @return bool
124
     */
125 39
    private function compareNullValues ($array, $key, $comparison, $value)
126
    {
127 39
        if ($comparison != '==' && $comparison != '!=')
128 39
        {
129 25
            return false;
130
        }
131
132 14
        if (!($array instanceof JailObject))
133 14
        {
134 14
            return false;
135
        }
136
137 4
        if ($array->coreInstanceOf(FrontMatterObject::class) && !isset($array[$key]))
138 4
        {
139 4
            if ($comparison == '==' && is_null($value))  { return true; }
140 3
            if ($comparison == '!=' && !is_null($value)) { return true; }
141 2
        }
142
143 4
        return false;
144
    }
145
146 38
    private function comparisonSymbol ($lhs, $comparison, $rhs)
147
    {
148
        switch ($comparison)
149
        {
150 38
            case '==':
151 9
                return ($lhs === $rhs);
152
153 29
            case '!=':
154 4
                return ($lhs !== $rhs);
155
156 25
            case '>' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
157 3
                return ($lhs > $rhs);
158
159 22
            case '>=':
160 4
                return ($lhs >= $rhs);
161
162 18
            case '<' :
0 ignored issues
show
Coding Style introduced by
There must be no space before the colon in a CASE statement

As per the PSR-2 coding standard, there must not be a space in front of the colon in case statements.

switch ($selector) {
    case "A": //right
        doSomething();
        break;
    case "B" : //wrong
        doSomethingElse();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
163 3
                return ($lhs < $rhs);
164
165 15
            case '<=':
166 3
                return ($lhs <= $rhs);
167
168 12
            case '~=':
169 7
                return $this->contains($lhs, $rhs);
170
171 5
            case '_=':
172 3
                return $this->containsCaseInsensitive($lhs, $rhs);
173
174 2
            case '/=':
175 1
                return $this->regexMatches($lhs, $rhs);
176
177 1
            default:
178 1
                throw new Twig_Error_Syntax("Invalid where comparison ({$comparison})");
179 1
        }
180
    }
181
182 7
    private function contains ($haystack, $needle)
183
    {
184
        return (
185 7
            (is_array($haystack) && in_array($needle, $haystack)) ||
186 7
            (is_string($haystack) && strpos($haystack, $needle) !== false)
187 7
        );
188
    }
189
190 3
    private function containsCaseInsensitive ($haystack, $needle)
191
    {
192 3
        if (is_array($haystack))
193 3
        {
194 1
            $downCase = array_combine(array_map('strtolower', $haystack), $haystack);
195
196 1
            return (isset($downCase[strtolower($needle)]));
197
        }
198
199 2
        return (is_string($haystack) && strpos(strtolower($haystack), strtolower($needle)) !== false);
200
    }
201
202 1
    private function regexMatches ($haystack, $regex)
203
    {
204 1
        return (preg_match($regex, $haystack) === 1);
205
    }
206
}