Publier Globinours 1.0.0-rc.3
This commit is contained in:
parent
ea8c24d622
commit
9a2b4068da
325 changed files with 38230 additions and 20 deletions
131
app/Services/ImageService.php
Normal file
131
app/Services/ImageService.php
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
final class ImageService
|
||||
{
|
||||
private const ALLOWED = ['image/jpeg' => 'jpg', 'image/png' => 'png', 'image/webp' => 'webp', 'image/gif' => 'gif'];
|
||||
|
||||
public static function inspect(string $path, int $maxBytes = 20_000_000): array
|
||||
{
|
||||
if (!is_file($path) || filesize($path) > $maxBytes) {
|
||||
throw new RuntimeException(t('service.image.missing_large'));
|
||||
}
|
||||
$info = @getimagesize($path);
|
||||
$mime = (string) ($info['mime'] ?? '');
|
||||
if (!$info || !isset(self::ALLOWED[$mime])) {
|
||||
throw new RuntimeException(t('service.image.format'));
|
||||
}
|
||||
if ((int) $info[0] * (int) $info[1] > 60_000_000) {
|
||||
throw new RuntimeException(t('service.image.dimensions'));
|
||||
}
|
||||
return [
|
||||
'mime' => $mime,
|
||||
'extension' => self::ALLOWED[$mime],
|
||||
'width' => (int) $info[0],
|
||||
'height' => (int) $info[1],
|
||||
'size' => (int) filesize($path),
|
||||
];
|
||||
}
|
||||
|
||||
public static function storeUploaded(
|
||||
string $tmp,
|
||||
string $directory,
|
||||
string $prefix = 'photo',
|
||||
string $profile = 'photo',
|
||||
): array {
|
||||
if (!is_uploaded_file($tmp)) {
|
||||
throw new RuntimeException(t('service.image.invalid_upload'));
|
||||
}
|
||||
return self::store($tmp, $directory, $prefix, $profile, true);
|
||||
}
|
||||
|
||||
public static function storeLocal(
|
||||
string $source,
|
||||
string $directory,
|
||||
string $prefix = 'photo',
|
||||
string $profile = 'photo',
|
||||
): array {
|
||||
return self::store($source, $directory, $prefix, $profile, false);
|
||||
}
|
||||
|
||||
private static function store(
|
||||
string $tmp,
|
||||
string $directory,
|
||||
string $prefix,
|
||||
string $profile,
|
||||
bool $moveSource,
|
||||
): array {
|
||||
$source = self::inspect($tmp);
|
||||
if (!is_dir($directory) && !mkdir($directory, 0775, true) && !is_dir($directory)) {
|
||||
throw new RuntimeException(t('service.image.directory_failed'));
|
||||
}
|
||||
$prefix = preg_replace('/[^a-z0-9_-]+/i', '_', trim($prefix)) ?: 'photo';
|
||||
$useWebp = $profile !== 'logo';
|
||||
$extension = $useWebp ? 'webp' : $source['extension'];
|
||||
$filename = $prefix . '_' . date('Ymd_His') . '_' . bin2hex(random_bytes(4)) . '.' . $extension;
|
||||
$target = rtrim($directory, '/') . '/' . $filename;
|
||||
$stage = rtrim($directory, '/') . '/.' . $filename . '.part.' . $extension;
|
||||
$optimized = false;
|
||||
$binary = self::magickBinary();
|
||||
if ($binary !== null) {
|
||||
$max = $profile === 'logo' ? '1800x1800>' : '2560x2560>';
|
||||
$quality = $profile === 'medical' ? '88' : '82';
|
||||
$command = [$binary, $tmp . '[0]', '-auto-orient', '-strip', '-resize', $max];
|
||||
if ($extension === 'png') {
|
||||
$command = array_merge($command, ['-define', 'png:compression-level=9']);
|
||||
} else {
|
||||
$command = array_merge($command, ['-quality', (string) $quality]);
|
||||
}
|
||||
$command[] = $stage;
|
||||
$process = proc_open($command, [0 => ['pipe', 'r'], 1 => ['pipe', 'w'], 2 => ['pipe', 'w']], $pipes);
|
||||
if (is_resource($process)) {
|
||||
fclose($pipes[0]);
|
||||
stream_get_contents($pipes[1]);
|
||||
fclose($pipes[1]);
|
||||
stream_get_contents($pipes[2]);
|
||||
fclose($pipes[2]);
|
||||
$code = proc_close($process);
|
||||
$optimized = $code === 0 && is_file($stage) && filesize($stage) > 0 && @getimagesize($stage) !== false;
|
||||
}
|
||||
}
|
||||
if ($optimized) {
|
||||
if (!rename($stage, $target)) {
|
||||
@unlink($stage);
|
||||
throw new RuntimeException(t('service.image.optimized_failed'));
|
||||
}
|
||||
} else {
|
||||
if (is_file($stage)) {
|
||||
@unlink($stage);
|
||||
}
|
||||
$extension = $source['extension'];
|
||||
$filename = $prefix . '_' . date('Ymd_His') . '_' . bin2hex(random_bytes(4)) . '.' . $extension;
|
||||
$target = rtrim($directory, '/') . '/' . $filename;
|
||||
$saved = $moveSource ? move_uploaded_file($tmp, $target) : copy($tmp, $target);
|
||||
if (!$saved) {
|
||||
throw new RuntimeException(t('service.image.save_failed'));
|
||||
}
|
||||
}
|
||||
@chmod($target, 0644);
|
||||
$stored = self::inspect($target);
|
||||
return array_merge($stored, [
|
||||
'filename' => $filename,
|
||||
'path' => $target,
|
||||
'original_size' => $source['size'],
|
||||
'optimized' => $optimized,
|
||||
]);
|
||||
}
|
||||
|
||||
private static function magickBinary(): ?string
|
||||
{
|
||||
foreach (
|
||||
['/usr/bin/magick', '/usr/local/bin/magick', '/usr/bin/convert', '/usr/local/bin/convert']
|
||||
as $binary
|
||||
) {
|
||||
if (is_executable($binary)) {
|
||||
return $binary;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue