Skip to content

Commit 2891585

Browse files
committed
Refactor Command running logic out of CommandRunner controller to new library.
1 parent 2319754 commit 2891585

7 files changed

Lines changed: 175 additions & 115 deletions

File tree

system/CLI/BaseCommand.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -115,10 +115,10 @@ abstract class BaseCommand
115115
/**
116116
* BaseCommand constructor.
117117
*
118-
* @param \Psr\Log\LoggerInterface $logger
119-
* @param \CodeIgniter\CLI\CommandRunner $commands
118+
* @param \Psr\Log\LoggerInterface $logger
119+
* @param Commands $commands
120120
*/
121-
public function __construct(LoggerInterface $logger, CommandRunner $commands)
121+
public function __construct(LoggerInterface $logger, Commands $commands)
122122
{
123123
$this->logger = $logger;
124124
$this->commands = $commands;
@@ -151,7 +151,7 @@ protected function call(string $command, array $params = [])
151151
// for the command name.
152152
array_unshift($params, $command);
153153

154-
return $this->commands->index($params);
154+
return $this->commands->run($command, $params);
155155
}
156156

157157
//--------------------------------------------------------------------

system/CLI/CommandRunner.php

Lines changed: 10 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -50,21 +50,19 @@ class CommandRunner extends Controller
5050
{
5151

5252
/**
53-
* Stores the info about found Commands.
53+
* The Command Manager
5454
*
55-
* @var array
55+
* @var Commands
5656
*/
57-
protected $commands = [];
58-
59-
/**
60-
* Message logger.
61-
*
62-
* @var \CodeIgniter\Log\Logger
63-
*/
64-
protected $logger;
57+
protected $commands;
6558

6659
//--------------------------------------------------------------------
6760

61+
public function __construct()
62+
{
63+
$this->commands = service('commands');
64+
}
65+
6866
/**
6967
* We map all un-routed CLI methods through this function
7068
* so we have the chance to look for a Command first.
@@ -100,113 +98,21 @@ public function index(array $params)
10098
{
10199
$command = array_shift($params);
102100

103-
$this->createCommandList();
104-
105101
if (is_null($command))
106102
{
107103
$command = 'list';
108104
}
109105

110-
return $this->runCommand($command, $params);
111-
}
112-
113-
//--------------------------------------------------------------------
114-
115-
/**
116-
* Actually runs the command.
117-
*
118-
* @param string $command
119-
* @param array $params
120-
*
121-
* @return mixed
122-
*/
123-
protected function runCommand(string $command, array $params)
124-
{
125-
if (! isset($this->commands[$command]))
126-
{
127-
CLI::error(lang('CLI.commandNotFound', [$command]));
128-
CLI::newLine();
129-
return;
130-
}
131-
132-
// The file would have already been loaded during the
133-
// createCommandList function...
134-
$className = $this->commands[$command]['class'];
135-
$class = new $className($this->logger, $this);
136-
137-
return $class->run($params);
106+
return service('commands')->run($command, $params);
138107
}
139108

140-
//--------------------------------------------------------------------
141-
142-
/**
143-
* Scans all Commands directories and prepares a list
144-
* of each command with it's group and file.
145-
*
146-
* @throws \ReflectionException
147-
*/
148-
protected function createCommandList()
149-
{
150-
$files = Services::locator()->listFiles('Commands/');
151-
152-
// If no matching command files were found, bail
153-
if (empty($files))
154-
{
155-
// This should never happen in unit testing.
156-
// if it does, we have far bigger problems!
157-
// @codeCoverageIgnoreStart
158-
return;
159-
// @codeCoverageIgnoreEnd
160-
}
161-
162-
// Loop over each file checking to see if a command with that
163-
// alias exists in the class. If so, return it. Otherwise, try the next.
164-
foreach ($files as $file)
165-
{
166-
$className = Services::locator()->findQualifiedNameFromPath($file);
167-
if (empty($className) || ! class_exists($className))
168-
{
169-
continue;
170-
}
171-
172-
$class = new \ReflectionClass($className);
173-
174-
if (! $class->isInstantiable() || ! $class->isSubclassOf(BaseCommand::class))
175-
{
176-
continue;
177-
}
178-
179-
$class = new $className($this->logger, $this);
180-
181-
// Store it!
182-
if ($class->group !== null)
183-
{
184-
$this->commands[$class->name] = [
185-
'class' => $className,
186-
'file' => $file,
187-
'group' => $class->group,
188-
'description' => $class->description,
189-
];
190-
}
191-
192-
$class = null;
193-
unset($class);
194-
}
195-
196-
asort($this->commands);
197-
}
198-
199-
//--------------------------------------------------------------------
200-
201109
/**
202110
* Allows access to the current commands that have been found.
203111
*
204112
* @return array
205113
*/
206114
public function getCommands(): array
207115
{
208-
return $this->commands;
116+
return $this->commands->getCommands();
209117
}
210-
211-
//--------------------------------------------------------------------
212118
}

system/CLI/Commands.php

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
<?php namespace CodeIgniter\CLI;
2+
3+
use CodeIgniter\Log\Logger;
4+
use Config\Services;
5+
6+
/**
7+
* Class Commands
8+
*
9+
* Core functionality for running, listing, etc commands.
10+
*
11+
* @package CodeIgniter\CLI
12+
*/
13+
class Commands
14+
{
15+
/**
16+
* The found commands.
17+
*
18+
* @var array
19+
*/
20+
protected $commands = [];
21+
22+
/**
23+
* Logger instance.
24+
*
25+
* @var Logger
26+
*/
27+
protected $logger;
28+
29+
public function __construct($logger = null)
30+
{
31+
$this->logger = $logger === null ? service('logger') : $logger;
32+
}
33+
34+
/**
35+
* Runs a command given
36+
*
37+
* @param string $command
38+
* @param array $params
39+
*/
40+
public function run(string $command, array $params)
41+
{
42+
$this->discoverCommands();
43+
44+
if (! isset($this->commands[$command]))
45+
{
46+
CLI::error(lang('CLI.commandNotFound', [$command]));
47+
CLI::newLine();
48+
return;
49+
}
50+
51+
// The file would have already been loaded during the
52+
// createCommandList function...
53+
$className = $this->commands[$command]['class'];
54+
$class = new $className($this->logger, $this);
55+
56+
return $class->run($params);
57+
}
58+
59+
/**
60+
* Provide access to the list of commands.
61+
*
62+
* @return array
63+
*/
64+
public function getCommands()
65+
{
66+
$this->discoverCommands();
67+
68+
return $this->commands;
69+
}
70+
71+
/**
72+
* Discovers all commands in the framework and within user code,
73+
* and collects instances of them to work with.
74+
*/
75+
public function discoverCommands()
76+
{
77+
if (! empty($this->commands))
78+
{
79+
return;
80+
}
81+
82+
$files = service('locator')->listFiles('Commands/');
83+
84+
// If no matching command files were found, bail
85+
if (empty($files))
86+
{
87+
// This should never happen in unit testing.
88+
// if it does, we have far bigger problems!
89+
// @codeCoverageIgnoreStart
90+
return;
91+
// @codeCoverageIgnoreEnd
92+
}
93+
94+
// Loop over each file checking to see if a command with that
95+
// alias exists in the class. If so, return it. Otherwise, try the next.
96+
foreach ($files as $file)
97+
{
98+
$className = Services::locator()->findQualifiedNameFromPath($file);
99+
if (empty($className) || ! class_exists($className))
100+
{
101+
continue;
102+
}
103+
104+
try
105+
{
106+
$class = new \ReflectionClass($className);
107+
108+
if (! $class->isInstantiable() || ! $class->isSubclassOf(BaseCommand::class))
109+
{
110+
continue;
111+
}
112+
113+
$class = new $className($this->logger, $this);
114+
115+
// Store it!
116+
if ($class->group !== null)
117+
{
118+
$this->commands[$class->name] = [
119+
'class' => $className,
120+
'file' => $file,
121+
'group' => $class->group,
122+
'description' => $class->description,
123+
];
124+
}
125+
126+
$class = null;
127+
unset($class);
128+
}
129+
catch (\ReflectionException $e)
130+
{
131+
$this->logger->error($e->getMessage());
132+
}
133+
}
134+
135+
asort($this->commands);
136+
}
137+
}

system/Config/Services.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,23 @@ public static function clirequest(App $config = null, bool $getShared = true)
151151

152152
//--------------------------------------------------------------------
153153

154+
/**
155+
* The commands utility for running and working with CLI commands.
156+
*
157+
* @param boolean $getShared
158+
*
159+
* @return \CodeIgniter\CLI\Commands|mixed
160+
*/
161+
public static function commands(bool $getShared = true)
162+
{
163+
if ($getShared)
164+
{
165+
return static::getSharedInstance('commands');
166+
}
167+
168+
return new \CodeIgniter\CLI\Commands();
169+
}
170+
154171
/**
155172
* The CURL Request class acts as a simple HTTP client for interacting
156173
* with other servers, typically through APIs.

tests/system/CLI/CommandRunnerTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ public function testCommandProperties()
9999
$this->runner->index(['help']);
100100
$result = CITestStreamFilter::$buffer;
101101
$commands = $this->runner->getCommands();
102-
$command = new $commands['help']['class']($this->logger, $this->runner);
102+
$command = new $commands['help']['class']($this->logger, service('commands'));
103103

104104
$this->assertEquals('Displays basic usage information.', $command->description);
105105
$this->assertNull($command->notdescription);

tests/system/Commands/CommandClassTest.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,28 +18,28 @@ protected function setUp(): void
1818

1919
public function testMagicIssetTrue()
2020
{
21-
$command = new \Tests\Support\Commands\AppInfo($this->logger, $this->runner);
21+
$command = new \Tests\Support\Commands\AppInfo($this->logger, service('commands'));
2222

2323
$this->assertTrue(isset($command->group));
2424
}
2525

2626
public function testMagicIssetFalse()
2727
{
28-
$command = new \Tests\Support\Commands\AppInfo($this->logger, $this->runner);
28+
$command = new \Tests\Support\Commands\AppInfo($this->logger, service('commands'));
2929

3030
$this->assertFalse(isset($command->foobar));
3131
}
3232

3333
public function testMagicGet()
3434
{
35-
$command = new \Tests\Support\Commands\AppInfo($this->logger, $this->runner);
35+
$command = new \Tests\Support\Commands\AppInfo($this->logger, service('commands'));
3636

3737
$this->assertEquals('demo', $command->group);
3838
}
3939

4040
public function testMagicGetMissing()
4141
{
42-
$command = new \Tests\Support\Commands\AppInfo($this->logger, $this->runner);
42+
$command = new \Tests\Support\Commands\AppInfo($this->logger, service('commands'));
4343

4444
$this->assertNull($command->foobar);
4545
}

tests/system/Commands/CommandsTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ public function testShowError()
8787
{
8888
$this->runner->index(['app:info']);
8989
$commands = $this->runner->getCommands();
90-
$command = new $commands['app:info']['class']($this->logger, $this->runner);
90+
$command = new $commands['app:info']['class']($this->logger, service('commands'));
9191

9292
$command->helpme();
9393
$result = CITestStreamFilter::$buffer;
@@ -99,7 +99,7 @@ public function testCommandCall()
9999
$this->error_filter = stream_filter_append(STDERR, 'CITestStreamFilter');
100100
$this->runner->index(['app:info']);
101101
$commands = $this->runner->getCommands();
102-
$command = new $commands['app:info']['class']($this->logger, $this->runner);
102+
$command = new $commands['app:info']['class']($this->logger, service('commands'));
103103

104104
$command->bomb();
105105
$result = CITestStreamFilter::$buffer;

0 commit comments

Comments
 (0)