Mailcode_Commands::resolveClassName()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 17
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 7
nc 3
nop 2
dl 0
loc 17
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * File containing the {@see \Mailcode\Mailcode_Commands} class.
4
 *
5
 * @package Mailcode
6
 * @subpackage Commands
7
 * @see \Mailcode\Mailcode_Commands
8
 */
9
10
declare(strict_types=1);
11
12
namespace Mailcode;
13
14
use AppUtils\FileHelper;
15
16
/**
17
 * Mailcode commands repository: factory for command instances,
18
 * and for fetching command information.
19
 *
20
 * @package Mailcode
21
 * @subpackage Commands
22
 * @author Sebastian Mordziol <[email protected]>
23
 */
24
class Mailcode_Commands
25
{
26
    public const ERROR_COMMAND_NAME_DOES_NOT_EXIST = 45901;
27
    public const ERROR_COMMAND_DOES_NOT_EXIST = 45902;
28
    public const ERROR_INVALID_DUMMY_COMMAND_TYPE = 45903;
29
    public const ERROR_INVALID_COMMAND_CLASS = 45904;
30
    
31
   /**
32
    * @var Mailcode_Commands_Command[]
33
    */
34
    private array $commands = array();
35
    
36
   /**
37
    * @var array<string,Mailcode_Commands_Command>
38
    */
39
    private static array $dummyCommands = array();
40
    
41
   /**
42
    * Retrieves a list of all available command IDs.
43
    * 
44
    * @return string[]
45
    */
46
    public function getIDs() : array
47
    {
48
        static $ids = array();
49
        
50
        if(empty($ids)) {
51
            $ids = FileHelper::createFileFinder(__DIR__.'/Commands/Command')
0 ignored issues
show
Deprecated Code introduced by
The function AppUtils\FileHelper\FileFinder::getPHPClassNames() has been deprecated: Use {@see self::getFiles()} > {@see FileCollector::PHPClassNames()} instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

51
            $ids = /** @scrutinizer ignore-deprecated */ FileHelper::createFileFinder(__DIR__.'/Commands/Command')

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

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

Loading history...
52
            ->getPHPClassNames();
53
        }
54
        
55
        return $ids;
56
    }
57
58
    /**
59
     * Retrieves a list of all available commands, sorted by label.
60
     *
61
     * NOTE: These instances are only used for information purposes.
62
     *
63
     * @return Mailcode_Commands_Command[]
64
     * @throws Mailcode_Factory_Exception
65
     */
66
    public function getAll() : array
67
    {
68
        if(!empty($this->commands)) {
69
            return $this->commands;
70
        }
71
        
72
        $ids = $this->getIDs();
73
        
74
        $result = array();
75
        
76
        foreach($ids as $id) 
77
        {
78
            $result[] = $this->getDummyCommand($id);
79
        }
80
        
81
        usort($result, static function(Mailcode_Commands_Command $a, Mailcode_Commands_Command $b)
82
        {
83
            return strnatcasecmp($a->getLabel(), $b->getLabel());
84
        });
85
        
86
        $this->commands = $result; 
87
        
88
        return $result;
89
    }
90
    
91
   /**
92
    * Gets an available command by its ID.
93
    * 
94
    * @param string $id
95
    * @return Mailcode_Commands_Command
96
    */
97
    public function getByID(string $id) : Mailcode_Commands_Command
98
    {
99
        static $instances = array();
100
        
101
        if(!isset($instances[$id])) 
102
        {
103
            $instances[$id] = $this->createCommand($id, '__dummy', '', '');
104
        }
105
        
106
        return $instances[$id];
107
    }
108
    
109
   /**
110
    * Retrieves the ID of a command by its name.
111
    * 
112
    * @param string $name
113
    * @throws Mailcode_Exception
114
    * @return string
115
    * 
116
    * @see Mailcode_Commands::ERROR_COMMAND_NAME_DOES_NOT_EXIST
117
    */
118
    public function getIDByName(string $name) : string
119
    {
120
        $items = $this->getAll();
121
        
122
        foreach($items as $item) 
123
        {
124
            if($item->getName() === $name) {
125
                return $item->getID();
126
            }
127
        }
128
        
129
        throw new Mailcode_Exception(
130
            'No such command name',
131
            sprintf(
132
                'The command name [%1$s] does not exist.',
133
                $name
134
            ),
135
            self::ERROR_COMMAND_NAME_DOES_NOT_EXIST
136
        );
137
    }
138
    
139
    public function idExists(string $id) : bool
140
    {
141
        $ids = $this->getIDs();
142
        
143
        return in_array($id, $ids);
144
    }
145
146
    /**
147
     * Checks whether the specified name exists.
148
     *
149
     * @param string $name For example: "showvar".
150
     * @return bool
151
     * @throws Mailcode_Factory_Exception
152
     */
153
    public function nameExists(string $name) : bool
154
    {
155
        $items = $this->getAll();
156
        
157
        foreach($items as $item)
158
        {
159
            if($item->getName() === $name) {
160
                return true;
161
            }
162
        }
163
        
164
        return false;
165
    }
166
167
    /**
168
     * @param string $id The command ID, e.g. `ShowVar`.
169
     * @param string $type The command's subtype, e.g. `variable` for the `if variable` command.
170
     * @param string $params The parameter string
171
     * @param string $matchedString The original matched string of the parsed command.
172
     * @return Mailcode_Commands_Command
173
     * @throws Mailcode_Factory_Exception
174
     */
175
    public function createCommand(string $id, string $type, string $params, string $matchedString) : Mailcode_Commands_Command
176
    {
177
        $class = $this->resolveClassName($id, $type);
178
        
179
        if(!class_exists($class))
180
        {
181
            throw new Mailcode_Factory_Exception(
182
                'No such command',
183
                sprintf(
184
                    'The command ID [%1$s] does not exist, class [%2$s] not found.',
185
                    $id,
186
                    $class
187
                ),
188
                self::ERROR_COMMAND_DOES_NOT_EXIST
189
            );
190
        }
191
        
192
        $command = new $class($type, $params, $matchedString);
193
194
        if($command instanceof Mailcode_Commands_Command)
195
        {
196
            return $command;
197
        }
198
199
        throw new Mailcode_Factory_Exception(
200
            'Invalid command class',
201
            sprintf(
202
                'The class [%s] does not extend the base command class.',
203
                $class
204
            ),
205
            self::ERROR_INVALID_COMMAND_CLASS
206
        );
207
    }
208
    
209
    protected function resolveClassName(string $id, string $type) : string
210
    {
211
        $class = 'Mailcode\Mailcode_Commands_Command_'.$id;
212
        
213
        $dummy = $this->getDummyCommand($id);
214
        
215
        if($dummy->supportsType())
216
        {
217
            if(empty($type))
218
            {
219
                $type = $dummy->getDefaultType();
220
            }
221
            
222
            $class .= '_'.$this->adjustTypeName($type);
223
        }
224
        
225
        return $class;
226
    }
227
    
228
   /**
229
    * Translates the command type to the expected class naming scheme.
230
    * 
231
    * Example: not-empty => NotEmpty
232
    * 
233
    * @param string $type
234
    * @return string
235
    */
236
    private function adjustTypeName(string $type) : string
237
    {
238
        $type = str_replace('-', ' ', $type);
239
        $type = ucwords($type);
240
        $type = str_replace(' ', '', $type);
241
        
242
        return $type;
243
    }
244
245
    /**
246
     * Retrieves the dummy command of the specified type, which
247
     * is used to retrieve information on the command's capabilities.
248
     *
249
     * @param string $id
250
     * @return Mailcode_Commands_Command
251
     * @throws Mailcode_Factory_Exception
252
     */
253
    private function getDummyCommand(string $id) : Mailcode_Commands_Command
254
    {
255
        if(isset(self::$dummyCommands[$id])) {
256
            return self::$dummyCommands[$id];
257
        }
258
259
        $class = 'Mailcode\Mailcode_Commands_Command_'.$id;
260
        $cmd = new $class('__dummy');
261
262
        if($cmd instanceof Mailcode_Commands_Command)
263
        {
264
            self::$dummyCommands[$id] = $cmd;
265
            return $cmd;
266
        }
267
        
268
        throw new Mailcode_Factory_Exception(
269
            'Invalid dummy command type',
270
            sprintf('The stored variable type is %1$s.', gettype(self::$dummyCommands[$id])),
271
            self::ERROR_INVALID_DUMMY_COMMAND_TYPE
272
        );
273
    }
274
275
    /**
276
     * Retrieves all commands that can contain content
277
     * that is not parsed by the main parsing process.
278
     *
279
     * @return Mailcode_Interfaces_Commands_ProtectedContent[]
280
     * @throws Mailcode_Exception
281
     */
282
    public function getContentCommands() : array
283
    {
284
        $result = array();
285
        $commands = $this->getAll();
286
287
        foreach($commands as $command)
288
        {
289
            if($command instanceof Mailcode_Interfaces_Commands_ProtectedContent)
290
            {
291
                $result[] = $command;
292
            }
293
        }
294
295
        return $result;
296
    }
297
}
298