Commit Graph

26 Commits

Author SHA1 Message Date
Tim 797cbedb27 Add route to create book 2023-08-17 15:53:58 +02:00
Tim 7a4a9257c3 Implement doctrine orm as command 2023-08-17 15:41:49 +02:00
Tim f8d1c66934 Implement commands 2023-08-16 16:04:35 +02:00
Tim 1ee5c1c992 Add console 2023-08-16 14:33:21 +02:00
Tim 59200be680 Add doctrine and seperate out the renderer 2023-08-16 14:28:06 +02:00
Tim 56e8caa26c Add key based genericConfig 2023-08-15 21:21:24 +02:00
Tim 18848e041f Play around with the rendering 2023-08-15 13:56:04 +02:00
Tim 24683e70a8 Streamline view rendering 2023-08-15 13:37:03 +02:00
Tim 45797ba20a Add ViewHelper in view for injecting some basic classes in views 2023-08-15 12:10:04 +02:00
Tim de5ff3a2b9 Add toUri function to router 2023-08-15 03:18:03 +02:00
Tim c80dcd2860 Implement simple PHP based view renderer 2023-08-15 03:05:28 +02:00
Tim 9b8985640c Add appConfig to container
diff --git a/public/index.php b/public/index.php
index 78fef51..e15604b 100644
--- a/public/index.php
+++ b/public/index.php
@@ -8,8 +8,7 @@ error_reporting(E_ALL);

 use App\Kernel;

-require_once dirname(__DIR__).'/vendor/autoload.php';
+require_once dirname(__DIR__) . '/vendor/autoload.php';

-$kernel = new Kernel();
-
-$kernel();
\ No newline at end of file
+$kernel = new Kernel(__DIR__ . '/../app');
+$kernel->run();
\ No newline at end of file
diff --git a/src/AppConfig.php b/src/AppConfig.php
new file mode 100644
index 0000000..bcf0ee6
--- /dev/null
+++ b/src/AppConfig.php
@@ -0,0 +1,17 @@
+<?php
+
+namespace Ardent\Undercurrent;
+
+class AppConfig
+{
+    public function __construct(
+        private readonly string $rootPath,
+    )
+    {
+    }
+
+    public function getRootPath(): string
+    {
+        return $this->rootPath;
+    }
+}
\ No newline at end of file
diff --git a/src/Kernel/BaseKernel.php b/src/Kernel/BaseKernel.php
index 60164d8..c27f49a 100644
--- a/src/Kernel/BaseKernel.php
+++ b/src/Kernel/BaseKernel.php
@@ -2,6 +2,7 @@

 namespace Ardent\Undercurrent\Kernel;

+use Ardent\Undercurrent\AppConfig;
 use Ardent\Undercurrent\Container\ContainerInterface;
 use Ardent\Undercurrent\Container\GenericContainer;
 use Ardent\Undercurrent\Http\GenericRequest;
@@ -13,18 +14,26 @@ use Ardent\Undercurrent\Http\RouterInterface;
 use Ardent\Undercurrent\Http\StatusEnum;
 use Ardent\Undercurrent\Logger\LogContainer;
 use Ardent\Undercurrent\Logger\LoggerInterface;
-use Exception;

 class BaseKernel
 {
-    public function __invoke(): void
+    public function __construct(
+        private readonly string $rootDirectory,
+    )
     {
+    }
+
+    public function run(): void
+    {
+        $appConfig = new AppConfig($this->rootDirectory);
+
         $container = (new GenericContainer());
         $container
             ->alias(RouterInterface::class, GenericRouter::class)
             ->alias(ContainerInterface::class, GenericContainer::class)
             ->alias(LoggerInterface::class, LogContainer::class)
             ->add(GenericContainer::class, fn($container) => $container)
+            ->add(AppConfig::class, fn() => $appConfig)
             ->add(GenericRouter::class)
             ->add(LogContainer::class);
2023-08-15 02:16:12 +02:00
Tim 1aded029fc Add logger
diff --git a/src/Http/GenericRouter.php b/src/Http/GenericRouter.php
index 334ecac..5a2ac6f 100644
--- a/src/Http/GenericRouter.php
+++ b/src/Http/GenericRouter.php
@@ -4,6 +4,7 @@ namespace Ardent\Undercurrent\Http;

 use Ardent\Undercurrent\Container\ClassNotFoundException;
 use Ardent\Undercurrent\Container\ContainerInterface;
+use Ardent\Undercurrent\Logger\LoggerInterface;
 use Exception;
 use ReflectionMethod;

@@ -11,6 +12,7 @@ class GenericRouter implements RouterInterface
 {
     public function __construct(
         private readonly ContainerInterface $container,
+        private readonly LoggerInterface    $logger,
         private readonly RouterConfig       $config,
     )
     {
@@ -28,6 +30,8 @@ class GenericRouter implements RouterInterface
                 continue;
             }

+            $this->logger->add(sprintf('Matched route %s', $route->getRoute()->path));
+
             return $this->container->call(
                 $route->getController(),
                 $route->getMethod(),
@@ -44,9 +48,9 @@ class GenericRouter implements RouterInterface
             return [];
         }

-        $result = preg_match_all('/{(\w+)}/', $routeUri, $tokens);
+        $result = preg_match_all('/{\w+}/', $routeUri, $tokens);

-        if (!$result) {
+        if (!$result === null) {
             return null;
         }

@@ -59,6 +63,7 @@ class GenericRouter implements RouterInterface
             ) . '$/';

         $result = preg_match($matchingRegex, $requestUri, $matches);
+
         if (!$result) {
             return null;
         }
diff --git a/src/Kernel/BaseKernel.php b/src/Kernel/BaseKernel.php
index 634cafc..60164d8 100644
--- a/src/Kernel/BaseKernel.php
+++ b/src/Kernel/BaseKernel.php
@@ -5,10 +5,15 @@ namespace Ardent\Undercurrent\Kernel;
 use Ardent\Undercurrent\Container\ContainerInterface;
 use Ardent\Undercurrent\Container\GenericContainer;
 use Ardent\Undercurrent\Http\GenericRequest;
+use Ardent\Undercurrent\Http\GenericResponse;
 use Ardent\Undercurrent\Http\GenericRouter;
 use Ardent\Undercurrent\Http\MethodEnum;
 use Ardent\Undercurrent\Http\RouterConfig;
 use Ardent\Undercurrent\Http\RouterInterface;
+use Ardent\Undercurrent\Http\StatusEnum;
+use Ardent\Undercurrent\Logger\LogContainer;
+use Ardent\Undercurrent\Logger\LoggerInterface;
+use Exception;

 class BaseKernel
 {
@@ -18,8 +23,10 @@ class BaseKernel
         $container
             ->alias(RouterInterface::class, GenericRouter::class)
             ->alias(ContainerInterface::class, GenericContainer::class)
+            ->alias(LoggerInterface::class, LogContainer::class)
             ->add(GenericContainer::class, fn($container) => $container)
-            ->add(GenericRouter::class);
+            ->add(GenericRouter::class)
+            ->add(LogContainer::class);

         $this->dependencies($container);

@@ -34,7 +41,20 @@ class BaseKernel
             $_REQUEST,
         );
         $router = $container->get(RouterInterface::class);
-        $response = $router->dispatch($request);
+        $log = $container->get(LogContainer::class);
+        try {
+            $response = $router->dispatch($request);
+        } catch (\Throwable $e) {
+            $response = new GenericResponse(
+                $e->getMessage(),
+                StatusEnum::NOT_FOUND
+            );
+            //$response = $container->get(RouterConfig::class)->getExceptionRoute()->getController()::exception($e);
+        }
+
+        foreach ($log->getLogs() as $log) {
+            echo sprintf('<p>%s</p>', $log);
+        }

         http_response_code($response->getStatus()->value);

diff --git a/src/Logger/LogContainer.php b/src/Logger/LogContainer.php
new file mode 100644
index 0000000..39f3855
--- /dev/null
+++ b/src/Logger/LogContainer.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace Ardent\Undercurrent\Logger;
+
+class LogContainer implements LoggerInterface
+{
+    private array $logs = [];
+
+    public function add(string $log): void
+    {
+        $this->logs[] = $log;
+    }
+
+    public function getLogs(): array
+    {
+        return $this->logs;
+    }
+}
\ No newline at end of file
diff --git a/src/Logger/LoggerInterface.php b/src/Logger/LoggerInterface.php
new file mode 100644
index 0000000..1b2867a
--- /dev/null
+++ b/src/Logger/LoggerInterface.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace Ardent\Undercurrent\Logger;
+
+interface LoggerInterface
+{
+    public function add(string $log): void;
+}
\ No newline at end of file
2023-08-11 01:08:05 +02:00
Tim 42c590dfd6 Finalize routing with injection and parameters
diff --git a/app/Controller/HelloWorldController.php b/app/Controller/HelloWorldController.php
index 7f2af8b..86c5b8e 100644
--- a/app/Controller/HelloWorldController.php
+++ b/app/Controller/HelloWorldController.php
@@ -28,13 +28,13 @@ class HelloWorldController
         return new GenericResponse('Hello World!');
     }

-    #[Route('/routes')]
-    public function routes(RouterConfig $config): ResponseInterface
+    #[Route('/routes/{word}')]
+    public function routes(RouterConfig $config, string $word): ResponseInterface
     {
         $routes = implode('<br>', array_map(
-            fn($route) => sprintf('%s - %s->%s', $route->getRoute()->path, $route->getController(), $route->getMethod()),
-            $config->getRoutes()
-        ));
+                fn($route) => sprintf('%s - %s->%s', $route->getRoute()->path, $route->getController(), $route->getMethod()),
+                $config->getRoutes()
+            )) . "<br>$word";

         return new GenericResponse($routes);
     }
diff --git a/src/Container/ContainerInterface.php b/src/Container/ContainerInterface.php
index 7b55a94..5119eb5 100644
--- a/src/Container/ContainerInterface.php
+++ b/src/Container/ContainerInterface.php
@@ -19,4 +19,6 @@ interface ContainerInterface
      * @throws ClassNotFoundException
      */
     public function get(string $className): object;
+
+    public function call(string $className, string $methodName, array $parameters = []): mixed;
 }
\ No newline at end of file
diff --git a/src/Container/GenericContainer.php b/src/Container/GenericContainer.php
index 8fdb26e..0e467b6 100644
--- a/src/Container/GenericContainer.php
+++ b/src/Container/GenericContainer.php
@@ -2,8 +2,10 @@

 namespace Ardent\Undercurrent\Container;

+use Ardent\Undercurrent\Http\RouteConfig;
 use Exception;
 use ReflectionClass;
+use ReflectionMethod;

 class GenericContainer implements ContainerInterface
 {
@@ -61,23 +63,39 @@ class GenericContainer implements ContainerInterface
         return $instance;
     }

+    public function call(string $className, string $methodName, array $parameters = []): mixed
+    {
+        $params = $this->methodParams($className, $methodName, $parameters) + $parameters;
+        return $this->get($className)->$methodName(...$params);
+    }
+
     private function autowire(string $className): ?object
     {
         $reflection = new ReflectionClass($className);
-        $constructor = $reflection->getConstructor();
-        if (!$constructor) {
+        $reflectionMethod = $reflection->getConstructor();
+        if (!$reflectionMethod) {
             return new $className();
         }

+        return new $className(...$this->methodParams($className, $reflectionMethod->getName()));
+    }
+
+    private function methodParams(string $className, string $methodName, array $exceptParams = []): array
+    {
+        $reflectionMethod = new ReflectionMethod($className, $methodName);
+
         $params = [];
-        foreach ($constructor->getParameters() as $parameter) {
+        foreach ($reflectionMethod->getParameters() as $parameter) {
+            if (in_array($parameter->getName(), array_keys($exceptParams))) {
+                continue;
+            }
             $type = $parameter->getType();
             if (!$type) {
-                throw new Exception("Parameter {$parameter->getName()} in $className has no type");
+                throw new Exception('Cannot autowire parameter without type');
             }
             $params[] = $this->get($type->getName());
         }

-        return new $className(...$params);
+        return $params;
     }
 }
\ No newline at end of file
diff --git a/src/Http/GenericRouter.php b/src/Http/GenericRouter.php
index d5ffb95..334ecac 100644
--- a/src/Http/GenericRouter.php
+++ b/src/Http/GenericRouter.php
@@ -17,7 +17,7 @@ class GenericRouter implements RouterInterface
     }

     /**
-     * @throws RouteNotFoundException|ClassNotFoundException
+     * @throws RouteNotFoundException
      */
     public function dispatch(RequestInterface $request): ResponseInterface
     {
@@ -28,11 +28,11 @@ class GenericRouter implements RouterInterface
                 continue;
             }

-            $controller = $this->container->get($route->getController());
-            $method = $route->getMethod();
-//            $params = $this->autowire($route) + $this->resolveParams($route->getRoute()->path, $request->getUri());
-            $params = $this->resolveParams($route->getRoute()->path, $request->getUri());
-            return $controller->$method(...$params);
+            return $this->container->call(
+                $route->getController(),
+                $route->getMethod(),
+                $params,
+            );
         }

         throw new RouteNotFoundException($request);
@@ -40,6 +40,10 @@ class GenericRouter implements RouterInterface

     private function resolveParams(string $routeUri, string $requestUri): ?array
     {
+        if ($routeUri === $requestUri) {
+            return [];
+        }
+
         $result = preg_match_all('/{(\w+)}/', $routeUri, $tokens);

         if (!$result) {
@@ -55,27 +59,11 @@ class GenericRouter implements RouterInterface
             ) . '$/';

         $result = preg_match($matchingRegex, $requestUri, $matches);
-        if ($result === 0) {
-            return [];
+        if (!$result) {
+            return null;
         }
         $matches = array_slice($matches, 1);

         return array_combine(array_map(fn($token) => trim($token, '{}'), $tokens), $matches);
     }
-
-    private function autowire(RouteConfig $route): array
-    {
-        $reflectionMethod = new ReflectionMethod($route->getController(), $route->getMethod());
-
-        $params = [];
-        foreach ($reflectionMethod->getParameters() as $parameter) {
-            $type = $parameter->getType();
-            if (!$type) {
-                throw new Exception('Cannot autowire parameter without type');
-            }
-            $params[] = $this->container->get($type->getName());
-        }
-
-        return $params;
-    }
 }
\ No newline at end of file
2023-08-10 17:57:25 +02:00
Tim 2fd6e50b90 Start with controller parameter injection
diff --git a/src/Http/GenericRouter.php b/src/Http/GenericRouter.php
index 23e9311..d5ffb95 100644
--- a/src/Http/GenericRouter.php
+++ b/src/Http/GenericRouter.php
@@ -22,16 +22,47 @@ class GenericRouter implements RouterInterface
     public function dispatch(RequestInterface $request): ResponseInterface
     {
         foreach ($this->config->getRoutes() as $route) {
-            if ($route->getRoute()->path === $request->getUri()) {
-                $controller = $this->container->get($route->getController());
-                $method = $route->getMethod();
-                return $controller->$method(...$this->autowire($route));
+            $params = $this->resolveParams($route->getRoute()->path, $request->getUri());
+
+            if ($params === null) {
+                continue;
             }
+
+            $controller = $this->container->get($route->getController());
+            $method = $route->getMethod();
+//            $params = $this->autowire($route) + $this->resolveParams($route->getRoute()->path, $request->getUri());
+            $params = $this->resolveParams($route->getRoute()->path, $request->getUri());
+            return $controller->$method(...$params);
         }

         throw new RouteNotFoundException($request);
     }

+    private function resolveParams(string $routeUri, string $requestUri): ?array
+    {
+        $result = preg_match_all('/{(\w+)}/', $routeUri, $tokens);
+
+        if (!$result) {
+            return null;
+        }
+
+        $tokens = $tokens[0];
+
+        $matchingRegex = '/^' . str_replace(
+                ['/', ...$tokens],
+                ['\/', ...array_fill(0, count($tokens), '([\w\s]+)')],
+                $routeUri
+            ) . '$/';
+
+        $result = preg_match($matchingRegex, $requestUri, $matches);
+        if ($result === 0) {
+            return [];
+        }
+        $matches = array_slice($matches, 1);
+
+        return array_combine(array_map(fn($token) => trim($token, '{}'), $tokens), $matches);
+    }
+
     private function autowire(RouteConfig $route): array
     {
         $reflectionMethod = new ReflectionMethod($route->getController(), $route->getMethod());
2023-08-09 18:17:34 +02:00
Tim 6f247f3ab7 Make route debug more readable 2023-08-09 00:59:07 +02:00
Tim fdd1744b35 Update README 2023-08-09 00:55:11 +02:00
Tim 64ff0c243d Add controller method autowiring
diff --git a/app/Controller/HelloWorldController.php b/app/Controller/HelloWorldController.php
index 8361e9d..282a990 100644
--- a/app/Controller/HelloWorldController.php
+++ b/app/Controller/HelloWorldController.php
@@ -5,6 +5,7 @@ namespace App\Controller;
 use Ardent\Undercurrent\Attribute\Route;
 use Ardent\Undercurrent\Http\GenericResponse;
 use Ardent\Undercurrent\Http\ResponseInterface;
+use Ardent\Undercurrent\Http\RouterConfig;
 use Ardent\Undercurrent\Http\StatusEnum;

 class HelloWorldController
@@ -26,4 +27,21 @@ class HelloWorldController
     {
         return new GenericResponse('Hello World!');
     }
+
+    #[Route('/routes')]
+    public function routes(RouterConfig $config): ResponseInterface
+    {
+        $routes = implode('<br>', array_map(
+            fn($route) => $route->getRoute()->path,
+            $config->getRoutes()
+        ));
+
+        return new GenericResponse($routes);
+    }
+
+    #[Route('/world/{name}')]
+    public function world(string $name): ResponseInterface
+    {
+        return new GenericResponse("Hello $name!");
+    }
 }
\ No newline at end of file
diff --git a/src/Http/GenericRouter.php b/src/Http/GenericRouter.php
index b2d8e1d..23e9311 100644
--- a/src/Http/GenericRouter.php
+++ b/src/Http/GenericRouter.php
@@ -4,6 +4,8 @@ namespace Ardent\Undercurrent\Http;

 use Ardent\Undercurrent\Container\ClassNotFoundException;
 use Ardent\Undercurrent\Container\ContainerInterface;
+use Exception;
+use ReflectionMethod;

 class GenericRouter implements RouterInterface
 {
@@ -23,10 +25,26 @@ class GenericRouter implements RouterInterface
             if ($route->getRoute()->path === $request->getUri()) {
                 $controller = $this->container->get($route->getController());
                 $method = $route->getMethod();
-                return $controller->$method();
+                return $controller->$method(...$this->autowire($route));
             }
         }

         throw new RouteNotFoundException($request);
     }
+
+    private function autowire(RouteConfig $route): array
+    {
+        $reflectionMethod = new ReflectionMethod($route->getController(), $route->getMethod());
+
+        $params = [];
+        foreach ($reflectionMethod->getParameters() as $parameter) {
+            $type = $parameter->getType();
+            if (!$type) {
+                throw new Exception('Cannot autowire parameter without type');
+            }
+            $params[] = $this->container->get($type->getName());
+        }
+
+        return $params;
+    }
 }
\ No newline at end of file
2023-08-09 00:49:47 +02:00
Tim 5d6ad29db8 Expand the response with headers and set status code
diff --git a/app/Controller/BaseController.php b/app/Controller/BaseController.php
deleted file mode 100644
index 8e24148..0000000
--- a/app/Controller/BaseController.php
+++ /dev/null
@@ -1,16 +0,0 @@
-<?php
-
-namespace App\Controller;
-
-use Ardent\Undercurrent\Attribute\Route;
-use Ardent\Undercurrent\Http\GenericResponse;
-use Ardent\Undercurrent\Http\ResponseInterface;
-
-class BaseController
-{
-    #[Route('/hello')]
-    public function helloWorld(): ResponseInterface
-    {
-        return new GenericResponse('Hello World!');
-    }
-}
\ No newline at end of file
diff --git a/app/Controller/HelloWorldController.php b/app/Controller/HelloWorldController.php
new file mode 100644
index 0000000..8361e9d
--- /dev/null
+++ b/app/Controller/HelloWorldController.php
@@ -0,0 +1,29 @@
+<?php
+
+namespace App\Controller;
+
+use Ardent\Undercurrent\Attribute\Route;
+use Ardent\Undercurrent\Http\GenericResponse;
+use Ardent\Undercurrent\Http\ResponseInterface;
+use Ardent\Undercurrent\Http\StatusEnum;
+
+class HelloWorldController
+{
+    #[Route('/')]
+    public function index(): ResponseInterface
+    {
+        return new GenericResponse('Index, <a href="/hello">Hello</a>');
+    }
+
+    #[Route('/error')]
+    public function error(): ResponseInterface
+    {
+        return new GenericResponse('error', StatusEnum::NOT_FOUND);
+    }
+
+    #[Route('/hello')]
+    public function hello(): ResponseInterface
+    {
+        return new GenericResponse('Hello World!');
+    }
+}
\ No newline at end of file
diff --git a/app/Kernel.php b/app/Kernel.php
index a365d39..8b28577 100644
--- a/app/Kernel.php
+++ b/app/Kernel.php
@@ -2,7 +2,7 @@

 namespace App;

-use App\Controller\BaseController;
+use App\Controller\HelloWorldController;
 use Ardent\Undercurrent\Container\ContainerInterface;
 use Ardent\Undercurrent\Kernel\BaseKernel;

@@ -11,7 +11,7 @@ class Kernel extends BaseKernel
     protected function dependencies(ContainerInterface $container): void
     {
         $this->addControllers($container, [
-            BaseController::class,
+            HelloWorldController::class,
         ]);
     }
 }
\ No newline at end of file
diff --git a/src/Container/ContainerInterface.php b/src/Container/ContainerInterface.php
index bd50325..7b55a94 100644
--- a/src/Container/ContainerInterface.php
+++ b/src/Container/ContainerInterface.php
@@ -16,7 +16,7 @@ interface ContainerInterface
      * @template TClassName
      * @param class-string<TClassName> $className
      * @return TClassName
-     * @throws Exception
+     * @throws ClassNotFoundException
      */
     public function get(string $className): object;
 }
\ No newline at end of file
diff --git a/src/Http/GenericResponse.php b/src/Http/GenericResponse.php
index d80d350..18852ff 100644
--- a/src/Http/GenericResponse.php
+++ b/src/Http/GenericResponse.php
@@ -7,6 +7,7 @@ class GenericResponse implements ResponseInterface
     public function __construct(
         private readonly string     $body,
         private readonly StatusEnum $status = StatusEnum::OK,
+        private readonly array      $headers = [],
     )
     {
     }
@@ -20,4 +21,12 @@ class GenericResponse implements ResponseInterface
     {
         return $this->body;
     }
+
+    /**
+     * @inheritDoc
+     */
+    public function getHeaders(): array
+    {
+        return $this->headers;
+    }
 }
\ No newline at end of file
diff --git a/src/Http/GenericRouter.php b/src/Http/GenericRouter.php
index 73ce8f1..b2d8e1d 100644
--- a/src/Http/GenericRouter.php
+++ b/src/Http/GenericRouter.php
@@ -2,9 +2,7 @@

 namespace Ardent\Undercurrent\Http;

-use App\Controller\BaseController;
-use Ardent\Undercurrent\Attribute\Route;
-use Ardent\Undercurrent\Collector\ClassAttributeCollector;
+use Ardent\Undercurrent\Container\ClassNotFoundException;
 use Ardent\Undercurrent\Container\ContainerInterface;

 class GenericRouter implements RouterInterface
@@ -16,6 +14,9 @@ class GenericRouter implements RouterInterface
     {
     }

+    /**
+     * @throws RouteNotFoundException|ClassNotFoundException
+     */
     public function dispatch(RequestInterface $request): ResponseInterface
     {
         foreach ($this->config->getRoutes() as $route) {
diff --git a/src/Http/ResponseInterface.php b/src/Http/ResponseInterface.php
index abdc290..8047eb9 100644
--- a/src/Http/ResponseInterface.php
+++ b/src/Http/ResponseInterface.php
@@ -7,4 +7,9 @@ interface ResponseInterface
     public function getStatus(): StatusEnum;

     public function getBody(): string;
+
+    /**
+     * @return array<string>
+     */
+    public function getHeaders(): array;
 }
\ No newline at end of file
diff --git a/src/Kernel/BaseKernel.php b/src/Kernel/BaseKernel.php
index 7122dcc..634cafc 100644
--- a/src/Kernel/BaseKernel.php
+++ b/src/Kernel/BaseKernel.php
@@ -33,9 +33,16 @@ class BaseKernel
             $_SERVER['REQUEST_URI'],
             $_REQUEST,
         );
-
         $router = $container->get(RouterInterface::class);
-        echo $router->dispatch($request)->getBody();
+        $response = $router->dispatch($request);
+
+        http_response_code($response->getStatus()->value);
+
+        foreach ($response->getHeaders() as $header) {
+            header($header);
+        }
+
+        echo $response->getBody();
     }

     protected function addControllers(ContainerInterface $container, array $controllers): void
2023-08-08 17:18:18 +02:00
Tim 7c13318b9a Introduce ClassNotFoundException for container 2023-08-08 16:56:55 +02:00
Tim a854480ff1 Implement basic version of route matching
Make RouteConfig more readable
diff --git a/app/Kernel.php b/app/Kernel.php
index 4425355..a365d39 100644
--- a/app/Kernel.php
+++ b/app/Kernel.php
@@ -10,7 +10,7 @@ class Kernel extends BaseKernel
 {
     protected function dependencies(ContainerInterface $container): void
     {
-        $this->addRoutes($container, [
+        $this->addControllers($container, [
             BaseController::class,
         ]);
     }
diff --git a/src/Attribute/Route.php b/src/Attribute/Route.php
index 6bbd272..7129654 100644
--- a/src/Attribute/Route.php
+++ b/src/Attribute/Route.php
@@ -2,13 +2,15 @@

 namespace Ardent\Undercurrent\Attribute;

+use Ardent\Undercurrent\Http\MethodEnum;
 use Attribute;

 #[Attribute]
 class Route
 {
     public function __construct(
-        public string $path,
+        public string      $path,
+        public ?MethodEnum $method = null,
     )
     {
     }
diff --git a/src/Http/GenericRouter.php b/src/Http/GenericRouter.php
index fa1bc6a..73ce8f1 100644
--- a/src/Http/GenericRouter.php
+++ b/src/Http/GenericRouter.php
@@ -11,15 +11,21 @@ class GenericRouter implements RouterInterface
 {
     public function __construct(
         private readonly ContainerInterface $container,
-        private readonly RouterConfig $config,
+        private readonly RouterConfig       $config,
     )
     {
     }

     public function dispatch(RequestInterface $request): ResponseInterface
     {
-        $controller = $this->container->get(BaseController::class);
-        $method = 'helloWorld';
-        return $controller->$method();
+        foreach ($this->config->getRoutes() as $route) {
+            if ($route->getRoute()->path === $request->getUri()) {
+                $controller = $this->container->get($route->getController());
+                $method = $route->getMethod();
+                return $controller->$method();
+            }
+        }
+
+        throw new RouteNotFoundException($request);
     }
 }
\ No newline at end of file
diff --git a/src/Http/RouteConfig.php b/src/Http/RouteConfig.php
new file mode 100644
index 0000000..00c2cb0
--- /dev/null
+++ b/src/Http/RouteConfig.php
@@ -0,0 +1,31 @@
+<?php
+
+namespace Ardent\Undercurrent\Http;
+
+use Ardent\Undercurrent\Attribute\Route;
+
+class RouteConfig
+{
+    public function __construct(
+        private readonly Route  $route,
+        private readonly string $controller,
+        private readonly string $method
+    )
+    {
+    }
+
+    public function getRoute(): Route
+    {
+        return $this->route;
+    }
+
+    public function getController(): string
+    {
+        return $this->controller;
+    }
+
+    public function getMethod(): string
+    {
+        return $this->method;
+    }
+}
\ No newline at end of file
diff --git a/src/Http/RouteNotFoundException.php b/src/Http/RouteNotFoundException.php
new file mode 100644
index 0000000..081c625
--- /dev/null
+++ b/src/Http/RouteNotFoundException.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace Ardent\Undercurrent\Http;
+
+use Exception;
+
+class RouteNotFoundException extends Exception
+{
+    public function __construct(RequestInterface $request)
+    {
+        parent::__construct(sprintf(
+            'No controller found for uri: %s',
+            $request->getUri()
+        ));
+    }
+}
\ No newline at end of file
diff --git a/src/Http/RouterConfig.php b/src/Http/RouterConfig.php
index 60e5af7..eec5cde 100644
--- a/src/Http/RouterConfig.php
+++ b/src/Http/RouterConfig.php
@@ -2,16 +2,45 @@

 namespace Ardent\Undercurrent\Http;

+use Ardent\Undercurrent\Attribute\Route;
+use ReflectionAttribute;
+use ReflectionClass;
+
 class RouterConfig
 {
-    public function __construct(
-        private readonly array $controllers = [],
-    )
+    /**
+     * @var array<RouteConfig>
+     */
+    private array $routes = [];
+
+    public function addController(string $controller): self
     {
+        $reflectionClass = new ReflectionClass($controller);
+
+        foreach ($reflectionClass->getMethods() as $method) {
+            $attributes = $method->getAttributes(Route::class, ReflectionAttribute::IS_INSTANCEOF);
+
+            if (count($attributes) === 0) {
+                continue;
+            }
+
+            $route = $attributes[0]->newInstance();
+
+            $this->routes[] = new RouteConfig(
+                $route,
+                $controller,
+                $method->getName(),
+            );
+        }
+
+        return $this;
     }

-    public function getControllers(): array
+    /**
+     * @return array<RouteConfig>
+     */
+    public function getRoutes(): array
     {
-        return $this->controllers;
+        return $this->routes;
     }
 }
\ No newline at end of file
diff --git a/src/Kernel/BaseKernel.php b/src/Kernel/BaseKernel.php
index 149aa8b..7122dcc 100644
--- a/src/Kernel/BaseKernel.php
+++ b/src/Kernel/BaseKernel.php
@@ -38,13 +38,16 @@ class BaseKernel
         echo $router->dispatch($request)->getBody();
     }

-    protected function addRoutes(ContainerInterface $container, array $routes): void
+    protected function addControllers(ContainerInterface $container, array $controllers): void
     {
-        foreach ($routes as $route) {
-            $container->add($route);
+        $config = new RouterConfig();
+
+        foreach ($controllers as $controller) {
+            $container->add($controller);
+            $config->addController($controller);
         }

-        $container->add(RouterConfig::class, fn() => new RouterConfig($routes));
+        $container->add(RouterConfig::class, fn() => $config);
     }

     protected function dependencies(ContainerInterface $container): void
2023-08-08 16:53:27 +02:00
Tim 96e833fb4b Move route config to app
diff --git a/app/Kernel.php b/app/Kernel.php
index 0e8e047..4425355 100644
--- a/app/Kernel.php
+++ b/app/Kernel.php
@@ -2,8 +2,16 @@

 namespace App;

+use App\Controller\BaseController;
+use Ardent\Undercurrent\Container\ContainerInterface;
 use Ardent\Undercurrent\Kernel\BaseKernel;

 class Kernel extends BaseKernel
 {
+    protected function dependencies(ContainerInterface $container): void
+    {
+        $this->addRoutes($container, [
+            BaseController::class,
+        ]);
+    }
 }
\ No newline at end of file
diff --git a/src/Http/RouterConfig.php b/src/Http/RouterConfig.php
index ad856d6..60e5af7 100644
--- a/src/Http/RouterConfig.php
+++ b/src/Http/RouterConfig.php
@@ -4,7 +4,6 @@ namespace Ardent\Undercurrent\Http;

 class RouterConfig
 {
-
     public function __construct(
         private readonly array $controllers = [],
     )
diff --git a/src/Kernel/BaseKernel.php b/src/Kernel/BaseKernel.php
index a6732f4..149aa8b 100644
--- a/src/Kernel/BaseKernel.php
+++ b/src/Kernel/BaseKernel.php
@@ -2,7 +2,6 @@

 namespace Ardent\Undercurrent\Kernel;

-use App\Controller\BaseController;
 use Ardent\Undercurrent\Container\ContainerInterface;
 use Ardent\Undercurrent\Container\GenericContainer;
 use Ardent\Undercurrent\Http\GenericRequest;
@@ -20,12 +19,9 @@ class BaseKernel
             ->alias(RouterInterface::class, GenericRouter::class)
             ->alias(ContainerInterface::class, GenericContainer::class)
             ->add(GenericContainer::class, fn($container) => $container)
-            ->add(GenericRouter::class)
-            ->add(BaseController::class);
+            ->add(GenericRouter::class);

-        $container->add(RouterConfig::class, fn() => new RouterConfig([
-            BaseController::class,
-        ]));
+        $this->dependencies($container);

         $this->render($container);
     }
@@ -41,4 +37,17 @@ class BaseKernel
         $router = $container->get(RouterInterface::class);
         echo $router->dispatch($request)->getBody();
     }
+
+    protected function addRoutes(ContainerInterface $container, array $routes): void
+    {
+        foreach ($routes as $route) {
+            $container->add($route);
+        }
+
+        $container->add(RouterConfig::class, fn() => new RouterConfig($routes));
+    }
+
+    protected function dependencies(ContainerInterface $container): void
+    {
+    }
 }
\ No newline at end of file
2023-08-08 15:42:57 +02:00
Tim a55d1c3c2e Properly implement the router with config and interfaces
Expand the container with aliases and argument autowiring
2023-08-07 17:51:53 +02:00
Tim e9a636554f Add response and request objects and interfaces
diff --git a/README.md b/README.md
index 4b64da1..c860957 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,7 @@
 Todos:
 - [ ] Collect routes properly
 - [ ] Process routes correctly
+- [ ] Add request and response objects

diff --git a/app/Controller/BaseController.php b/app/Controller/BaseController.php
index aa0a79b..7df9387 100644
--- a/app/Controller/BaseController.php
+++ b/app/Controller/BaseController.php
@@ -3,12 +3,14 @@
 namespace App\Controller;

 use Ardent\Undercurrent\Attribute\Route;
+use Ardent\Undercurrent\Http\GenericResponse;
+use Ardent\Undercurrent\Http\ResponseInterface;

 class BaseController
 {
     #[Route('/hello')]
-    public function HelloWorld(): string
+    public function HelloWorld(): ResponseInterface
     {
-        return 'Hello, World!';
+        return new GenericResponse('Hello World!');
     }
 }
\ No newline at end of file
diff --git a/src/Http/GenericRequest.php b/src/Http/GenericRequest.php
new file mode 100644
index 0000000..c758a40
--- /dev/null
+++ b/src/Http/GenericRequest.php
@@ -0,0 +1,30 @@
+<?php
+
+namespace Ardent\Undercurrent\Http;
+
+class GenericRequest implements RequestInterface
+{
+
+    public function __construct(
+        private readonly MethodEnum $method,
+        private readonly string $uri,
+        private readonly array  $body,
+    )
+    {
+    }
+
+    public function getMethod(): MethodEnum
+    {
+        return $this->method;
+    }
+
+    public function getUri(): string
+    {
+        return $this->uri;
+    }
+
+    public function getBody(): array
+    {
+        return $this->body;
+    }
+}
\ No newline at end of file
diff --git a/src/Http/GenericResponse.php b/src/Http/GenericResponse.php
new file mode 100644
index 0000000..d80d350
--- /dev/null
+++ b/src/Http/GenericResponse.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace Ardent\Undercurrent\Http;
+
+class GenericResponse implements ResponseInterface
+{
+    public function __construct(
+        private readonly string     $body,
+        private readonly StatusEnum $status = StatusEnum::OK,
+    )
+    {
+    }
+
+    public function getStatus(): StatusEnum
+    {
+        return $this->status;
+    }
+
+    public function getBody(): string
+    {
+        return $this->body;
+    }
+}
\ No newline at end of file
diff --git a/src/Controller/GenericRouter.php b/src/Http/GenericRouter.php
similarity index 89%
rename from src/Controller/GenericRouter.php
rename to src/Http/GenericRouter.php
index 28f75b1..0332ce5 100644
--- a/src/Controller/GenericRouter.php
+++ b/src/Http/GenericRouter.php
@@ -1,6 +1,6 @@
 <?php

-namespace Ardent\Undercurrent\Controller;
+namespace Ardent\Undercurrent\Http;

 use App\Controller\BaseController;
 use Ardent\Undercurrent\Attribute\Route;
diff --git a/src/Http/MethodEnum.php b/src/Http/MethodEnum.php
new file mode 100644
index 0000000..76948c9
--- /dev/null
+++ b/src/Http/MethodEnum.php
@@ -0,0 +1,16 @@
+<?php
+
+namespace Ardent\Undercurrent\Http;
+
+enum MethodEnum: string
+{
+    case GET = 'GET';
+    case POST = 'POST';
+//    case PUT = 'PUT';
+//    case PATCH = 'PATCH';
+//    case DELETE = 'DELETE';
+//    case HEAD = 'HEAD';
+//    case OPTIONS = 'OPTIONS';
+//    case TRACE = 'TRACE';
+//    case CONNECT = 'CONNECT';
+}
diff --git a/src/Http/RequestInterface.php b/src/Http/RequestInterface.php
new file mode 100644
index 0000000..0f564b8
--- /dev/null
+++ b/src/Http/RequestInterface.php
@@ -0,0 +1,12 @@
+<?php
+
+namespace Ardent\Undercurrent\Http;
+
+interface RequestInterface
+{
+    public function getMethod(): MethodEnum;
+
+    public function getUri(): string;
+
+    public function getBody(): array;
+}
\ No newline at end of file
diff --git a/src/Http/ResponseInterface.php b/src/Http/ResponseInterface.php
new file mode 100644
index 0000000..abdc290
--- /dev/null
+++ b/src/Http/ResponseInterface.php
@@ -0,0 +1,10 @@
+<?php
+
+namespace Ardent\Undercurrent\Http;
+
+interface ResponseInterface
+{
+    public function getStatus(): StatusEnum;
+
+    public function getBody(): string;
+}
\ No newline at end of file
diff --git a/src/Http/RouterInterface.php b/src/Http/RouterInterface.php
new file mode 100644
index 0000000..186baac
--- /dev/null
+++ b/src/Http/RouterInterface.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace Ardent\Undercurrent\Http;
+
+interface RouterInterface
+{
+    public function dispatch(RequestInterface $request): ResponseInterface;
+}
\ No newline at end of file
diff --git a/src/Http/StatusEnum.php b/src/Http/StatusEnum.php
new file mode 100644
index 0000000..6dce100
--- /dev/null
+++ b/src/Http/StatusEnum.php
@@ -0,0 +1,10 @@
+<?php
+
+namespace Ardent\Undercurrent\Http;
+
+enum StatusEnum: int
+{
+    case OK = 200;
+    case FORBIDDEN = 403;
+    case NOT_FOUND = 404;
+}
diff --git a/src/Kernel/BaseKernel.php b/src/Kernel/BaseKernel.php
index 6bc8ed5..05ecf06 100644
--- a/src/Kernel/BaseKernel.php
+++ b/src/Kernel/BaseKernel.php
@@ -4,11 +4,12 @@ namespace Ardent\Undercurrent\Kernel;

 use App\Controller\BaseController;
 use Ardent\Undercurrent\Container\GenericContainer;
-use Ardent\Undercurrent\Controller\GenericRouter;
+use Ardent\Undercurrent\Http\GenericRouter;
+use Ardent\Undercurrent\Http\ResponseInterface;

 class BaseKernel
 {
-    public function __invoke()
+    public function __invoke(): void
     {
         $container = new GenericContainer();
         $container->add(GenericRouter::class);
@@ -23,6 +24,6 @@ class BaseKernel
         $route = $router->getRoute($_SERVER['REQUEST_URI']);
         $controller = $container->get($route['controller']);
         $method = $route['method'];
-        echo $controller->$method();
+        echo $controller->$method()->getBody();
     }
 }
\ No newline at end of file
2023-08-07 14:23:00 +02:00
Tim 68ccd37f07 Move public to make it compatible with symfony nginx files 2023-07-28 16:45:32 +02:00
Tim 503d8c524a First working version with non working routes 2023-07-28 16:14:55 +02:00