1024 || strpbrk($rel, "\0\\") !== false || $rel[0] === '/' || preg_match('#^[A-Za-z]:#', $rel) || !mb_check_encoding($rel, 'UTF-8')) { return false; } foreach (explode('/', $rel) as $segment) { if ($segment === '' || $segment === '.' || $segment === '..' || preg_match('/[\x00-\x1f\x7f]/', $segment)) { return false; } } return true; } /** The top-level item a path belongs to: the theme or plugin folder, the uploads year, the file itself... */ public static function top(string $rel): string { return explode('/', $rel, 2)[0]; } /** @param string[] $protected "group/top-level" items (see Groups::protected_items()), compared case-insensitively */ public static function is_protected(string $group, string $rel, array $protected): bool { $item = strtolower($group . '/' . self::top($rel)); foreach ($protected as $p) { if (strtolower($p) === $item) { return true; } } return false; } }