Initial commit, start creating a webmention sender

This commit is contained in:
Lewis Dale 2023-03-08 15:31:49 +00:00
commit 4eba80387d
7 changed files with 2588 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/vendor/

23
composer.json Normal file
View File

@ -0,0 +1,23 @@
{
"name": "lewisdale/webmentions",
"type": "project",
"require-dev": {
"phpunit/phpunit": "^10.0"
},
"autoload": {
"psr-4": {
"Lewisdale\\Webmentions\\": "src/"
}
},
"authors": [
{
"name": "Lewis Dale",
"email": "lewis@ihd.software"
}
],
"require": {
"symfony/dom-crawler": "^6.2",
"symfony/css-selector": "^6.2",
"symfony/http-client": "^6.2"
}
}

2412
composer.lock generated Normal file

File diff suppressed because it is too large Load Diff

11
index.php Normal file
View File

@ -0,0 +1,11 @@
<?php declare(strict_types=1);
require_once __DIR__ . "/vendor/autoload.php";
use Lewisdale\Webmentions\Webmention;
$mentioner = new Webmention();
$mentioner->sendForPage("https://lewisdale.dev/post/bringing-my-omg-lol-now-page-into-eleventy/");
?>

31
src/EndpointParser.php Normal file
View File

@ -0,0 +1,31 @@
<?php declare(strict_types=1);
namespace Lewisdale\Webmentions;
use Symfony\Contracts\HttpClient\ResponseInterface;
class EndpointParser {
public static function parse(ResponseInterface $response) : string | null
{
$headers = $response->getHeaders();
$endpoint = null;
if (isset($headers["link"])) {
$link = $headers["link"][0];
if (preg_match('/rel=("?)webmention("?)/', $link)) {
$matches = [];
preg_match('/\<(..*?)\>/', $link, $matches);
if (count($matches) > 1) {
$endpoint = $matches[1];
}
}
}
if ($endpoint && !str_contains($endpoint, "https://")) {
$res = parse_url($response->getInfo('url'));
$endpoint = $res["scheme"] . "://" . $res["host"] . $endpoint;
}
return $endpoint;
}
}

72
src/Webmention.php Normal file
View File

@ -0,0 +1,72 @@
<?php
namespace Lewisdale\Webmentions;
use DOMDocument;
use Symfony\Component\DomCrawler\Crawler;
use Symfony\Component\HttpClient\HttpClient;
use Symfony\Contracts\HttpClient\HttpClientInterface;
class Webmention {
private readonly HttpClientInterface $client;
function __construct()
{
$this->client = HttpClient::create();
}
public function sendForPage(string $source) : void {
// $urls = $this->getUrls($source);
// foreach($urls as $target) {
// $this->sendWebmention($source, $target);
// }
$this->sendWebmention($source, "https://webmention.rocks/test/1");
}
private function getUrls(string $url) : array {
$page = file_get_contents($url);
$doc = new Crawler($page);
$urls = [];
foreach ($doc->filter('.h-entry a') as $anchor) {
$target_url = $anchor->attributes->getNamedItem('href')->textContent;
if ($target_url !== null && strlen($target_url)) {
$urls[] = $target_url;
}
}
return $urls;
}
private function sendWebmention(string $source, string $target) {
// 1. Endpoint discovery
// 1.1 Parse page
// 1.2 Look for rel="webmention"
$endpoint = $this->getWebmentionEndpoint($target);
echo ($endpoint ?? "no :(") . "<br />";
// 2. Send webmention to endpoint
}
private function getWebmentionEndpoint(string $url) : string | null {
$response = $this->client->request('GET', $url);
// echo "<pre>";
// var_dump($response->getInfo());
// echo "</pre>";
return EndpointParser::parse($response);
// $doc = new Crawler($page);
// $links = $doc->filter('link[rel="webmention"]');
// if ($links->count()) {
// return $links->first()->attr('href');
// } else {
// return null;
// }
}
}
?>

View File

@ -0,0 +1,38 @@
<?php declare(strict_types=1);
use PHPUnit\Framework\TestCase;
use Symfony\Contracts\HttpClient\ResponseInterface;
use Lewisdale\Webmentions\EndpointParser;
class EndpointParserTest extends TestCase {
public function testParsesRelativeEndpointFromResponseHeaders() {
$response = $this->createStub(ResponseInterface::class);
$response->method('getHeaders')
->willReturn([
"link" => ["</test/1/webmention>; rel=webmention"],
]);
$response->method('getInfo')
->willReturn('https://webmention.rocks/test/1');
$this->assertSame("https://webmention.rocks/test/1/webmention", EndpointParser::parse($response));
}
public function testParsesRelativeEndpointWithQuotedRelInResponseHeader() {
$response = $this->createStub(ResponseInterface::class);
$response->method('getHeaders')
->willReturn([
"link" => ['</test/5/webmention>; rel="webmention"'],
]);
$response->method('getInfo')
->willReturn('https://webmention.rocks/test/5');
$this->assertSame("https://webmention.rocks/test/5/webmention", EndpointParser::parse($response));
}
}