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

NotRule::negateOperand()   F

Complexity

Conditions 19
Paths 23

Size

Total Lines 134

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 86
CRAP Score 19.0042

Importance

Changes 0
Metric Value
cc 19
nc 23
nop 2
dl 0
loc 134
ccs 86
cts 88
cp 0.9773
crap 19.0042
rs 3.6133
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
namespace JClaveau\LogicalFilter\Rule;
3
4
/**
5
 * Logical negation:
6
 * @see https://en.wikipedia.org/wiki/Negation
7
 */
8
class NotRule extends AbstractOperationRule
9
{
10
    /** @var string operator */
11
    const operator = 'not';
12
13
    /**
14
     */
15 34
    public function __construct( AbstractRule $operand=null, array $options=[] )
16
    {
17 34
        if ( ! empty($options)) {
18 11
            $this->setOptions($options);
19 11
        }
20
21
        // Negation has only one operand. If more is required, group them
22
        // into an AndRule
23 34
        if ($operand) {
24 20
            $this->addOperand($operand);
25 20
        }
26 34
    }
27
28
    /**
29
     */
30 28
    public function isNormalizationAllowed(array $current_simplification_options=[])
31
    {
32 28
        $operand = $this->getOperandAt(0);
33
34 28
        return null !== $operand;
35
    }
36
37
    /**
38
     * Transforms all composite rules in the tree of operands into
39
     * atomic rules.
40
     *
41
     * @todo use get_class instead of instanceof to avoid order issue
42
     *       in the conditions.
43
     *
44
     * @return array
45
     */
46 30
    public function negateOperand($remove_generated_negations=false, array $current_simplification_options)
0 ignored issues
show
Unused Code introduced by
The parameter $remove_generated_negations is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
47
    {
48 30
        if ( ! $this->isNormalizationAllowed($current_simplification_options)) {
49 21
            return $this;
50
        }
51
52 16
        $operand = $this->getOperandAt(0);
53
54 16
        if (method_exists($operand, 'getField')) {
55 13
            $field = $operand->getField();
56 13
        }
57
58 16
        if ($operand instanceof AboveRule) {
59 5
            $new_rule = new OrRule([
60 5
                new BelowRule($field, $operand->getMinimum()),
0 ignored issues
show
Bug introduced by
The variable $field does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
Deprecated Code introduced by
The method JClaveau\LogicalFilter\R...AboveRule::getMinimum() has been deprecated with message: getLowerLimit

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
61 5
                new EqualRule($field, $operand->getMinimum()),
0 ignored issues
show
Deprecated Code introduced by
The method JClaveau\LogicalFilter\R...AboveRule::getMinimum() has been deprecated with message: getLowerLimit

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
62 5
            ]);
63 5
        }
64 14
        elseif ($operand instanceof BelowRule) {
65
            // ! (v >  a) : v <= a : (v < a || a = v)
66 3
            $new_rule = new OrRule([
67 3
                new AboveRule($field, $operand->getMaximum()),
0 ignored issues
show
Deprecated Code introduced by
The method JClaveau\LogicalFilter\R...BelowRule::getMaximum() has been deprecated with message: getUpperLimit

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
68 3
                new EqualRule($field, $operand->getMaximum()),
0 ignored issues
show
Deprecated Code introduced by
The method JClaveau\LogicalFilter\R...BelowRule::getMaximum() has been deprecated with message: getUpperLimit

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
69 3
            ]);
70 3
        }
71 14
        elseif ($operand instanceof NotRule) {
72
            // ! (  !  a) : a
73 1
            $new_rule = $operand->getOperandAt(0);
74 1
        }
75 14
        elseif ($operand instanceof EqualRule && null === $operand->getValue()) {
76 2
            $new_rule = new NotEqualRule($field, null);
0 ignored issues
show
Documentation introduced by
null is of type null, but the function expects a array.

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...
77 2
        }
78 13
        elseif ($operand instanceof EqualRule) {
79
            // ! (v =  a) : (v < a) || (v > a)
80 9
            if ($this->getOption('not_equal.normalization', $current_simplification_options)) {
81 6
                $new_rule = new OrRule([
82 6
                    new AboveRule($field, $operand->getValue()),
83 6
                    new BelowRule($field, $operand->getValue()),
84 6
                ]);
85 6
            }
86
            else {
87 4
                $new_rule = new NotEqualRule( $field, $operand->getValue() );
88
            }
89 9
        }
90 9
        elseif ($operand instanceof NotEqualRule) {
91
            $new_rule = new EqualRule( $field, $operand->getValue() );
92
        }
93 9
        elseif ($operand instanceof AndRule) {
94
            // @see https://github.com/jclaveau/php-logical-filter/issues/40
95
            // ! (B && A) : (!B && A) || (B && !A) || (!B && !A)
96
            // ! (A && B && C) :
97
            //    (!A && !B && !C)
98
            // || (!A && B && C) || (!A && !B && C) || (!A && B && !C)
99
            // || (A && !B && C) || (!A && !B && C) || (A && !B && !C)
100
            // || (A && B && !C) || (!A && B && !C) || (A && !B && !C)
101
102
            // We combine all possibilities of rules and themselves negated
103 2
            $new_rule       = new OrRule;
104 2
            $child_operands = $operand->getOperands();
105
106 2
            $current_operand               = array_shift($child_operands);
107 2
            $current_operand_possibilities = new OrRule([
108 2
                new AndRule([
109 2
                    $current_operand->copy(),
110 2
                ]),
111 2
                new AndRule([
112 2
                    new NotRule($current_operand->copy()),
113 2
                ]),
114 2
            ]);
115
116
            // for every remaining operand, we duplicate the already made
117
            // combinations and add on half of them !$next_operand
118
            // and $next_operand on the other half
119 2
            while ($next_operand = array_shift($child_operands)) {
120 2
                $next_operand_possibilities = $current_operand_possibilities->copy();
121
122 2
                $tmp = [];
123 2
                foreach ($current_operand_possibilities->getOperands() as $current_operand_possibility) {
124 2
                    $tmp[] = $current_operand_possibility->addOperand( $next_operand->copy() );
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class JClaveau\LogicalFilter\Rule\AbstractRule as the method addOperand() does only exist in the following sub-classes of JClaveau\LogicalFilter\Rule\AbstractRule: JClaveau\LogicalFilter\Rule\AboveOrEqualRule, JClaveau\LogicalFilter\Rule\AbstractOperationRule, JClaveau\LogicalFilter\Rule\AndRule, JClaveau\LogicalFilter\Rule\BelowOrEqualRule, JClaveau\LogicalFilter\Rule\BetweenOrEqualBothRule, JClaveau\LogicalFilter\R...BetweenOrEqualLowerRule, JClaveau\LogicalFilter\R...BetweenOrEqualUpperRule, JClaveau\LogicalFilter\Rule\BetweenRule, JClaveau\LogicalFilter\Rule\InRule, JClaveau\LogicalFilter\Rule\NotEqualRule, JClaveau\LogicalFilter\Rule\NotInRule, JClaveau\LogicalFilter\Rule\NotRule, JClaveau\LogicalFilter\Rule\OrRule. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
125 2
                }
126 2
                $current_operand_possibilities->setOperands($tmp);
127
128 2
                foreach ($next_operand_possibilities->getOperands() as $next_operand_possibility) {
129 2
                    $current_operand_possibilities->addOperand(
130 2
                        $next_operand_possibility->addOperand( new NotRule($next_operand->copy()) )
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class JClaveau\LogicalFilter\Rule\AbstractRule as the method addOperand() does only exist in the following sub-classes of JClaveau\LogicalFilter\Rule\AbstractRule: JClaveau\LogicalFilter\Rule\AboveOrEqualRule, JClaveau\LogicalFilter\Rule\AbstractOperationRule, JClaveau\LogicalFilter\Rule\AndRule, JClaveau\LogicalFilter\Rule\BelowOrEqualRule, JClaveau\LogicalFilter\Rule\BetweenOrEqualBothRule, JClaveau\LogicalFilter\R...BetweenOrEqualLowerRule, JClaveau\LogicalFilter\R...BetweenOrEqualUpperRule, JClaveau\LogicalFilter\Rule\BetweenRule, JClaveau\LogicalFilter\Rule\InRule, JClaveau\LogicalFilter\Rule\NotEqualRule, JClaveau\LogicalFilter\Rule\NotInRule, JClaveau\LogicalFilter\Rule\NotRule, JClaveau\LogicalFilter\Rule\OrRule. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
131 2
                    );
132 2
                }
133 2
            }
134
135
            // We remove the only possibility where no rule is negated
136 2
            $combinations = $current_operand_possibilities->getOperands();
137 2
            array_shift($combinations); // The first rule contains no negation
138 2
            $new_rule->setOperands( $combinations )
139
                // ->dump(true)
140
                ;
141 2
        }
142 8
        elseif ($operand instanceof OrRule) {
143
144
            // $operand->dump(true);
145
            if (     $operand instanceof InRule
146 7
                && ! $operand->isNormalizationAllowed($current_simplification_options)
147 7
            ) {
148
                // ['not', ['field', 'in', [2, 3]]] <=> ['field', '!in', [2, 3]]
149 1
                $new_rule = new NotInRule(
150 1
                    $operand->getField(),
151 1
                    $operand->getPossibilities()
152 1
                );
153 1
            }
154
            else {
155
                // ! (A || B) : !A && !B
156
                // ! (A || B || C || D) : !A && !B && !C && !D
157 7
                $new_rule = new AndRule;
158 7
                foreach ($operand->getOperands() as $sub_operand) {
159 7
                    $negation = new NotRule;
160 7
                    $negation = $negation->setOperandsOrReplaceByOperation(
161 7
                        [$sub_operand->copy()],
162
                        $current_simplification_options
163 7
                    );
164 7
                    $new_rule->addOperand( $negation );
165 7
                }
166
            }
167
            // $new_rule->dump(!true);
168 7
        }
169
        else {
170 1
            throw new \LogicException(
171 1
                'Removing NotRule(' . var_export($operand, true) . ') '
172 1
                . ' not implemented'
173 1
            );
174
        }
175
176
        // $new_rule->dump(!true);
177
178 15
        return $new_rule;
179
    }
180
181
    /**
182
     * Replace all the OrRules of the RuleTree by one OrRule at its root.
183
     *
184
     * @todo rename as RootifyDisjunjctions?
185
     * @todo return $this (implements a Rule monad?)
186
     *
187
     * @return OrRule copied operands with one OR at its root
188
     */
189 24
    public function rootifyDisjunctions($simplification_options)
190
    {
191 24
        if ( ! $this->isNormalizationAllowed($simplification_options)) {
192 24
            return $this;
193
        }
194
195
        $this->moveSimplificationStepForward( self::rootify_disjunctions, $simplification_options );
196
197
        foreach ($this->operands as $id => $operand) {
198
            if ($operand instanceof AbstractOperationRule) {
199
                $this->operands[$id] = $operand->rootifyDisjunctions($simplification_options);
200
            }
201
        }
202
203
        return $this;
204
    }
205
206
    /**
207
     * Not rules can only have one operand.
208
     *
209
     * @return $this
210
     */
211 30
    public function unifyAtomicOperands($simplification_strategy_step = false, array $contextual_options)
212
    {
213 30
        if ( ! $this->isNormalizationAllowed($contextual_options)) {
214 27
            return $this;
215
        }
216
217 13
        if ($simplification_strategy_step) {
218
            $this->moveSimplificationStepForward( self::unify_atomic_operands, $contextual_options );
219
        }
220
221 13
        return $this;
222
    }
223
224
    /**
225
     * @param array $options   + show_instance=false Display the operator of the rule or its instance id
226
     *
227
     * @return array
228
     */
229 24
    public function toArray(array $options=[])
230
    {
231
        $default_options = [
232 24
            'show_instance' => false,
233 24
            'semantic'      => false,
234 24
        ];
235 24
        foreach ($default_options as $default_option => &$default_value) {
236 24
            if ( ! isset($options[ $default_option ])) {
237 24
                $options[ $default_option ] = $default_value;
238 24
            }
239 24
        }
240
241 24
        if ( ! $options['show_instance'] && isset($this->cache['array'])) {
242 2
            return $this->cache['array'];
243
        }
244
245
        $array = [
246 24
            $options['show_instance'] ? $this->getInstanceId() : self::operator,
247 24
            $this->getOperandAt(0) ? $this->getOperandAt(0)->toArray($options) : false,
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class JClaveau\LogicalFilter\Rule\AbstractRule as the method toArray() does only exist in the following sub-classes of JClaveau\LogicalFilter\Rule\AbstractRule: JClaveau\LogicalFilter\Rule\AboveOrEqualRule, JClaveau\LogicalFilter\Rule\AboveRule, JClaveau\LogicalFilter\Rule\AbstractAtomicRule, JClaveau\LogicalFilter\Rule\AndRule, JClaveau\LogicalFilter\Rule\BelowOrEqualRule, JClaveau\LogicalFilter\Rule\BelowRule, JClaveau\LogicalFilter\Rule\BetweenOrEqualBothRule, JClaveau\LogicalFilter\R...BetweenOrEqualLowerRule, JClaveau\LogicalFilter\R...BetweenOrEqualUpperRule, JClaveau\LogicalFilter\Rule\BetweenRule, JClaveau\LogicalFilter\Rule\EqualRule, JClaveau\LogicalFilter\Rule\InRule, JClaveau\LogicalFilter\Rule\NotEqualRule, JClaveau\LogicalFilter\Rule\NotInRule, JClaveau\LogicalFilter\Rule\NotRule, JClaveau\LogicalFilter\Rule\OrRule, JClaveau\LogicalFilter\Rule\RegexpRule. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
248 24
        ];
249
250
        // TODO make a dedicated cache entry for semantic array?
251 24
        if ( ! $options['show_instance'] && ! $options['semantic']) {
252 13
            return $this->cache['array'] = $array;
253
        }
254
        else {
255 24
            return $array;
256
        }
257
    }
258
259
    /**
260
     */
261 1
    public function toString(array $options=[])
262
    {
263 1
        $operator = self::operator;
264 1
        if ( ! $this->operands) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->operands of type JClaveau\LogicalFilter\Rule\AbstractRule[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
265
            return "['{$operator}']";
266
        }
267
268 1
        $indent_unit = isset($options['indent_unit']) ? $options['indent_unit'] : '';
269 1
        $line_break  = $indent_unit ? "\n" : '';
270
271 1
        $out = "['{$operator}',"
272
            . $line_break
273 1
            . ($indent_unit ? : ' ')
274 1
            . str_replace($line_break, $line_break.$indent_unit, $this->getOperandAt(0)->toString($options))
0 ignored issues
show
Bug introduced by
The method toString() does not exist on JClaveau\LogicalFilter\Rule\AbstractRule. Did you maybe mean __toString()?

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...
275 1
            . ','
276 1
            . $line_break
277 1
            . ']'
278 1
            ;
279
280 1
        return $out;
281
    }
282
283
    /**
284
     * This method is meant to be used during simplification that would
285
     * need to change the class of the current instance by a normal one.
286
     *
287
     * @return OrRule The current instance (of or or subclass) or a new OrRule
288
     */
289 14
    public function setOperandsOrReplaceByOperation($new_operands, array $contextual_options)
290
    {
291 14
        if (count($new_operands) > 1) {
292 1
            foreach ($new_operands as &$new_operand) {
293 1
                if ($new_operand instanceof AbstractRule) {
294 1
                    $new_operand = $new_operand->toString();
0 ignored issues
show
Bug introduced by
The method toString() does not exist on JClaveau\LogicalFilter\Rule\AbstractRule. Did you maybe mean __toString()?

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...
295 1
                }
296 1
            }
297
298 1
            throw new \InvalidArgumentException(
299
                "Negations can handle only one operand instead of: \n"
300 1
                .var_export($new_operands, true)
301 1
            );
302
        }
303
304 14
        $new_operand = reset($new_operands);
305
        // $new_operand->dump();
306
307 14
        if ($new_operand instanceof NotRule) {
308 1
            $operands = $new_operand->getOperands();
309 1
            return reset( $operands );
0 ignored issues
show
Comprehensibility Best Practice introduced by
The expression reset($operands); of type JClaveau\LogicalFilter\Rule\AbstractRule|false adds false to the return on line 309 which is incompatible with the return type documented by JClaveau\LogicalFilter\R...ndsOrReplaceByOperation of type JClaveau\LogicalFilter\Rule\OrRule. It seems like you forgot to handle an error condition.
Loading history...
310
        }
311 14
        elseif ($new_operand instanceof EqualRule && ! $this->getOption('not_equal.normalization', $contextual_options)) {
312 6
            return new NotEqualRule( $new_operand->getField(), $new_operand->getValue(), $this->options );
0 ignored issues
show
Bug Best Practice introduced by
The return type of return new \JClaveau\Log...lue(), $this->options); (JClaveau\LogicalFilter\Rule\NotEqualRule) is incompatible with the return type documented by JClaveau\LogicalFilter\R...ndsOrReplaceByOperation of type JClaveau\LogicalFilter\Rule\OrRule.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
313
        }
314 10
        elseif ($new_operand instanceof InRule && ! $this->getOption('not_in.normalization', $contextual_options)) {
315 2
            return new NotInRule( $new_operand->getField(), $new_operand->getPossibilities(), $this->options );
0 ignored issues
show
Unused Code introduced by
The call to NotInRule::__construct() has too many arguments starting with $this->options.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
Bug Best Practice introduced by
The return type of return new \JClaveau\Log...ies(), $this->options); (JClaveau\LogicalFilter\Rule\NotInRule) is incompatible with the return type documented by JClaveau\LogicalFilter\R...ndsOrReplaceByOperation of type JClaveau\LogicalFilter\Rule\OrRule.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
316
        }
317
318
        try {
319
            // Don't use addOperand here to allow inheritance for optimizations (e.g. NotInRule)
320 9
            $out = $this->setOperands( $new_operands );
321
        }
322 9
        catch (\LogicException $e) {
323
            $out = new NotRule( $new_operand );
324
        }
325
326
        // $out->dump();
327
328 9
        return $out;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $out; (JClaveau\LogicalFilter\Rule\NotRule) is incompatible with the return type documented by JClaveau\LogicalFilter\R...ndsOrReplaceByOperation of type JClaveau\LogicalFilter\Rule\OrRule.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
329
    }
330
331
    /**
332
     *
333
     */
334
    public function hasSolution(array $contextual_options=[])
335
    {
336
        $operand = $this->getOperandAt(0);
337
338
        return $operand instanceof AbstractAtomicRule ? ! $operand->hasSolution($contextual_options) : true;
339
    }
340
341
    /**/
342
}
343