Extend router a little, add routes for sending and receiving webmentions

This commit is contained in:
Lewis Dale 2023-03-10 09:47:55 +00:00
parent 10b6ace85a
commit 83ec00c8a0
6 changed files with 101 additions and 43 deletions

View File

@ -2,18 +2,41 @@
require_once __DIR__ . "/vendor/autoload.php"; require_once __DIR__ . "/vendor/autoload.php";
use Lewisdale\Webmentions\Endpoint;
use Lewisdale\Webmentions\Exceptions\InvalidTargetException;
use Lewisdale\Webmentions\Exceptions\InvalidUrlException;
use Lewisdale\Webmentions\Router\Request;
use Lewisdale\Webmentions\Router\Router; use Lewisdale\Webmentions\Router\Router;
use Lewisdale\Webmentions\Router\Response; use Lewisdale\Webmentions\Router\Response;
use Lewisdale\Webmentions\Router\StatusCode;
use Lewisdale\Webmentions\Webmention; use Lewisdale\Webmentions\Webmention;
$mentioner = new Webmention(); $mentioner = new Webmention();
// $mentioner->sendForPage("https://lewisdale.dev/post/bringing-my-omg-lol-now-page-into-eleventy/");
$router = new Router(); $router = new Router();
$router->get("/send", function($req, Response $response) { $router->post("/send", function(Request $req, Response $response) use ($webmention) {
return "<h1>Hello world</h1>"; $source = $req->query["source"];
$webmention->sendForPage($source);
});
$router->post("/endpoint", function(Request $req, Response $response) {
$source = $req->post['source'];
$target = $req->post['target'];
$endpoint = new Endpoint();
try {
$endpoint->receiveWebmention($source, $target);
} catch (InvalidUrlException $e) {
$response->status_code = StatusCode::BadRequest;
return "Source and target must be valid URLs";
} catch (InvalidTargetException $e) {
$response->status_code = StatusCode::BadRequest;
return "Target must be on the domain lewisdale.dev";
}
$response->status_code = StatusCode::Created;
}); });
$router->dispatch(); $router->dispatch();

View File

@ -8,4 +8,14 @@ enum Method {
// Fallback method // Fallback method
case UNKNOWN; case UNKNOWN;
public static function from(string $str) : self
{
switch ($str)
{
case "GET": return Method::GET;
case "POST": return Method::POST;
default: return METHOD::UNKNOWN;
}
}
}; };

View File

@ -8,6 +8,7 @@ class Request {
public readonly string $uri, public readonly string $uri,
public readonly Method $method, public readonly Method $method,
public readonly array $post = [], public readonly array $post = [],
public readonly array $query = [],
) {} ) {}
} }

View File

@ -2,6 +2,33 @@
namespace Lewisdale\Webmentions\Router; namespace Lewisdale\Webmentions\Router;
use Exception;
/**
* This is a dumb, custom Router I slapped together as a way of reminding myself how to PHP.
*
* It's not great, and currently on supports two methods: GET and POST.
*
* Usage is as follows:
*
* ```
* $router = new Lewisdale\Webmentions\Router\Router();
* $router->get('/a/get/route', function($request, $response) {
* $response->status_code = StatusCode::OK;
* return "Hello world!";
* });
*
* $router->get('/a/json/route', function($request, $response) {
* return $response->json(new MyClass());
* });
*
* $router->dispatch();
* ```
*
* TL;DR? Don't use this. There are some good routing libraries already.
*
* But if you're reading this, it's already too late.
*/
class Router { class Router {
private array $routes; private array $routes;
@ -25,30 +52,16 @@ class Router {
$this->routes[] = new Route(Method::POST, $this->format_route($route), $fn); $this->routes[] = new Route(Method::POST, $this->format_route($route), $fn);
} }
private function method_to_enum(string $method): Method {
switch ($method) {
case "GET":
return Method::GET;
case "POST":
return Method::POST;
default:
return Method::UNKNOWN;
}
}
private function get_params(array $matches): array { private function get_params(array $matches): array {
array_shift($matches); return array_map(
$results = array(); function($match) { return $match[0]; },
array_shift($matches)
foreach ($matches as $match) { );
$results[] = $match[0];
}
return $results;
} }
public function dispatch() { public function dispatch() {
$uri = $_SERVER['REQUEST_URI']; $uri = $_SERVER['REQUEST_URI'];
$method = $this->method_to_enum($_SERVER['REQUEST_METHOD']); $method = Method::from($_SERVER['REQUEST_METHOD']);
$num_matched = 0; $num_matched = 0;
@ -61,8 +74,13 @@ class Router {
$num_matched++; $num_matched++;
$fn = $route->fn; $fn = $route->fn;
$params = $this->get_params($matches); $params = $this->get_params($matches);
$response->status_code = StatusCode::Ok; $response->status_code = StatusCode::Ok; // Default status code to OK for the "average" route
$response->body .= $fn(new Request($params, $uri, $method, $_POST), $response); try {
$response->body .= $fn($this->construct_request_object($params), $response);
} catch (Exception) {
$response->status_code = StatusCode::InternalError;
// TODO: Log the error
}
} }
} }
} }
@ -75,28 +93,22 @@ class Router {
$this->respond($response); $this->respond($response);
} }
private function map_status_code(StatusCode $code): int {
switch ($code) {
case StatusCode::NotFound:
return 404;
case StatusCode::Ok:
return 200;
case StatusCode::InternalError:
return 500;
case StatusCode::Redirect:
return 300;
case StatusCode::BadRequest:
return 400;
default:
return 501;
}
}
private function respond(Response $response) { private function respond(Response $response) {
http_response_code($this->map_status_code($response->status_code)); http_response_code($response->status_code->code());
echo $response->body; echo $response->body;
} }
private function construct_request_object(array $params)
{
$uri = $_SERVER['REQUEST_URI'];
$method = Method::from($_SERVER['REQUEST_METHOD']);
$query = $_GET;
$post = $_POST;
return new Request($params, $uri, $method, $post, $query);
}
} }
?> ?>

View File

@ -8,6 +8,19 @@ enum StatusCode {
case Redirect; case Redirect;
case InternalError; case InternalError;
case BadRequest; case BadRequest;
case Created;
public function code() : int
{
return match($this) {
StatusCode::NotFound => 404,
StatusCode::Ok => 200,
StatusCode::InternalError => 500,
StatusCode::Redirect => 301,
StatusCode::BadRequest => 400,
StatusCode::Created => 201,
};
}
} }
?> ?>

View File

@ -2,7 +2,6 @@
namespace Lewisdale\Webmentions; namespace Lewisdale\Webmentions;
use Lewisdale\Webmentions\Gateways\WebmentionGatewayInterface;
use Symfony\Component\DomCrawler\Crawler; use Symfony\Component\DomCrawler\Crawler;
use Symfony\Component\HttpClient\HttpClient; use Symfony\Component\HttpClient\HttpClient;
use Symfony\Contracts\HttpClient\HttpClientInterface; use Symfony\Contracts\HttpClient\HttpClientInterface;