Denn Markdown enthält keine unnötigen optischen oder funktionale Resourcen sondern fokussiert sich auf den Inhalt. Auch die Vielzahl an HTML-Tags können bei der Übertragung ganz einfach eingespart werden.
Um das einzurichten, braucht es lediglich eine kleine Middleware in eurer Sitetemplate-Extension. In nachfolgendem Beispiel liefert die Website sofort MD statt HTML zurück, falls der anfragende Browser oder Crawler Markdown unterstützt:
<?php
declare(strict_types=1);
namespace In2code\In2template\Middleware;
use League\HTMLToMarkdown\HtmlConverterInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\StreamFactoryInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
/**
* Returns a Markdown representation of the rendered page when the client
* announces `Accept: text/markdown`. The HTML pipeline runs unchanged; this
* middleware only post-processes the resulting body.
*/
final class MarkdownContentNegotiation implements MiddlewareInterface
{
private const string ACCEPT_TOKEN = 'text/markdown';
private const string CONTENT_WRAPPER_ID = 'content';
private const int TOKEN_CHAR_RATIO = 4;
public function __construct(
private readonly HtmlConverterInterface $htmlConverter,
private readonly StreamFactoryInterface $streamFactory,
) {
}
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$response = $handler->handle($request);
if ($this->isMarkdownNegotiated($request) && $this->isHtmlResponse($response)) {
$response = $this->convertResponseToMarkdown($response);
}
return $response->withAddedHeader('Vary', 'Accept');
}
private function isMarkdownNegotiated(ServerRequestInterface $request): bool
{
return str_contains($request->getHeaderLine('Accept'), self::ACCEPT_TOKEN);
}
private function isHtmlResponse(ResponseInterface $response): bool
{
return str_contains($response->getHeaderLine('Content-Type'), 'text/html');
}
private function convertResponseToMarkdown(ResponseInterface $response): ResponseInterface
{
$html = (string)$response->getBody();
$contentFragment = $this->extractContentFragment($html);
$markdown = trim($this->htmlConverter->convert($contentFragment));
return $response
->withHeader('Content-Type', 'text/markdown; charset=utf-8')
->withHeader('X-Markdown-Tokens', (string)$this->estimateTokenCount($markdown))
->withHeader('ETag', '"' . md5($markdown) . '"')
->withoutHeader('Content-Length')
->withBody($this->streamFactory->createStream($markdown));
}
private function extractContentFragment(string $html): string
{
$fragment = $html;
$previousState = libxml_use_internal_errors(true);
$dom = new \DOMDocument();
if ($dom->loadHTML('<?xml encoding="UTF-8">' . $html, LIBXML_NOERROR | LIBXML_NOWARNING)) {
$contentNode = $dom->getElementById(self::CONTENT_WRAPPER_ID);
if ($contentNode !== null) {
$fragment = (string)$dom->saveHTML($contentNode);
}
}
libxml_clear_errors();
libxml_use_internal_errors($previousState);
return $fragment;
}
private function estimateTokenCount(string $markdown): int
{
return (int)ceil(mb_strlen($markdown) / self::TOKEN_CHAR_RATIO);
}
}
Damit das Ganze funktioniert, braucht es allerdings noch ein Drittpaket, das ihr via composer.json einfach hinzuladen könnt:
{
"name": "in2code/cms-boilerplate",
"description": "in2code GmbH TYPO3 CMS Boilerplate",
"license": "GPL-2.0",
"require": {
"league/html-to-markdown": "^5.1",
...
Tipp: Ob das Ganze auch wirklich funktioniert, könnt ihr anschließend mit einem einfachen CURL-Befehl selber testen:
curl -ks -H 'Accept: text/markdown' https:// local.website.de/




