From 45024faa5f4237b021f1beb62766821a76f963c3 Mon Sep 17 00:00:00 2001 From: Lewis Dale Date: Thu, 9 Mar 2023 21:50:59 +0000 Subject: [PATCH] Add some routing, start working on actually searching webmentions --- index.php | 12 ++- src/Endpoint.php | 30 +++++- src/Exceptions/InvalidTargetException.php | 6 ++ src/Exceptions/InvalidUrlException.php | 6 ++ src/Gateways/SqliteGateway.php | 16 +++ src/Gateways/WebmentionGatewayInterface.php | 1 + src/Router/Method.php | 11 +++ src/Router/Request.php | 14 +++ src/Router/Response.php | 37 +++++++ src/Router/Route.php | 14 +++ src/Router/Router.php | 102 ++++++++++++++++++++ src/Router/StatusCode.php | 13 +++ tests/Gateways/SqliteGatewayTest.php | 58 +++++++++-- 13 files changed, 308 insertions(+), 12 deletions(-) create mode 100644 src/Exceptions/InvalidTargetException.php create mode 100644 src/Exceptions/InvalidUrlException.php create mode 100644 src/Router/Method.php create mode 100644 src/Router/Request.php create mode 100644 src/Router/Response.php create mode 100644 src/Router/Route.php create mode 100644 src/Router/Router.php create mode 100644 src/Router/StatusCode.php diff --git a/index.php b/index.php index 4acadba..791c814 100644 --- a/index.php +++ b/index.php @@ -2,10 +2,20 @@ require_once __DIR__ . "/vendor/autoload.php"; +use Lewisdale\Webmentions\Router\Router; +use Lewisdale\Webmentions\Router\Response; use Lewisdale\Webmentions\Webmention; $mentioner = new Webmention(); -$mentioner->sendForPage("https://lewisdale.dev/post/bringing-my-omg-lol-now-page-into-eleventy/"); +// $mentioner->sendForPage("https://lewisdale.dev/post/bringing-my-omg-lol-now-page-into-eleventy/"); + +$router = new Router(); + +$router->get("/send", function($req, Response $response) { + return "

Hello world

"; +}); + +$router->dispatch(); ?> \ No newline at end of file diff --git a/src/Endpoint.php b/src/Endpoint.php index 71d363a..efe961f 100644 --- a/src/Endpoint.php +++ b/src/Endpoint.php @@ -2,7 +2,11 @@ namespace Lewisdale\Webmentions; +use Lewisdale\Webmentions\Exceptions\InvalidTargetException; +use Lewisdale\Webmentions\Exceptions\InvalidUrlException; use Lewisdale\Webmentions\Gateways\WebmentionGatewayInterface; +use Lewisdale\Webmentions\Models\Webmention; +use Symfony\Component\HttpClient\HttpClient; class Endpoint { private readonly WebmentionGatewayInterface $gateway; @@ -16,19 +20,35 @@ class Endpoint { // Validate that both source and target are actual domains if (!$this->validateUrl($source) || !$this->validateUrl($target)) { - return; + throw new InvalidUrlException(); } // Validate that the webmention target is a domain I care about if (!str_contains($target, "https://lewisdale.dev")) { - // Should throw a Bad Request error - return; + throw new InvalidTargetException(); } // Parse content from the source + $client = HttpClient::create(); + $response = $client->request('GET', $source); - // Store the webmention + if ($response->getStatusCode() === 200) + { + $content = $this->parseContent($response->getContent()); + $author = $this->parseAuthor($response->getContent()); - // Send the appropriate response + $webmention = new Webmention(null, $target, $source, $content, $author); + $this->gateway->save($webmention); + } + } + + private function parseContent(string $content) : ?string + { + return null; + } + + private function parseAuthor(string $author) : ?string + { + return null; } } \ No newline at end of file diff --git a/src/Exceptions/InvalidTargetException.php b/src/Exceptions/InvalidTargetException.php new file mode 100644 index 0000000..a16fd9c --- /dev/null +++ b/src/Exceptions/InvalidTargetException.php @@ -0,0 +1,6 @@ +execute(["id" => $webmention->id]); $statement->closeCursor(); } + + public function find(array $values) : array + { + $keys = implode(" AND ", array_map(function($v) { + return "$v=:$v"; + }, array_keys($values))); + $sql = <<connection->prepare($sql); + $statement->execute($values); + + return $statement->fetchAll(); + } } ?> \ No newline at end of file diff --git a/src/Gateways/WebmentionGatewayInterface.php b/src/Gateways/WebmentionGatewayInterface.php index 9d996a5..8a16ba6 100644 --- a/src/Gateways/WebmentionGatewayInterface.php +++ b/src/Gateways/WebmentionGatewayInterface.php @@ -10,6 +10,7 @@ abstract class WebmentionGatewayInterface { abstract public function save(Webmention $webmention) : ?int; abstract public function delete(Webmention $webmention) : void; abstract public function get(int $id) : ?Webmention; + abstract public function find(array $values) : array; } ?> \ No newline at end of file diff --git a/src/Router/Method.php b/src/Router/Method.php new file mode 100644 index 0000000..2db42ed --- /dev/null +++ b/src/Router/Method.php @@ -0,0 +1,11 @@ + \ No newline at end of file diff --git a/src/Router/Response.php b/src/Router/Response.php new file mode 100644 index 0000000..3570f48 --- /dev/null +++ b/src/Router/Response.php @@ -0,0 +1,37 @@ +render($values); + // } +} \ No newline at end of file diff --git a/src/Router/Route.php b/src/Router/Route.php new file mode 100644 index 0000000..cab3217 --- /dev/null +++ b/src/Router/Route.php @@ -0,0 +1,14 @@ + diff --git a/src/Router/Router.php b/src/Router/Router.php new file mode 100644 index 0000000..388af46 --- /dev/null +++ b/src/Router/Router.php @@ -0,0 +1,102 @@ +trim_route($route); + return "#^$formatted$#"; + } + + public function get(string $route, callable $fn) { + $this->routes[] = new Route(Method::GET, $this->format_route($route), $fn); + } + + public function post(string $route, callable $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 { + array_shift($matches); + $results = array(); + + foreach ($matches as $match) { + $results[] = $match[0]; + } + return $results; + } + + public function dispatch() { + $uri = $_SERVER['REQUEST_URI']; + $method = $this->method_to_enum($_SERVER['REQUEST_METHOD']); + + $num_matched = 0; + + $response = new Response(); + + foreach($this->routes as $route) { + if ($method == $route->type) { + $matches = array(); + if (preg_match_all($route->pattern, $uri, $matches)) { + $num_matched++; + $fn = $route->fn; + $params = $this->get_params($matches); + $response->status_code = StatusCode::Ok; + $response->body .= $fn(new Request($params, $uri, $method, $_POST), $response); + } + } + } + + if (!$num_matched) { + // Handle 404 + $response->status_code = StatusCode::NotFound; + } + + $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) { + http_response_code($this->map_status_code($response->status_code)); + + echo $response->body; + } +} + +?> \ No newline at end of file diff --git a/src/Router/StatusCode.php b/src/Router/StatusCode.php new file mode 100644 index 0000000..effe9a4 --- /dev/null +++ b/src/Router/StatusCode.php @@ -0,0 +1,13 @@ + \ No newline at end of file diff --git a/tests/Gateways/SqliteGatewayTest.php b/tests/Gateways/SqliteGatewayTest.php index 3bc416f..1f62aba 100644 --- a/tests/Gateways/SqliteGatewayTest.php +++ b/tests/Gateways/SqliteGatewayTest.php @@ -34,8 +34,6 @@ class SqliteGatewayTest extends TestCase public function testCanRetrieveAWebmention() { - $this->gateway = new SqliteGateway(":memory:"); - $webmention = new Webmention( null, "https://lewisdale.dev/post/a-post", @@ -53,8 +51,6 @@ class SqliteGatewayTest extends TestCase public function testCanDeleteAWebmention() { - $this->gateway = new SqliteGateway(":memory:"); - $webmention = new Webmention( null, "https://lewisdale.dev/post/a-post", @@ -73,8 +69,6 @@ class SqliteGatewayTest extends TestCase public function testCanGetByPost() { - $this->gateway = new SqliteGateway(":memory:"); - foreach(range(0, 4) as $_) { $this->gateway->save(new Webmention( null, @@ -99,4 +93,56 @@ class SqliteGatewayTest extends TestCase $this->assertCount(5, $mentions); } + + public function testCanFindByParams() + { + $this->gateway->save(new Webmention( + null, + "https://lewisdale.dev/post/a-new-post", + "https://a-source.url", + "No content", + "Some Author Name" + )); + + $this->gateway->save(new Webmention( + null, + "https://lewisdale.dev/post/a-new-post", + "https://a-different-source.url", + "No content", + "Some Author Name" + )); + + $this->gateway->save(new Webmention( + null, + "https://lewisdale.dev/post/a-new-post", + "https://a-source.url", + "Some content", + "Some Author Name" + )); + + $this->assertCount( + 2, + $this->gateway->find([ + "target" => "https://lewisdale.dev/post/a-new-post", + "source" => "https://a-source.url" + ]) + ); + + $this->assertCount( + 1, + $this->gateway->find([ + "target" => "https://lewisdale.dev/post/a-new-post", + "source" => "https://a-different-source.url" + ]) + ); + + $this->assertCount( + 1, + $this->gateway->find([ + "target" => "https://lewisdale.dev/post/a-new-post", + "source" => "https://a-source.url", + "content" => "Some content" + ]) + ); + } }