Instantly share code, notes, and snippets.
Created
November 22, 2024 08:45
-
Star
0
(0)
You must be signed in to star a gist -
Fork
0
(0)
You must be signed in to fork a gist
-
Save danlapteacru/0eac97c354051cb1abfff1b76a22dc1e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
declare(strict_types=1); | |
namespace App\Extensions; | |
use function Roots\asset; | |
final class Favicons | |
{ | |
private array $config; | |
private array $faviconConfig = [ | |
'android' => [ | |
'rel' => 'icon', | |
'type' => 'image/png', | |
'prefix' => 'android-chrome-' | |
], | |
'appleIcon' => [ | |
'rel' => 'apple-touch-icon', | |
'type' => 'image/png', | |
'prefix' => 'apple-touch-icon-' | |
], | |
'appleStartup' => [ | |
'rel' => 'apple-touch-startup-image', | |
'type' => 'image/png', | |
'prefix' => 'apple-touch-startup-image-' | |
], | |
'favicons' => [ | |
'rel' => 'icon', | |
'type' => 'image/png', | |
'prefix' => 'favicon-' | |
], | |
'windows' => [ | |
'rel' => 'msapplication-TileImage', | |
'type' => 'image/png', | |
'prefix' => 'mstile-' | |
], | |
'yandex' => [ | |
'rel' => 'yandex-tableau-widget', | |
'type' => 'image/png', | |
'prefix' => 'yandex-browser-' | |
] | |
]; | |
public function __construct(array $config) | |
{ | |
$this->config = $config; | |
add_action('wp_head', [$this, 'addFavicons']); | |
} | |
public static function register(): ?self | |
{ | |
$config = array_filter((array) config('favicons')); | |
if (empty($config)) { | |
return null; | |
} | |
return new self($config); | |
} | |
protected function getIconPlatforms(): array | |
{ | |
return array_keys( | |
array_filter($this->config['favicons']['icons'] ?? []) | |
); | |
} | |
protected function getPublicPath(string $path): ?string | |
{ | |
if (! asset($path)->exists()) { | |
return null; | |
} | |
return asset($path)->uri(); | |
} | |
public function addFavicons(): void | |
{ | |
$tags = $this->generateAllFaviconTags(); | |
if (empty($tags)) { | |
return; | |
} | |
echo PHP_EOL . '<!-- Generated by Favicons -->' . PHP_EOL; | |
echo implode(PHP_EOL, $tags); | |
echo '<!-- /Generated by Favicons -->' . PHP_EOL; | |
} | |
/** | |
* Generate meta tags for all favicon types | |
* | |
* @return array | |
*/ | |
public function generateAllFaviconTags(): array | |
{ | |
$tags = []; | |
foreach ($this->faviconConfig as $type => $config) { | |
if (! in_array($type, $this->getIconPlatforms(), true)) { | |
continue; | |
} | |
$tags = array_merge($tags, $this->generateLinkTagsByType($type)); | |
} | |
return array_filter( | |
array_merge( | |
$tags, | |
$this->generateManifestTags(), | |
$this->generatePwaMetaTags() | |
) | |
); | |
} | |
/** | |
* Generate meta tags for a specific favicon type | |
* | |
* @param string $type | |
* @return array | |
*/ | |
protected function generateLinkTagsByType(string $type): array | |
{ | |
$method = "generate{$type}MetaTags"; | |
return method_exists($this, $method) ? $this->$method() : []; | |
} | |
/** | |
* Generate manifest related tags | |
* | |
* @return array | |
*/ | |
protected function generateManifestTags(): array | |
{ | |
$attributes = [ | |
[ | |
'rel' => 'manifest', | |
'href' => $this->getPublicPath('manifest.webmanifest'), | |
], | |
[ | |
'rel' => 'shortcut icon', | |
'href' => $this->getPublicPath('favicon.ico'), | |
], | |
]; | |
return array_map( | |
fn (array $attr): string => $this->buildMetaTag($attr), | |
$attributes | |
); | |
} | |
/** | |
* Generate PWA specific meta tags | |
* | |
* @return array | |
*/ | |
protected function generatePwaMetaTags(): array | |
{ | |
$attributes = [ | |
[ | |
'name' => 'application-name', | |
'content' => $this->getAppName(), | |
], | |
[ | |
'name' => 'mobile-web-app-capable', | |
'content' => 'yes', | |
], | |
]; | |
if (! empty($this->config['theme_color'])) { | |
$attributes[] = [ | |
'name' => 'theme-color', | |
'content' => $this->config['theme_color'], | |
]; | |
} | |
return array_map( | |
fn (array $attr): string => $this->buildMetaTag($attr, 'meta'), | |
$attributes | |
); | |
} | |
/** | |
* Generate Android favicon meta tags | |
* | |
* @return array | |
*/ | |
protected function generateAndroidMetaTags(): array | |
{ | |
$sizes = [36, 48, 72, 96, 144, 192, 256, 384, 512]; | |
return $this->generateSizedMetaTags('android', $sizes); | |
} | |
/** | |
* Generate Apple icon meta tags | |
* | |
* @return array | |
*/ | |
protected function generateAppleIconMetaTags(): array | |
{ | |
$sizes = [57, 60, 72, 76, 114, 120, 144, 152, 167, 180, 1024]; | |
$tags = $this->generateSizedMetaTags('appleIcon', $sizes); | |
// Add default apple-touch-icon tags. | |
$tags[] = $this->generateLinkTag('appleIcon', 'apple-touch-icon.png'); | |
$tags[] = $this->generateLinkTag('appleIcon', 'apple-touch-icon-precomposed.png'); | |
return $tags; | |
} | |
/** | |
* Generate Apple startup image meta tags | |
* | |
* @return array | |
*/ | |
protected function generateAppleStartupMetaTags(): array | |
{ | |
$dimensions = [ | |
'1125x2436', '1242x2688', '1536x2048', '1668x2388', '2048x2732', | |
'640x1136', '750x1334', '828x1792', '1179x2556', '2556x1179', | |
'1290x2796', '2796x1290', '1488x2266', '2266x1488', '1640x2360', | |
'2360x1640', | |
]; | |
$tags = array_map( | |
fn (string $dimension): string => | |
$this->generateLinkTag( | |
'appleStartup', | |
"apple-touch-startup-image-{$dimension}.png", | |
$dimension | |
), | |
$dimensions | |
); | |
$attributes = [ | |
[ | |
'name' => 'apple-mobile-web-app-capable', | |
'content' => 'yes', | |
], | |
[ | |
'name' => 'apple-mobile-web-app-title', | |
'content' => $this->getAppName(), | |
] | |
]; | |
$appleStatusBarStyle = $this->config['appleStatusBarStyle'] ?? ''; | |
if (! empty($appleStatusBarStyle)) { | |
$attributes[] = [ | |
'name' => 'apple-mobile-web-app-status-bar-style', | |
'content' => $appleStatusBarStyle, | |
]; | |
} | |
return array_merge( | |
$tags, | |
array_map( | |
fn (array $attr): string => $this->buildMetaTag($attr, 'meta'), | |
$attributes | |
) | |
); | |
} | |
/** | |
* Generate standard favicon meta tags | |
* | |
* @return array | |
*/ | |
protected function generateFaviconsMetaTags(): array | |
{ | |
$sizes = [16, 32, 48]; | |
return $this->generateSizedMetaTags('favicons', $sizes); | |
} | |
/** | |
* Generate Windows tile meta tags | |
* | |
* @return array | |
*/ | |
protected function generateWindowsMetaTags(): array | |
{ | |
$sizes = ['70x70', '144x144', '150x150', '310x150', '310x310']; | |
$tags = array_map( | |
fn (string $size): string => $this->generateLinkTag('windows', "mstile-{$size}.png", $size), | |
$sizes | |
); | |
$attributes = [ | |
[ | |
'name' => 'msapplication-TileColor', | |
'content' => $this->config['msapplicationTileColor'] ?? '#ffffff', | |
], | |
[ | |
'name' => 'msapplication-config', | |
'content' => $this->getPublicPath('browserconfig.xml'), | |
], | |
]; | |
return array_merge( | |
$tags, | |
array_map( | |
fn (array $attr): string => $this->buildMetaTag($attr, 'meta'), | |
$attributes | |
) | |
); | |
} | |
/** | |
* Generate Yandex meta tags | |
* | |
* @return array | |
*/ | |
protected function generateYandexMetaTags(): array | |
{ | |
return [ | |
$this->generateLinkTag('yandex', 'yandex-browser-50x50.png', '50x50'), | |
]; | |
} | |
/** | |
* Generate meta tags for icons with specific sizes | |
* | |
* @param string $type | |
* @param array $sizes | |
* @return array | |
*/ | |
protected function generateSizedMetaTags(string $type, array $sizes): array | |
{ | |
return array_map(function (int|string $size) use ($type): string { | |
$dimension = "{$size}x{$size}"; | |
return $this->generateLinkTag( | |
$type, | |
"{$this->faviconConfig[$type]['prefix']}{$dimension}.png", | |
$dimension | |
); | |
}, $sizes); | |
} | |
/** | |
* Generate a single meta link tag | |
* | |
* @param string $type | |
* @param string $filename | |
* @param string|null $size | |
* @return string | |
*/ | |
protected function generateLinkTag(string $type, string $filename, ?string $size = null): string | |
{ | |
$config = $this->faviconConfig[$type]; | |
$attributes = [ | |
'rel' => $config['rel'] ?? '', | |
'type' => $config['type'] ?? '', | |
'href' => $this->getPublicPath($filename) | |
]; | |
if ($size) { | |
$attributes['sizes'] = $size; | |
} | |
return $this->buildMetaTag($attributes); | |
} | |
/** | |
* Generate a meta tag for a specific name and content | |
* | |
* @param string $name | |
* @param string $content | |
* @return string | |
*/ | |
protected function generateMetaTag(string $name, string $content): string | |
{ | |
return $this->buildMetaTag([ | |
'name' => $name, | |
'content' => $content | |
], 'meta'); | |
} | |
/** | |
* Build HTML meta tag from attributes | |
* | |
* @param array $attributes | |
* @param string $tag | |
* @return string | |
*/ | |
protected function buildMetaTag(array $attributes, string $tag = 'link'): string | |
{ | |
$attrs = []; | |
foreach ($attributes as $key => $value) { | |
if (empty($value)) { | |
continue; | |
} | |
$attrs[] = sprintf('%s="%s"', $key, htmlspecialchars($value, ENT_QUOTES, 'UTF-8')); | |
} | |
return sprintf('<%s %s>', $tag, implode(' ', $attrs)); | |
} | |
/** | |
* Get the app name | |
* | |
* @return string | |
*/ | |
protected function getAppName(): string | |
{ | |
return $this->config['appName'] ?? get_bloginfo('name'); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment