Created
October 17, 2025 14:02
-
-
Save trovster/831c84641a23af2f899add01a1339327 to your computer and use it in GitHub Desktop.
A service to send Webmentions to links found in HTML.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?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