Skip to content

Instantly share code, notes, and snippets.

@jschwendener
Created February 3, 2022 14:01
Show Gist options
  • Save jschwendener/d296b5bdf9ca5ee18b4fe31cb13ea8f2 to your computer and use it in GitHub Desktop.
Save jschwendener/d296b5bdf9ca5ee18b4fe31cb13ea8f2 to your computer and use it in GitHub Desktop.
<?php
namespace App\Services;
use Arr;
use Str;
use Illuminate\Support\Facades\Cache;
class FaviconExtractor
{
protected string $baseUrl;
public function __construct(string $baseUrl)
{
$this->baseUrl = $baseUrl;
}
public static function from(string $baseUrl)
{
$baseUrl = rtrim($baseUrl, '/');
$extractor = new static($baseUrl);
return Cache::remember(
'favicon-extractor.' . md5($baseUrl),
now()->addWeek(),
function () use ($baseUrl, $extractor) {
try {
return $extractor->extract($baseUrl);
} catch (\Exception $e) {
return null;
}
}
);
}
protected function extract()
{
$webManifestUrl = $this->parseWebManifestUrl();
if (!$webManifestUrl) {
return null;
}
$iconUrl = $this->parseIconUrl($webManifestUrl);
if (!$iconUrl) {
return null;
}
return file_get_contents($iconUrl);
}
protected function parseWebManifestUrl(): ?string
{
$html = file_get_contents($this->baseUrl);
preg_match('/<link[^>]+rel="manifest"[^>]+href="(?<manifestUrl>[^"]+)"/', $html, $matches);
if (isset($matches['manifestUrl']) && $matches['manifestUrl']) {
$manifestUrl = $matches['manifestUrl'];
if (!Str::startsWith($manifestUrl, 'http')) {
$manifestUrl = $this->baseUrl . $manifestUrl;
}
return $manifestUrl;
}
return null;
}
protected function parseIconUrl(string $webManifestUrl): ?string
{
$webManifest = json_decode(file_get_contents($webManifestUrl), true);
$iconUrl = Arr::get($webManifest, 'icons.0.src');
if (!$iconUrl) {
return null;
}
if (!Str::startsWith($iconUrl, ['http', '/'])) {
$iconUrl = Str::beforeLast($webManifestUrl, '/') . Str::start($iconUrl, '/');
} else {
$iconUrl = $this->baseUrl . $iconUrl;
}
return $iconUrl;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment