loadHTML(
'
'.$html.'
',
LIBXML_NOERROR | LIBXML_NOWARNING,
);
if (! $loaded) {
return $html;
}
foreach ($document->getElementsByTagName('a') as $anchor) {
$this->applyToAnchor($anchor);
}
$root = $document->getElementById('pr-link-policy-root');
if (! $root) {
return $html;
}
$result = '';
foreach ($root->childNodes as $child) {
$result .= $document->saveHTML($child);
}
return $result;
}
private function applyToAnchor(DOMElement $anchor): void
{
$href = trim($anchor->getAttribute('href'));
// rel ist systemgesteuert — Autoren-Eingaben zählen nie.
$anchor->removeAttribute('rel');
if ($href === '' || Str::startsWith($href, ['mailto:', 'tel:', '#'])) {
$anchor->removeAttribute('target');
return;
}
if ($this->isInternal($href)) {
$anchor->removeAttribute('target');
return;
}
$anchor->setAttribute('rel', 'sponsored nofollow noopener');
$anchor->setAttribute('target', '_blank');
}
/**
* Intern = relative Pfade sowie absolute URLs auf eine der
* konfigurierten Portal-Domains (inkl. www-Variante).
*/
private function isInternal(string $href): bool
{
if (! preg_match('#^https?://#i', $href)) {
// Relative Pfade (/firma/...) — kein Protokoll, keine Domain.
return ! Str::startsWith($href, '//');
}
$host = strtolower((string) parse_url($href, PHP_URL_HOST));
if ($host === '') {
return false;
}
$internalHosts = $this->internalHosts();
return in_array($host, $internalHosts, true)
|| in_array(Str::after($host, 'www.'), $internalHosts, true);
}
/**
* @return list
*/
private function internalHosts(): array
{
$hosts = [];
foreach ((array) config('domains.domains', []) as $domain) {
foreach ([$domain['domain_name'] ?? null, parse_url((string) ($domain['url'] ?? ''), PHP_URL_HOST)] as $candidate) {
if (is_string($candidate) && $candidate !== '') {
$host = strtolower($candidate);
$hosts[] = $host;
$hosts[] = Str::after($host, 'www.');
}
}
}
return array_values(array_unique($hosts));
}
}