• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

codeigniter4 / CodeIgniter4 / 24836837944

23 Apr 2026 01:04PM UTC coverage: 88.311% (+0.04%) from 88.267%
24836837944

push

github

web-flow
feat: introduce modern attribute-based `spark` commands (#10120)

* add the Command attribute

* add the CLI exceptions

* add the `Argument` and `Option` value objects

* add `AbstractCommand` class

* refactor `Console` and `Commands` to cater modern commands

* migrate some built-in commands

* move legacy command fixtures

* add tests to AbstractCommand

* remove support for the `-h` option in `routes` command

* add documentation and changelog

* address reviews

548 of 596 new or added lines in 15 files covered. (91.95%)

6 existing lines in 1 file now uncovered.

23255 of 26333 relevant lines covered (88.31%)

216.66 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

82.35
/system/CLI/BaseCommand.php
1
<?php
2

3
declare(strict_types=1);
4

5
/**
6
 * This file is part of CodeIgniter 4 framework.
7
 *
8
 * (c) CodeIgniter Foundation <admin@codeigniter.com>
9
 *
10
 * For the full copyright and license information, please view
11
 * the LICENSE file that was distributed with this source code.
12
 */
13

14
namespace CodeIgniter\CLI;
15

16
use Config\Exceptions;
17
use Psr\Log\LoggerInterface;
18
use ReflectionException;
19
use Throwable;
20

21
/**
22
 * BaseCommand is the base class used in creating CLI commands.
23
 *
24
 * @property array<string, string> $arguments
25
 * @property Commands              $commands
26
 * @property string                $description
27
 * @property string                $group
28
 * @property LoggerInterface       $logger
29
 * @property string                $name
30
 * @property array<string, string> $options
31
 * @property string                $usage
32
 */
33
abstract class BaseCommand
34
{
35
    /**
36
     * The group the command is lumped under
37
     * when listing commands.
38
     *
39
     * @var string
40
     */
41
    protected $group;
42

43
    /**
44
     * The Command's name
45
     *
46
     * @var string
47
     */
48
    protected $name;
49

50
    /**
51
     * the Command's usage description
52
     *
53
     * @var string
54
     */
55
    protected $usage;
56

57
    /**
58
     * the Command's short description
59
     *
60
     * @var string
61
     */
62
    protected $description;
63

64
    /**
65
     * the Command's options description
66
     *
67
     * @var array<string, string>
68
     */
69
    protected $options = [];
70

71
    /**
72
     * the Command's Arguments description
73
     *
74
     * @var array<string, string>
75
     */
76
    protected $arguments = [];
77

78
    /**
79
     * The Logger to use for a command
80
     *
81
     * @var LoggerInterface
82
     */
83
    protected $logger;
84

85
    /**
86
     * Instance of Commands so
87
     * commands can call other commands.
88
     *
89
     * @var Commands
90
     */
91
    protected $commands;
92

93
    public function __construct(LoggerInterface $logger, Commands $commands)
94
    {
95
        $this->logger   = $logger;
179✔
96
        $this->commands = $commands;
179✔
97
    }
98

99
    /**
100
     * Actually execute a command.
101
     *
102
     * @param array<int|string, string|null> $params
103
     *
104
     * @return int|null
105
     */
106
    abstract public function run(array $params);
107

108
    /**
109
     * Can be used by a command to run other commands.
110
     *
111
     * @param array<int|string, string|null> $params
112
     *
113
     * @return int|null
114
     *
115
     * @throws ReflectionException
116
     */
117
    protected function call(string $command, array $params = [])
118
    {
119
        return $this->commands->runLegacy($command, $params);
10✔
120
    }
121

122
    /**
123
     * A simple method to display an error with line/file, in child commands.
124
     *
125
     * @return void
126
     */
127
    protected function showError(Throwable $e)
128
    {
129
        $exception = $e;
3✔
130
        $message   = $e->getMessage();
3✔
131
        $config    = config(Exceptions::class);
3✔
132

133
        require $config->errorViewPath . '/cli/error_exception.php';
3✔
134
    }
135

136
    /**
137
     * Show Help includes (Usage, Arguments, Description, Options).
138
     *
139
     * @return void
140
     */
141
    public function showHelp()
142
    {
143
        CLI::write(lang('CLI.helpUsage'), 'yellow');
2✔
144

145
        if ($this->usage !== null) {
2✔
UNCOV
146
            $usage = $this->usage;
×
147
        } else {
148
            $usage = $this->name;
2✔
149

150
            if ($this->arguments !== []) {
2✔
151
                $usage .= ' [arguments]';
2✔
152
            }
153
        }
154

155
        CLI::write($this->setPad($usage, 0, 0, 2));
2✔
156

157
        if ($this->description !== null) {
2✔
158
            CLI::newLine();
2✔
159
            CLI::write(lang('CLI.helpDescription'), 'yellow');
2✔
160
            CLI::write($this->setPad($this->description, 0, 0, 2));
2✔
161
        }
162

163
        if ($this->arguments !== []) {
2✔
164
            CLI::newLine();
2✔
165
            CLI::write(lang('CLI.helpArguments'), 'yellow');
2✔
166
            $length = max(array_map(strlen(...), array_keys($this->arguments)));
2✔
167

168
            foreach ($this->arguments as $argument => $description) {
2✔
169
                CLI::write(CLI::color($this->setPad($argument, $length, 2, 2), 'green') . $description);
2✔
170
            }
171
        }
172

173
        if ($this->options !== []) {
2✔
UNCOV
174
            CLI::newLine();
×
UNCOV
175
            CLI::write(lang('CLI.helpOptions'), 'yellow');
×
UNCOV
176
            $length = max(array_map(strlen(...), array_keys($this->options)));
×
177

UNCOV
178
            foreach ($this->options as $option => $description) {
×
UNCOV
179
                CLI::write(CLI::color($this->setPad($option, $length, 2, 2), 'green') . $description);
×
180
            }
181
        }
182
    }
183

184
    /**
185
     * Pads our string out so that all titles are the same length to nicely line up descriptions.
186
     *
187
     * @param int $extra How many extra spaces to add at the end
188
     */
189
    public function setPad(string $item, int $max, int $extra = 2, int $indent = 0): string
190
    {
191
        $max += $extra + $indent;
3✔
192

193
        return str_pad(str_repeat(' ', $indent) . $item, $max);
3✔
194
    }
195

196
    /**
197
     * Makes it simple to access our protected properties.
198
     *
199
     * @return array<string, string>|Commands|LoggerInterface|string|null
200
     */
201
    public function __get(string $key)
202
    {
203
        return $this->{$key} ?? null;
61✔
204
    }
205

206
    /**
207
     * Makes it simple to check our protected properties.
208
     */
209
    public function __isset(string $key): bool
210
    {
211
        return isset($this->{$key});
1✔
212
    }
213
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc