getRealPath() ?: $file->getPathname()); if ($path === '' || ! is_readable($path)) { throw ValidationException::withMessages([ 'image' => 'Unable to read the uploaded image.', ]); } $binary = file_get_contents($path); if (! is_string($binary) || $binary === '') { throw ValidationException::withMessages([ 'image' => 'Unable to read the uploaded image.', ]); } return $this->validateBinary($binary, $options, (int) ($file->getSize() ?? strlen($binary))); } public function validateBinary(string $binary, array $options, ?int $filesize = null): array { $normalized = $this->normalizeOptions($options); $size = $filesize ?? strlen($binary); if ($size <= 0) { throw ValidationException::withMessages([ 'image' => 'Uploaded image is empty.', ]); } $maxBytes = (int) config('enhance.max_upload_mb', 20) * 1024 * 1024; if ($maxBytes > 0 && $size > $maxBytes) { throw ValidationException::withMessages([ 'image' => sprintf('The image may not be greater than %d MB.', (int) config('enhance.max_upload_mb', 20)), ]); } $finfo = new \finfo(FILEINFO_MIME_TYPE); $mime = strtolower((string) $finfo->buffer($binary)); if (! in_array($mime, (array) config('enhance.allowed_mimes', []), true)) { throw ValidationException::withMessages([ 'image' => 'Unsupported image format. Upload a JPEG, PNG, or WebP image.', ]); } $dimensions = @getimagesizefromstring($binary); if (! is_array($dimensions) || (int) ($dimensions[0] ?? 0) < 1 || (int) ($dimensions[1] ?? 0) < 1) { throw ValidationException::withMessages([ 'image' => 'Uploaded file is not a valid image.', ]); } $width = (int) ($dimensions[0] ?? 0); $height = (int) ($dimensions[1] ?? 0); if ($width > (int) config('enhance.max_input_width', 4096)) { throw ValidationException::withMessages([ 'image' => sprintf('Image width may not exceed %d pixels.', (int) config('enhance.max_input_width', 4096)), ]); } if ($height > (int) config('enhance.max_input_height', 4096)) { throw ValidationException::withMessages([ 'image' => sprintf('Image height may not exceed %d pixels.', (int) config('enhance.max_input_height', 4096)), ]); } return $normalized + [ 'input_width' => $width, 'input_height' => $height, 'input_filesize' => $size, 'input_mime' => $mime, ]; } public function normalizeOptions(array $options): array { $allowedScales = array_map('intval', (array) config('enhance.allowed_scales', [2, 4])); $allowedModes = array_map('strval', (array) config('enhance.allowed_modes', ['standard', 'artwork', 'photo', 'illustration'])); $allowedEngines = [ \App\Models\EnhanceJob::ENGINE_STUB, \App\Models\EnhanceJob::ENGINE_EXTERNAL_WORKER, ]; $scale = (int) ($options['scale'] ?? config('enhance.allowed_scales.0', 2)); $mode = trim((string) ($options['mode'] ?? 'standard')); $engine = trim((string) ($options['engine'] ?? config('enhance.default_engine', \App\Models\EnhanceJob::ENGINE_STUB))); if (! in_array($scale, $allowedScales, true)) { throw ValidationException::withMessages([ 'scale' => 'Please select a supported scale.', ]); } if (! in_array($mode, $allowedModes, true)) { throw ValidationException::withMessages([ 'mode' => 'Please select a supported enhance mode.', ]); } if (! in_array($engine, $allowedEngines, true)) { throw ValidationException::withMessages([ 'engine' => 'Please select a supported enhance engine.', ]); } return [ 'scale' => $scale, 'mode' => $mode, 'engine' => $engine, ]; } }