Skip to content

Commit c0730f8

Browse files
authored
Merge pull request #1 from maplephp/develop
feat: add commands and migrations
2 parents 54b241f + dab2dcb commit c0730f8

21 files changed

+1135
-213
lines changed

src/AbstractKernel.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use MaplePHP\Core\Configs\LoadConfigFiles;
88
use MaplePHP\Core\Support\ServiceProvider;
9+
use MaplePHP\DTO\Format\Clock;
910
use MaplePHP\Emitron\Contracts\KernelInterface;
1011
use MaplePHP\Http\Stream;
1112
use Psr\Container\ContainerInterface;
@@ -46,6 +47,11 @@ public function __construct(string $dir)
4647
$this->container = new Container();
4748
$this->container->set("config", $this->config);
4849
$this->container->set("app", $app);
50+
51+
52+
53+
Clock::setDefaultLocale($this->config['configs']['locale']);
54+
Clock::setDefaultTimezone($this->config['configs']['timezone']);
4955
}
5056

5157
/**
@@ -58,13 +64,12 @@ public function __construct(string $dir)
5864
*/
5965
protected function load(ServerRequestInterface $request, ?DispatchConfigInterface $config = null): KernelInterface
6066
{
61-
6267
$this->bootServiceProviders();
6368
return new Kernel($this->container, $this->middlewares, $config);
6469
}
6570

6671
/**
67-
* Boot servcice providers
72+
* Boot service providers
6873
*
6974
* @return void
7075
*/

src/CliKernel.php

Lines changed: 174 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,11 @@
88
use MaplePHP\Blunder\Handlers\CliHandler;
99
use MaplePHP\Core\Middlewares\CliStatusError;
1010
use MaplePHP\Core\Router\RouterDispatcher;
11+
use MaplePHP\Core\Routing\DefaultCommand;
1112
use MaplePHP\Emitron\Contracts\KernelInterface;
1213
use MaplePHP\Http\Stream;
14+
use MaplePHP\Prompts\Command;
15+
use MaplePHP\Prompts\Themes\Blocks;
1316
use Psr\Http\Message\ServerRequestInterface;
1417
use MaplePHP\Emitron\Contracts\DispatchConfigInterface;
1518
use MaplePHP\Emitron\DispatchConfig;
@@ -19,29 +22,30 @@
1922
use MaplePHP\Http\Uri;
2023
use MaplePHP\Core\Middlewares\CheckAllowedProps;
2124
use MaplePHP\Unitary\Console\Middlewares\{AddCommandMiddleware,
22-
CliInitMiddleware,
23-
ConfigPropsMiddleware,
24-
LocalMiddleware};
25+
CliInitMiddleware,
26+
ConfigPropsMiddleware,
27+
LocalMiddleware
28+
};
2529

2630
final class CliKernel extends AbstractKernel
2731
{
28-
protected array $middlewares = [
29-
AddCommandMiddleware::class,
30-
ConfigPropsMiddleware::class,
31-
CheckAllowedProps::class,
32-
LocalMiddleware::class,
33-
CliInitMiddleware::class,
34-
CliStatusError::class,
35-
];
36-
37-
38-
public function __construct(string $dir)
39-
{
40-
parent::__construct($dir);
41-
// Default config
42-
Kernel::setRouterFilePath($dir . "/routers/console.php");
43-
$this->stream = new Stream(Stream::STDERR);
44-
}
32+
protected array $middlewares = [
33+
AddCommandMiddleware::class,
34+
ConfigPropsMiddleware::class,
35+
CheckAllowedProps::class,
36+
LocalMiddleware::class,
37+
CliInitMiddleware::class,
38+
CliStatusError::class,
39+
];
40+
41+
42+
public function __construct(string $dir)
43+
{
44+
parent::__construct($dir);
45+
// Default config
46+
Kernel::setRouterFilePath($dir . "/routers/console.php");
47+
$this->stream = new Stream(Stream::STDERR);
48+
}
4549

4650
/**
4751
* Initialize the HTTP kernel with default framework configuration.
@@ -62,43 +66,154 @@ public function init(): self
6266
->withErrorHandler(new CliHandler());
6367
}
6468

65-
/**
66-
* @param array $parts
67-
* @return Kernel
68-
* @throws \Exception
69-
*/
70-
public function boot(array $parts): KernelInterface
71-
{
72-
$env = new Environment();
73-
$request = new ServerRequest(new Uri($env->getUriParts($parts)), $env);
74-
$config = $this->dispatch($request);
75-
$kernel = $this->load($request, $config);
76-
$kernel->run($request, $this->stream);
77-
return $kernel;
78-
}
79-
80-
/**
81-
* @param ServerRequestInterface $request
82-
* @return DispatchConfigInterface
83-
* @throws \Exception
84-
*/
85-
private function dispatch(ServerRequestInterface $request): DispatchConfigInterface
86-
{
87-
$config = new DispatchConfig();
88-
89-
return $config
90-
->setRouter(function ($routerFile) use ($request) {
91-
92-
$router = new RouterDispatcher($request);
93-
$router->setDispatchPath($request->getCliKeyword());
94-
//$router = new Router($request->getCliKeyword(), $request->getCliArgs());
95-
if (!is_file($routerFile)) {
96-
throw new Exception('The routes file (' . $routerFile . ') is missing.');
97-
}
98-
require_once $routerFile;
99-
require_once App::get()->coreDir() . "/Router/console.php";
100-
return $router;
101-
})
102-
->setProp('exitCode', 0);
103-
}
69+
/**
70+
* Boot the CLI app
71+
*
72+
* @param array $parts
73+
* @return Kernel
74+
* @throws \Exception
75+
*/
76+
public function boot(array $parts): KernelInterface
77+
{
78+
$env = new Environment();
79+
$request = new ServerRequest(new Uri($env->getUriParts($parts)), $env);
80+
$config = $this->dispatch($request);
81+
$kernel = $this->load($request, $config);
82+
83+
$commandName = $request->getCliKeyword();
84+
85+
if (!$commandName) {
86+
$this->renderHelp();
87+
exit(0);
88+
}
89+
90+
$commandClass = $this->resolve($commandName);
91+
if (!$commandClass) {
92+
$command = new Command($this->stream);
93+
$command->error("\nUnknown command: '$commandName'\n");
94+
exit(1);
95+
}
96+
97+
$kernel->run($request, $this->stream, function ($classInst, $response) {
98+
if ($classInst instanceof DefaultCommand) {
99+
$exitCode = $classInst->handle();
100+
if ($exitCode === 1) {
101+
exit(0);
102+
}
103+
}
104+
});
105+
return $kernel;
106+
}
107+
108+
/**
109+
* Access the router dispatcher
110+
*
111+
* @param ServerRequestInterface $request
112+
* @return DispatchConfigInterface
113+
* @throws \Exception
114+
*/
115+
private function dispatch(ServerRequestInterface $request): DispatchConfigInterface
116+
{
117+
$config = new DispatchConfig();
118+
return $config
119+
->setRouter(function ($routerFile) use ($request) {
120+
121+
$commandName = $request->getCliKeyword();
122+
123+
$router = new RouterDispatcher($request);
124+
$router->setDispatchCommand($commandName);
125+
126+
if (!is_file(App::get()->coreDir() . "/Router/console.php")) {
127+
throw new \RuntimeException('The CORE routes file (' . $routerFile . ') is missing.');
128+
}
129+
130+
if (is_file($routerFile)) {
131+
require_once $routerFile;
132+
}
133+
134+
require_once App::get()->coreDir() . "/Router/console.php";
135+
return $router;
136+
})
137+
->setProp('exitCode', 0);
138+
}
139+
140+
/**
141+
* Get command if exits or return null to flag as unknown command
142+
*
143+
* @param string $name
144+
* @return string|null
145+
*/
146+
private function resolve(string $name): ?string
147+
{
148+
foreach (RouterDispatcher::getCommands() as $commandClass) {
149+
[$commandName] = $commandClass;
150+
151+
if ($commandName === $name) {
152+
return $commandClass[0];
153+
}
154+
}
155+
return null;
156+
}
157+
158+
/**
159+
* List all available commands
160+
*
161+
* @return void
162+
*/
163+
private function renderHelp(): void
164+
{
165+
$command = new Command($this->stream);
166+
$blocks = new Blocks($command);
167+
168+
$blocks->addHeadline("\n--- MaplePHP Help ---", "green");
169+
$blocks->addSection("Usage", function (Blocks $inst) {
170+
return $inst
171+
->addExamples(
172+
"./maple [type] [options]",
173+
"You can always trigger a command and with options"
174+
)->addExamples(
175+
"./maple serve --help",
176+
"You can trigger a dedicated help for each command."
177+
);
178+
});
179+
180+
$blocks->addSection("Available commands", function (Blocks $inst) {
181+
foreach ($this->getParentCommands() as $name => $desc) {
182+
$inst = $inst
183+
->addOption(
184+
"./maple $name",
185+
$desc
186+
);
187+
}
188+
return $inst;
189+
});
190+
191+
$blocks->addSpace();
192+
}
193+
194+
/**
195+
* Will filter out all child command and only return the main command
196+
*
197+
* @return array
198+
*/
199+
protected function getParentCommands(): array
200+
{
201+
$filtered = [];
202+
foreach (RouterDispatcher::getCommands() as $commandClass) {
203+
[$commandName, $className] = $commandClass;
204+
$desc = is_a($className[0], DefaultCommand::class, true)
205+
? $className[0]::description()
206+
: null;
207+
208+
$commandNames = explode(':', $commandName);
209+
$commandNameFirst = array_shift($commandNames);
210+
$commandNamesNext = implode(':', $commandNames);
211+
$filtered[$commandNameFirst][$commandNamesNext] = $desc;
212+
}
213+
214+
return array_map(function ($item) {
215+
ksort($item);
216+
return reset($item);
217+
}, $filtered);
218+
}
104219
}

src/Console/ArgDefinition.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace MaplePHP\Core\Console;
6+
7+
class ArgDefinition
8+
{
9+
public function __construct(
10+
public readonly string $name,
11+
public readonly string $description,
12+
public readonly bool $required = false
13+
)
14+
{
15+
}
16+
}

0 commit comments

Comments
 (0)