Completed
Push — master ( 1e37f2...6efb31 )
by Maxim
05:50 queued 02:42
created

ConditionsRunner::runStandardCondition()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 2
dl 0
loc 8
ccs 6
cts 6
cp 1
crap 2
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * This file is a part of "Axessors" library.
4
 *
5
 * @author <[email protected]>
6
 * @license GPL
7
 */
8
9
namespace NoOne4rever\Axessors;
10
11
use NoOne4rever\Axessors\Exceptions\TypeError;
12
13
/**
14
 * Class ConditionsSuit.
15
 * 
16
 * Processes Axessors conditions.
17
 * 
18
 * @package NoOne4rever\Axessors
19
 */
20
class ConditionsRunner extends RunningSuit
21
{
22
    /** @var string method name */
23
    private $method;
24
25
    /**
26
     * ConditionsSuit constructor.
27
     * 
28
     * @param int $mode mode of execution
29
     * @param PropertyData $data property data
30
     * @param string $class class
31
     * @param string $method method name
32
     * @param object|null $object object
33
     */
34 43
    public function __construct(int $mode, PropertyData $data, string $class, string $method, $object = null)
35
    {
36 43
        parent::__construct($mode, $data, $class, $object);
37 43
        $this->method = $method;
38 43
    }
39
40
    /**
41
     * Checks the conditions defined in the Axessors comment.
42
     *
43
     * Creates logical tree of the conditions and then checks if the general result is true.
44
     *
45
     * @param $value mixed value of the property
46
     * @return bool result of checking of the conditions
47
     */
48 43
    public function processConditions($value): bool
49
    {
50 43
        $conditions = $this->calculateConditions($value);
51 41
        if (empty($conditions)) {
52 9
            return true;
53
        } else {
54 32
            return array_reduce($conditions, function ($carry, $item) {
55 32
                return $this->reduce($carry, $item, true);
56 32
            });
57
        }
58
    }
59
60
    /**
61
     * Reduces conditions tree and returns the result.
62
     * 
63
     * @param mixed $carry previous item
64
     * @param mixed $item item
65
     * @param bool $mode mode
66
     * @return bool the result of reducing
67
     */
68 32
    private function reduce($carry, $item, bool $mode): bool 
69
    {
70 32
        if ($item === $mode || $carry === $mode) {
71 18
            return $mode;
72 14
        } elseif (is_array($item)) {
73
            return array_reduce($item, function ($carry, $item) {
74
                return $this->reduce($carry, $item, false);
75
            });
76
        } else {
77 14
            return !$mode;
78
        }
79
    }
80
81
    /**
82
     * Calculates every condition defined in the Axessors comment.
83
     *
84
     * @param $value mixed value of the property
85
     * @return bool[] results of the conditions
86
     */
87 43
    private function calculateConditions($value): array
88
    {
89 43
        $calculatedConditions = [];
90 43
        $conditions = $this->mode == RunningSuit::OUTPUT_MODE ? $this->propertyData->getOutputConditions() : $this->propertyData->getInputConditions();
91 43
        foreach ($conditions as $number => $complexCondition) {
92 34
            if (is_array($complexCondition)) {
93
                foreach ($complexCondition as $condition) {
94
                    $calculatedConditions[$number][] = $this->executeCondition($condition, $value);
95
                }
96
            } else {
97 34
                $calculatedConditions[$number] = $this->executeCondition($complexCondition, $value);
98
            }
99
        }
100 41
        return $calculatedConditions;
101
    }
102
103
    /**
104
     * Calculates a condition defined in the Axessors comment.
105
     *
106
     * @param $condition string the condition
107
     * @param $value mixed value of the property
108
     * @return bool result of the condition
109
     */
110 34
    private function executeCondition(string $condition, $value): bool
111
    {
112 34
        if (strpos($condition, '`') !== false) {
113 11
            return $this->executeInjectedString($condition, $value, true);
114
        } else {
115 23
            return $this->runStandardCondition($condition, $value);
116
        }
117
    }
118
119
    /**
120
     * Runs Axessors condition.
121
     * 
122
     * @param string $condition condition
123
     * @param mixed $value value to check 
124
     * @return bool the result of the checkout
125
     */
126 23
    private function runStandardCondition(string $condition, $value): bool 
127
    {
128 23
        $value = $this->count($value);
129 22
        if (strpos($condition, '..') !== false) {
130 6
            $condition = explode('..', $condition);
131 6
            $condition = "<= {$condition[1]} && $value >= {$condition[0]}";
132
        }
133 22
        return eval("return $value $condition;");
134
    }
135
136
    /**
137
     * Casts the property to integer.
138
     *
139
     * If the property is string or array returns it's length.
140
     * If the property is integer of float returns the property itself.
141
     *
142
     * @param $value mixed value of the property
143
     * @return int integer value of the property
144
     * @throws TypeError if the property can't be turned into integer
145
     */
146 23
    private function count($value): int
147
    {
148 23
        switch (gettype($value)) {
149 23
            case 'integer':
150 7
            case 'float':
151 16
                break;
152 7
            case 'string':
153 5
                $value = strlen($value);
154 5
                break;
155 2
            case 'array':
156 1
                $value = count($value);
157 1
                break;
158
            default:
159 1
                throw new TypeError('value "' . var_export($value, true) . "\" passed to {$this->class}::{$this->method}() is not countable");
160
        }
161 22
        return $value;
162
    }
163
}