First working version with non working routes

This commit is contained in:
Tim 2023-07-28 16:14:55 +02:00
commit 503d8c524a
12 changed files with 288 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
vendor/

9
README.md Normal file
View 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.

View 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
View File

@ -0,0 +1,9 @@
<?php
namespace App;
use Ardent\Undercurrent\Kernel\BaseKernel;
class Kernel extends BaseKernel
{
}

9
app/public/index.php Normal file
View 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
View 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
View 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
View File

@ -0,0 +1,15 @@
<?php
namespace Ardent\Undercurrent\Attribute;
use Attribute;
#[Attribute]
class Route
{
public function __construct(
public string $path,
)
{
}
}

View 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;
}
}

View 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;
}
}

View 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
View 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();
}
}