Имена медиафайлов в WordPress

WordPress недостаточно хорошо фильтрует названия имен файлов, которые пользователи загружают на сервер. Если файлы загружаете только вы сами, то, скорее всего, проблема не так существенна. Однако, если такая возможность доступна для большого числа пользователей, то катастрофа неизбежна. На моей небольшой практике в именах файлов встречалось много чего. В результате мы можем столкнуться со следующими проблемами:

  1. Ад при бэкапе. «Карнавал» начинается, когда файловая система для бэкапа не чувствительна к регистру, а сервер чувствителен. Например, большим удивлением для многих будет то, что файловая система Mac OS Extended (Journaled) не чувствительна к регистру.
  2. Недоступность данных по HTTP. Так многие картинки, которые есть на сервере и вставлены через тег img на страницах не будут загружаться.
  3. Файл существует и виден через SFTP, но недоступен через SFTP-клиент и HTTP.

А вот только несколько примеров того, что может встретиться в имени файла:

  1. Диакретические знаки (знаки с различными точечками и черточками над буквами: Ä, Š, ζ).
  2. Знак процента (%).
  3. Кириллические символы (а, б, в, г, д…).
  4. iOS-смайлики. Они же Юникод-символы, которые часто встречаются в именах файлов от пользователей Apple.

Еще одна потенциальная проблема — длина имени файла. На файловых системах ext4 и NTFS она составляет 255 байт. Я не сталкивался с такой проблемой на своих проектах, но она также допустима.

В поисках решения

Покопавшись в исходниках и документации можно найти фильтр sanitize_file_name, который позволяет приводить в порядок имя, которое будет задано файлу на сервере. В результате получилась функция, которая делает имена хорошими. Версия на Bitbucket, доступная, как Bower-компонент.

function kolya_korobochkin_sanitize_file_name ($name, $original_name) {
	$chars_table = array (
		/*
		 * Таблица взята из плагина Filenames to latin
		 * http://wordpress.org/plugins/filenames-to-latin/
		 * Версия 2.0
		 */
		// Cyrillic alphabet
		'А' => 'a', 'Б' => 'b', 'В' => 'v', 'Г' => 'g', 'Д' => 'd',
		'а' => 'a', 'б' => 'b', 'в' => 'v', 'г' => 'g', 'д' => 'd',

		'Е' => 'e', 'Ж' => 'zh', 'З' => 'z', 'И' => 'i', 'Й' => 'j',
		'е' => 'e', 'ж' => 'zh', 'з' => 'z', 'и' => 'i', 'й' => 'j',

		'К' => 'k', 'Л' => 'l', 'М' => 'm', 'Н' => 'n', 'О' => 'o',
		'к' => 'k', 'л' => 'l', 'м' => 'm', 'н' => 'n', 'о' => 'o',

		'П' => 'p', 'Р' => 'r', 'С' => 's', 'Т' => 't', 'У' => 'u',
		'п' => 'p', 'р' => 'r', 'с' => 's', 'т' => 't', 'у' => 'u',

		'Ф' => 'f', 'Х' => 'h', 'Ц' => 'c', 'Ч' => 'ch', 'Ш' => 'sh',
		'ф' => 'f', 'х' => 'h', 'ц' => 'c', 'ч' => 'ch', 'ш' => 'sh',

		'Щ' => 'shch', 'Ь' => '', 'Ю' => 'ju', 'Я' => 'ja',
		'щ' => 'shch', 'ь' => '', 'ю' => 'ju', 'я' => 'ja',

		// Ukrainian
		'Ґ' => 'g', 'Є' => 'ye', 'І' => 'i', 'Ї' => 'yi',
		'ґ' => 'g', 'є' => 'ye', 'і' => 'i', 'ї' => 'yi',
				
		// Russian
		'Ё' => 'yo', 'Ы' => 'y', 'Ъ' => '', 'Э' => 'e',
		'ё' => 'yo', 'ы' => 'y', 'ъ' => '', 'э' => 'e',
				
		// Belorussian
		'Ў' => 'u',
		'ў' => 'u',

		// German
		'Ä' => 'ae', 'Ö' => 'oe', 'Ü' => 'ue', 'ß' => 'ss',
		'ä' => 'ae', 'ö' => 'oe', 'ü' => 'ue',
				
		// Polish
		'Ą' => 'a', 'Ć' => 'c', 'Ę' => 'e', 'Ł' => 'l', 'Ń' => 'n',
		'ą' => 'a', 'ć' => 'c', 'ę' => 'e', 'ł' => 'l', 'ń' => 'n',
		'Ó' => 'o', 'Ś' => 's', 'Ź' => 'z', 'Ż' => 'z',
		'ó' => 'o', 'ś' => 's', 'ź' => 'z', 'ż' => 'z',

		// Hungarian
		'Ő' => 'o', 'Ű' => 'u',
		'ő' => 'o', 'ű' => 'u',

		// Czech
		'Ě' => 'e', 'Š' => 's', 'Č' => 'c', 'Ř' => 'r', 'Ž' => 'z',
		'ě' => 'e', 'š' => 's', 'č' => 'c', 'ř' => 'r', 'ž' => 'z',

		'Ý' => 'y', 'Á' => 'a', 'É' => 'e', 'Ď' => 'd', 'Ť' => 't',
		'ý' => 'y', 'á' => 'a', 'é' => 'e', 'ď' => 'd', 'ť' => 't',

		'Ň' => 'n', 'Ú' => 'u', 'Ů' => 'u',
		'ň' => 'n', 'ú' => 'u', 'ů' => 'u',

		// Greek alphabet & modern polytonic characters
		'Α' => 'a', 'Β' => 'v', 'Γ' => 'g', 'Δ' => 'd', 'Ε' => 'e',
		'α' => 'a', 'β' => 'v', 'γ' => 'g', 'δ' => 'd', 'ε' => 'e',

		'Ζ' => 'z', 'Η' => 'i', 'Θ' => 'th', 'Ι' => 'i', 'Κ' => 'k',
		'ζ' => 'z', 'η' => 'i', 'θ' => 'th', 'ι' => 'i', 'κ' => 'k',

		'Λ' => 'l', 'Μ' => 'm', 'Ν' => 'n', 'Ξ' => 'x', 'Ο' => 'o',
		'λ' => 'l', 'μ' => 'm', 'ν' => 'n', 'ξ' => 'x', 'ο' => 'o',

		'Π' => 'p', 'Ρ' => 'r', 'Σ' => 's', 'Τ' => 't', 'Υ' => 'y',
		'π' => 'p', 'ρ' => 'r', 'σ' => 's', 'τ' => 't', 'υ' => 'y',

		'Φ' => 'f', 'Χ' => 'ch', 'Ψ' => 'ps', 'Ω' => 'o', 'Ά' => 'a',
		'φ' => 'f', 'χ' => 'ch', 'ψ' => 'ps', 'ω' => 'o', 'ά' => 'a',

		'Έ' => 'e', 'Ή' => 'i', 'Ί' => 'i', 'Ό' => 'o', 'Ύ' => 'y',
		'έ' => 'e', 'ή' => 'i', 'ί' => 'i', 'ό' => 'o', 'ύ' => 'y',

		'Ώ' => 'o', 'Ϊ' => 'i', 'Ϋ' => 'y',
		'ώ' => 'o', 'ς' => 's', 'ΐ' => 'i', 'ϊ' => 'i', 'ϋ' => 'y', 'ΰ' => 'y',

		// Extra all (http:www.atm.ox.ac.ukuseriwicharmap.html)
		'À' => 'a', 'Á' => 'a', 'Â' => 'a', 'Ã' => 'a', 'Å' => 'a',
		'à' => 'a', 'á' => 'a', 'â' => 'a', 'ã' => 'a', 'å' => 'a',

		'Æ' => 'ae', 'Ç' => 'c', 'È' => 'e', 'É' => 'e', 'Ê' => 'e',
		'æ' => 'ae', 'ç' => 'c', 'è' => 'e', 'é' => 'e', 'ê' => 'e',

		'Ë' => 'e', 'Ì' => 'i', 'Í' => 'i', 'Î' => 'i', 'Ï' => 'i',
		'ë' => 'e', 'ì' => 'i', 'í' => 'i', 'î' => 'i', 'ï' => 'i',

		'Ð' => 'd', 'Ñ' => 'n', 'Ò' => 'o', 'Ô' => 'o', 'Õ' => 'o',
		'ð' => 'd', 'ñ' => 'n', 'ò' => 'o', 'ô' => 'o', 'õ' => 'o',

		'×' => 'x', 'Ø' => 'o', 'Ù' => 'u', 'Ú' => 'u', 'Û' => 'u',
		'×' => 'x', 'ø' => 'o', 'ù' => 'u', 'ú' => 'u', 'û' => 'u',

		'Þ' => 'p', 'Ÿ' => 'y',
		'þ' => 'p', 'ÿ' => 'y',

		// Other
		'№' => '', '“' => '', '”' => '', '«' => '', '»' => '',
		'„' => '', '@' => '', '%' => '', '‘' => '', '’' => '',
		'`' => '', '´' => '', 'º' => 'o', 'ª' => 'a',
	);
	$name = remove_accents ($name);
	$name = strtr ($name, $chars_table);
	$name = urlencode ($name);
	// Перезапись % на ничего после urlencode
	$name = str_replace (array ('%'), '', $name );

	/*
	 * Проверка на длину файла
	 * UNIX поддерживает 255 символов в имени максимум.
	 * Мы обрезаем длину имени файла до 130 символов, как и на wordpress.com,
	 * WordPress добавляет дополнительные символы в имя файла, например
	 * image-1000x1000.jpg (поэтому мы резервируем половину возможной длины).
	 */
	$info = pathinfo ($name);
	$name = substr ($info ['filename'], 0, 130);
	$name .= '.' . $info ['extension'];
	return $name;
}
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s