Test Setup Failed
Push — master ( e4f8c2...f9e497 )
by Sebastian
03:34
created

Mailcode_Collection::finalize()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 5
rs 10
c 1
b 0
f 0
1
<?php
2
/**
3
 * File containing the {@see Mailcode_Collection} class.
4
 *
5
 * @package Mailcode
6
 * @subpackage Collection
7
 * @see Mailcode_Collection
8
 */
9
10
declare(strict_types=1);
11
12
namespace Mailcode;
13
14
use AppUtils\OperationResult;
15
16
/**
17
 * Commands collection: container for commands.
18
 *
19
 * @package Mailcode
20
 * @subpackage Collection
21
 * @author Sebastian Mordziol <[email protected]>
22
 */
23
class Mailcode_Collection
24
{
25
    const ERROR_CANNOT_RETRIEVE_FIRST_ERROR = 52301;
26
    const ERROR_CANNOT_MODIFY_FINALIZED = 52302;
27
    
28
   /**
29
    * @var Mailcode_Commands_Command[]
30
    */
31
    protected $commands = array();
32
    
33
    /**
34
     * @var Mailcode_Collection_Error[]
35
     */
36
    protected $errors = array();
37
    
38
   /**
39
    * @var OperationResult|NULL
40
    */
41
    protected $validationResult;
42
43
    /**
44
     * @var bool
45
     */
46
    private $finalized = false;
47
48
    /**
49
    * Adds a command to the collection.
50
    * 
51
    * @param Mailcode_Commands_Command $command
52
    * @return Mailcode_Collection
53
    */
54
    public function addCommand(Mailcode_Commands_Command $command) : Mailcode_Collection
55
    {
56
        if($this->finalized)
57
        {
58
            throw new Mailcode_Exception(
59
                'Cannot add commands to a finalized collection',
60
                'When a collection has been finalized, it may not be modified anymore.',
61
                self::ERROR_CANNOT_MODIFY_FINALIZED
62
            );
63
        }
64
65
        $this->commands[] = $command;
66
67
        // reset the collection's validation result, since it
68
        // depends on the commands.
69
        $this->validationResult = null;
70
        
71
        return $this;
72
    }
73
    
74
   /**
75
    * Whether there are any commands in the collection.
76
    * 
77
    * @return bool
78
    */
79
    public function hasCommands() : bool
80
    {
81
        return !empty($this->commands);
82
    }
83
    
84
   /**
85
    * Counts the amount of commands in the collection.
86
    * 
87
    * @return int
88
    */
89
    public function countCommands() : int
90
    {
91
        return count($this->commands);
92
    }
93
94
    public function addErrorMessage(string $matchedText, string $message, int $code) : void
95
    {
96
        $this->errors[] = new Mailcode_Collection_Error_Message(
97
            $matchedText,
98
            $code,
99
            $message
100
        );
101
    }
102
    
103
    public function addInvalidCommand(Mailcode_Commands_Command $command) : void
104
    {
105
        // Remove the command in case it was already added
106
        $this->removeCommand($command);
107
108
        $this->errors[] = new Mailcode_Collection_Error_Command($command);
109
    }
110
111
    public function removeCommand(Mailcode_Commands_Command $command) : void
112
    {
113
        $keep = array();
114
115
        foreach($this->commands as $existing)
116
        {
117
            if($existing !== $command)
118
            {
119
                $keep[] = $existing;
120
            }
121
        }
122
123
        $this->commands = $keep;
124
    }
125
    
126
   /**
127
    * @return Mailcode_Collection_Error[]
128
    */
129
    public function getErrors()
130
    {
131
        $result = $this->getValidationResult();
132
        
133
        $errors = $this->errors;
134
        
135
        if(!$result->isValid())
136
        {
137
            $errors[] = new Mailcode_Collection_Error_Message(
138
                '',
139
                $result->getCode(),
140
                $result->getErrorMessage()
141
            );
142
        }
143
        
144
        return $errors;
145
    }
146
    
147
    public function getFirstError() : Mailcode_Collection_Error
148
    {
149
        $errors = $this->getErrors();
150
        
151
        if(!empty($errors))
152
        {
153
            return array_shift($errors);
154
        }
155
        
156
        throw new Mailcode_Exception(
157
            'Cannot retrieve first error: no errors detected',
158
            null,
159
            self::ERROR_CANNOT_RETRIEVE_FIRST_ERROR
160
        );
161
    }
162
    
163
    public function isValid() : bool
164
    {
165
        $errors = $this->getErrors();
166
        
167
        return empty($errors);
168
    }
169
    
170
   /**
171
    * Retrieves all commands that were detected, in the exact order
172
    * they were found.
173
    * 
174
    * @return \Mailcode\Mailcode_Commands_Command[]
175
    */
176
    public function getCommands()
177
    {
178
       return $this->commands;
179
    }
180
    
181
   /**
182
    * Retrieves all unique commands by their matched
183
    * string hash: this ensures only commands that were
184
    * written the exact same way (including spacing)
185
    * are returned.
186
    * 
187
    * @return \Mailcode\Mailcode_Commands_Command[]
188
    */
189
    public function getGroupedByHash()
190
    {
191
        $hashes = array();
192
        
193
        foreach($this->commands as $command)
194
        {
195
            $hash = $command->getHash();
196
            
197
            if(!isset($hashes[$hash]))
198
            {
199
                $hashes[$hash] = $command;
200
            }
201
        }
202
            
203
        return array_values($hashes);
204
    }
205
206
   /**
207
    * Adds several commands at once.
208
    * 
209
    * @param Mailcode_Commands_Command[] $commands
210
    * @return Mailcode_Collection
211
    */
212
    public function addCommands(array $commands) : Mailcode_Collection
213
    {
214
        foreach($commands as $command)
215
        {
216
            $this->addCommand($command);
217
        }
218
        
219
        return $this;
220
    }
221
    
222
    public function mergeWith(Mailcode_Collection $collection) : Mailcode_Collection
223
    {
224
        $merged = new Mailcode_Collection();
225
        $merged->addCommands($this->getCommands());
226
        $merged->addCommands($collection->getCommands());
227
228
        return $merged;
229
    }
230
    
231
    public function getVariables() : Mailcode_Variables_Collection
232
    {
233
        $collection = new Mailcode_Variables_Collection_Regular();
234
        
235
        foreach($this->commands as $command)
236
        {
237
            $collection->mergeWith($command->getVariables());
238
        }
239
        
240
        return $collection;
241
    }
242
    
243
    public function getValidationResult() : OperationResult
244
    {
245
        if($this->validationResult instanceof OperationResult)
246
        {
247
            return $this->validationResult;
248
        }
249
        
250
        $nesting = new Mailcode_Collection_NestingValidator($this);
251
        
252
        $this->validationResult = $nesting->validate(); 
253
        
254
        return $this->validationResult;
255
    }
256
    
257
    public function hasErrorCode(int $code) : bool
258
    {
259
        $errors = $this->getErrors();
260
        
261
        foreach($errors as $error)
262
        {
263
            if($error->getCode() === $code)
264
            {
265
                return true;
266
            }
267
        }
268
        
269
        return false;
270
    }
271
272
    /**
273
     * Retrieves only ShowXXX commands in the collection, if any.
274
     * Includes ShowVariable, ShowDate, ShowNumber, ShowSnippet.
275
     *
276
     * @return Mailcode_Commands_ShowBase[]
277
     */
278
    public function getShowCommands()
279
    {
280
        return $this->getCommandsByClass(Mailcode_Commands_ShowBase::class);
281
    }
282
283
    /**
284
    * Retrieves only show variable commands in the collection, if any.
285
    * 
286
    * @return Mailcode_Commands_Command_ShowVariable[]
287
    */
288
    public function getShowVariableCommands()
289
    {
290
        return $this->getCommandsByClass(Mailcode_Commands_Command_ShowVariable::class);
291
    }
292
293
    /**
294
     * @return Mailcode_Commands_Command_For[]
295
     */
296
    public function getForCommands()
297
    {
298
        return $this->getCommandsByClass(Mailcode_Commands_Command_For::class);
299
    }
300
301
   /**
302
    * Retrieves only show date commands in the collection, if any.
303
    *
304
    * @return Mailcode_Commands_Command_ShowDate[]
305
    */
306
    public function getShowDateCommands() : array
307
    {
308
        return $this->getCommandsByClass(Mailcode_Commands_Command_ShowDate::class);
309
    }
310
311
    /**
312
     * @param string $className
313
     * @return Mailcode_Commands_Command[]
314
     */
315
    public function getCommandsByClass(string $className) : array
316
    {
317
        $result = array();
318
319
        foreach($this->commands as $command)
320
        {
321
            if($command instanceof $className)
322
            {
323
                $result[] = $command;
324
            }
325
        }
326
327
        return $result;
328
    }
329
    
330
    public function getFirstCommand() : ?Mailcode_Commands_Command
331
    {
332
        $commands = $this->getCommands();
333
        
334
        if(!empty($commands))
335
        {
336
            return array_shift($commands);
337
        }
338
        
339
        return null;
340
    }
341
342
    public function finalize() : void
343
    {
344
        $this->finalized = true;
345
346
        $this->validateNesting();
347
    }
348
349
    public function isFinalized() : bool
350
    {
351
        return $this->finalized;
352
    }
353
354
    private function validateNesting() : void
355
    {
356
        foreach($this->commands as $command)
357
        {
358
            $command->validateNesting();
359
360
            if(!$command->isValid()) {
361
                $this->addInvalidCommand($command);
362
            }
363
        }
364
    }
365
}
366