87 lines
2.7 KiB
PHP
87 lines
2.7 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use Illuminate\Support\Facades\File;
|
|
|
|
class EmailImageInliner
|
|
{
|
|
/**
|
|
* Replace <img src="/storage/..."> with base64 data URIs using files from storage/app/public.
|
|
* Only affects local public storage images; external URLs and existing data URIs are left intact.
|
|
*/
|
|
public function inline(string $html): string
|
|
{
|
|
if ($html === '' || stripos($html, '<img') === false) {
|
|
return $html;
|
|
}
|
|
|
|
return preg_replace_callback('#<img([^>]+)src=[\"\']([^\"\']+)[\"\']([^>]*)>#i', function (array $m): string {
|
|
$before = $m[1] ?? '';
|
|
$src = $m[2] ?? '';
|
|
$after = $m[3] ?? '';
|
|
|
|
// Skip if already data URI or external
|
|
if (stripos($src, 'data:') === 0) {
|
|
return $m[0];
|
|
}
|
|
|
|
// Accept either relative (/storage/...) OR absolute URLs whose path begins with /storage/
|
|
$path = $src;
|
|
if (preg_match('#^https?://#i', $src)) {
|
|
$parts = parse_url($src);
|
|
$path = $parts['path'] ?? '';
|
|
}
|
|
if (! preg_match('#^/?storage/#i', (string) $path)) {
|
|
return $m[0];
|
|
}
|
|
|
|
$rel = ltrim(preg_replace('#^/?storage/#i', '', (string) $path), '/');
|
|
$full = storage_path('app/public/'.$rel);
|
|
if (! File::exists($full)) {
|
|
return $m[0];
|
|
}
|
|
|
|
// Determine mime type
|
|
$mime = null;
|
|
try {
|
|
$mime = File::mimeType($full);
|
|
} catch (\Throwable) {
|
|
$mime = null;
|
|
}
|
|
if ($mime === null) {
|
|
$ext = strtolower(pathinfo($full, PATHINFO_EXTENSION));
|
|
$map = [
|
|
'jpg' => 'image/jpeg',
|
|
'jpeg' => 'image/jpeg',
|
|
'png' => 'image/png',
|
|
'gif' => 'image/gif',
|
|
'svg' => 'image/svg+xml',
|
|
'webp' => 'image/webp',
|
|
];
|
|
$mime = $map[$ext] ?? 'application/octet-stream';
|
|
}
|
|
|
|
// Cap size to avoid huge emails (e.g., 5 MB)
|
|
$max = 5 * 1024 * 1024;
|
|
try {
|
|
if (File::size($full) > $max) {
|
|
return $m[0];
|
|
}
|
|
} catch (\Throwable) {
|
|
// ignore size errors
|
|
}
|
|
|
|
try {
|
|
$data = base64_encode(File::get($full));
|
|
} catch (\Throwable) {
|
|
return $m[0];
|
|
}
|
|
|
|
$dataUri = 'data:'.$mime.';base64,'.$data;
|
|
|
|
return '<img'.$before.'src="'.$dataUri.'"'.$after.'>';
|
|
}, $html);
|
|
}
|
|
}
|