CommandRegistry::handleCommandSpacing()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 16
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 10
c 1
b 0
f 0
nc 3
nop 1
dl 0
loc 16
rs 9.9332
1
<?php
2
/**
3
 * Livia
4
 * Copyright 2017-2019 Charlotte Dunois, All Rights Reserved
5
 *
6
 * Website: https://charuru.moe
7
 * License: https://github.com/CharlotteDunois/Livia/blob/master/LICENSE
8
*/
9
10
namespace CharlotteDunois\Livia;
11
12
/**
13
 * Handles registration and searching of commands and groups.
14
 *
15
 * @property \CharlotteDunois\Livia\Client             $client               The client which initiated the instance.
16
 * @property \CharlotteDunois\Collect\Collection       $commands             Registered commands, mapped by their name.
17
 * @property string[]                                  $commandsDirectories  List of fully resolved path to the bot's commands directories.
18
 * @property \CharlotteDunois\Collect\Collection       $groups               Registered command groups, mapped by their id.
19
 * @property \CharlotteDunois\Collect\Collection       $types                Registered argument types, mapped by their name.
20
 */
21
class CommandRegistry implements \Serializable {
22
    /**
23
     * The client which initiated the instance.
24
     * @var \CharlotteDunois\Livia\Client
25
     */
26
    protected $client;
27
    
28
    /**
29
     * The basepath commands directory of Livia.
30
     * @var string
31
     */
32
    protected $basepath;
33
    
34
    /**
35
     * Registered commands, mapped by their name.
36
     * @var \CharlotteDunois\Collect\Collection
37
     */
38
    protected $commands;
39
    
40
    /**
41
     * List of fully resolved path to the bot's commands directories.
42
     * @var string[]
43
     */
44
    protected $commandsDirectories = array();
45
    
46
    /**
47
     * Registered command groups, mapped by their id.
48
     * @var \CharlotteDunois\Collect\Collection
49
     */
50
    protected $groups;
51
    
52
    /**
53
     * Registered argument types, mapped by their name.
54
     * @var \CharlotteDunois\Collect\Collection
55
     */
56
    protected $types;
57
    
58
    /**
59
     * Used for serialization/unserialization of commands.
60
     * @var string[]
61
     */
62
    protected $internalSerializedCommands = array();
63
    
64
    /**
65
     * @internal
66
     */
67
    function __construct(\CharlotteDunois\Livia\Client $client) {
68
        $this->client = $client;
69
        $this->basepath = \realpath(__DIR__.'/Commands/');
70
        
71
        $this->commands = new \CharlotteDunois\Collect\Collection();
72
        $this->groups = new \CharlotteDunois\Collect\Collection();
73
        $this->types = new \CharlotteDunois\Collect\Collection();
74
    }
75
    
76
    /**
77
     * @param string  $name
78
     * @return bool
79
     * @throws \Exception
80
     * @internal
81
     */
82
    function __isset($name) {
83
        try {
84
            return $this->$name !== null;
85
        } catch (\RuntimeException $e) {
86
            if($e->getTrace()[0]['function'] === '__get') {
87
                return false;
88
            }
89
            
90
            throw $e;
91
        }
92
    }
93
    
94
    /**
95
     * @param string  $name
96
     * @return mixed
97
     * @throws \RuntimeException
98
     * @internal
99
     */
100
    function __get($name) {
101
        if(\property_exists($this, $name)) {
102
            return $this->$name;
103
        }
104
        
105
        throw new \RuntimeException('Unknown property '.\get_class($this).'::$'.$name);
106
    }
107
    
108
    /**
109
     * @return string
110
     * @internal
111
     */
112
    function serialize() {
113
        $vars = \get_object_vars($this);
114
        
115
        unset($vars['client']);
116
        
117
        $vars['internalSerializedCommands'] = $vars['commands']->map(function (\CharlotteDunois\Livia\Commands\Command $cmd) {
118
            return ($cmd->groupID.':'.$cmd->name);
119
        });
120
        $vars['commands'] = array();
121
        
122
        return \serialize($vars);
123
    }
124
    
125
    /**
126
     * Depends on Client for command re-registration.
127
     * @return void
128
     * @internal
129
     */
130
    function unserialize($vars) {
131
        if(\CharlotteDunois\Yasmin\Models\ClientBase::$serializeClient === null) {
132
            throw new \Exception('Unable to unserialize a class without ClientBase::$serializeClient being set');
133
        }
134
        
135
        $vars = \unserialize($vars);
136
        
137
        foreach($vars as $name => $val) {
138
            $this->$name = $val;
139
        }
140
        
141
        $this->client = \CharlotteDunois\Yasmin\Models\ClientBase::$serializeClient;
142
        $this->commands = new \CharlotteDunois\Collect\Collection();
143
    }
144
    
145
    /**
146
     * Finds all commands that match the search string.
147
     * @param string                                       $searchString
148
     * @param bool                                         $exact         Whether the search should be exact.
149
     * @param \CharlotteDunois\Yasmin\Models\Message|null  $message       The message to check usability against.
150
     * @return \CharlotteDunois\Livia\Commands\Command[]
151
     */
152
    function findCommands(string $searchString, bool $exact = false, \CharlotteDunois\Yasmin\Models\Message $message = null) {
153
        $parts = array();
154
        $searchString = \mb_strtolower($searchString);
155
        
156
        if(\mb_strpos($searchString, ':') !== false) {
157
            $parts = \explode(':', $searchString);
158
            $searchString = \array_pop($parts);
159
        }
160
        
161
        if($message !== null) {
162
            $cmdmsg = new \CharlotteDunois\Livia\Commands\Context($this->client, $message);
163
        }
164
        
165
        $matches = array();
166
        foreach($this->commands as $command) {
167
            if($exact) {
168
                if(!empty($parts[0]) && $parts[0] === $command->groupID && ($command->name === $searchString || \in_array($searchString, $command->aliases)) && ($message === null || $command->hasPermission($cmdmsg) === true)) {
169
                    return array($command);
170
                }
171
                
172
                if(($command->name === $searchString || \in_array($searchString, $command->aliases)) && ($message === null || $command->hasPermission($cmdmsg) === true)) {
173
                    $matches[] = $command;
174
                }
175
            } else {
176
                if(!empty($parts[0]) && $parts[0] === $command->groupID && \mb_stripos($command->name, $searchString) !== false && ($message === null || $command->hasPermission($cmdmsg) === true)) {
177
                    return array($command);
178
                }
179
                
180
                if(\mb_stripos($command->name, $searchString) !== false && ($message === null || $command->hasPermission($cmdmsg) === true)) {
181
                    $matches[] = $command;
182
                }
183
            }
184
        }
185
        
186
        if($exact) {
187
            return $matches;
188
        }
189
        
190
        foreach($matches as $command) {
191
            if($command->name === $searchString || \in_array($searchString, $command->aliases)) {
192
                return array($command);
193
            }
194
        }
195
        
196
        return $matches;
197
    }
198
    
199
    /**
200
     * Finds all commands that match the search string.
201
     * @param string   $searchString
202
     * @param bool     $exact         Whether the search should be exact.
203
     * @return \CharlotteDunois\Livia\Commands\CommandGroup[]
204
     */
205
    function findGroups(string $searchString, bool $exact = false) {
206
        $searchString = \mb_strtolower($searchString);
207
        if(\mb_strpos($searchString, ':') !== false) {
208
            $parts = \explode(':', $searchString);
209
            $searchString = \array_pop($parts);
210
        }
211
        
212
        $matches = array();
213
        foreach($this->groups as $group) {
214
            if($exact) {
215
                if($group->id === $searchString || \mb_strtolower($group->name) === $searchString) {
216
                    $matches[] = $group;
217
                }
218
            } else {
219
                if(\mb_stripos($group->id, $searchString) !== false || \mb_stripos($group->name, $searchString) !== false) {
220
                    $matches[] = $group;
221
                }
222
            }
223
        }
224
        
225
        if($exact) {
226
            return $matches;
227
        }
228
        
229
        foreach($matches as $group) {
230
            if($group->id === $searchString || \mb_strtolower($group->name) === $searchString) {
231
                return array($group);
232
            }
233
        }
234
        
235
        return $matches;
236
    }
237
    
238
    /**
239
     * Resolves a given command, command name or command message to the command.
240
     * @param string|\CharlotteDunois\Livia\Commands\Command|\CharlotteDunois\Livia\Commands\Context  $resolvable
241
     * @return \CharlotteDunois\Livia\Commands\Command
242
     * @throws \RuntimeException
243
     */
244
    function resolveCommand($resolvable) {
245
        if($resolvable instanceof \CharlotteDunois\Livia\Commands\Command) {
246
            return $resolvable;
247
        }
248
        
249
        if($resolvable instanceof \CharlotteDunois\Livia\Commands\Context) {
250
            return $resolvable->command;
251
        }
252
        
253
        $commands = $this->findCommands($resolvable, true);
254
        if(\count($commands) === 1) {
255
            return $commands[0];
256
        }
257
        
258
        throw new \RuntimeException('Unable to resolve command');
259
    }
260
    /**
261
     * Resolves a given commandgroup, command group name or command message to the command group.
262
     * @param string|\CharlotteDunois\Livia\Commands\CommandGroup|\CharlotteDunois\Livia\Commands\Context  $resolvable
263
     * @return \CharlotteDunois\Livia\Commands\CommandGroup
264
     * @throws \RuntimeException
265
     */
266
    function resolveGroup($resolvable) {
267
        if($resolvable instanceof \CharlotteDunois\Livia\Commands\CommandGroup) {
268
            return $resolvable;
269
        }
270
        
271
        if($resolvable instanceof \CharlotteDunois\Livia\Commands\Context) {
272
            return $resolvable->command->group;
273
        }
274
        
275
        $groups = $this->findGroups($resolvable, true);
276
        if(\count($groups) === 1) {
277
            return $groups[0];
278
        }
279
        
280
        throw new \RuntimeException('Unable to resolve command group');
281
    }
282
    
283
    /**
284
     * Registers a command. Emits a commandRegister event for each command.
285
     * @param string|\CharlotteDunois\Livia\Commands\Command  ...$command  The full qualified command name (`groupID:name`) or an initiated instance of it.
286
     * @return $this
287
     * @throws \RuntimeException
288
     * @throws \InvalidArgumentException
289
     */
290
    function registerCommand(...$command) {
291
        foreach($command as $cmd) {
292
            if(!($cmd instanceof \CharlotteDunois\Livia\Commands\Command)) {
293
                if(!\is_string($cmd)) {
294
                    throw new \InvalidArgumentException('Passed argument, '.\var_export($cmd, true).', is not a string or an instance of Command');
295
                }
296
                
297
                $oldCmd = $cmd;
298
                
299
                $cmd = $this->handleCommandSpacing($cmd);
300
                if(!\is_callable($cmd)) {
301
                    throw new \RuntimeException('Unable to resolve '.$oldCmd.' to an anonymous function');
302
                }
303
                
304
                $cmd = $cmd($this->client);
305
                if(!($cmd instanceof \CharlotteDunois\Livia\Commands\Command)) {
306
                    throw new \RuntimeException('Anonymous function in '.$oldCmd.' does not return an instance of Command');
307
                }
308
            }
309
            
310
            if($this->commands->has($cmd->name)) {
311
                throw new \RuntimeException('Can not register another command with the name '.$cmd->name);
312
            }
313
            
314
            $this->commands->set($cmd->name, $cmd);
315
            
316
            $group = $this->resolveGroup($cmd->groupID);
317
            if($group) {
318
                $group->commands->set($cmd->name, $cmd);
319
            }
320
            
321
            $this->client->emit('debug', 'Registered command '.$cmd->groupID.':'.$cmd->name);
322
            $this->client->emit('commandRegister', $cmd, $this);
323
        }
324
        
325
        return $this;
326
    }
327
    
328
    /**
329
     * Registers all commands in a directory. The path gets saved as commands path. Emits a commandRegister event for each command.
330
     * @param string        $path
331
     * @param bool|string   $ignoreSameLevelFiles  Ignores files in the specified directory and only includes files in sub directories. As string it will ignore the file if the filename matches with the string.
332
     * @return $this
333
     * @throws \RuntimeException
334
     */
335
    function registerCommandsIn(string $path, $ignoreSameLevelFiles = false) {
336
        $path = \realpath($path);
337
        if(!$path) {
338
            throw new \RuntimeException('Invalid path specified');
339
        }
340
        
341
        $this->addCommandsDirectory($path);
342
        $files = \CharlotteDunois\Livia\Utils\FileHelpers::recursiveFileSearch($path, '*.php');
343
        
344
        foreach($files as $file) {
345
            if($ignoreSameLevelFiles === true) {
346
                $filepath = \ltrim(\str_replace(array($path, '\\'), array('', '/'), $file), '/');
347
                if(\mb_substr_count($filepath, '/') === 0) {
348
                    continue;
349
                }
350
            } elseif(!empty($ignoreSameLevelFiles) && \mb_stripos($file, $ignoreSameLevelFiles) !== false) {
351
                continue;
352
            }
353
            
354
            $command = include $file;
355
            
356
            if(!\is_callable($command)) {
357
                throw new \RuntimeException('Command file '.\str_replace($path, '', $file).' does not return an anonymous function');
358
            }
359
            
360
            $cmd = $command($this->client);
361
            if(!($cmd instanceof \CharlotteDunois\Livia\Commands\Command)) {
362
                throw new \RuntimeException('Anonymous function in command file '.\str_replace($path, '', $file).' does not return an instance of Command');
363
            }
364
            
365
            if($this->commands->has($cmd->name)) {
366
                throw new \RuntimeException('Can not register another command with the name '.$cmd->name);
367
            }
368
            
369
            $this->commands->set($cmd->name, $cmd);
370
            
371
            $group = $this->resolveGroup($cmd->groupID);
372
            $group->commands->set($cmd->name, $cmd);
373
            
374
            $this->client->emit('debug', 'Registered command '.$cmd->groupID.':'.$cmd->name);
375
            $this->client->emit('commandRegister', $cmd, $this);
376
        }
377
        
378
        return $this;
379
    }
380
    
381
    /**
382
     * Registers a group. Emits a groupRegister event for each group.
383
     * @param \CharlotteDunois\Livia\Commands\CommandGroup|array  ...$group  An instance of CommandGroup or an associative array `[ 'id' => string, 'name' => string, 'guarded' => bool ]`. Guarded is optional, defaults to false.
384
     * @return $this
385
     * @throws \RuntimeException
386
     * @throws \InvalidArgumentException
387
     * @see \CharlotteDunois\Livia\Commands\CommandGroup
388
     */
389
    function registerGroup(...$group) {
390
        foreach($group as $gr) {
391
            $oldGr = $gr;
392
            
393
            if(!($gr instanceof \CharlotteDunois\Livia\Commands\CommandGroup)) {
394
                if(!\is_array($gr)) {
395
                    throw new \InvalidArgumentException('Argument, '.\var_export($gr, true).', is not of type array or an instance of CommandGroup');
396
                }
397
                
398
                if(empty($gr['id']) || empty($gr['name'])) {
399
                    throw new \InvalidArgumentException('Argument, '.\var_export($gr, true).', is missing at least one require element');
400
                }
401
                
402
                $gr = new \CharlotteDunois\Livia\Commands\CommandGroup($this->client, $gr['id'], $gr['name'], (bool) ($gr['guarded'] ?? false));
403
            }
404
            
405
            if(!($gr instanceof \CharlotteDunois\Livia\Commands\CommandGroup)) {
406
                throw new \RuntimeException(\var_export($oldGr, true).' is not an array, with id and name elements, or an instance of CommandGroup');
407
            }
408
            
409
            if($this->groups->has($gr->id)) {
410
                throw new \RuntimeException('Can not register another command group with the ID '.$gr->id);
411
            }
412
            
413
            $this->groups->set($gr->id, $gr);
414
            
415
            $this->client->emit('debug', 'Registered group '.$gr->id);
416
            $this->client->emit('groupRegister', $gr, $this);
417
        }
418
        
419
        return $this;
420
    }
421
    
422
    /**
423
     * Registers a type. Emits a typeRegister event for each type.
424
     * @param \CharlotteDunois\Livia\Types\ArgumentType|string  ...$type  The full qualified class name or an initiated instance of it.
425
     * @return $this
426
     * @throws \RuntimeException
427
     * @throws \InvalidArgumentException
428
     */
429
    function registerType(...$type) {
430
        foreach($type as $t) {
431
            $oldT = $t;
432
            
433
            if(!($t instanceof \CharlotteDunois\Livia\Types\ArgumentType)) {
434
                $t = new $t($this->client);
435
            }
436
            
437
            if(!($t instanceof \CharlotteDunois\Livia\Types\ArgumentType)) {
438
                throw new \InvalidArgumentException(\var_export($oldT, true).' is not an instance of ArgumentType');
439
            }
440
            
441
            if($this->types->has($t->id)) {
442
                throw new \RuntimeException('Can not register another argument type with the ID '.$t->id);
443
            }
444
            
445
            $this->types->set($t->id, $t);
446
            
447
            $this->client->emit('debug', 'Registered type '.$t->id);
448
            $this->client->emit('typeRegister', $t, $this);
449
        }
450
        
451
        return $this;
452
    }
453
    
454
    /**
455
     * Registers all types in a directory. Emits a typeRegister event for each type.
456
     * @param string       $path
457
     * @param bool|string  $ignoreSameLevelFiles  Ignores files in the specified directory and only includes files in sub directories. As string it will ignore the file if the filename matches with the string.
458
     * @return $this
459
     * @throws \RuntimeException
460
     */
461
    function registerTypesIn(string $path, $ignoreSameLevelFiles = false) {
462
        $path = \realpath($path);
463
        if(!$path) {
464
            throw new \RuntimeException('Invalid path specified');
465
        }
466
        
467
        $files = \CharlotteDunois\Livia\Utils\FileHelpers::recursiveFileSearch($path, '*.php');
468
        foreach($files as $file) {
469
            if($ignoreSameLevelFiles === true) {
470
                $filepath = \ltrim(\str_replace(array($path, '\\'), array('', '/'), $file), '/');
471
                if(\mb_substr_count($filepath, '/') === 0) {
472
                    continue;
473
                }
474
            } elseif(!empty($ignoreSameLevelFiles)) {
475
                $filepath = \ltrim(\str_replace(array($path, '\\'), array('', '/'), $file), '/');
476
                if(\mb_stripos($filepath, $ignoreSameLevelFiles) === 0) {
477
                    continue;
478
                }
479
            }
480
            
481
            $code = \file_get_contents($file);
482
            
483
            preg_match('/namespace(.*?);/i', $code, $matches);
484
            if(empty($matches[1])) {
485
                throw new \RuntimeException($file.' is not a valid argument type file');
486
            }
487
            
488
            $namespace = \trim($matches[1]);
489
            
490
            preg_match('/class(.*){?/i', $code, $matches);
491
            if(empty($matches[1])) {
492
                throw new \RuntimeException($file.' is not a valid argument type file');
493
            }
494
            
495
            $name = \trim(\explode('implements', \explode('extends', $matches[1])[0])[0]);
496
            $fqn = '\\'.$namespace.'\\'.$name;
497
            
498
            $type = new $fqn($this->client);
499
            $this->types->set($type->id, $type);
500
            
501
            $this->client->emit('debug', 'Registered type '.$type->id);
502
            $this->client->emit('typeRegister', $type, $this);
503
        }
504
        
505
        return $this;
506
    }
507
    
508
    /**
509
     * Registers the default argument types, groups, and commands.
510
     * @return $this
511
     * @throws \RuntimeException
512
     */
513
    function registerDefaults() {
514
        $this->registerDefaultTypes();
515
        $this->registerDefaultGroups();
516
        $this->registerDefaultCommands();
517
        
518
        return $this;
519
    }
520
    
521
    /**
522
     * Registers the default commands.
523
     * @return $this
524
     * @throws \RuntimeException
525
     */
526
    function registerDefaultCommands() {
527
        $this->registerCommandsIn(__DIR__.'/Commands', true);
528
        return $this;
529
    }
530
    
531
    /**
532
     * Registers the default command groups.
533
     * @return $this
534
     * @throws \RuntimeException
535
     */
536
    function registerDefaultGroups() {
537
        $this->registerGroup(
538
            (new \CharlotteDunois\Livia\Commands\CommandGroup($this->client, 'commands', 'Commands', true)),
539
            (new \CharlotteDunois\Livia\Commands\CommandGroup($this->client, 'utils', 'Utilities', true))
540
        );
541
        
542
        return $this;
543
    }
544
    
545
    /**
546
     * Registers the default argument types.
547
     * @return $this
548
     * @throws \RuntimeException
549
     */
550
    function registerDefaultTypes() {
551
        $this->registerTypesIn(__DIR__.'/Types', 'ArgumentType.php');
552
        return $this;
553
    }
554
    
555
    /**
556
     * Reregisters a command. Emits a commandReregister event.
557
     * @param \CharlotteDunois\Livia\Commands\Command|string  $command     The full qualified command name (groupID:name) or an initiated instance of it.
558
     * @param \CharlotteDunois\Livia\Commands\Command         $oldCommand
559
     * @return $this
560
     * @throws \RuntimeException
561
     */
562
    function reregisterCommand($command, \CharlotteDunois\Livia\Commands\Command $oldCommand) {
563
        $oldCommand->group->commands->delete($oldCommand->name);
564
        $this->commands->delete($oldCommand->name);
565
        
566
        if(!($command instanceof \CharlotteDunois\Livia\Commands\Command)) {
567
            $command = $this->handleCommandSpacing($command);
568
            $command = $command($this->client);
569
        }
570
        
571
        $this->commands->set($command->name, $command);
572
        $command->group->commands->set($command->name, $command);
573
        
574
        $this->client->emit('debug', 'Reregistered command '.$command->groupID.':'.$command->name);
575
        $this->client->emit('commandReregister', $command, $oldCommand, $this);
576
        
577
        return $this;
578
    }
579
    
580
    /**
581
     * Unregisters a command. Emits a commandUnregister event.
582
     * @param \CharlotteDunois\Livia\Commands\Command  $command
583
     * @return $this
584
     * @throws \RuntimeException
585
     */
586
    function unregisterCommand(\CharlotteDunois\Livia\Commands\Command $command) {
587
        $group = $this->resolveGroup($command->groupID);
588
        $group->commands->delete($command->name);
589
        $this->commands->delete($command->name);
590
        
591
        $this->client->emit('debug', 'Unregistered command '.$command->groupID.':'.$command->name);
592
        $this->client->emit('commandUnregister', $command, $this);
593
        
594
        return $this;
595
    }
596
    
597
    /**
598
     * Resolves a given group ID and command name to the path.
599
     * @param string  $groupID
600
     * @param string  $command
601
     * @return string
602
     * @throws \InvalidArgumentException
603
     */
604
    function resolveCommandPath(string $groupID, string $command) {
605
        $paths = array();
606
        
607
        foreach($this->commandsDirectories as $dir) {
608
            $paths[] = $dir.'/'.\mb_strtolower($groupID);
609
        }
610
        
611
        $paths[] = __DIR__.'/Commands/'.\mb_strtolower($groupID);
612
        $filename = '/'.\mb_strtolower($command).'.php';
613
        
614
        foreach($paths as $path) {
615
            $file = $path.$filename;
616
            if(\file_exists($file)) {
617
                return \realpath($file);
618
            }
619
        }
620
        
621
        throw new \InvalidArgumentException('Unable to resolve command path');
622
    }
623
    
624
    /**
625
     * Adds a commands directory to be used in `resolveCommandPath`.
626
     * @param string  $path
627
     * @return $this
628
     * @throws \InvalidArgumentException
629
     */
630
    function addCommandsDirectory(string $path) {
631
        $path = \realpath($path);
632
        if($path === false) {
633
            throw new \InvalidArgumentException('Invalid path specified');
634
        }
635
        
636
        if($path !== $this->basepath && !\in_array($path, $this->commandsDirectories, true)) {
637
            $this->commandsDirectories[] = $path;
638
        }
639
        
640
        return $this;
641
    }
642
    
643
    /**
644
     * Removes a commands directory (used in `resolveCommandPath`).
645
     * @param string  $path
646
     * @return $this
647
     * @throws \InvalidArgumentException
648
     */
649
    function removeCommandsDirectory(string $path) {
650
        $path = \realpath($path);
651
        if($path === false) {
652
            throw new \InvalidArgumentException('Invalid path specified');
653
        }
654
        
655
        $pos = \array_search($path, $this->commandsDirectories, true);
656
        if($pos !== false) {
657
            unset($this->commandsDirectories[$pos]);
658
        }
659
        
660
        return $this;
661
    }
662
    
663
    /**
664
     * @param string  $command
665
     * @return \Closure
666
     * @throws \RuntimeException
667
     */
668
    protected function handleCommandSpacing(string $command) {
669
        $commanddot = \explode(':', $command);
670
        if(\count($commanddot) === 2) {
671
            $command = $this->resolveCommandPath($commanddot[0], $commanddot[1]);
672
            
673
            $cmd = include $command;
674
            return $cmd;
675
        }
676
        
677
        $command = \realpath($command);
678
        if($command !== false) {
679
            $cmd = include $command;
680
            return $cmd;
681
        }
682
        
683
        throw new \RuntimeException('Unable to resolve command');
684
    }
685
    
686
    /**
687
     * Unsets (resets) the internally used serialized commands array.
688
     * @return void
689
     * @internal
690
     */
691
    function unsetInternalSerializedCommands() {
692
        $this->internalSerializedCommands = array();
693
    }
694
}
695