From af2bd17c0b2c86d254569a5a58ddf0966e5ca61d Mon Sep 17 00:00:00 2001 From: indy koning Date: Tue, 5 Nov 2024 17:27:43 +0100 Subject: [PATCH] Pass more information from the link headers --- src/Data/LinkHeaders.php | 3 ++- src/Listeners/AddFromBody.php | 27 +++++++++++++++++++-------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/Data/LinkHeaders.php b/src/Data/LinkHeaders.php index 18afe20..3ce46b4 100644 --- a/src/Data/LinkHeaders.php +++ b/src/Data/LinkHeaders.php @@ -101,7 +101,8 @@ public function makeUnique() $handledHashes = []; foreach ($this->getLinkProvider()->getLinks() as $link) { - $hash = md5(serialize($link)); + /** @var Link $link */ + $hash = md5($link->getHref(), serialize($link->getRels())); if (! in_array($hash, $handledHashes)) { $handledHashes[] = $hash; diff --git a/src/Listeners/AddFromBody.php b/src/Listeners/AddFromBody.php index 9e6a9f5..0a053e2 100644 --- a/src/Listeners/AddFromBody.php +++ b/src/Listeners/AddFromBody.php @@ -19,13 +19,15 @@ public function handle(GenerateEarlyHints $event) $excludeKeywords = array_filter(config('http3earlyhints.exclude_keywords', [])); $headers = $this->fetchLinkableNodes($event->response) ->flatMap(function ($element) { - [$src, $href, $data, $rel, $type] = $element; + [$src, $href, $data, $rel, $type, $crossorigin, $as, $fetchpriority, $integrity, $referrerpolicy, $imagesizes, $imagesrcset] = $element; $rel = $type === 'module' ? 'modulepreload' : $rel; + $attributes = array_filter(@compact('crossorigin', 'as', 'fetchpriority', 'integrity', 'referrerpolicy', 'imagesizes', 'imagesrcset')); + return [ - $this->buildLinkHeader($src ?? '', $rel ?? null), - $this->buildLinkHeader($href ?? '', $rel ?? null), - $this->buildLinkHeader($data ?? '', $rel ?? null), + $this->buildLinkHeader($href ?? '', $rel ?? null, $attributes), + $this->buildLinkHeader($src ?? '', $rel ?? null, $attributes), + $this->buildLinkHeader($data ?? '', $rel ?? null, $attributes), ]; }) ->filter(function (?Link $value) use ($excludeKeywords) { @@ -60,13 +62,16 @@ protected function fetchLinkableNodes(Response $response): Collection { $crawler = $this->getCrawler($response); - return collect($crawler->filter('link:not([rel*="icon"]):not([rel="canonical"]):not([rel="manifest"]):not([rel="alternate"]), script[src]:not([defer]):not([async]), *:not(picture)>img[src]:not([loading="lazy"]), object[data]')->extract(['src', 'href', 'data', 'rel', 'type'])); + return collect( + $crawler->filter('link:not([rel*="icon"]):not([rel="canonical"]):not([rel="manifest"]):not([rel="alternate"]), script[src]:not([defer]):not([async]), *:not(picture)>img[src]:not([loading="lazy"]), object[data]') + ->extract(['src', 'href', 'data', 'rel', 'type', 'crossorigin', 'as', 'fetchpriority', 'integrity', 'referrerpolicy', 'imagesizes', 'imagesrcset']) + ); } /** * Build out header string based on asset extension. */ - private function buildLinkHeader(string $url, ?string $rel = 'preload'): ?Link + private function buildLinkHeader(string $url, ?string $rel = 'preload', ?array $attributes = []): ?Link { $linkTypeMap = [ '.CSS' => 'style', @@ -102,12 +107,18 @@ private function buildLinkHeader(string $url, ?string $rel = 'preload'): ?Link $link = new Link($rel, $url); + foreach ($attributes as $key => $value) { + $link = $link->withAttribute($key, $value); + } + if ($rel === 'preconnect' && $url) { return $link; } - $link = $link->withAttribute('as', $type ?? 'fetch'); - if ($type === 'font') { + if (empty($attributes['as'])) { + $link = $link->withAttribute('as', $type ?? 'fetch'); + } + if ($type === 'font' && empty($attributes['crossorigin'])) { $link = $link->withAttribute('crossorigin', true); }