Logical::executeLogicStatement()   A
last analyzed

Complexity

Conditions 4
Paths 8

Size

Total Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 19
rs 9.6333
c 0
b 0
f 0
cc 4
nc 8
nop 3
1
<?php namespace Cornford\Logical;
2
3
use Cornford\Logical\Contracts\LogicalInterface;
4
use Cornford\Logical\Exceptions\LogicalDecodingException;
5
use Cornford\Logical\Exceptions\LogicalExecutionException;
6
use Cornford\Logical\Exceptions\LogicalFieldValueException;
7
use Exception;
8
use function GuzzleHttp\Psr7\str;
9
10
class Logical extends LogicalAbstract implements LogicalInterface
11
{
12
    /**
13
     * The temporary logic method.
14
     *
15
     * @var string
16
     */
17
    protected $tempLogic;
18
19
    /**
20
     * The temporary logic method.
21
     *
22
     * @var string
23
     */
24
    protected $tempMethod;
25
26
    /**
27
     * The temporary logic expected.
28
     *
29
     * @var string|array
30
     */
31
    protected $tempExpected;
32
33
    /**
34
     * The temporary logic field.
35
     *
36
     * @var string
37
     */
38
    protected $tempField;
39
40
    /**
41
     * The temporary results.
42
     *
43
     * @var array
44
     */
45
    protected $tempResults;
46
47
    /**
48
     * Decode the logic string into an array of logical statements.
49
     *
50
     * @return boolean
51
     */
52
    protected function decodeLogicStatements()
53
    {
54
        if (!$this->getLogic()) {
55
            return true;
56
        }
57
58
        $orStatements = preg_split("/.OR./", $this->getLogic());
59
60
        foreach ($orStatements as $orKey => $orStatement) {
61
            $andStatements = preg_split("/.AND./", $orStatement);
62
63
            foreach ($andStatements as $andStatement) {
64
                $items = preg_split("/\./", $andStatement, 2);
65
                $decodedStatement = [];
66
67
                foreach ($items as $item) {
68
                    $matches = null;
69
                    if (stristr($item, 'where')) {
70
                        if (!preg_match("/\((?<field>.*)\)/", $item, $matches)) {
71
                            return false;
72
                        }
73
74
                        $this->tempField = trim(trim($matches['field'], '\''), '"');
75
76
                        continue;
77
                    }
78
79
                    if (!preg_match("/^(?<method>[a-zA-Z]{1,})\((?<expected>[\'\"]*.*[\'\"]*)\)/", $item, $matches)) {
80
                        return false;
81
                    } else {
82
                        $this->tempMethod = trim(trim($matches['method'], '\''), '"');
83
                        $this->tempExpected = trim(trim($matches['expected'], '\''), '"');
84
                    }
85
86
                    if (!is_array($this->tempExpected) &&
87
                        strpos($this->tempExpected, '[') !== 0 &&
88
                        strpos($this->tempExpected, ']') !== (strlen($this->tempExpected) - 1) &&
89
                        stristr($this->tempExpected, ',') &&
90
                        !stristr($this->tempExpected, '{') &&
91
                        !stristr($this->tempExpected, '}')
92
                    ) {
93
                        $this->tempExpected = explode(', ', $this->tempExpected);
94
95 View Code Duplication
                        if (is_array($this->tempExpected)) {
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...
96
                            foreach ($this->tempExpected as &$expected) {
97
                                $expected = trim(trim($expected, '\''), '"');
98
                            }
99
                        }
100
                    }
101
102
                    if (!is_array($this->tempExpected) &&
103
                        strpos($this->tempExpected, '[') === 0 &&
104
                        strpos($this->tempExpected, ']') === (strlen($this->tempExpected) - 1) &&
105
                        !stristr($this->tempExpected, '{') &&
106
                        !stristr($this->tempExpected, '}')
107
                    ) {
108
                        $this->tempExpected = json_decode($this->tempExpected, false);
109
110 View Code Duplication
                        if (is_array($this->tempExpected)) {
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...
111
                            foreach ($this->tempExpected as &$expected) {
112
                                $expected = trim(trim($expected, '\''), '"');
113
                            }
114
                        }
115
                    }
116
117
                    if (!is_array($this->tempExpected) &&
118
                        stristr($this->tempExpected, '{') &&
119
                        stristr($this->tempExpected, '}')
120
                    ) {
121
                        $this->tempExpected = eval(str_replace('{', 'return (', str_replace('}', ');', $this->tempExpected)));
122
                    } elseif (is_array($this->tempExpected)) {
123
                        foreach ($this->tempExpected as &$expected) {
124
                            if (stristr($expected, '{') && stristr($expected, '}')) {
125
                                $expected = eval(str_replace('{', 'return (', str_replace('}', ');', $expected)));
126
                            }
127
                        }
128
                    }
129
130
                    $decodedStatement[$orKey]['method'] = $this->tempMethod;
131
                    $decodedStatement[$orKey]['expected'] = $this->tempExpected;
132
                    $decodedStatement[$orKey]['field'] = $this->tempField;
133
                }
134
135
                $this->setDecodedLogicStatement($decodedStatement);
136
            }
137
        }
138
139
        return true;
140
    }
141
142
    /**
143
     * Execute a logic statement method on an input value against an expected value.
144
     *
145
     * @param string                 $method
146
     * @param string|integer|boolean $input
147
     * @param string|integer|boolean $expected
148
     *
149
     * @throws LogicalExecutionException
150
     *
151
     * @return boolean
152
     */
153
    protected function executeLogicStatement($method, $input, $expected = null)
154
    {
155
        $logicalStatementInstance = $this->getLogicalStatementInstance();
156
        $result = null;
157
158
        if (method_exists($logicalStatementInstance, $method)) {
159
            $result = $logicalStatementInstance->$method($input, $expected);
160
        }
161
162
        if ($logicalStatementInstance->customStatementExists($method)) {
163
            $result = $logicalStatementInstance->callCustomStatement($method, $input, $expected);
164
        }
165
166
        if (is_null($result)) {
167
            throw new LogicalExecutionException("Unable to execute logic statement method: {$method}().");
168
        }
169
170
        return $result;
171
    }
172
173
    /**
174
     * Decodes logic input into statements and executes each statement removing un-matching results.
175
     *
176
     * @throws LogicalDecodingException
177
     * @throws LogicalFieldValueException
178
     * @throws LogicalExecutionException
179
     *
180
     * @return self
181
     */
182
    public function execute()
183
    {
184
        if (!$this->decodeLogicStatements()) {
185
            throw new LogicalDecodingException('Unable to decode logic input.');
186
        }
187
188
        foreach ($this->getDecodedLogicStatements() as $orStatement) {
189
            $this->tempResults = $this->getInput();
190
191
            foreach ($this->getInput() as $key => $input) {
192
                foreach ($orStatement as $andStatement) {
193
                    switch (gettype($input)) {
194
                        case 'object':
195
                            break;
196
                        case 'array':
197
                        default:
198
                            $andStatement['field'] = '[' . $andStatement['field'] . ']';
199
                    }
200
201
                    try {
202
                        $value = $this->getPropertyAccessorInstance()->getValue($input, $andStatement['field']);
203
                    } catch (Exception $exception) {
204
                        throw new LogicalFieldValueException('Unable to locate logical statement field value.');
205
                    }
206
207
                    if (!isset($value)) {
208
                        throw new LogicalFieldValueException('Unable to locate logical statement field value.');
209
                    }
210
211
                    if (!$this->executeLogicStatement($andStatement['method'], $value, $andStatement['expected'])) {
212
                        unset($this->tempResults[$key]);
213
                    }
214
                }
215
            }
216
217
            $this->mergeResults($this->tempResults);
218
        }
219
220
        return $this;
221
    }
222
}
223