Skip to content

Instantly share code, notes, and snippets.

@trovster
Created October 17, 2025 14:02
Show Gist options
  • Save trovster/831c84641a23af2f899add01a1339327 to your computer and use it in GitHub Desktop.
Save trovster/831c84641a23af2f899add01a1339327 to your computer and use it in GitHub Desktop.
A service to send Webmentions to links found in HTML.
<?php
namespace App\Services;
use App\Support\Str;
use Illuminate\Http\Client\Response;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Http;
class Webmention
{
public Collection $links;
public function __construct(private string $html)
{
$this->links = new Collection();
$this->discoverOutgoingLinks($html);
}
public function notify(string $source, string $target): Response|false
{
$endpoint = $this->discoverEndpoint($target);
if (! $endpoint) {
return false;
}
return Http::post($endpoint, [
'source' => $source,
'target' => $target,
]);
}
private function discoverOutgoingLinks(string $html): void
{
$url = Config::get('app.url');
preg_match_all("/<a[^>]+href=.(https?:\/\/[^'\"]+)/i", $html, $matches);
$this->links = $this->links
->merge(array_unique($matches[1]))
->reject(static fn (string $link): bool => Str::of($link)->startsWith($url));
}
private function discoverEndpoint(string $target): ?string
{
return $this->discoverEndpointInHeader($target) ?? $this->discoverEndpointInBody($target);
}
private function discoverEndpointInHeader(string $target): string|false
{
$response = Http::head($target);
$headers = $response->headers();
if (array_key_exists('Link', $headers)) {
return false;
}
$linkHeader = is_array($headers['Link']) ? implode(", ", $headers['Link']) : $headers['Link'];
if (preg_match(
'~<((?:https?://)?[^>]+)>; rel="?(?:https?://webmention.org/?|webmention)"?~',
$linkHeader,
$match
)) {
return $match[1];
}
return false;
}
private function discoverEndpointInBody(string $target): string|false
{
$response = Http::get($target);
$body = preg_replace('/<!--(.*)-->/Us', '', (string) $response->body());
if (preg_match('/<(?:link|a)[ ]+href="([^"]*)"[ ]+rel="[^" ]* ?webmention ?[^" ]*"[ ]*\/?>/i', $body, $match)
|| preg_match('/<(?:link|a)[ ]+rel="[^" ]* ?webmention ?[^" ]*"[ ]+href="([^"]*)"[ ]*\/?>/i', $body, $match)
) {
return $match[1];
}
return false;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment