Skip to content
This repository has been archived by the owner on Aug 1, 2021. It is now read-only.

Commit

Permalink
Added a PHP 8 Attribute route parser
Browse files Browse the repository at this point in the history
  • Loading branch information
o-alquimista committed May 1, 2021
1 parent bf26aa4 commit 7d47e86
Show file tree
Hide file tree
Showing 7 changed files with 171 additions and 21 deletions.
21 changes: 19 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Fragments aims to be a small PHP framework for web applications. Keep in mind th
It has its own router component and is heavily inspired by [Symfony](https://symfony.com/).

## Requirements
- PHP 8
- PHP 8 or newer
- [Composer](https://getcomposer.org/)
- PHP XML extension. This package is called `php-xml` on Ubuntu.

Expand All @@ -26,7 +26,24 @@ username = example
password = example
```

4. Add routes in the file `/config/routes.xml` and start building your first controller at `/src/Controller/`. You can also try our [Fragments application](https://github.com/o-alquimista/fragments-app) to get an idea of how things work.
4. Create your first controller at `/src/Controller/`.
```php
namespace App\Controller;

use Fragments\Bundle\Controller\AbstractController;
use Fragments\Bundle\Attribute\Route;
use Fragments\Component\Http\Response;

class MyController extends AbstractController
{
#[Route("/", name: "main_page", methods: ["GET"])]
public function mainPage(): Response
{
// Render a template
return $this->render('main/main_page.php');
}
}
```

## License
Copyright 2019-2021 Douglas Silva (0x9fd287d56ec107ac)
Expand Down
39 changes: 39 additions & 0 deletions src/Fragments/Bundle/Attribute/Route.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

/**
* Copyright 2019-2021 Douglas Silva (0x9fd287d56ec107ac)
*
* This file is part of Fragments.
*
* Fragments is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Fragments. If not, see <https://www.gnu.org/licenses/>.
*/

namespace Fragments\Bundle\Attribute;

#[\Attribute]
class Route
{
public string $name;

public string $path;

public array $methods = [];

public function __construct(string $path, string $name, array $methods = [])
{
$this->name = $name;
$this->path = $path;
$this->methods = $methods;
}
}
5 changes: 4 additions & 1 deletion src/Fragments/Component/Html/Templating.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,10 @@ public function render(string $template, array $variables = []): Response

include "../templates/{$template}";

return new Response(ob_get_clean());
$contents = ob_get_contents();
ob_end_clean();

return new Response($contents);
}

/**
Expand Down
16 changes: 1 addition & 15 deletions src/Fragments/Component/Routing/Model/Route.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,24 +46,10 @@ class Route
/**
* The request methods supported.
*/
private $methods = [];
public array $methods = [];

/**
* Route parameters injected by the router.
*/
public array $parameters = [];

public function getMethods(): array
{
return $this->methods;
}

public function setMethods(string $methods): self
{
// Store them in an array
$methods = explode('|', $methods);
$this->methods = $methods;

return $this;
}
}
92 changes: 92 additions & 0 deletions src/Fragments/Component/Routing/Parser/AttributeParser.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<?php

/**
* Copyright 2019-2021 Douglas Silva (0x9fd287d56ec107ac)
*
* This file is part of Fragments.
*
* Fragments is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Fragments. If not, see <https://www.gnu.org/licenses/>.
*/

namespace Fragments\Component\Routing\Parser;

use Fragments\Component\Routing\Model\Route;
use Fragments\Bundle\Attribute\Route as RouteAttribute;

class AttributeParser implements ParserInterface
{
public function getRoutes(): array
{
$routes = [];
$controllers = $this->getControllers();

foreach ($controllers as $controller) {
$class = new \ReflectionClass($controller);

foreach ($class->getMethods() as $method) {
foreach ($method->getAttributes(RouteAttribute::class) as $attribute) {
$routes[] = $this->getRoute($attribute->newInstance(), $class->getName(), $method->getName());
}
}
}

return $routes;
}

private function getRoute(RouteAttribute $routeAttribute, string $controller, string $action): Route
{
$route = new Route;
$route->id = $routeAttribute->name;
$route->path = $routeAttribute->path;
$route->controller = $controller;
$route->action = $action;
$route->methods = $routeAttribute->methods;

return $route;
}

/**
* Include all files from the Controller directory
*/
private function includeControllers()
{
$files = new \FilesystemIterator('../src/Controller/');

ob_start();

foreach ($files as $file) {
require_once $file->getRealPath();
}

ob_end_clean();
}

/**
* Get all controller classes from the list of declared classes.
*/
private function getControllers(): array
{
$this->includeControllers();

$controllers = [];

foreach (get_declared_classes() as $fqcn) {
if (str_starts_with($fqcn, "App\Controller")) {
$controllers[] = $fqcn;
}
}

return $controllers;
}
}
4 changes: 3 additions & 1 deletion src/Fragments/Component/Routing/Parser/XMLParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ public function getRoutes(): array
$route->path = $entry->path;
$route->controller = $entry->controller;
$route->action = $entry->action;
$route->setMethods($entry->methods);

$methods = explode('|', $entry->methods);
$route->methods = $methods;

$routes[] = $route;
}
Expand Down
15 changes: 13 additions & 2 deletions src/Fragments/Component/Routing/Router.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
namespace Fragments\Component\Routing;

use Fragments\Component\Routing\Model\Route;
use Fragments\Component\Routing\Parser\ParserInterface;
use Fragments\Component\Routing\Parser\AttributeParser;
use Fragments\Component\Routing\Parser\XMLParser;
use Fragments\Component\Http\Request;
use Fragments\Component\Http\Exception\HttpException;
Expand All @@ -32,7 +34,16 @@ class Router

public function __construct()
{
$this->parser = new XMLParser;
$this->parser = $this->getParser();
}

private function getParser(): ParserInterface
{
if (file_exists('../config/routes.xml')) {
return new XMLParser();
}

return new AttributeParser();
}

public function getRouteFromRequest(Request $request): Route
Expand Down Expand Up @@ -82,7 +93,7 @@ public function getRouteFromRequest(Request $request): Route
}
}

if (false === in_array(needle: $request->server['REQUEST_METHOD'], haystack: $route->getMethods())) {
if (false === in_array(needle: $request->server['REQUEST_METHOD'], haystack: $route->methods)) {
throw new HttpException(statusCode: 405, message: 'Method not allowed.');
}

Expand Down

0 comments on commit 7d47e86

Please sign in to comment.