Passed
Branch master (2a417c)
by Jean
05:00
created

RuleFilterer::validateRule()   F

Complexity

Conditions 27
Paths 108

Size

Total Lines 130

Duplication

Lines 13
Ratio 10 %

Code Coverage

Tests 56
CRAP Score 70.6774

Importance

Changes 0
Metric Value
cc 27
nc 108
nop 7
dl 13
loc 130
ccs 56
cts 92
cp 0.6087
crap 70.6774
rs 3.28
c 0
b 0
f 0

How to fix   Long Method    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
 * RuleFilterer
4
 *
5
 * @package php-logical-filter
6
 * @author  Jean Claveau
7
 */
8
namespace JClaveau\LogicalFilter\Filterer;
9
use       JClaveau\LogicalFilter\LogicalFilter;
10
use       JClaveau\LogicalFilter\Rule\EqualRule;
11
use       JClaveau\LogicalFilter\Rule\BelowRule;
12
use       JClaveau\LogicalFilter\Rule\AboveRule;
13
use       JClaveau\LogicalFilter\Rule\OrRule;
14
use       JClaveau\LogicalFilter\Rule\AndRule;
15
use       JClaveau\LogicalFilter\Rule\NotRule;
16
use       JClaveau\LogicalFilter\Rule\NotEqualRule;
17
use       JClaveau\LogicalFilter\Rule\AbstractOperationRule;
18
use       JClaveau\LogicalFilter\Rule\AbstractRule;
19
use       JClaveau\LogicalFilter\Rule\InRule;
20
use       JClaveau\LogicalFilter\Rule\NotInRule;
21
use       JClaveau\LogicalFilter\Rule\RegexpRule;
22
23
/**
24
 * This filterer is intended to validate Rules.
25
 *
26
 * Manipulating the rules of a logical filter is easier with another one.
27
 * This filterer is used for the functions of the exposed api like
28
 * removeRules(), manipulateRules()
29
 *
30
 * @todo addRules()
31
 *
32
 */
33
class RuleFilterer extends Filterer
34
{
35
    const this        = 'instance';
36
    const field       = 'field';
37
    const operator    = 'operator';
38
    const value       = 'value';
39
    const depth       = 'depth';
40
    const children    = 'children';
41
    const description = 'description';
42
    const path        = 'path';
43
44
    /**
45
     * @return array
46
     */
47 20
    public function getChildren($row) // strict issue if forcing  AbstractRule with php 5.6 here
48
    {
49 20
        if ($row instanceof InRule) {
50
            // We do not need to parse the EqualRule operands of InRules
51
            // as they all share the same field
52 3
            return [];
53
        }
54
55 20
        if ($row instanceof AbstractOperationRule) {
56 20
            return $row->getOperands();
57
        }
58 18
    }
59
60
    /**
61
     */
62 17
    public function setChildren( &$row, $filtered_children ) // strict issue if forcing  AbstractRule with php 5.6 here
63
    {
64 17
        if ($row instanceof AbstractOperationRule) {
65 17
            return $row->setOperandsOrReplaceByOperation( $filtered_children, [] ); // no simplification options?
0 ignored issues
show
Bug introduced by
The method setOperandsOrReplaceByOperation() does not exist on JClaveau\LogicalFilter\Rule\AbstractOperationRule. Did you maybe mean setOperands()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
66
        }
67
    }
68
69
    /**
70
     *
71
     * @return true | false | null
72
     */
73 19
    public function validateRule ($field, $operator, $value, $rule, array $path, $all_operands, $options)
74
    {
75 19
        if (    ! empty($options[ Filterer::leaves_only ])
76 19
            && in_array( get_class($rule), [OrRule::class, AndRule::class, NotRule::class] )
77 19
        ) {
78
            // return true;
79 4
            return null;
80
        }
81
82 19
        if (self::field === $field) {
83 13
            if ( ! method_exists($rule, 'getField')) {
84
                // if (in_array( get_class($rule), [AndRule::class, OrRule::class]))
85 7
                return null; // The filter cannot be applied to this rule
86
            }
87
88
            try {
89 13
                $value_to_compare = $rule->getField();
90
            }
91 13
            catch (\LogicException $e) {
92
                // This is due to NotInRule.
93
                // TODO replace it by a TrueRule in this case
94
                return  null;
95
            }
96 13
        }
97 17
        elseif (self::operator === $field) {
98 13
            $value_to_compare = $rule::operator;
99 13
        }
100 8
        elseif (self::value === $field) {
101 2
            $description = $rule->toArray();
102
103 2
            if (    3 === count($description)
104 2
                && is_string($description[0])
105 2
                && is_string($description[1]) ) {
106 2
                $value_to_compare = $description[2];
107 2
            }
108
            else {
109 2
                return null; // The filter cannot be applied to this rule
110
            }
111 2
        }
112 6
        elseif (self::description === $field) {
113
            $value_to_compare = $rule->toArray();
114
        }
115 6
        elseif (self::depth === $field) {
116
            // original $depth is lost once the filter is simplified
117 1
            throw new \InvalidArgumentException('Depth rule uppport not implemented');
118
        }
119 5
        elseif (self::path === $field) {
120
            // TODO the description of its parents
121 1
            throw new \InvalidArgumentException('Path rule uppport not implemented');
122
        }
123 4
        elseif (self::children === $field) {
124 4
            if ( ! method_exists($rule, 'getOperands')) {
125 1
                return null; // The filter cannot be applied to this rule
126
            }
127 4
            $value_to_compare = count( $rule->getOperands() );
128 4
        }
129
        else {
130
            throw new \InvalidArgumentException(
131
                "Rule filters must belong to ["
132
                . implode(', ', [
133
                    self::field,
134
                    self::operator,
135
                    self::value,
136
                    self::depth,
137
                    self::description,
138
                    self::children,
139
                    self::path,
140
                ])
141
                ."] contrary to : ".var_export($field, true)
142
            );
143
        }
144
145 17
        if (EqualRule::operator === $operator) {
146 17 View Code Duplication
            if (null === $value) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
147
                $out = is_null($value_to_compare);
148
            }
149
            else {
150
                // TODO support strict comparisons
151 17
                $out = $value_to_compare == $value;
152
            }
153 17
        }
154 6
        elseif (InRule::operator === $operator) {
155 3
            $out = in_array($value_to_compare, $value);
156 3
        }
157 3
        elseif (BelowRule::operator === $operator) {
158
            $out = $value_to_compare < $value;
159
        }
160 3
        elseif (AboveRule::operator === $operator) {
161
            $out = $value_to_compare > $value;
162
        }
163 3
        elseif (RegexpRule::operator === $operator) {
164
            // TODO support optionnal parameters
165 1
            $out = preg_match($value, $value_to_compare);
166 1
            if (false === $out) {
167
                throw new \InvalidArgumentEXception(
168
                    "Error while calling preg_match( $value ) on: \n"
169
                    .var_export($value_to_compare, true)
170
                );
171
            }
172 1
            $out = (bool) $out;
173 1
        }
174 3
        elseif (NotEqualRule::operator === $operator) {
175 2 View Code Duplication
            if (null === $value) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
176
                $out = ! is_null($value_to_compare);
177
            }
178
            else {
179 2
                $out = $value != $value_to_compare;
180
            }
181 2
        }
182 1
        elseif (NotInRule::operator === $operator) {
183 1
            $out = ! in_array($value_to_compare, $value);
184 1
        }
185
        else {
186
            throw new \InvalidArgumentException(
187
                "Unhandled operator: " . $operator
188
            );
189
        }
190
191 17
        if ( ! empty($options['debug'])) {
192
            var_dump(
0 ignored issues
show
Security Debugging Code introduced by
var_dump("{$field}, {$op...xport($options, true)); looks like debug code. Are you sure you do not want to remove it? This might expose sensitive data.
Loading history...
193
                "$field, $operator, " . var_export($value, true)
194
                 . ' ||  '. $value_to_compare . ' => ' . var_export($out, true)
195
                 . "\n" . get_class($rule)
196
                 . "\n" . var_export($options, true)
197
            );
198
            // $rule->dump();
199
        }
200
201 17
        return $out;
202
    }
203
204
    /**
205
     * @param LogicalFilter      $filter
206
     * @param array|AbstractRule $ruleTree_to_filter
207
     * @param array              $options leaves_only | debug
208
     */
209 21
    public function apply( LogicalFilter $filter, $ruleTree_to_filter, $options=[] )
210
    {
211 21
        if ( ! $ruleTree_to_filter) {
212 1
            return $ruleTree_to_filter;
213
        }
214
215 20
        if ($ruleTree_to_filter instanceof AbstractRule) {
216 20
            $ruleTree_to_filter = [$ruleTree_to_filter];
217 20
        }
218
219 20
        if ( ! is_array($ruleTree_to_filter)) {
220
            throw new \InvalidArgumentException(
221
                "\$ruleTree_to_filter must be an array or an AbstractRule "
222
                ."instead of: " . var_export($ruleTree_to_filter, true)
223
            );
224
        }
225
226
        // Produces "Only variables should be passed by reference" on Travis
227 20
        $result = parent::apply($filter, $ruleTree_to_filter, $options);
0 ignored issues
show
Documentation introduced by
$ruleTree_to_filter is of type array, but the function expects a object<JClaveau\LogicalFilter\Filterer\Iterable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
228
229 17
        return reset( $result );
230
    }
231
232
    /**/
233
}
234