First commit

This commit is contained in:
Kamil Niemczycki 2022-02-22 19:07:39 +01:00
commit b67fac10bc
28 changed files with 1194 additions and 0 deletions

10
.env.example Normal file
View File

@ -0,0 +1,10 @@
APP_NAME="KamilCraft.com"
APP_DEBUG=true
APP_URL=http://127.0.0.1
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=test
DB_USERNAME=root
DB_PASSWORD=

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
.idea
vendor
.env

22
README.md Normal file
View File

@ -0,0 +1,22 @@
# KamilCraftApi
API for kamilcraft.com projects
## Installation
### Required
* PHP 8.0 or later
* Composer 2.3.x or later
### Installation of dependencies
```shell
composer install
```
## Launching
```shell
php -S localhost:80 public/index.php
```

View File

@ -0,0 +1,43 @@
<?php
namespace KamilCraftApi\App\Controllers;
use KamilCraftApi\App\Models\Category;
use KamilCraftApi\Request\Request;
use KamilCraftApi\Response;
class CategoriesController extends Controller
{
private Category $category;
public function __construct(Request $request) {
parent::__construct();
$this->category = new Category($request);
}
public function showWhereName(string $slug): Response
{
if ( ! ($category = $this->category->getCategoryWhereName($slug)) ) {
return (new Response())->json([
'message' => 'Not found category'
], 404);
}
return (new Response())->json($category);
}
public function show(int $id): Response
{
if ( ! ($category = $this->category->getCategory($id)) ) {
return (new Response())->json([
'message' => 'Not found category'
], 404);
}
return (new Response())->json($category);
}
public function __invoke(): Response
{
$categories = $this->category;
return (new Response())->json($categories());
}
}

View File

@ -0,0 +1,21 @@
<?php
namespace KamilCraftApi\App\Controllers;
use KamilCraftApi\Interfaces\ControllerInterface;
use KamilCraftApi\Response;
class Controller implements ControllerInterface
{
private Response $response;
public function __construct()
{
$this->response = new Response();
}
protected function response(): Response
{
return $this->response;
}
}

View File

@ -0,0 +1,17 @@
<?php
namespace KamilCraftApi\App\Controllers;
use KamilCraftApi\Interfaces\ControllerInterface;
use KamilCraftApi\Request\Request;
use KamilCraftApi\Response;
class HomeController implements ControllerInterface
{
public function __invoke(Request $request): Response
{
return (new Response())->json((object)[
'message' => 'Hello World'
]);
}
}

View File

@ -0,0 +1,39 @@
<?php
namespace KamilCraftApi\App\Controllers;
use KamilCraftApi\App\Models\Project;
use KamilCraftApi\Response;
class ProjectController extends Controller
{
private Project $project;
public function __construct()
{
parent::__construct();
$this->project = new Project();
}
public function showWhereCategory(string $category): Response
{
$projects = $this->project->getProjectWhereCategory($category);
return (new Response())->json($projects);
}
public function show(int $id): Response
{
if ( ! ($project = $this->project->getProject($id)) ) {
return (new Response())->json([
'message' => 'Not found project'
], 404);
}
return (new Response())->json($project);
}
public function __invoke(): Response
{
$projects = $this->project;
return (new Response())->json($projects());
}
}

78
app/Models/Category.php Normal file
View File

@ -0,0 +1,78 @@
<?php
namespace KamilCraftApi\App\Models;
use KamilCraftApi\Request\Request;
use KamilCraftApi\Response;
use stdClass;
class Category
{
const JSON_PATH = ROOT_PATH . 'resources/json';
private array $allCategory = [];
public function __construct(
private Request $request
) {
$categories = file_get_contents(self::JSON_PATH . '/Categories.json');
if ( $category = json_decode($categories) ) {
$this->allCategory = $category;
} else {
$this->parseError();
}
}
public function getCategoryWhereName(string $slug): object|bool
{
if ( ( $key = array_search($slug, array_column($this->allCategory, 'slug') ) ) !== false ) {
$category = $this->allCategory[$key];
$category->default = $category->default ?? false;
$category->visible = $category->visible ?? true;
return $category;
}
return false;
}
public function getCategory(int $id): object|bool
{
if ( ( $key = array_search($id, array_column($this->allCategory, 'id') ) ) !== false ) {
$category = $this->allCategory[$key];
$category->default = $category->default ?? false;
$category->visible = $category->visible ?? true;
return $category;
}
return false;
}
private function parseError(): void
{
if ( $this->request->getContentType() === 'application/json' ) {
(new Response())->json((object)[
'message' => 'Server error'
], 500)->render();
} else {
(new Response())(
file_get_contents(ROOT_PATH . '/errors/error500.html'),
500
)->render();
}
exit;
}
public function __invoke(): array
{
$tmp_categories = [];
foreach ($this->allCategory as $category) {
$tmp_category = new stdClass;
$tmp_category->id = $category->id;
$tmp_category->name = $category->name;
$tmp_category->slug = $category->slug;
$tmp_category->default = $category->default ?? false;
$tmp_category->visible = $category->visible ?? true;
$tmp_categories[] = $tmp_category;
}
return $tmp_categories;
}
}

72
app/Models/Project.php Normal file
View File

@ -0,0 +1,72 @@
<?php
namespace KamilCraftApi\App\Models;
use stdClass;
class Project
{
const JSON_PATH = ROOT_PATH . 'resources/json';
const SHORT_PROJECT = [
'id',
'title',
'image',
'categories',
'version'
];
private array $allProjects = [];
public function __construct()
{
$projects = file_get_contents(self::JSON_PATH . '/Projects.json');
if ( $projects = json_decode($projects) ) {
$this->allProjects = $projects;
}
}
public function getProject(int $id): object|bool
{
if ( ( $key = array_search($id, array_column($this->allProjects, 'id')) ) !== false ) {
return $this->allProjects[$key];
}
return false;
}
public function getProjectWhereCategory(string $category): array
{
$listWhereCategory = [];
foreach ( array_column($this->allProjects, 'categories') as $key => $value ) {
if ( in_array($category, $value) ) {
$listWhereCategory[] = $this->shortProjectSchema($this->allProjects[$key]);
}
}
return $listWhereCategory;
}
private function shortProjectSchema(object $project): object
{
$tmp_project = new stdClass;
foreach ($project as $key => $value) {
if ( in_array($key, self::SHORT_PROJECT) ) {
$tmp_project->{$key} = $value;
}
}
$tmp_project->short_description = substr(
explode("\n", $project->description)[0],
0,
255
);
return $tmp_project;
}
public function __invoke(): array
{
$arr_map = [];
foreach ($this->allProjects as $project) {
$arr_map[] = $this->shortProjectSchema($project);
}
return $arr_map;
}
}

25
composer.json Normal file
View File

@ -0,0 +1,25 @@
{
"name": "kamilniemczycki/kamilcraft-api",
"description": "Api for kamilcraft.com",
"keywords": ["kamilcraft", "api"],
"license": "MIT",
"type": "project",
"authors": [
{
"name": "Kamil Niemczycki",
"email": "contact@kamilcraft.com"
}
],
"minimum-stability": "dev",
"prefer-stable": true,
"require": {
"php": "^8.0",
"symfony/dotenv": "5.3.7"
},
"autoload": {
"psr-4": {
"KamilCraftApi\\": "src/",
"KamilCraftApi\\App\\": "app/"
}
}
}

158
composer.lock generated Normal file
View File

@ -0,0 +1,158 @@
{
"_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": "a90b92d03b08e514cb09f27c75280b4c",
"packages": [
{
"name": "symfony/deprecation-contracts",
"version": "v2.4.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/deprecation-contracts.git",
"reference": "5f38c8804a9e97d23e0c8d63341088cd8a22d627"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/5f38c8804a9e97d23e0c8d63341088cd8a22d627",
"reference": "5f38c8804a9e97d23e0c8d63341088cd8a22d627",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "2.4-dev"
},
"thanks": {
"name": "symfony/contracts",
"url": "https://github.com/symfony/contracts"
}
},
"autoload": {
"files": [
"function.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "A generic function and convention to trigger deprecation notices",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/deprecation-contracts/tree/v2.4.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2021-03-23T23:28:01+00:00"
},
{
"name": "symfony/dotenv",
"version": "v5.3.7",
"source": {
"type": "git",
"url": "https://github.com/symfony/dotenv.git",
"reference": "99a18c2e23f4d901c36cea2012f9f1a8e25e99e4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/dotenv/zipball/99a18c2e23f4d901c36cea2012f9f1a8e25e99e4",
"reference": "99a18c2e23f4d901c36cea2012f9f1a8e25e99e4",
"shasum": ""
},
"require": {
"php": ">=7.2.5",
"symfony/deprecation-contracts": "^2.1"
},
"require-dev": {
"symfony/process": "^4.4|^5.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Symfony\\Component\\Dotenv\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Registers environment variables from a .env file",
"homepage": "https://symfony.com",
"keywords": [
"dotenv",
"env",
"environment"
],
"support": {
"source": "https://github.com/symfony/dotenv/tree/v5.3.7"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2021-07-02T16:35:09+00:00"
}
],
"packages-dev": [],
"aliases": [],
"minimum-stability": "dev",
"stability-flags": [],
"prefer-stable": true,
"prefer-lowest": false,
"platform": {
"php": "^8.0"
},
"platform-dev": [],
"plugin-api-version": "2.0.0"
}

14
errors/error400.html Normal file
View File

@ -0,0 +1,14 @@
<!doctype html>
<html lang="pl">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Błąd 400</title>
</head>
<body>
<h1>Błąd 400</h1>
<p>Niepoprawne zapytanie!</p>
</body>
</html>

14
errors/error404.html Normal file
View File

@ -0,0 +1,14 @@
<!doctype html>
<html lang="pl">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Błąd 404</title>
</head>
<body>
<h1>Błąd 404</h1>
<p>Nie znaleziono szukanej strony!</p>
</body>
</html>

14
errors/error500.html Normal file
View File

@ -0,0 +1,14 @@
<!doctype html>
<html lang="pl">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Błąd 500</title>
</head>
<body>
<h1>Błąd 500</h1>
<p>Wystąpił wewnętrzny błąd serwera!</p>
</body>
</html>

10
public/index.php Normal file
View File

@ -0,0 +1,10 @@
<?php
const ROOT_PATH = __DIR__ . '/../';
use KamilCraftApi\Application;
require ROOT_PATH . 'vendor/autoload.php';
$app = new Application(ROOT_PATH . '.env');
$app->run();

View File

@ -0,0 +1,24 @@
[
{
"id": 1,
"name": "Wszystkie",
"slug": "all",
"default": true
},
{
"id": 3,
"name": "Prywatne",
"slug": "private"
},
{
"id": 4,
"name": "Zlecenie",
"slug": "custom"
},
{
"id": 5,
"name": "Wybrane",
"slug": "selected",
"visible": false
}
]

View File

@ -0,0 +1,24 @@
[
{
"id": 1,
"title": "Memstacja.pl",
"categories": ["selected", "private"],
"author": "Kamil Niemczycki",
"image": "assets/img/projects/kamilcraft.jpg",
"release_data": "28 sierpnia 2021",
"project_url": "https://memstacja.pl",
"version": "v1.4.9",
"description": "Memstacja.pl jest to strona z memami. Umożliwia dodanie własnego mema przez panel administracyjny, nadanie tagów i opisu. Użytkownicy mogą głosować przy użyciu serduszek, mając wpływ na pozycję danego mema na liście polecanych i TOP 10."
},
{
"id": 2,
"title": "edwardgoff-art.co.uk",
"categories": ["selected", "custom"],
"author": "Kamil Niemczycki",
"image": "assets/img/projects/edward.jpg",
"release_data": "25 sierpnia 2021",
"project_url": "https://edwardgoff-art.co.uk",
"version": "v1.1.4",
"description": "Strona jest wizytówką autora i galerią jego prac. Strona umożliwa tworzenie, a także dodawanie zdjęć i opisów dla utworzonych galerii."
}
]

32
router.php Normal file
View File

@ -0,0 +1,32 @@
<?php
use KamilCraftApi\Router\Router;
use KamilCraftApi\App\Controllers\HomeController;
use KamilCraftApi\App\Controllers\ProjectController;
use KamilCraftApi\App\Controllers\CategoriesController;
$router = new Router;
$router
->get('/', HomeController::class)
->name('home');
$router
->get('/projects', ProjectController::class)
->name('projects');
$router
->get('/projects/:id', ProjectController::class .'@show')
->name('projects.show');
$router
->get('/projects/category/:category', ProjectController::class .'@showWhereCategory')
->name('projects.category.show');
$router
->get('/categories', CategoriesController::class)
->name('categories');
$router
->get('/categories/:id', CategoriesController::class .'@show')
->name('categories.show');
$router
->get('/categories_name/:slug', CategoriesController::class .'@showWhereName')
->name('categories.name.show');
return $router;

175
src/Application.php Normal file
View File

@ -0,0 +1,175 @@
<?php
namespace KamilCraftApi;
use Exception;
use KamilCraftApi\Exceptions\ApplicationException;
use KamilCraftApi\Interfaces\ControllerInterface;
use KamilCraftApi\Interfaces\RenderInterface;
use KamilCraftApi\Request\Request;
use KamilCraftApi\Router\Router;
use KamilCraftApi\Router\RouterElement;
use ReflectionClass;
use ReflectionException;
use ReflectionMethod;
use ReflectionNamedType;
use stdClass;
class Application
{
protected Request $request;
protected Router $router;
protected ?RouterElement $selectedRouterElement;
protected string $controller = '';
public function __construct(
protected string $envFileConfig
) {
$this->request = new Request($_SERVER);
$this->router = new Router($this->request);
}
public function getRequest(): Request
{
return $this->request;
}
public function getRouter(): Router
{
return $this->router;
}
/**
* @throws Exception
*/
private function checkParameters(array $controller)
{
try {
$methodParameters = (new ReflectionMethod($controller[0], $controller[1]))->getParameters();
foreach ($methodParameters as $parameter) {
if (($type = $parameter->getType()) instanceof ReflectionNamedType) {
if (
$type->getName() === 'int' &&
isset($this->selectedRouterElement->getAttributes()[$parameter->getName()]) &&
!is_numeric($this->selectedRouterElement->getAttributes()[$parameter->getName()])
) {
throw new Exception('Error type of attribute');
}
}
}
} catch (Exception $e) {
throw new Exception($e->getMessage());
}
}
private function selectController()
{
$controller = explode('@', $this->controller);
$controller[1] = $controller[1] ?? '__invoke';
try {
if (! class_exists($controller[0]) ) {
throw new ApplicationException('Class not exists');
}
if (! method_exists($controller[0], $controller[1]) ) {
throw new ApplicationException('Method not exists');
}
$this->checkParameters($controller);
$classController = $this->createClass($controller[0]);
$loadedController = $this->createMethod($classController, $controller[1]);
$this->responseRender($loadedController);
} catch (ApplicationException $e) {
if ( $this->request->getContentType() === 'application/json' ) {
(new Response())->json((object)[
'message' => 'Server error'
], 500)->render();
} else {
(new Response())(
file_get_contents(ROOT_PATH . 'errors/error500.html'), 500
)->render();
}
} catch (Exception $e) {
if ( $this->request->getContentType() === 'application/json' ) {
(new Response())->json((object)[
'message' => 'Invalid request'
], 400)->render();
} else {
(new Response())(
file_get_contents(ROOT_PATH . 'errors/error400.html'), 400
)->render();
}
}
}
public function run(): void
{
$this->router = require ROOT_PATH . 'router.php';
if ( ! $this->router->requestIsAdded() ) {
$this->router->setRequest($this->request);
}
$selected = $this->getRouter()->selectRouter();
if ( $selected ) {
$this->controller = $selected->controller;
$this->selectedRouterElement = $selected;
$this->selectController();
} else {
if ( $this->request->getContentType() === 'application/json' ) {
(new Response())->json((object)[
'message' => 'Not found'
], 404)->render();
} else {
(new Response())(
file_get_contents(ROOT_PATH . 'errors/error404.html'), 404
)->render();
}
}
}
/**
* @throws ReflectionException
*/
private function createClass(string $className): ControllerInterface|stdClass
{
$parameters = [];
if ( $classReflection = (new ReflectionClass($className))->getConstructor() ) {
foreach ($classReflection->getParameters() as $parameter) {
if (
$parameter->getType() instanceof ReflectionNamedType &&
$parameter->getType()->getName() === Request::class
) {
$parameters[$parameter->getName()] = $this->request;
}
}
}
return new $className(...$parameters);
}
/**
* @throws ReflectionException
*/
private function createMethod(ControllerInterface|stdClass $controller, string $methodName): mixed
{
$reflectionMethod = new ReflectionMethod($controller, $methodName);
$parameters = $this->selectedRouterElement->getAttributes();
foreach ( $reflectionMethod->getParameters() as $parameter ) {
if (
$parameter->getType() instanceof ReflectionNamedType &&
$parameter->getType()->getName() === Request::class
) {
$parameters[$parameter->getName()] = $this->request;
}
}
return call_user_func_array([$controller, $methodName], $parameters);
}
private function responseRender($loadedController): void
{
if($loadedController instanceof RenderInterface) {
$loadedController->render();
exit;
}
}
}

View File

@ -0,0 +1,8 @@
<?php
namespace KamilCraftApi\Exceptions;
use Exception;
class ApplicationException extends Exception
{}

View File

@ -0,0 +1,6 @@
<?php
namespace KamilCraftApi\Interfaces;
interface ControllerInterface
{}

View File

@ -0,0 +1,8 @@
<?php
namespace KamilCraftApi\Interfaces;
interface RenderInterface
{
public function render(): void;
}

96
src/Request/Request.php Normal file
View File

@ -0,0 +1,96 @@
<?php
namespace KamilCraftApi\Request;
class Request
{
protected array $request = [
'method' => '',
'uri' => '',
'attributes' => [],
'content_type' => ''
];
protected array $uri_root = [];
public function __construct(
protected array $request_data
)
{
$this->request['method'] = $this->request_data['REQUEST_METHOD'];
$this->request['uri'] = explode('?', $this->request_data['REQUEST_URI'])[0];
$this->setAttributes();
$this->setContentType();
$this->createUriRoot($this->getUri());
}
public function getMethod(): string
{
return $this->request['method'];
}
public function getUri(): string
{
return $this->request['uri'];
}
public function getAttributes(): RequestData
{
return $this->request['attributes'];
}
public function getUriRoot(): array
{
return $this->uri_root;
}
private function setAttributes()
{
$requestData = new RequestData();
$this->setGetAttributes($requestData);
$this->setPostAttributes($requestData);
$this->request['attributes'] = $requestData;
}
private function setGetAttributes(RequestData $requestData)
{
foreach ( $_GET ?? [] as $name => $value ) {
$requestData->add('get', [ 'name' => $name, 'value' => $this->valueParser($value) ]);
}
}
private function setPostAttributes(RequestData $requestData)
{
foreach ( $_POST ?? [] as $name => $value ) {
$requestData->add('post', [ 'name' => $name, 'value' => $this->valueParser($value) ]);
}
}
private function valueParser(string $value): string|int|null
{
$value = is_numeric($value) ? (int)$value : $value;
return empty($value) ? null : $value;
}
private function setContentType()
{
$this->request['content_type'] =
$this->request_data['HTTP_CONTENT_TYPE'] ??
$this->request_data['CONTENT_TYPE'] ??
'GET';
}
public function getContentType()
{
return $this->request['content_type'];
}
private function createUriRoot(string $uri): void
{
foreach (explode('/', $uri) as $uri_root_element) {
if ( is_numeric($uri_root_element) || !empty($uri_root_element) ) {
$this->uri_root[] = $uri_root_element;
}
}
}
}

View File

@ -0,0 +1,29 @@
<?php
namespace KamilCraftApi\Request;
class RequestData
{
protected array $data = [
'get' => [],
'post' => [],
'json' => [],
'other' => []
];
public function add(string $type, array $data = []): void
{
if ( array_key_exists($type, $this->data) ) {
array_push($this->data[$type], $data);
} else {
array_push($this->data['other'], $data);
}
}
public function get(string $type, string $name): array|null
{
$key = array_search($name, array_column($this->data[$type], 'name'));
return $this->data[$type][$key] ?? null;
}
}

52
src/Response.php Normal file
View File

@ -0,0 +1,52 @@
<?php
namespace KamilCraftApi;
use KamilCraftApi\Interfaces\RenderInterface;
class Response implements RenderInterface
{
protected int $http_code = 200;
protected string $content_type = 'text/html';
protected string $body_content = '';
public function __construct(
protected string $charset = 'utf-8'
)
{}
public function json(object|array $response, int $http_code = 200): Response
{
$this->http_code = $http_code;
$this->content_type = 'application/json';
$this->body_content = json_encode($response);
return $this;
}
private function html(string $body_content = '', int $http_code = 200): Response
{
$this->http_code = $http_code;
$this->content_type = 'text/html';
$this->body_content = $body_content;
return $this;
}
private function responseHeader(): void
{
http_response_code($this->http_code);
header('Content-Type: '. $this->content_type .'; charset='. $this->charset);
}
public function render(): void
{
$this->responseHeader();
echo $this->body_content;
}
public function __invoke(string $body_content = '', int $http_code = 200): Response
{
return $this->html($body_content, $http_code);
}
}

73
src/Router/Router.php Normal file
View File

@ -0,0 +1,73 @@
<?php
namespace KamilCraftApi\Router;
use KamilCraftApi\Request\Request;
/**
* @method RouterElement|null get(string $uri, string $controller, array $attributes = [])
* @method RouterElement|null post(string $uri, string $controller, array $attributes = [])
* @method RouterElement|null put(string $uri, string $controller, array $attributes = [])
* @method RouterElement|null path(string $uri, string $controller, array $attributes = [])
* @method RouterElement|null delete(string $uri, string $controller, array $attributes = [])
*/
class Router
{
const METHODS = [
'get',
'post',
'put',
'path',
'delete'
];
protected ?RouterCollection $routerCollection;
public function __construct(
protected ?Request $request = null
)
{
$this->routerCollection = new RouterCollection();
}
public function setRequest(Request $request): void
{
$this->request = $request;
}
public function requestIsAdded(): bool
{
return isset($this->request);
}
public function selectRouter(): RouterElement|null
{
return $this->routerCollection->findUri($this->request->getUriRoot(), $this->request);
}
private function addToCollection(
string $method,
string $uri,
string $controller,
array $attributes = []
): RouterElement
{
$routerElement = new RouterElement($method, $uri, $controller, $attributes);
$this->routerCollection->add($routerElement);
return $routerElement;
}
public function __call(
string $name,
array $arguments
): RouterElement|null
{
if ( in_array($name, self::METHODS, true) ) {
$args = array_merge([$name], $arguments);
return call_user_func_array([$this, 'addToCollection'], $args);
}
return null;
}
}

View File

@ -0,0 +1,49 @@
<?php
namespace KamilCraftApi\Router;
use KamilCraftApi\Request\Request;
class RouterCollection
{
private array $collection;
public function add(RouterElement $routerElement): void
{
$this->collection[] = $routerElement;
}
/**
* @return array<RouterElement>|null
*/
public function getAll(): array|null
{
return $this->collection ?? null;
}
public function findUri(array $request_uri_root, Request $request): RouterElement|null
{
foreach ($this->getAll() as $routerElement) {
/** @var RouterElement $routerElement */
if (
$routerElement->itsMe($request_uri_root) &&
$routerElement->method_request === strtolower($request->getMethod())
) {
return $routerElement;
}
}
return null;
}
public function get(string $name): RouterElement|null
{
foreach ( $this->collection as $routeElement ) {
/** @var RouterElement $routeElement */
if ( $routeElement->name === $name ) {
return $routeElement;
}
}
return null;
}
}

View File

@ -0,0 +1,78 @@
<?php
namespace KamilCraftApi\Router;
class RouterElement
{
public string $name;
private array $uri_root = [];
public function __construct(
public string $method_request,
public string $uri,
public string $controller,
public array $attributes = []
)
{
$this->createUriRoot($uri);
}
public function name(string $name)
{
$this->name = $name;
}
public function getUriRoot(): array
{
return $this->uri_root;
}
public function addAttributes(array $attributes): void
{
$this->attributes = array_merge($this->attributes, $attributes);
}
public function getAttributes(): array
{
return $this->attributes;
}
public function itsMe(array $request_root = []): bool
{
if ( count($request_root) !== count($this->getUriRoot()) ) {
return false;
}
$ok = 0;
$tmpAttributes = [];
foreach ( $request_root as $key => $root_element ) {
$localRootElement = $this->getUriRoot()[$key];
if ( $root_element === $localRootElement ) {
$ok++;
} else if ( preg_match('/^:[_a-zA-Z\-]+/i', $localRootElement) ) {
$arrayKey = explode(':', $localRootElement)[1];
if ( $arrayKey ) {
$tmpAttributes[$arrayKey] = $root_element;
}
$ok++;
} else {
$tmpAttributes = [];
}
}
if ( count($request_root) === $ok ) {
$this->addAttributes($tmpAttributes);
return true;
}
return false;
}
private function createUriRoot(string $uri): void
{
foreach (explode('/', $uri) as $uri_root_element) {
if ( is_numeric($uri_root_element) || !empty($uri_root_element) ) {
$this->uri_root[] = $uri_root_element;
}
}
}
}