85 lines
2.7 KiB
PHP
85 lines
2.7 KiB
PHP
<?php
|
|
|
|
namespace App\Shared\Service;
|
|
|
|
use App\Shared\Constant\ErrorCodes;
|
|
use App\Shared\Exception\AppException;
|
|
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
|
|
|
class FileValidatorService
|
|
{
|
|
private const ALLOWED_SIGNATURES = [
|
|
'image/jpeg' => ["\xFF\xD8\xFF"],
|
|
'image/png' => ["\x89\x50\x4E\x47\x0D\x0A\x1A\x0A"],
|
|
'image/webp' => ["RIFF"],
|
|
];
|
|
|
|
private const ALLOWED_EXTENSIONS = ['jpg', 'jpeg', 'png', 'webp'];
|
|
|
|
public function __construct(private readonly int $maxSizeBytes = 5_242_880) {}
|
|
|
|
public function validateUploadedFile(UploadedFile $file): string
|
|
{
|
|
if ($file->getSize() > $this->maxSizeBytes) {
|
|
throw new AppException(ErrorCodes::ERR_FILE_002, null, 422);
|
|
}
|
|
|
|
$binaryContent = (string) file_get_contents($file->getPathname());
|
|
return $this->validate($binaryContent, $file->getClientOriginalName());
|
|
}
|
|
|
|
public function validate(string $binaryContent, string $claimedFilename): string
|
|
{
|
|
if (strlen($binaryContent) > $this->maxSizeBytes) {
|
|
throw new AppException(ErrorCodes::ERR_FILE_002, null, 422);
|
|
}
|
|
|
|
$detected = false;
|
|
foreach (self::ALLOWED_SIGNATURES as $signatures) {
|
|
foreach ($signatures as $sig) {
|
|
if (str_starts_with($binaryContent, $sig)) {
|
|
$detected = true;
|
|
break 2;
|
|
}
|
|
}
|
|
}
|
|
if (!$detected) {
|
|
throw new AppException(ErrorCodes::ERR_FILE_001, null, 422);
|
|
}
|
|
|
|
return $this->sanitizeFilename($claimedFilename);
|
|
}
|
|
|
|
public function sanitizeFilename(string $filename): string
|
|
{
|
|
$safeName = preg_replace('/[^a-zA-Z0-9._-]/', '', basename($filename));
|
|
if (empty($safeName) || str_contains($safeName, '..')) {
|
|
throw new AppException(ErrorCodes::ERR_FILE_001, null, 422);
|
|
}
|
|
|
|
$ext = strtolower(pathinfo($safeName, PATHINFO_EXTENSION));
|
|
if (!in_array($ext, self::ALLOWED_EXTENSIONS, true)) {
|
|
throw new AppException(ErrorCodes::ERR_FILE_001, null, 422);
|
|
}
|
|
|
|
return bin2hex(random_bytes(16)) . '.' . $ext;
|
|
}
|
|
|
|
public function detectMimeType(string $filePath): string
|
|
{
|
|
$handle = fopen($filePath, 'rb');
|
|
$header = fread($handle, 12);
|
|
fclose($handle);
|
|
|
|
foreach (self::ALLOWED_SIGNATURES as $mime => $signatures) {
|
|
foreach ($signatures as $sig) {
|
|
if (str_starts_with($header, $sig)) {
|
|
return $mime;
|
|
}
|
|
}
|
|
}
|
|
|
|
throw new AppException(ErrorCodes::ERR_FILE_001, 'نوع فایل پشتیبانی نمیشود', 422);
|
|
}
|
|
}
|