First working version with non working routes
This commit is contained in:
		
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | vendor/ | ||||||
							
								
								
									
										9
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | |||||||
|  | Todos: | ||||||
|  | - [ ] Collect routes properly | ||||||
|  | - [ ] Process routes correctly | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | Routing: | ||||||
|  | Currently the routes are hardcoded, we need we way to process | ||||||
|  | all classes maybe and sort them into a list of routes. | ||||||
							
								
								
									
										14
									
								
								app/Controller/BaseController.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								app/Controller/BaseController.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | |||||||
|  | <?php | ||||||
|  |  | ||||||
|  | namespace App\Controller; | ||||||
|  |  | ||||||
|  | use Ardent\Undercurrent\Attribute\Route; | ||||||
|  |  | ||||||
|  | class BaseController | ||||||
|  | { | ||||||
|  |     #[Route('/hello')] | ||||||
|  |     public function HelloWorld(): string | ||||||
|  |     { | ||||||
|  |         return 'Hello, World!'; | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										9
									
								
								app/Kernel.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								app/Kernel.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | |||||||
|  | <?php | ||||||
|  |  | ||||||
|  | namespace App; | ||||||
|  |  | ||||||
|  | use Ardent\Undercurrent\Kernel\BaseKernel; | ||||||
|  |  | ||||||
|  | class Kernel extends BaseKernel | ||||||
|  | { | ||||||
|  | } | ||||||
							
								
								
									
										9
									
								
								app/public/index.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								app/public/index.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | |||||||
|  | <?php | ||||||
|  |  | ||||||
|  | use App\Kernel; | ||||||
|  |  | ||||||
|  | require_once dirname(__DIR__).'/../vendor/autoload.php'; | ||||||
|  |  | ||||||
|  | $kernel = new Kernel(); | ||||||
|  |  | ||||||
|  | $kernel(); | ||||||
							
								
								
									
										15
									
								
								composer.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								composer.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | |||||||
|  | { | ||||||
|  |     "name": "ardent/undercurrent", | ||||||
|  |     "description": "A tiny no nonsense php framework", | ||||||
|  |     "type": "library", | ||||||
|  |     "require": { | ||||||
|  |         "php": "^8.1" | ||||||
|  |     }, | ||||||
|  |     "autoload": { | ||||||
|  |         "psr-4": { | ||||||
|  |             "Ardent\\Undercurrent\\": "src/", | ||||||
|  |             "App\\": "app/" | ||||||
|  |         } | ||||||
|  |     }, | ||||||
|  |     "license": "GPL-3.0-only" | ||||||
|  | } | ||||||
							
								
								
									
										20
									
								
								composer.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								composer.lock
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | |||||||
|  | { | ||||||
|  |     "_readme": [ | ||||||
|  |         "This file locks the dependencies of your project to a known state", | ||||||
|  |         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", | ||||||
|  |         "This file is @generated automatically" | ||||||
|  |     ], | ||||||
|  |     "content-hash": "04c6989f1bd672a6accd35142e2adac1", | ||||||
|  |     "packages": [], | ||||||
|  |     "packages-dev": [], | ||||||
|  |     "aliases": [], | ||||||
|  |     "minimum-stability": "stable", | ||||||
|  |     "stability-flags": [], | ||||||
|  |     "prefer-stable": false, | ||||||
|  |     "prefer-lowest": false, | ||||||
|  |     "platform": { | ||||||
|  |         "php": "^8.1" | ||||||
|  |     }, | ||||||
|  |     "platform-dev": [], | ||||||
|  |     "plugin-api-version": "2.2.0" | ||||||
|  | } | ||||||
							
								
								
									
										15
									
								
								src/Attribute/Route.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/Attribute/Route.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | |||||||
|  | <?php | ||||||
|  |  | ||||||
|  | namespace Ardent\Undercurrent\Attribute; | ||||||
|  |  | ||||||
|  | use Attribute; | ||||||
|  |  | ||||||
|  | #[Attribute] | ||||||
|  | class Route | ||||||
|  | { | ||||||
|  |     public function __construct( | ||||||
|  |         public string $path, | ||||||
|  |     ) | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										83
									
								
								src/Collector/ClassAttributeCollector.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								src/Collector/ClassAttributeCollector.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,83 @@ | |||||||
|  | <?php | ||||||
|  |  | ||||||
|  | namespace Ardent\Undercurrent\Collector; | ||||||
|  |  | ||||||
|  | use RecursiveDirectoryIterator; | ||||||
|  | use RecursiveIteratorIterator; | ||||||
|  | use ReflectionClass; | ||||||
|  |  | ||||||
|  | class ClassAttributeCollector | ||||||
|  | { | ||||||
|  |     public function __construct( | ||||||
|  |         private readonly string $path, | ||||||
|  |         private readonly string $attribute, | ||||||
|  |     ) | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public function collect(): array | ||||||
|  |     { | ||||||
|  |         $classes = []; | ||||||
|  |         $files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->path)); | ||||||
|  |         foreach ($files as $file) { | ||||||
|  |             if ($file->getExtension() !== 'php') { | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  |             $class = $this->getClassFromFile($file); | ||||||
|  |             if (!$class) { | ||||||
|  |                 continue; | ||||||
|  |             } | ||||||
|  |             $classes[] = $class; | ||||||
|  |         } | ||||||
|  |         return $classes; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private function getClassFromFile(mixed $file) | ||||||
|  |     { | ||||||
|  |         $contents = file_get_contents($file->getPathname()); | ||||||
|  |         $tokens = token_get_all($contents); | ||||||
|  |         $namespace = ''; | ||||||
|  |         $class = ''; | ||||||
|  |         $classFound = false; | ||||||
|  |         $namespaceFound = false; | ||||||
|  |         foreach ($tokens as $token) { | ||||||
|  |             if (is_array($token)) { | ||||||
|  |                 if ($token[0] === T_NAMESPACE) { | ||||||
|  |                     $namespaceFound = true; | ||||||
|  |                     continue; | ||||||
|  |                 } | ||||||
|  |                 if ($token[0] === T_CLASS) { | ||||||
|  |                     $classFound = true; | ||||||
|  |                     continue; | ||||||
|  |                 } | ||||||
|  |                 if ($namespaceFound && $token[0] === T_STRING) { | ||||||
|  |                     $namespace .= $token[1] . '\\'; | ||||||
|  |                     continue; | ||||||
|  |                 } | ||||||
|  |                 if ($classFound && $token[0] === T_STRING) { | ||||||
|  |                     $class = $token[1]; | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         if (!$class) { | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  |         $class = $namespace . $class; | ||||||
|  |         $attributes = $this->getClassAttributes($class); | ||||||
|  |         if (!in_array($this->attribute, $attributes)) { | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  |         return $class; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private function getClassAttributes(string $class): array | ||||||
|  |     { | ||||||
|  |         $attributes = []; | ||||||
|  |         $reflection = new ReflectionClass($class); | ||||||
|  |         foreach ($reflection->getAttributes() as $attribute) { | ||||||
|  |             $attributes[] = $attribute->getName(); | ||||||
|  |         } | ||||||
|  |         return $attributes; | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										54
									
								
								src/Container/GenericContainer.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								src/Container/GenericContainer.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,54 @@ | |||||||
|  | <?php | ||||||
|  |  | ||||||
|  | namespace Ardent\Undercurrent\Container; | ||||||
|  |  | ||||||
|  | use Exception; | ||||||
|  |  | ||||||
|  | class GenericContainer | ||||||
|  | { | ||||||
|  |     private array $definitions = []; | ||||||
|  |  | ||||||
|  |     private array $instances = []; | ||||||
|  |  | ||||||
|  |     public function add(string $className, ?callable $definition = null, bool $singleton = true): self | ||||||
|  |     { | ||||||
|  |         if (!$definition) { | ||||||
|  |             $definition = function () use ($className) { | ||||||
|  |                 return new $className(); | ||||||
|  |             }; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         $this->definitions[$className] = [ | ||||||
|  |             'definition' => $definition, | ||||||
|  |             'singleton' => $singleton, | ||||||
|  |         ]; | ||||||
|  |  | ||||||
|  |         return $this; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * @template TClassName | ||||||
|  |      * @param class-string<TClassName> $className | ||||||
|  |      * @return TClassName | ||||||
|  |      * @throws Exception | ||||||
|  |      */ | ||||||
|  |     public function get(string $className): object | ||||||
|  |     { | ||||||
|  |         if (!isset($this->definitions[$className])) { | ||||||
|  |             throw new Exception("Class $className not found in container"); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         $definition = $this->definitions[$className]['definition']; | ||||||
|  |         if ($this->definitions[$className]['singleton']) { | ||||||
|  |             if (isset($this->instances[$className])) { | ||||||
|  |                 return $this->instances[$className]; | ||||||
|  |             } | ||||||
|  |             $instance = $definition(); | ||||||
|  |             $this->instances[$className] = $instance; | ||||||
|  |         } else { | ||||||
|  |             $instance = $definition(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return $instance; | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										31
									
								
								src/Controller/GenericRouter.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/Controller/GenericRouter.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | |||||||
|  | <?php | ||||||
|  |  | ||||||
|  | namespace Ardent\Undercurrent\Controller; | ||||||
|  |  | ||||||
|  | use App\Controller\BaseController; | ||||||
|  | use Ardent\Undercurrent\Attribute\Route; | ||||||
|  | use Ardent\Undercurrent\Collector\ClassAttributeCollector; | ||||||
|  |  | ||||||
|  | class GenericRouter | ||||||
|  | { | ||||||
|  |     private array $routes = []; | ||||||
|  |  | ||||||
|  |     public function __construct() | ||||||
|  |     { | ||||||
|  |         $collector = new ClassAttributeCollector( | ||||||
|  |             __DIR__ . '/../../app/Controller', | ||||||
|  |             Route::class, | ||||||
|  |         ); | ||||||
|  |  | ||||||
|  |         $this->routes = $collector->collect(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public function getRoute(string $route): array | ||||||
|  |     { | ||||||
|  | //        return $this->routes[0]; | ||||||
|  |         return [ | ||||||
|  |             'controller' => BaseController::class, | ||||||
|  |             'method' => 'HelloWorld', | ||||||
|  |         ]; | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										28
									
								
								src/Kernel/BaseKernel.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/Kernel/BaseKernel.php
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | |||||||
|  | <?php | ||||||
|  |  | ||||||
|  | namespace Ardent\Undercurrent\Kernel; | ||||||
|  |  | ||||||
|  | use App\Controller\BaseController; | ||||||
|  | use Ardent\Undercurrent\Container\GenericContainer; | ||||||
|  | use Ardent\Undercurrent\Controller\GenericRouter; | ||||||
|  |  | ||||||
|  | class BaseKernel | ||||||
|  | { | ||||||
|  |     public function __invoke() | ||||||
|  |     { | ||||||
|  |         $container = new GenericContainer(); | ||||||
|  |         $container->add(GenericRouter::class); | ||||||
|  |         $container->add(BaseController::class); | ||||||
|  |  | ||||||
|  |         $this->render($container); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private function render(GenericContainer $container): void | ||||||
|  |     { | ||||||
|  |         $router = $container->get(GenericRouter::class); | ||||||
|  |         $route = $router->getRoute($_SERVER['REQUEST_URI']); | ||||||
|  |         $controller = $container->get($route['controller']); | ||||||
|  |         $method = $route['method']; | ||||||
|  |         echo $controller->$method(); | ||||||
|  |     } | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user