* @link https://getkirby.com * @copyright Bastian Allgeier * @license https://opensource.org/licenses/MIT */ class Sane { /** * Handler Type Aliases */ public static array $aliases = [ 'application/xml' => 'xml', 'image/svg' => 'svg', 'image/svg+xml' => 'svg', 'text/html' => 'html', 'text/xml' => 'xml', ]; /** * All registered handlers */ public static array $handlers = [ 'html' => 'Kirby\Sane\Html', 'svg' => 'Kirby\Sane\Svg', 'svgz' => 'Kirby\Sane\Svgz', 'xml' => 'Kirby\Sane\Xml', ]; /** * Handler getter * * @param bool $lazy If set to `true`, `null` is returned for undefined handlers * * @throws \Kirby\Exception\NotFoundException If no handler was found and `$lazy` was set to `false` */ public static function handler(string $type, bool $lazy = false): Handler|null { // normalize the type $type = mb_strtolower($type); // find a handler or alias $alias = static::$aliases[$type] ?? null; $handler = static::$handlers[$type] ?? ($alias ? static::$handlers[$alias] ?? null : null); if (empty($handler) === false && class_exists($handler) === true) { return new $handler(); } if ($lazy === true) { return null; } throw new NotFoundException('Missing handler for type: "' . $type . '"'); } /** * Sanitizes the given string with the specified handler * @since 3.6.0 */ public static function sanitize(string $string, string $type): string { return static::handler($type)->sanitize($string); } /** * Sanitizes the contents of a file by overwriting * the file with the sanitized version; * the sane handlers are automatically chosen by * the extension and MIME type if not specified * @since 3.6.0 * * @param string|bool $typeLazy Explicit handler type string, * `true` for lazy autodetection or * `false` for normal autodetection * * @throws \Kirby\Exception\InvalidArgumentException If the file didn't pass validation * @throws \Kirby\Exception\LogicException If more than one handler applies * @throws \Kirby\Exception\NotFoundException If the handler was not found * @throws \Kirby\Exception\Exception On other errors */ public static function sanitizeFile(string $file, string|bool $typeLazy = false): void { if (is_string($typeLazy) === true) { static::handler($typeLazy)->sanitizeFile($file); return; } // try to find exactly one matching handler $handlers = static::handlersForFile($file, $typeLazy === true); switch (count($handlers)) { case 0: // lazy autodetection didn't find a handler break; case 1: $handlers[0]->sanitizeFile($file); break; default: // more than one matching handler; // sanitizing with all handlers will not leave much in the output $handlerNames = array_map('get_class', $handlers); throw new LogicException( 'Cannot sanitize file as more than one handler applies: ' . implode(', ', $handlerNames) ); } } /** * Validates file contents with the specified handler * * @throws \Kirby\Exception\InvalidArgumentException If the file didn't pass validation * @throws \Kirby\Exception\NotFoundException If the handler was not found * @throws \Kirby\Exception\Exception On other errors */ public static function validate(string $string, string $type): void { static::handler($type)->validate($string); } /** * Validates the contents of a file; * the sane handlers are automatically chosen by * the extension and MIME type if not specified * * @param string|bool $typeLazy Explicit handler type string, * `true` for lazy autodetection or * `false` for normal autodetection * * @throws \Kirby\Exception\InvalidArgumentException If the file didn't pass validation * @throws \Kirby\Exception\NotFoundException If the handler was not found * @throws \Kirby\Exception\Exception On other errors */ public static function validateFile(string $file, string|bool $typeLazy = false): void { if (is_string($typeLazy) === true) { static::handler($typeLazy)->validateFile($file); return; } foreach (static::handlersForFile($file, $typeLazy === true) as $handler) { $handler->validateFile($file); } } /** * Returns all handler objects that apply to the given file based on * file extension and MIME type * * @param bool $lazy If set to `true`, undefined handlers are skipped * @return array<\Kirby\Sane\Handler> */ protected static function handlersForFile(string $file, bool $lazy = false): array { $handlers = $handlerClasses = []; // all values that can be used for the handler search; // filter out all empty options $options = array_filter([F::extension($file), F::mime($file)]); foreach ($options as $option) { $handler = static::handler($option, $lazy); $handlerClass = $handler ? get_class($handler) : null; // ensure that each handler class is only returned once if ($handler && in_array($handlerClass, $handlerClasses) === false) { $handlers[] = $handler; $handlerClasses[] = $handlerClass; } } return $handlers; } }