Test Failed
Push — master ( f51294...9c5f94 )
by Sebastian
03:08
created

Mailcode_Commands_LogicKeywords::createKeyword()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 19
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 10
nc 3
nop 3
dl 0
loc 19
rs 9.9332
c 1
b 0
f 0
1
<?php
2
/**
3
 * File containing the {@see Mailcode_Commands_Command} class.
4
 *
5
 * @package Mailcode
6
 * @subpackage Commands
7
 * @see Mailcode_Commands_Command
8
 */
9
10
declare(strict_types=1);
11
12
namespace Mailcode;
13
14
use AppUtils\ConvertHelper;
15
use AppUtils\OperationResult;
16
17
/**
18
 * Handles parsing logic keywords in commands, if any.
19
 *
20
 * @package Mailcode
21
 * @subpackage Commands
22
 * @author Sebastian Mordziol <[email protected]>
23
 * 
24
 * @property Mailcode_Commands_Command $subject
25
 */
26
class Mailcode_Commands_LogicKeywords extends OperationResult
27
{
28
    const ERROR_CANNOT_APPEND_INVALID_KEYWORD = 60501;
29
    
30
    const VALIDATION_CANNOT_MIX_LOGIC_KEYWORDS = 60701;
31
    const VALIDATION_INVALID_SUB_COMMAND = 60702;
32
    
33
   /**
34
    * @var string
35
    */
36
    private $paramsString;
37
    
38
   /**
39
    * @var string[]
40
    */
41
    private $names = array(
42
        'and', 
43
        'or'
44
    );
45
    
46
   /**
47
    * @var Mailcode_Commands_LogicKeywords_Keyword[]
48
    */
49
    private $keywords = array();
50
    
51
   /**
52
    * @var string
53
    */
54
    private $mainParams = '';
55
    
56
    public function __construct(Mailcode_Commands_Command $command, string $paramsString)
57
    {
58
        parent::__construct($command);
59
        
60
        $this->paramsString = $paramsString;
61
        
62
        $this->parse();
63
        $this->validate();
64
    }
65
    
66
    public function getCommand() : Mailcode_Commands_Command
67
    {
68
        return $this->subject;
69
    }
70
    
71
    private function parse() : void
72
    {
73
        foreach($this->names as $name)
74
        {
75
            if(!stristr($this->paramsString, $name))
76
            {
77
                continue;
78
            }
79
            
80
            $this->keywords = array_merge(
81
                $this->keywords, 
82
                $this->detectKeywords($name)
83
            );
84
        }
85
    }
86
    
87
    private function validate() : void
88
    {
89
        $names = $this->getDetectedNames();
90
        $amount = count($names);
91
        
92
        if($amount > 1)
93
        {
94
            $this->makeError(
95
                t(
96
                    'Cannot mix the logical keywords %1$s:',
97
                    ConvertHelper::implodeWithAnd($names, ', ', ' '.t('and').' ')
98
                ).' '.
99
                t('Only one keyword may be used within the same command.'),
100
                self::VALIDATION_CANNOT_MIX_LOGIC_KEYWORDS
101
            );
102
            
103
            return;
104
        }
105
        
106
        $this->splitParams();
107
    }
108
    
109
    private function splitParams() : void
110
    {
111
        if(empty($this->keywords))
112
        {
113
            $this->mainParams = $this->paramsString;
114
            
115
            return;
116
        }
117
        
118
        $params = $this->detectParameters();
119
        
120
        foreach($this->keywords as $keyword)
121
        {
122
            $kParams = array_shift($params);
123
            
124
            $keyword->setParamsString($kParams);
125
            
126
            if(!$keyword->isValid())
127
            {
128
                $this->makeError(
129
                    t('Error #%1$s:', $keyword->getCode()).' '.$keyword->getErrorMessage(),
130
                    self::VALIDATION_INVALID_SUB_COMMAND
131
                );
132
                
133
                return;
134
            }
135
        }
136
    }
137
    
138
    private function detectParameters() : array
139
    {
140
        $params = $this->paramsString;
141
        $stack = array();
142
        
143
        foreach($this->keywords as $keyword)
144
        {
145
            $search = $keyword->getMatchedString();
146
            $pos = strpos($params, $search);
147
            $length = strlen($search);
148
            
149
            $store = substr($params, 0, $pos);
150
            $params = trim(substr($params, $pos+$length));
151
            
152
            $stack[] = $store;
153
        }
154
        
155
        $stack[] = $params;
156
        
157
        $this->mainParams = array_shift($stack);
158
        
159
        return $stack;
160
    }
161
    
162
   /**
163
    * Extracts the parameters string to use for the 
164
    * original command itself, omitting all the logic
165
    * keywords for the sub-commands.
166
    * 
167
    * @return string
168
    */
169
    public function getMainParamsString() : string
170
    {
171
        return $this->mainParams;
172
    }
173
    
174
   /**
175
    * Retrieves the detected keyword names.
176
    * @return string[]
177
    */
178
    public function getDetectedNames() : array
179
    {
180
        $names = array();
181
        
182
        foreach($this->keywords as $keyword)
183
        {
184
            $name = $keyword->getName();
185
            
186
            if(!in_array($name, $names))
187
            {
188
                $names[] = $name;
189
            }
190
        }
191
        
192
        return $names;
193
    }
194
    
195
   /**
196
    * Retrieves all keywords that were detected in the
197
    * command's parameters string, if any.
198
    * 
199
    * @return Mailcode_Commands_LogicKeywords_Keyword[]
200
    */
201
    public function getKeywords() : array
202
    {
203
        return $this->keywords;
204
    }
205
    
206
   /**
207
    * Detects any keyword statements in the parameters by keyword name.
208
    * 
209
    * @param string $name
210
    * @return Mailcode_Commands_LogicKeywords_Keyword[]
211
    */
212
    private function detectKeywords(string $name) : array
213
    {
214
        $regex = sprintf('/%1$s\s+([a-z\-0-9]+):|%1$s:/x', $name);
215
        
216
        $matches = array();
217
        preg_match_all($regex, $this->paramsString, $matches, PREG_PATTERN_ORDER);
218
        
219
        if(!isset($matches[0][0]) || empty($matches[0][0]))
220
        {
221
            return array();
222
        }
223
        
224
        $amount = count($matches[0]);
225
        
226
        for($i=0; $i < $amount; $i++)
227
        {
228
            $result[] = $this->createKeyword(
229
                $name, 
230
                $matches[1][$i],
231
                $matches[0][$i] 
232
            );
233
        }
234
        
235
        return $result;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $result does not seem to be defined for all execution paths leading up to this point.
Loading history...
236
    }
237
    
238
    public function hasKeywords() : bool
239
    {
240
        return !empty($this->keywords);
241
    }
242
    
243
    public function appendAND(string $paramsString, string $type='') : Mailcode_Commands_LogicKeywords_Keyword
244
    {
245
        return $this->appendKeyword('and', $paramsString, $type);
246
    }
247
    
248
    public function appendOR(string $paramsString, string $type='') : Mailcode_Commands_LogicKeywords_Keyword
249
    {
250
        return $this->appendKeyword('or', $paramsString, $type);
251
    }
252
    
253
    public function appendKeyword(string $name, string $paramsString, string $type='') : Mailcode_Commands_LogicKeywords_Keyword
254
    {
255
        $keyword = $this->createKeyword($name, $type);
256
        $keyword->setParamsString($paramsString);
257
        
258
        if(!$keyword->isValid())
259
        {
260
            throw new Mailcode_Exception(
261
                'Cannot append invalid logic keyword',
262
                sprintf(
263
                    'The keyword [%s] cannot be added with parameters [%s] and type [%s]: it is invalid. Validation details: #%s %s',
264
                    $name,
265
                    $paramsString,
266
                    $type,
267
                    $keyword->getCode(),
268
                    $keyword->getErrorMessage()
269
                ),
270
                self::ERROR_CANNOT_APPEND_INVALID_KEYWORD
271
            );
272
        }
273
        
274
        $this->keywords[] = $keyword;
275
        
276
        return $keyword;
277
    }
278
    
279
    private function createKeyword(string $name, string $type='', string $matchedString='') : Mailcode_Commands_LogicKeywords_Keyword
280
    {
281
        if(empty($matchedString))
282
        {
283
            $matchedString = $name;
284
            
285
            if(!empty($type))
286
            {
287
                $matchedString .= ' '.$type;
288
            }
289
            
290
            $matchedString .= ':';
291
        }
292
        
293
        return new Mailcode_Commands_LogicKeywords_Keyword(
294
            $this,
295
            $name,
296
            $matchedString,
297
            $type
298
        );
299
    }
300
}
301