commit b67fac10bc5cf925776b59f749f46043221f82bf Author: Kamil Niemczycki Date: Tue Feb 22 19:07:39 2022 +0100 First commit diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..c48ef91 --- /dev/null +++ b/.env.example @@ -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= diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fad1a04 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.idea +vendor +.env diff --git a/README.md b/README.md new file mode 100644 index 0000000..6fb03a5 --- /dev/null +++ b/README.md @@ -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 +``` diff --git a/app/Controllers/CategoriesController.php b/app/Controllers/CategoriesController.php new file mode 100644 index 0000000..4ee4037 --- /dev/null +++ b/app/Controllers/CategoriesController.php @@ -0,0 +1,43 @@ +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()); + } +} diff --git a/app/Controllers/Controller.php b/app/Controllers/Controller.php new file mode 100644 index 0000000..62d1d3c --- /dev/null +++ b/app/Controllers/Controller.php @@ -0,0 +1,21 @@ +response = new Response(); + } + + protected function response(): Response + { + return $this->response; + } +} diff --git a/app/Controllers/HomeController.php b/app/Controllers/HomeController.php new file mode 100644 index 0000000..9e84c07 --- /dev/null +++ b/app/Controllers/HomeController.php @@ -0,0 +1,17 @@ +json((object)[ + 'message' => 'Hello World' + ]); + } +} diff --git a/app/Controllers/ProjectController.php b/app/Controllers/ProjectController.php new file mode 100644 index 0000000..0299202 --- /dev/null +++ b/app/Controllers/ProjectController.php @@ -0,0 +1,39 @@ +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()); + } +} diff --git a/app/Models/Category.php b/app/Models/Category.php new file mode 100644 index 0000000..49bef00 --- /dev/null +++ b/app/Models/Category.php @@ -0,0 +1,78 @@ +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; + } +} diff --git a/app/Models/Project.php b/app/Models/Project.php new file mode 100644 index 0000000..934401c --- /dev/null +++ b/app/Models/Project.php @@ -0,0 +1,72 @@ +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; + } +} diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..a5ae985 --- /dev/null +++ b/composer.json @@ -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/" + } + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..4333ca5 --- /dev/null +++ b/composer.lock @@ -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" +} diff --git a/errors/error400.html b/errors/error400.html new file mode 100644 index 0000000..bbd91b1 --- /dev/null +++ b/errors/error400.html @@ -0,0 +1,14 @@ + + + + + + + Błąd 400 + + +

Błąd 400

+

Niepoprawne zapytanie!

+ + \ No newline at end of file diff --git a/errors/error404.html b/errors/error404.html new file mode 100644 index 0000000..917309f --- /dev/null +++ b/errors/error404.html @@ -0,0 +1,14 @@ + + + + + + + Błąd 404 + + +

Błąd 404

+

Nie znaleziono szukanej strony!

+ + \ No newline at end of file diff --git a/errors/error500.html b/errors/error500.html new file mode 100644 index 0000000..46ce21d --- /dev/null +++ b/errors/error500.html @@ -0,0 +1,14 @@ + + + + + + + Błąd 500 + + +

Błąd 500

+

Wystąpił wewnętrzny błąd serwera!

+ + \ No newline at end of file diff --git a/public/index.php b/public/index.php new file mode 100644 index 0000000..db191e0 --- /dev/null +++ b/public/index.php @@ -0,0 +1,10 @@ +run(); diff --git a/resources/json/Categories.json b/resources/json/Categories.json new file mode 100644 index 0000000..4b11eed --- /dev/null +++ b/resources/json/Categories.json @@ -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 + } +] \ No newline at end of file diff --git a/resources/json/Projects.json b/resources/json/Projects.json new file mode 100644 index 0000000..9d3d591 --- /dev/null +++ b/resources/json/Projects.json @@ -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." + } +] \ No newline at end of file diff --git a/router.php b/router.php new file mode 100644 index 0000000..587fda2 --- /dev/null +++ b/router.php @@ -0,0 +1,32 @@ +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; diff --git a/src/Application.php b/src/Application.php new file mode 100644 index 0000000..77d0de2 --- /dev/null +++ b/src/Application.php @@ -0,0 +1,175 @@ +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; + } + } +} diff --git a/src/Exceptions/ApplicationException.php b/src/Exceptions/ApplicationException.php new file mode 100644 index 0000000..6e5e8ef --- /dev/null +++ b/src/Exceptions/ApplicationException.php @@ -0,0 +1,8 @@ + '', + '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; + } + } + } +} diff --git a/src/Request/RequestData.php b/src/Request/RequestData.php new file mode 100644 index 0000000..a6a258f --- /dev/null +++ b/src/Request/RequestData.php @@ -0,0 +1,29 @@ + [], + '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; + } +} diff --git a/src/Response.php b/src/Response.php new file mode 100644 index 0000000..ad0c630 --- /dev/null +++ b/src/Response.php @@ -0,0 +1,52 @@ +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); + } +} diff --git a/src/Router/Router.php b/src/Router/Router.php new file mode 100644 index 0000000..434532f --- /dev/null +++ b/src/Router/Router.php @@ -0,0 +1,73 @@ +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; + } +} diff --git a/src/Router/RouterCollection.php b/src/Router/RouterCollection.php new file mode 100644 index 0000000..6564022 --- /dev/null +++ b/src/Router/RouterCollection.php @@ -0,0 +1,49 @@ +collection[] = $routerElement; + } + + /** + * @return array|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; + } +} diff --git a/src/Router/RouterElement.php b/src/Router/RouterElement.php new file mode 100644 index 0000000..e0bb985 --- /dev/null +++ b/src/Router/RouterElement.php @@ -0,0 +1,78 @@ +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; + } + } + } +}