extensions = $extensions; } public function validate(string $attribute, mixed $value, Closure $fail): void { $this->attribute = $attribute; $failed = false; try { $envelope = json_decode(trim($value)); $name = ! empty($envelope->name) ? $envelope->name : ''; $uri = ! empty($envelope->data) ? $envelope->data : ''; } catch (\Exception $e) { $failed = true; } if (! in_array(pathinfo($name, PATHINFO_EXTENSION), $this->extensions)) { $failed = true; } if (! preg_match('/^data:\w+\/[\w\+]+;base64,[\w\+\=\/]+$/', $uri)) { $failed = true; } $segments = explode(',', $uri); if (! isset($segments[1]) || empty($segments[1])) { $failed = true; } try { $bytes = base64_decode($segments[1]); $handle = finfo_open(); $sniffed = finfo_buffer($handle, $bytes, FILEINFO_EXTENSION); if ($sniffed === '???') { $failed = true; } // A sniff may answer with several equivalent extensions joined by // slashes, e.g. "jpeg/jpg/jpe/jfif"; any one of them matching is // enough to accept the upload. if (strpos($sniffed, '/')) { foreach (explode('/', $sniffed) as $candidate) { if (in_array($candidate, $this->extensions)) { $failed = false; } } } else { if (in_array($sniffed, $this->extensions)) { $failed = false; } } } catch (\Exception $e) { $failed = true; } if ($failed) { $fail('The '.$this->attribute.' must be a json with file of type: '.implode(', ', $this->extensions).' encoded in base64.'); } } }