diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml
new file mode 100644
index 0000000..79ee123
--- /dev/null
+++ b/.idea/codeStyles/codeStyleConfig.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
index 4439b32..1f74c75 100644
--- a/README.md
+++ b/README.md
@@ -31,8 +31,10 @@
## Develop Installation
-1. `npm install`
-2. Setup [Sample](sample/README.md)
+1. `nvm install`
+2. `nvm use`
+3. `npm ci --also=dev`
+4. Setup [Sample](sample/README.md)
## Author
diff --git a/core/src/Domain/Exception/HarmonyException.php b/core/src/Domain/Exception/HarmonyException.php
new file mode 100644
index 0000000..8a72e39
--- /dev/null
+++ b/core/src/Domain/Exception/HarmonyException.php
@@ -0,0 +1,8 @@
+add($path);
+ }
+ }
+
+ /**
+ * @throws FileNotExistException
+ */
+ public function add(string $path): void {
+ $this->paths[] = new ValidFilePath($path);
+ }
+
+ /**
+ * @return ValidFilePath[]
+ */
+ public function getEnvPaths(): array {
+ return $this->paths;
+ }
+}
diff --git a/core/src/Module/Config/Env/DotEnvPathsContainerInterface.php b/core/src/Module/Config/Env/DotEnvPathsContainerInterface.php
new file mode 100644
index 0000000..e9f178f
--- /dev/null
+++ b/core/src/Module/Config/Env/DotEnvPathsContainerInterface.php
@@ -0,0 +1,12 @@
+[] */
+ public function getProviders(): array;
+}
diff --git a/core/src/Module/Config/ProviderInterface.php b/core/src/Module/Config/ProviderInterface.php
new file mode 100644
index 0000000..918b305
--- /dev/null
+++ b/core/src/Module/Config/ProviderInterface.php
@@ -0,0 +1,23 @@
+[]
+ */
+ public function getCommands(): array;
+
+ /**
+ * @return array
+ */
+ public function getResolverDefinitions(): array;
+}
diff --git a/core/src/Module/Config/ResolverInterface.php b/core/src/Module/Config/ResolverInterface.php
new file mode 100644
index 0000000..9b0ee55
--- /dev/null
+++ b/core/src/Module/Config/ResolverInterface.php
@@ -0,0 +1,10 @@
+
+ */
+ public function __invoke(): array;
+}
diff --git a/core/src/Module/FileSystem/Exception/FileNotExistException.php b/core/src/Module/FileSystem/Exception/FileNotExistException.php
new file mode 100644
index 0000000..41bf7cb
--- /dev/null
+++ b/core/src/Module/FileSystem/Exception/FileNotExistException.php
@@ -0,0 +1,11 @@
+validateOrFail();
+ }
+
+ /**
+ * @throws FileNotExistException
+ */
+ protected function validateOrFail(): void {
+ if (!file_exists($this->value)) {
+ throw new FileNotExistException($this->value);
+ }
+ }
+}
diff --git a/core/src/Module/Kernel/ConsoleKernel.php b/core/src/Module/Kernel/ConsoleKernel.php
new file mode 100644
index 0000000..b64800d
--- /dev/null
+++ b/core/src/Module/Kernel/ConsoleKernel.php
@@ -0,0 +1,35 @@
+commands as $command) {
+ $commandName = $command::getDefaultName();
+
+ if (is_string($commandName)) {
+ $commands[$commandName] = function () use ($command): Command {
+ return new $command();
+ };
+ }
+ }
+
+ $commandLoader = new FactoryCommandLoader($commands);
+
+ $application = new Application();
+ $application->setCommandLoader($commandLoader);
+
+ return $application->run();
+ }
+}
diff --git a/core/src/Module/Kernel/HttpKernel.php b/core/src/Module/Kernel/HttpKernel.php
new file mode 100644
index 0000000..0df2f19
--- /dev/null
+++ b/core/src/Module/Kernel/HttpKernel.php
@@ -0,0 +1,29 @@
+request = $request;
+
+ $matcher = new UrlMatcher($this->routes, $this->context);
+ $parameters = $matcher->match($this->request->getPathInfo());
+ /** @var class-string $controllerActionClass */
+ $controllerActionClass = $parameters["_controller"];
+
+ /** @var ControllerActionInterface $controllerAction */
+ $controllerAction = $this->diContainer->get($controllerActionClass);
+
+ $response = $controllerAction($this->request);
+ $response->prepare($this->request);
+
+ return $response;
+ }
+}
diff --git a/core/src/Module/Kernel/Kernel.php b/core/src/Module/Kernel/Kernel.php
new file mode 100644
index 0000000..d178d03
--- /dev/null
+++ b/core/src/Module/Kernel/Kernel.php
@@ -0,0 +1,112 @@
+[] */
+ protected array $commands = [];
+
+ protected RouteCollection $routes;
+ protected UrlGenerator $urlGenerator;
+
+ protected RequestContext $context;
+
+ public function __construct(
+ protected ?DotEnvPathsContainerInterface $dotEnvs = null,
+ protected ?ModulesToLoadInterface $modulesToLoad = null,
+ ) {
+ $this->loadEnv();
+ $this->loadModules();
+ $this->loadDI();
+ $this->loadCommands();
+ $this->loadRouting();
+ }
+
+ protected function loadEnv(): void {
+ if ($this->dotEnvs === null) {
+ return;
+ }
+
+ $dotenv = new Dotenv();
+ $dotEnvPaths = $this->dotEnvs->getEnvPaths();
+
+ foreach ($dotEnvPaths as $path) {
+ $dotenv->load($path->value);
+
+ unset($path);
+ }
+ }
+
+ protected function loadModules(): void {
+ if ($this->modulesToLoad === null) {
+ return;
+ }
+
+ foreach ($this->modulesToLoad->getProviders() as $module) {
+ $this->modules[] = new $module();
+
+ unset($module);
+ }
+ }
+
+ /**
+ * @throws Exception
+ */
+ protected function loadDI(): void {
+ $diBuilder = new ContainerBuilder();
+
+ foreach ($this->modules as $module) {
+ $definitions = $module->getResolverDefinitions();
+ $diBuilder->addDefinitions($definitions);
+
+ unset($definitions);
+ }
+
+ $this->diContainer = $diBuilder->build();
+ }
+
+ protected function loadCommands(): void {
+ foreach ($this->modules as $module) {
+ $commands = $module->getCommands();
+ $this->commands += $commands;
+
+ unset($commands);
+ }
+ }
+
+ protected function loadRouting(): void {
+ $this->routes = new RouteCollection();
+
+ foreach ($this->modules as $module) {
+ $routes = $module->getRoutes();
+
+ foreach ($routes as $route) {
+ $symfonyRoute = new Route($route->uri, [
+ "_controller" => $route->controllerAction,
+ ]);
+ $this->routes->add($route->name, $symfonyRoute);
+ }
+ }
+
+ $this->context = new RequestContext();
+ $this->urlGenerator = new UrlGenerator($this->routes, $this->context);
+ }
+}
diff --git a/core/src/Module/Pdo/PdoWrapper.php b/core/src/Module/Pdo/PdoWrapper.php
index 2542c4d..22ed1b9 100644
--- a/core/src/Module/Pdo/PdoWrapper.php
+++ b/core/src/Module/Pdo/PdoWrapper.php
@@ -18,14 +18,18 @@ public function __construct(protected readonly PDO $pdoConnection) {
}
/**
- * @param string $sql
+ * @param string $sql
* @param array $params
*
* @return object|null
* @throws PdoConnectionNotReadyException
*/
- public function findOne(string $sql, array $params): ?object {
- $query = $this->execute($sql, $params);
+ public function findOne(
+ string $sql,
+ array $params,
+ string $returnClass = null,
+ ): ?object {
+ $query = $this->execute($sql, $params, $returnClass);
$item = $query->fetch();
if ($item === false || !is_object($item)) {
@@ -36,16 +40,28 @@ public function findOne(string $sql, array $params): ?object {
}
/**
- * @param string $sql
+ * @param string $sql
* @param array $params
*
* @return array