- PHP 100%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
Allows consumers to dispatch the WebmentionEvent manually — for instance, if they want to wrap it in a deferable, or handle it immediately. |
||
| spec | ||
| src | ||
| test | ||
| .gitattributes | ||
| .gitignore | ||
| composer.json | ||
| composer.lock | ||
| CONTRIBUTING.md | ||
| phpcs.xml.dist | ||
| phpunit.xml.dist | ||
| README.md | ||
weierophinney/webmention
Library for sending and receiving Webmentions in PHP 8.5+.
Requirements
- PHP 8.5+
- A PSR-18 HTTP client (
psr/http-client) - A PSR-17 HTTP factory (
psr/http-factory+psr/http-message) - A PSR-14 event dispatcher (
psr/event-dispatcher) - weierophinney/microformats2
Optional:
- PSR-3 logger (
psr/log) for logging errors and warnings
Installation
composer require weierophinney/webmention
Sending Webmentions
The WebmentionSender sends webmention notifications to a target URL's endpoint.
Usage
use Weierophinney\Webmention\WebmentionSender;
use Uri\WhatWg\Url;
$sender = new WebmentionSender(
httpClient: $psr18Client,
requestFactory: $psr17RequestFactory,
);
$sender->send(
webmentionEndpoint: Url::parse('https://target.example.com/webmention'),
source: Url::parse('https://source.example.com/my-post'),
target: Url::parse('https://target.example.com/their-post'),
);
Constructor
| Parameter | Type | Required | Description |
|---|---|---|---|
$httpClient |
Psr\Http\Client\ClientInterface |
Yes | PSR-18 HTTP client for sending requests |
$requestFactory |
Psr\Http\Message\RequestFactoryInterface |
Yes | PSR-17 factory for creating HTTP requests |
$logger |
Psr\Log\LoggerInterface |
No | PSR-3 logger for warnings on failed sends |
Methods
send(Url $webmentionEndpoint, Url $source, Url $target): void
Sends a webmention POST request to the endpoint. Follows up to 5 redirects. Logs a warning if the endpoint returns a non-2XX response.
Receiving Webmentions
Receiving webmentions requires two components: a PSR-15 request handler and an event listener.
WebmentionHandler
A PSR-15 RequestHandlerInterface that validates incoming webmention POST requests and dispatches a WebmentionRequestEvent.
Usage
use Weierophinney\Webmention\Handler\WebmentionHandler;
$handler = new WebmentionHandler(
responseFactory: $psr17ResponseFactory,
eventDispatcher: $psr14EventDispatcher,
);
// In your routing layer:
$response = $handler->handle($serverRequest);
Constructor
| Parameter | Type | Required | Description |
|---|---|---|---|
$responseFactory |
Psr\Http\Message\ResponseFactoryInterface |
Yes | PSR-17 factory for creating HTTP responses |
$eventDispatcher |
Psr\EventDispatcher\EventDispatcherInterface |
Yes | PSR-14 dispatcher for dispatching events |
Behavior
- Returns
405 Method Not Allowedfor non-POST requests - Returns
400 Bad Requestifsourceortargetfields are missing, empty, not valid URLs, or use a scheme other thanhttp/https - Dispatches a
WebmentionRequestEventwith the validatedsourceandtargetURLs - Returns
202 Acceptedon success
WebmountRequestEvent Listener
A callable listener that processes WebmentionRequestEvent by validating the target, fetching the source document, parsing microformats, and classifying the webmention.
Usage
use Weierophinney\Webmention\Event\WebmentionRequestListener;
$listener = new WebmentionRequestListener(
validateApplicationUrl: function (\Uri\WhatWg\Url $target): bool {
// Return true if the target URL belongs to your application
return str_starts_with($target->toAsciiString(), 'https://myapp.example.com/');
},
httpClient: $psr18Client,
requestFactory: $psr17RequestFactory,
eventDispatcher: $psr14EventDispatcher,
logger: $psr3Logger, // optional
);
// Register as a PSR-14 listener:
$eventDispatcher->addListener(WebmentionRequestEvent::class, $listener);
Constructor
| Parameter | Type | Required | Description |
|---|---|---|---|
$validateApplicationUrl |
callable(\Uri\WhatWg\Url): bool |
Yes | Callback to validate that a target URL belongs to your application |
$httpClient |
Psr\Http\Client\ClientInterface |
Yes | PSR-18 HTTP client for fetching source documents |
$requestFactory |
Psr\Http\Message\RequestFactoryInterface |
Yes | PSR-17 factory for creating HTTP requests |
$eventDispatcher |
Psr\EventDispatcher\EventDispatcherInterface |
Yes | PSR-14 dispatcher for dispatching results |
$parser |
Weierophinney\Microformats2\Parser |
No | Microformats2 parser (a new instance is created if omitted) |
$logger |
Psr\Log\LoggerInterface |
No | PSR-3 logger for warnings during processing |
Behavior
- Invokes the
validateApplicationUrlcallback with the target URL; returns early if invalid - Issues a HEAD request to the source URL, following up to 5 redirects
- Issues a GET request to the resolved source URL
- Parses the HTML using
weierophinney/microformats2 - Classifies the webmention using
weierophinney/microformats2'sClassifier - Dispatches a
WebmentionEventwith the classifiedWebmentionResult
Events
WebmentionRequestEvent
| Property | Type | Description |
|---|---|---|
$source |
Uri\WhatWg\Url |
The source URL of the webmention |
$target |
Uri\WhatWg\Url |
The target URL of the webmention |
WebmentionEvent
| Property | Type | Description |
|---|---|---|
$webmention |
Weierophinney\Microformats2\Webmention\WebmentionResult |
The classified webmention result |
The $webmention is a subclass of WebmentionResult indicating the type of webmention:
Comment— source is in reply to target (u-in-reply-to)Repost— source reposts target (u-repost-of)Like— source likes target (u-like-of)Mention— source mentions target (generic)Deletion— no link to target found in source
Full Example
use Weierophinney\Webmention\Handler\WebmentionHandler;
use Weierophinney\Webmention\Event\WebmentionRequestListener;
use Weierophinney\Webmention\Event\WebmentionEvent;
// Create dependencies (from your DI container)
$psr17ResponseFactory = /* PSR-17 ResponseFactoryInterface */;
$psr18Client = /* PSR-18 ClientInterface */;
$psr17RequestFactory = /* PSR-17 RequestFactoryInterface */;
$psr14Dispatcher = /* PSR-14 EventDispatcherInterface */;
$psr3Logger = /* PSR-3 LoggerInterface (optional) */;
// Create the handler
$handler = new WebmentionHandler(
responseFactory: $psr17ResponseFactory,
eventDispatcher: $psr14Dispatcher,
);
// Create and register the listener
$listener = new WebmentionRequestListener(
validateApplicationUrl: function (\Uri\WhatWg\Url $target): bool {
return str_starts_with($target->toAsciiString(), 'https://myapp.example.com/');
},
httpClient: $psr18Client,
requestFactory: $psr17RequestFactory,
eventDispatcher: $psr14Dispatcher,
logger: $psr3Logger,
);
$psr14Dispatcher->addListener(
\Weierophinney\Webmention\Event\WebmentionRequestEvent::class,
$listener,
);
// Register a handler for classified webmentions
$psr14Dispatcher->addListener(WebmentionEvent::class, function (WebmentionEvent $event): void {
$result = $event->result;
match (true) {
$result instanceof \Weierophinney\Microformats2\Webmention\Comment
=> error_log("Comment received from {$result->source}"),
$result instanceof \Weierophinney\Microformats2\Webmention\Like
=> error_log("Like received from {$result->source}"),
$result instanceof \Weierophinney\Microformats2\Webmention\Repost
=> error_log("Repost received from {$result->source}"),
default
=> error_log("Mention received from {$result->source}"),
};
});
// Route the webmention endpoint (framework-specific)
// POST /webmention => $handler->handle($request)
Development
Testing
./vendor/bin/phpunit
Coding Standards
./vendor/bin/phpcs
./vendor/bin/phpcbf
License
This library is licensed under the BSD-2-Clause license. See LICENSE for details.