Очистить строки, чтобы сделать их безопасными для URL-адресов и имен файлов?

Я пытаюсь придумать функцию, которая хорошо справляется с очисткой определенных строк, чтобы их можно было безопасно использовать в URL-адресе (например, ярлык сообщения), а также безопасно использовать в качестве имен файлов. Например, когда кто-то загружает файл, я хочу убедиться, что я удалил все опасные символы из имени.

До сих пор я придумал следующую функцию, которая, надеюсь, решает эту проблему и позволяет также использовать внешние данные UTF-8.

/**
 * Convert a string to the file/URL safe "slug" form
 *
 * @param string $string the string to clean
 * @param bool $is_filename TRUE will allow additional filename characters
 * @return string
 */
function sanitize($string = '', $is_filename = FALSE)
{
 // Replace all weird characters with dashes
 $string = preg_replace('/[^\w\-'. ($is_filename ? '~_\.' : ''). ']+/u', '-', $string);

 // Only allow one dash separator at a time (and make string lowercase)
 return mb_strtolower(preg_replace('/--+/u', '-', $string), 'UTF-8');
}

Есть ли у кого-нибудь сложные образцы данных, с которыми я могу справиться, или знает, как лучше защитить наши приложения от плохих имен?

$ is-filename позволяет использовать некоторые дополнительные символы, например временные файлы vim

обновление: удален символ звезды, так как я не мог придумать допустимого использования


person Xeoncross    schedule 19.04.2010    source источник
comment
Лучше удалите все, кроме [\ w.-]   -  person elias    schedule 19.04.2010
comment
Вы можете найти Normalizer и комментарии к нему полезными.   -  person Matt Gibson    schedule 21.10.2010


Ответы (22)


Некоторые наблюдения за вашим решением:

  1. 'u' в конце вашего шаблона означает, что шаблон, а не текст, который он соответствует, будет интерпретироваться как UTF-8 (я полагаю, вы предположили, что последнее?).
  2. \ w соответствует символу подчеркивания. Вы специально включаете его для файлов, что приводит к предположению, что они не нужны вам в URL-адресах, но в коде, в котором у вас есть URL-адреса, будет разрешено включать подчеркивание.
  3. Включение «чужого UTF-8», похоже, зависит от локали. Неясно, является ли это локалью сервера или клиента. Из документации PHP:

Символ «слова» - это любая буква, цифра или символ подчеркивания, то есть любой символ, который может быть частью «слова» Perl. Определение букв и цифр контролируется символьными таблицами PCRE и может варьироваться, если имеет место сопоставление для конкретной локали. Например, в языковом стандарте «fr» (французский) некоторые коды символов больше 128 используются для букв с диакритическими знаками, и им соответствует \ w.

Создание пули

Вероятно, вам не следует включать символы с диакритическими знаками и т. Д. В заголовок сообщения, поскольку технически они должны быть закодированы в процентах (согласно правилам кодирования URL-адресов), поэтому у вас будут уродливые URL-адреса.

Итак, если бы я был вами, после нижнего регистра я бы преобразовал любые «специальные» символы в их эквиваленты (например, é -> e) и заменил символы, отличные от [az], на '-', ограничившись запуском одного '-' как вы это сделали. Здесь есть реализация преобразования специальных символов: https://web.archive.org/web/20130208144021/http://neo22s.com/slug

Санитарная обработка в целом

OWASP имеет PHP-реализацию своего Enterprise Security API, которая, среди прочего, включает методы для безопасного кодирования и декодирования ввода и вывода в вашем приложении.

Интерфейс кодировщика обеспечивает:

canonicalize (string $input, [bool $strict = true])
decodeFromBase64 (string $input)
decodeFromURL (string $input)
encodeForBase64 (string $input, [bool $wrap = false])
encodeForCSS (string $input)
encodeForHTML (string $input)
encodeForHTMLAttribute (string $input)
encodeForJavaScript (string $input)
encodeForOS (Codec $codec, string $input)
encodeForSQL (Codec $codec, string $input)
encodeForURL (string $input)
encodeForVBScript (string $input)
encodeForXML (string $input)
encodeForXMLAttribute (string $input)
encodeForXPath (string $input)

https://github.com/OWASP/PHP-ESAPI https://www.owasp.org/index.php/Category:OWASP_Enterprise_Security_API

person Alan Donnelly    schedule 23.04.2010
comment
Вы правы относительно моего предположения о модификаторе u - я думал, что это было для текста. Я также забыл про модификатор \ w, включая подчеркивание. Обычно я конвертирую все символы с диакритическими знаками в ASCII, но я хочу, чтобы это работало и для других языков. Я предполагал, что будет какой-то безопасный для UTF-8 способ, позволяющий использовать любой символ языка в заголовке URL-адреса или имени файла, чтобы работали даже арабские заголовки. В конце концов, Linux поддерживает имена файлов UTF-8, а браузеры должны кодировать HTML-ссылки по мере необходимости. Большое спасибо за ваш вклад. - person Xeoncross; 23.04.2010
comment
Если подумать, вы действительно правы, но проблема не только в том, что браузер правильно кодирует ссылки. Самый простой способ приблизиться к тому, что вы хотите, - сопоставить символы, отличные от ASCII, с их ближайшим эквивалентом ASCII, а затем URL-кодировать вашу ссылку в теле HTML. Сложный путь - обеспечить согласованную кодировку UTF-8 (или UTF-16, я думаю для некоторых китайских диалектов) из вашего хранилища данных через ваш веб-сервер, уровень приложения (PHP), содержимое страницы, веб-браузер и not < / b> urlencode ваши URL-адреса (но по-прежнему удаляйте «нежелательные» символы). Это даст вам хорошие некодированные ссылки и URL-адреса. - person Alan Donnelly; 23.04.2010
comment
Хороший совет. Я попытаюсь создать чистую среду UTF-8. Затем, взяв несколько строк из языков, отличных от ASCII, я удалю опасные символы (./ ;: и т. Д.) И создаю файлы, а затем HTML-ссылки на эти файлы, чтобы посмотреть, могу ли я щелкнуть по ним и посмотреть, все ли это работает. Если нет, то мне, вероятно, придется вернуться к (raw)? Urlencode (), чтобы разрешить UTF-8. Я отправлю результаты здесь. - person Xeoncross; 24.04.2010
comment
Я создал файл с именем สังเวช พระปกเกศกองบู๊กู้ขึ้นใหม่.txt, а затем создал HTML-файл UTF-8 со ссылкой на него. Удивительно, но это сработало - даже на окнах! Однако тогда у меня был PHP file_put_contents('สังเวช พระปกเกศกองบู๊กู้ขึ้นใหม่.txt'), и он не смог создать имя файла базара из этой строки. Затем я попытался создать его с помощью fopen() и получил такое же испорченное имя файла. Таким образом, очевидно, что PHP (по крайней мере, в Windows) не может создавать имена файлов UTF-8. bugs.php.net/bug.php?id=46990&thanks=6 - person Xeoncross; 26.04.2010
comment
Я награждаю этот ответ, потому что он заставил меня задуматься больше всего, а также включил полезную ссылку на проект, о котором я никогда не слышал, и который стоит изучить. Я отправлю сообщение, как только найду ответ. - person Xeoncross; 29.04.2010
comment
Новая ссылка на OWASP PHP ESAPI: https://github.com/OWASP/PHP-ESAPI - person Kristoffer Bohmann; 25.10.2015

Я нашел эту большую функцию в коде Chyrp:

/**
 * Function: sanitize
 * Returns a sanitized string, typically for URLs.
 *
 * Parameters:
 *     $string - The string to sanitize.
 *     $force_lowercase - Force the string to lowercase?
 *     $anal - If set to *true*, will remove all non-alphanumeric characters.
 */
function sanitize($string, $force_lowercase = true, $anal = false) {
    $strip = array("~", "`", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "_", "=", "+", "[", "{", "]",
                   "}", "\\", "|", ";", ":", "\"", "'", "&#8216;", "&#8217;", "&#8220;", "&#8221;", "&#8211;", "&#8212;",
                   "—", "–", ",", "<", ".", ">", "/", "?");
    $clean = trim(str_replace($strip, "", strip_tags($string)));
    $clean = preg_replace('/\s+/', "-", $clean);
    $clean = ($anal) ? preg_replace("/[^a-zA-Z0-9]/", "", $clean) : $clean ;
    return ($force_lowercase) ?
        (function_exists('mb_strtolower')) ?
            mb_strtolower($clean, 'UTF-8') :
            strtolower($clean) :
        $clean;
}

и этот в коде wordpress

/**
 * Sanitizes a filename replacing whitespace with dashes
 *
 * Removes special characters that are illegal in filenames on certain
 * operating systems and special characters requiring special escaping
 * to manipulate at the command line. Replaces spaces and consecutive
 * dashes with a single dash. Trim period, dash and underscore from beginning
 * and end of filename.
 *
 * @since 2.1.0
 *
 * @param string $filename The filename to be sanitized
 * @return string The sanitized filename
 */
function sanitize_file_name( $filename ) {
    $filename_raw = $filename;
    $special_chars = array("?", "[", "]", "/", "\\", "=", "<", ">", ":", ";", ",", "'", "\"", "&", "$", "#", "*", "(", ")", "|", "~", "`", "!", "{", "}");
    $special_chars = apply_filters('sanitize_file_name_chars', $special_chars, $filename_raw);
    $filename = str_replace($special_chars, '', $filename);
    $filename = preg_replace('/[\s-]+/', '-', $filename);
    $filename = trim($filename, '.-_');
    return apply_filters('sanitize_file_name', $filename, $filename_raw);
}

Обновление сентябрь 2012 г.

Аликс Аксель проделал невероятную работу в этой области. Его phunction framework включает в себя несколько отличных текстовых фильтров и преобразований.

person Xeoncross    schedule 19.04.2010
comment
Код WordPress не переносится, поскольку в нем используется apply_filters - person Kevin Mark; 05.11.2012
comment
Обратите внимание, что версия wordpress заменяет /[\s-]+/ на -, что лучше, чем первая версия (которая заменяет только /\s+/), что может вызывать несколько тире подряд - person Yotam Omer; 27.09.2013
comment
Для справки можно найти фильтры apply_filters: здесь и sanitize_file_name через здесь. - person erikvimz; 19.12.2015
comment
как насчет нескольких пробелов? Заменять - person Maciek Semik; 09.05.2016
comment
Переменная $ anal звучит для меня очень пугающе с опцией force. - person viljun; 05.09.2016

Это должно сделать ваши имена безопасными ...

$string = preg_replace(array('/\s/', '/\.[\.]+/', '/[^\w_\.\-]/'), array('_', '.', ''), $string);

и более глубокое решение этого вопроса:

// Remove special accented characters - ie. sí.
$clean_name = strtr($string, array('Š' => 'S','Ž' => 'Z','š' => 's','ž' => 'z','Ÿ' => 'Y','À' => 'A','Á' => 'A','Â' => 'A','Ã' => 'A','Ä' => 'A','Å' => 'A','Ç' => 'C','È' => 'E','É' => 'E','Ê' => 'E','Ë' => 'E','Ì' => 'I','Í' => 'I','Î' => 'I','Ï' => 'I','Ñ' => 'N','Ò' => 'O','Ó' => 'O','Ô' => 'O','Õ' => 'O','Ö' => 'O','Ø' => 'O','Ù' => 'U','Ú' => 'U','Û' => 'U','Ü' => 'U','Ý' => 'Y','à' => 'a','á' => 'a','â' => 'a','ã' => 'a','ä' => 'a','å' => 'a','ç' => 'c','è' => 'e','é' => 'e','ê' => 'e','ë' => 'e','ì' => 'i','í' => 'i','î' => 'i','ï' => 'i','ñ' => 'n','ò' => 'o','ó' => 'o','ô' => 'o','õ' => 'o','ö' => 'o','ø' => 'o','ù' => 'u','ú' => 'u','û' => 'u','ü' => 'u','ý' => 'y','ÿ' => 'y'));
$clean_name = strtr($clean_name, array('Þ' => 'TH', 'þ' => 'th', 'Ð' => 'DH', 'ð' => 'dh', 'ß' => 'ss', 'Œ' => 'OE', 'œ' => 'oe', 'Æ' => 'AE', 'æ' => 'ae', 'µ' => 'u'));

$clean_name = preg_replace(array('/\s/', '/\.[\.]+/', '/[^\w_\.\-]/'), array('_', '.', ''), $clean_name);

Предполагается, что вам нужна точка в имени файла. если вы хотите перевести его в нижний регистр, просто используйте

$clean_name = strtolower($clean_name);

для последней строки.

person SoLoGHoST    schedule 28.04.2010
comment
По-прежнему отсутствуют некоторые чешские и словацкие символы: 'ľ' => 'l', 'Ľ' => 'L', 'č' => 'c', 'Č' => 'C', 'ť' => 't', 'Ť' => 'T', 'ň' => 'n', 'Ň' => 'N', 'ĺ' => 'l', 'Ĺ' => 'L', 'Ř' => 'R', 'ř' => 'r', 'ě' => 'e', 'Ě' => 'E', 'ů' => 'u', 'Ů' => 'U' - person Jasom Dotnet; 12.12.2015

Попробуй это:

function normal_chars($string)
{
    $string = htmlentities($string, ENT_QUOTES, 'UTF-8');
    $string = preg_replace('~&([a-z]{1,2})(acute|cedil|circ|grave|lig|orn|ring|slash|th|tilde|uml);~i', '$1', $string);
    $string = html_entity_decode($string, ENT_QUOTES, 'UTF-8');
    $string = preg_replace(array('~[^0-9a-z]~i', '~[ -]+~'), ' ', $string);

    return trim($string, ' -');
}

Examples:

echo normal_chars('Álix----_Ãxel!?!?'); // Alix Axel
echo normal_chars('áéíóúÁÉÍÓÚ'); // aeiouAEIOU
echo normal_chars('üÿÄËÏÖÜŸåÅ'); // uyAEIOUYaA

На основе выбранного ответа в этой теме: Удобное для URL-адресов имя пользователя в PHP?

person John Conde    schedule 19.04.2010
comment
Очень хорошо - я никогда не видел, чтобы это делалось без таблицы перевода (как в Wordpress). Однако я не думаю, что этой функции достаточно, поскольку она переводит только специальные символы, но не удаляет опасные символы. Может быть, его можно добавить к одному из вышеперечисленных ... - person Xeoncross; 19.04.2010
comment
Ха! Этот способ кодирования сущностей хорош! Хотя на первый взгляд не совсем понятно, как этот метод делает то, что делает. Но есть проблема. Не превратятся ли Фредерик и Эрик в Фредерика и Эрика? - person Alan Donnelly; 23.04.2010
comment
@AlanDonnelly: Действительно, я обновил функцию в своем исходном ответе (проверьте ссылку), trim() также должен быть trim($string, '-'). - person Alix Axel; 15.11.2011
comment
@Xeoncross: Последний preg_replace() должен удалить все опасные символы. - person Alix Axel; 15.11.2011
comment
@AlixAxel, ты просто везде, не так ли. Я только что читал PHP AWS SDK, и у них был ваш код для UUID. Потрясающий код функции просто трудно превзойти. - person Xeoncross; 16.11.2011
comment
@Xeoncross: Спасибо, что сообщили мне, даже не знал об этом! знак равно - person Alix Axel; 16.11.2011
comment
Я хотел использовать это, чтобы преобразовать кучу названий телевизионных эпизодов в имена файлов на базе Windows, сохранив их расширения, квадратные скобки, тире и одинарные кавычки, а также заменив двоеточия на точки или тире там, где это необходимо. Итак, вот моя версия: pastebin.com/0CsEV0Ax - person WackGet; 06.03.2015
comment
На 425 мс это довольно медленно, просто к вашему сведению - person Jonathan; 15.12.2016

Это не совсем ответ, поскольку он не предлагает никаких решений (пока!), но он слишком велик, чтобы поместиться в комментарии ...


Я провел небольшое тестирование (в отношении имен файлов) в Windows 7 и Ubuntu 12.04 и выяснил, что:

1. PHP не может обрабатывать имена файлов, отличные от ASCII

Хотя и Windows, и Ubuntu могут обрабатывать имена файлов Unicode (даже, кажется, RTL), PHP 5.3 требует хаков для работы даже с простым старым ISO-8859-1, поэтому лучше оставить его ASCII только для безопасности.

2. Длина имени файла имеет значение (особенно в Windows)

В Ubuntu максимальная длина имени файла (включая расширение) составляет 255 (исключая путь):

/var/www/uploads/123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345/

Однако в Windows 7 (NTFS) максимальная длина имени файла зависит от его абсолютного пути:

(0 + 0 + 244 + 11 chars) C:\1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234\1234567.txt
(0 + 3 + 240 + 11 chars) C:\123\123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\1234567.txt
(3 + 3 + 236 + 11 chars) C:\123\456\12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456\1234567.txt

Википедия говорит, что:

NTFS позволяет использовать каждый компонент пути (каталог или имя файла) длиной 255 символов.

Насколько мне известно (и по результатам тестирования), это неверно.

Всего (считая косые черты) во всех этих примерах 259 символов, если убрать C:\, что дает 256 символов (а не 255 ?!). Каталоги были созданы с помощью проводника, и вы заметите, что он не использует все доступное пространство для имени каталога. Причина в том, чтобы разрешить создание файлов с использованием соглашения об именах файлов 8.3. То же самое происходит и с другими разделами.

Разумеется, для файлов не требуется резервировать требования к длине 8,3:

(255 chars) E:\12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901.txt

Вы не можете создавать дополнительные подкаталоги, если абсолютный путь к родительскому каталогу содержит более 242 символов, потому что 256 = 242 + 1 + \ + 8 + . + 3. Используя проводник Windows, вы не можете создать другой каталог, если родительский каталог содержит более 233 символов (в зависимости от языкового стандарта системы), потому что 256 = 233 + 10 + \ + 8 + . + 3; 10 здесь - длина строки New folder.

Файловая система Windows представляет собой серьезную проблему, если вы хотите обеспечить взаимодействие между файловыми системами.

3. Остерегайтесь зарезервированных символов и ключевых слов

Помимо удаления не-ASCII, непечатаемых и управляющих символов, вам также необходимо повторно (разместить / переместить):

"*/:<>?\|

Простое удаление этих символов может быть не лучшей идеей, потому что имя файла может частично потерять свое значение. Я думаю, что, по крайней мере, несколько вхождений этих символов следует заменить одним подчеркиванием (_) или, возможно, чем-то более представительным (это всего лишь идея):

  • "*? -> _
  • /\| -> -
  • : -> [ ]-[ ]
  • < -> (
  • > -> )

Есть также специальные ключевые слова, которых следует избегать (например, NUL), хотя я не знаю, как это преодолеть. . Возможно, черный список со случайным резервным именем будет хорошим подходом для решения этой проблемы.

4. Учитывать регистр

Это само собой разумеется, но если вы хотите обеспечить уникальность файлов в разных операционных системах, вам следует преобразовать имена файлов в нормализованный регистр, чтобы my_file.txt и My_File.txt в Linux не стали одинаковыми my_file.txt файлом в Windows.

5. Убедитесь, что он уникален

Если имя файла уже существует, к его основе должен быть добавлен уникальный идентификатор имя файла.

Общие уникальные идентификаторы включают метку времени UNIX, дайджест содержимого файла или случайную строку.

6. Скрытые файлы

То, что это имя может быть названо, не означает, что оно должно ...

Точки обычно заносятся в белый список в именах файлов, но в Linux скрытый файл представлен ведущей точкой.

7. Другие соображения

Если вам нужно удалить некоторые символы из имени файла, расширение обычно более важно, чем базовое имя файла. Разрешив значительное максимальное количество символов для расширения файла (8–16), следует удалите символы из базового имени. Также важно отметить, что в маловероятном случае наличия более одного длинного расширения, такого как _.graphmlz.tag.gz - _.graphmlz.tag, только _ в этом случае следует рассматривать как базовое имя файла.

8. Ресурсы

Calibre довольно неплохо обрабатывает искажение имен файлов:

Страница Википедии об изменении имени файла и ссылка на глава об использовании Samba.


Если, например, вы попытаетесь создать файл, нарушающий какое-либо из правил 1/2/3, вы получите очень полезную ошибку:

Warning: touch(): Unable to create file ... because No error in ... on line ...
person Alix Axel    schedule 11.10.2012

Я всегда думал, что Кохана неплохо справился с этим..

public static function title($title, $separator = '-', $ascii_only = FALSE)
{
if ($ascii_only === TRUE)
{
// Transliterate non-ASCII characters
$title = UTF8::transliterate_to_ascii($title);

// Remove all characters that are not the separator, a-z, 0-9, or whitespace
$title = preg_replace('![^'.preg_quote($separator).'a-z0-9\s]+!', '', strtolower($title));
}
else
{
// Remove all characters that are not the separator, letters, numbers, or whitespace
$title = preg_replace('![^'.preg_quote($separator).'\pL\pN\s]+!u', '', UTF8::strtolower($title));
}

// Replace all separator characters and whitespace by a single separator
$title = preg_replace('!['.preg_quote($separator).'\s]+!u', $separator, $title);

// Trim separators from the beginning and end
return trim($title, $separator);
}

Удобный UTF8::transliterate_to_ascii() будет переворачивать такие вещи, как ñ => n.

Конечно, вы можете заменить остальное UTF8::* функциями mb_ *.

person alex    schedule 23.04.2010

Я адаптировал из другого источника и добавил еще пару, может быть, немного перебор

/**
 * Convert a string into a url safe address.
 *
 * @param string $unformatted
 * @return string
 */
public function formatURL($unformatted) {

    $url = strtolower(trim($unformatted));

    //replace accent characters, forien languages
    $search = array('À', 'Á', 'Â', 'Ã', 'Ä', 'Å', 'Æ', 'Ç', 'È', 'É', 'Ê', 'Ë', 'Ì', 'Í', 'Î', 'Ï', 'Ð', 'Ñ', 'Ò', 'Ó', 'Ô', 'Õ', 'Ö', 'Ø', 'Ù', 'Ú', 'Û', 'Ü', 'Ý', 'ß', 'à', 'á', 'â', 'ã', 'ä', 'å', 'æ', 'ç', 'è', 'é', 'ê', 'ë', 'ì', 'í', 'î', 'ï', 'ñ', 'ò', 'ó', 'ô', 'õ', 'ö', 'ø', 'ù', 'ú', 'û', 'ü', 'ý', 'ÿ', 'Ā', 'ā', 'Ă', 'ă', 'Ą', 'ą', 'Ć', 'ć', 'Ĉ', 'ĉ', 'Ċ', 'ċ', 'Č', 'č', 'Ď', 'ď', 'Đ', 'đ', 'Ē', 'ē', 'Ĕ', 'ĕ', 'Ė', 'ė', 'Ę', 'ę', 'Ě', 'ě', 'Ĝ', 'ĝ', 'Ğ', 'ğ', 'Ġ', 'ġ', 'Ģ', 'ģ', 'Ĥ', 'ĥ', 'Ħ', 'ħ', 'Ĩ', 'ĩ', 'Ī', 'ī', 'Ĭ', 'ĭ', 'Į', 'į', 'İ', 'ı', 'IJ', 'ij', 'Ĵ', 'ĵ', 'Ķ', 'ķ', 'Ĺ', 'ĺ', 'Ļ', 'ļ', 'Ľ', 'ľ', 'Ŀ', 'ŀ', 'Ł', 'ł', 'Ń', 'ń', 'Ņ', 'ņ', 'Ň', 'ň', 'ʼn', 'Ō', 'ō', 'Ŏ', 'ŏ', 'Ő', 'ő', 'Œ', 'œ', 'Ŕ', 'ŕ', 'Ŗ', 'ŗ', 'Ř', 'ř', 'Ś', 'ś', 'Ŝ', 'ŝ', 'Ş', 'ş', 'Š', 'š', 'Ţ', 'ţ', 'Ť', 'ť', 'Ŧ', 'ŧ', 'Ũ', 'ũ', 'Ū', 'ū', 'Ŭ', 'ŭ', 'Ů', 'ů', 'Ű', 'ű', 'Ų', 'ų', 'Ŵ', 'ŵ', 'Ŷ', 'ŷ', 'Ÿ', 'Ź', 'ź', 'Ż', 'ż', 'Ž', 'ž', 'ſ', 'ƒ', 'Ơ', 'ơ', 'Ư', 'ư', 'Ǎ', 'ǎ', 'Ǐ', 'ǐ', 'Ǒ', 'ǒ', 'Ǔ', 'ǔ', 'Ǖ', 'ǖ', 'Ǘ', 'ǘ', 'Ǚ', 'ǚ', 'Ǜ', 'ǜ', 'Ǻ', 'ǻ', 'Ǽ', 'ǽ', 'Ǿ', 'ǿ'); 
    $replace = array('A', 'A', 'A', 'A', 'A', 'A', 'AE', 'C', 'E', 'E', 'E', 'E', 'I', 'I', 'I', 'I', 'D', 'N', 'O', 'O', 'O', 'O', 'O', 'O', 'U', 'U', 'U', 'U', 'Y', 's', 'a', 'a', 'a', 'a', 'a', 'a', 'ae', 'c', 'e', 'e', 'e', 'e', 'i', 'i', 'i', 'i', 'n', 'o', 'o', 'o', 'o', 'o', 'o', 'u', 'u', 'u', 'u', 'y', 'y', 'A', 'a', 'A', 'a', 'A', 'a', 'C', 'c', 'C', 'c', 'C', 'c', 'C', 'c', 'D', 'd', 'D', 'd', 'E', 'e', 'E', 'e', 'E', 'e', 'E', 'e', 'E', 'e', 'G', 'g', 'G', 'g', 'G', 'g', 'G', 'g', 'H', 'h', 'H', 'h', 'I', 'i', 'I', 'i', 'I', 'i', 'I', 'i', 'I', 'i', 'IJ', 'ij', 'J', 'j', 'K', 'k', 'L', 'l', 'L', 'l', 'L', 'l', 'L', 'l', 'l', 'l', 'N', 'n', 'N', 'n', 'N', 'n', 'n', 'O', 'o', 'O', 'o', 'O', 'o', 'OE', 'oe', 'R', 'r', 'R', 'r', 'R', 'r', 'S', 's', 'S', 's', 'S', 's', 'S', 's', 'T', 't', 'T', 't', 'T', 't', 'U', 'u', 'U', 'u', 'U', 'u', 'U', 'u', 'U', 'u', 'U', 'u', 'W', 'w', 'Y', 'y', 'Y', 'Z', 'z', 'Z', 'z', 'Z', 'z', 's', 'f', 'O', 'o', 'U', 'u', 'A', 'a', 'I', 'i', 'O', 'o', 'U', 'u', 'U', 'u', 'U', 'u', 'U', 'u', 'U', 'u', 'A', 'a', 'AE', 'ae', 'O', 'o'); 
    $url = str_replace($search, $replace, $url);

    //replace common characters
    $search = array('&', '£', '$'); 
    $replace = array('and', 'pounds', 'dollars'); 
    $url= str_replace($search, $replace, $url);

    // remove - for spaces and union characters
    $find = array(' ', '&', '\r\n', '\n', '+', ',', '//');
    $url = str_replace($find, '-', $url);

    //delete and replace rest of special chars
    $find = array('/[^a-z0-9\-<>]/', '/[\-]+/', '/<[^>]*>/');
    $replace = array('', '-', '');
    $uri = preg_replace($find, $replace, $url);

    return $uri;
}
person John Magnolia    schedule 11.08.2011

Что касается загрузки файлов, безопаснее всего запретить пользователю управлять именем файла. Как уже упоминалось, сохраните канонизированное имя файла в базе данных вместе со случайно выбранным и уникальным именем, которое вы будете использовать в качестве фактического имени файла.

Используя OWASP ESAPI, эти имена могут быть сгенерированы таким образом:

$userFilename   = ESAPI::getEncoder()->canonicalize($input_string);
$safeFilename   = ESAPI::getRandomizer()->getRandomFilename();

Вы можете добавить отметку времени к $ safeFilename, чтобы гарантировать уникальность случайно сгенерированного имени файла, даже не проверяя существующий файл.

Что касается кодировки URL-адреса и снова с использованием ESAPI:

$safeForURL     = ESAPI::getEncoder()->encodeForURL($input_string);

Этот метод выполняет канонизацию перед кодированием строки и обрабатывает все кодировки символов.

person jah    schedule 25.04.2010
comment
Определенно - также, отнятие у пользователей контроля над именами файлов предотвратит возможность двух загрузок с одинаковыми именами. - person CodeVirtuoso; 02.01.2011

Я рекомендую * URLify для PHP (480+ звезд на Github) - "PHP-порт URLify.js из проект Django. Транслитерирует символы, отличные от ascii, для использования в URL ".

Основное использование:

Чтобы создать заголовки для URL:

<?php

echo URLify::filter (' J\'étudie le français ');
// "jetudie-le-francais"

echo URLify::filter ('Lo siento, no hablo español.');
// "lo-siento-no-hablo-espanol"

?>

Чтобы создать ярлыки для имен файлов:

<?php

echo URLify::filter ('фото.jpg', 60, "", true);
// "foto.jpg"

?>

* Ни одно из других предложений не соответствовало моим критериям:

  • Должен быть установлен через композитор
  • Не должно зависеть от iconv, так как в разных системах он ведет себя по-разному.
  • Должен быть расширяемым, чтобы разрешать переопределения и замену пользовательских символов
  • Популярные (например, много звезд на Github)
  • Есть тесты

В качестве бонуса URLify также удаляет определенные слова и удаляет все символы, не транслитерированные.

Вот тестовый пример с тоннами иностранных символов, которые правильно транслитерируются с помощью URLify: https://gist.github.com/motin/a65e6c1cc303e46900d10894bf2da87f

person Motin    schedule 18.07.2016
comment
Спасибо, это идеально подходит для моих целей. - person David Goodwin; 04.08.2016

это версия Joomla 3.3.2 из JFile::makeSafe($file)

public static function makeSafe($file)
{
    // Remove any trailing dots, as those aren't ever valid file names.
    $file = rtrim($file, '.');

    $regex = array('#(\.){2,}#', '#[^A-Za-z0-9\.\_\- ]#', '#^\.#');

    return trim(preg_replace($regex, '', $file));
}
person cedric.walter    schedule 17.07.2014

Я не думаю, что список символов для удаления безопасно. Я бы предпочел использовать следующее:

Для имен файлов: используйте внутренний идентификатор или хэш содержимого файла. Сохраните название документа в базе данных. Таким образом, вы можете сохранить исходное имя файла и при этом найти файл.

Для параметров URL: используйте urlencode() для кодирования любых специальных символов.

person ZeissS    schedule 19.04.2010
comment
Я согласен, большинство перечисленных здесь методов удаляют известные опасные символы - мой метод удаляет все, что не является известным безопасным символом. Так как большинство системных ярлыков кодируют URL-адреса сообщений, я бы посоветовал нам продолжать следовать этому проверенному методу, а не использовать документированный UTF-8 unsafe urlencode (). - person Xeoncross; 19.04.2010

В зависимости от того, как вы будете его использовать, вы можете добавить ограничение длины для защиты от переполнения буфера.

person Tgr    schedule 25.04.2010
comment
Да, тестирование на mb_strlen () всегда важно! - person Xeoncross; 26.04.2010

Это хороший способ защитить имя файла загрузки:

$file_name = trim(basename(stripslashes($name)), ".\x00..\x20");
person games    schedule 30.09.2012
comment
Я не так уверен в этом, потому что один .\x00..\x20 может быть уменьшен до .\x00\x20. - person Xeoncross; 01.10.2012
comment
@Xeoncross: Я думаю, что .\x00..\x20 удаляет точки и каждый символ между \x00 и \x20, тогда как .\x00\x20 должен удалять только эти 3 байта. - person Alix Axel; 28.10.2012
comment
Этот ответ требует дополнительных объяснений для его безопасного использования. Не так много информации о точном синтаксисе charlist в сети. - person Manuel Arwed Schmidt; 11.09.2015

Вот реализация CodeIgniter.

/**
 * Sanitize Filename
 *
 * @param   string  $str        Input file name
 * @param   bool    $relative_path  Whether to preserve paths
 * @return  string
 */
public function sanitize_filename($str, $relative_path = FALSE)
{
    $bad = array(
        '../', '<!--', '-->', '<', '>',
        "'", '"', '&', '$', '#',
        '{', '}', '[', ']', '=',
        ';', '?', '%20', '%22',
        '%3c',      // <
        '%253c',    // <
        '%3e',      // >
        '%0e',      // >
        '%28',      // (
        '%29',      // )
        '%2528',    // (
        '%26',      // &
        '%24',      // $
        '%3f',      // ?
        '%3b',      // ;
        '%3d'       // =
    );

    if ( ! $relative_path)
    {
        $bad[] = './';
        $bad[] = '/';
    }

    $str = remove_invisible_characters($str, FALSE);
    return stripslashes(str_replace($bad, '', $str));
}

И зависимость remove_invisible_characters.

function remove_invisible_characters($str, $url_encoded = TRUE)
{
    $non_displayables = array();

    // every control character except newline (dec 10),
    // carriage return (dec 13) and horizontal tab (dec 09)
    if ($url_encoded)
    {
        $non_displayables[] = '/%0[0-8bcef]/';  // url encoded 00-08, 11, 12, 14, 15
        $non_displayables[] = '/%1[0-9a-f]/';   // url encoded 16-31
    }

    $non_displayables[] = '/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+/S';   // 00-08, 11, 12, 14-31, 127

    do
    {
        $str = preg_replace($non_displayables, '', $str, -1, $count);
    }
    while ($count);

    return $str;
}
person Kevin Mark    schedule 05.11.2012

У меня есть заголовки записей со всевозможными странными латинскими символами, а также некоторые теги HTML, которые мне нужно было перевести в полезный формат имен файлов с разделителями тире. Я объединил ответ @ SoLoGHoST с парой пунктов из ответа @Xeoncross и немного настроил.

    function sanitize($string,$force_lowercase=true) {
    //Clean up titles for filenames
    $clean = strip_tags($string);
    $clean = strtr($clean, array('Š' => 'S','Ž' => 'Z','š' => 's','ž' => 'z','Ÿ' => 'Y','À' => 'A','Á' => 'A','Â' => 'A','Ã' => 'A','Ä' => 'A','Å' => 'A','Ç' => 'C','È' => 'E','É' => 'E','Ê' => 'E','Ë' => 'E','Ì' => 'I','Í' => 'I','Î' => 'I','Ï' => 'I','Ñ' => 'N','Ò' => 'O','Ó' => 'O','Ô' => 'O','Õ' => 'O','Ö' => 'O','Ø' => 'O','Ù' => 'U','Ú' => 'U','Û' => 'U','Ü' => 'U','Ý' => 'Y','à' => 'a','á' => 'a','â' => 'a','ã' => 'a','ä' => 'a','å' => 'a','ç' => 'c','è' => 'e','é' => 'e','ê' => 'e','ë' => 'e','ì' => 'i','í' => 'i','î' => 'i','ï' => 'i','ñ' => 'n','ò' => 'o','ó' => 'o','ô' => 'o','õ' => 'o','ö' => 'o','ø' => 'o','ù' => 'u','ú' => 'u','û' => 'u','ü' => 'u','ý' => 'y','ÿ' => 'y'));
    $clean = strtr($clean, array('Þ' => 'TH', 'þ' => 'th', 'Ð' => 'DH', 'ð' => 'dh', 'ß' => 'ss', 'Œ' => 'OE', 'œ' => 'oe', 'Æ' => 'AE', 'æ' => 'ae', 'µ' => 'u','—' => '-'));
    $clean = str_replace("--", "-", preg_replace("/[^a-z0-9-]/i", "", preg_replace(array('/\s/', '/[^\w-\.\-]/'), array('-', ''), $clean)));

    return ($force_lowercase) ?
        (function_exists('mb_strtolower')) ?
            mb_strtolower($clean, 'UTF-8') :
            strtolower($clean) :
        $clean;
}

Мне нужно было вручную добавить длинное тире (-) в массив перевода. Могут быть и другие, но пока мои имена файлов выглядят хорошо.

So:

Часть 1. «Бурбурты» моего отца? Они (не) лучшие!

становится:

part-1-my-dads-zurburts-theyre-not-the-best

Я просто добавляю ".html" к возвращаемой строке.

person cbmtrx    schedule 08.12.2015
comment
По-прежнему отсутствуют некоторые чешские и словацкие символы: 'ľ' => 'l', 'Ľ' => 'L', 'č' => 'c', 'Č' => 'C', 'ť' => 't', 'Ť' => 'T', 'ň' => 'n', 'Ň' => 'N', 'ĺ' => 'l', 'Ĺ' => 'L', 'Ř' => 'R', 'ř' => 'r', 'ě' => 'e', 'Ě' => 'E', 'ů' => 'u', 'Ů' => 'U' - person Jasom Dotnet; 12.12.2015
comment
И, без сомнения, многие другие. На самом деле я пытаюсь выяснить, существует ли ISO-набор, включающий комбинации символов. Как выбрать один набор, если контент требует персонажей от всех? UTF-8 Я предполагаю ... - person cbmtrx; 13.12.2015
comment
Я обнаружил как транслитерировать любую строку с помощью одной строчки PHP: $string = transliterator_transliterate('Any-Latin;Latin-ASCII;', $string); См. мой ответ ниже или прочтите связанный пост в блоге. - person Jasom Dotnet; 13.12.2015
comment
ЕСЛИ вы используете Drupal и ЕСЛИ вы устанавливаете расширение. На самом деле не одна строчка PHP. - person cbmtrx; 14.12.2015
comment
Нет, вы неправильно прочитали: ЕСЛИ вы можете установить расширения PHP на свой сервер (или хостинг) :-) Вот сообщение. - person Jasom Dotnet; 15.12.2015
comment
Ах, понял. Спасибо @JasomDotnet - мое текущее решение пока работает, но это ограниченный набор символов, поэтому стоит проверить расширение. - person cbmtrx; 15.12.2015

почему бы просто не использовать urlencode php? он заменяет "опасные" символы их шестнадцатеричным представлением для URL-адресов (т.е. %20 для пробела)

person knittl    schedule 27.04.2010
comment
Символ% не рекомендуется использовать для имен файлов, а символы в шестнадцатеричной кодировке не так хорошо выглядят в URL-адресе. Браузеры могут поддерживать строки UTF-8, которые намного удобнее и проще для языков, отличных от ascii. - person Xeoncross; 27.04.2010
comment
вы могли бы сделать urlencode и ТОГДА str_replace ('% 20', '-', url)? - person Francesco; 11.02.2011

Для этого вопроса уже есть несколько решений, но я прочитал и протестировал большую часть кода здесь, и в итоге я получил это решение, которое представляет собой смесь того, что я узнал здесь:

Функция

Функция входит здесь в пакет Symfony2, но ее можно извлечь для использования как простой PHP, она зависит только от функции iconv, которая должна быть включена:

Filesystem.php:

<?php

namespace COil\Bundle\COilCoreBundle\Component\HttpKernel\Util;

use Symfony\Component\HttpKernel\Util\Filesystem as BaseFilesystem;

/**
 * Extends the Symfony filesystem object.
 */
class Filesystem extends BaseFilesystem
{
    /**
     * Make a filename safe to use in any function. (Accents, spaces, special chars...)
     * The iconv function must be activated.
     *
     * @param string  $fileName       The filename to sanitize (with or without extension)
     * @param string  $defaultIfEmpty The default string returned for a non valid filename (only special chars or separators)
     * @param string  $separator      The default separator
     * @param boolean $lowerCase      Tells if the string must converted to lower case
     *
     * @author COil <https://github.com/COil>
     * @see    http://stackoverflow.com/questions/2668854/sanitizing-strings-to-make-them-url-and-filename-safe
     *
     * @return string
     */
    public function sanitizeFilename($fileName, $defaultIfEmpty = 'default', $separator = '_', $lowerCase = true)
    {
    // Gather file informations and store its extension
    $fileInfos = pathinfo($fileName);
    $fileExt   = array_key_exists('extension', $fileInfos) ? '.'. strtolower($fileInfos['extension']) : '';

    // Removes accents
    $fileName = @iconv('UTF-8', 'us-ascii//TRANSLIT', $fileInfos['filename']);

    // Removes all characters that are not separators, letters, numbers, dots or whitespaces
    $fileName = preg_replace("/[^ a-zA-Z". preg_quote($separator). "\d\.\s]/", '', $lowerCase ? strtolower($fileName) : $fileName);

    // Replaces all successive separators into a single one
    $fileName = preg_replace('!['. preg_quote($separator).'\s]+!u', $separator, $fileName);

    // Trim beginning and ending seperators
    $fileName = trim($fileName, $separator);

    // If empty use the default string
    if (empty($fileName)) {
        $fileName = $defaultIfEmpty;
    }

    return $fileName. $fileExt;
    }
}

Модульные тесты

Что интересно, я создал тесты PHPUnit, сначала для проверки крайних случаев, поэтому вы можете проверить, соответствует ли он вашим потребностям: (Если вы обнаружите ошибку, не стесняйтесь добавлять тестовый пример)

FilesystemTest.php:

<?php

namespace COil\Bundle\COilCoreBundle\Tests\Unit\Helper;

use COil\Bundle\COilCoreBundle\Component\HttpKernel\Util\Filesystem;

/**
 * Test the Filesystem custom class.
 */
class FilesystemTest extends \PHPUnit_Framework_TestCase
{
    /**
     * test sanitizeFilename()
     */
    public function testFilesystem()
    {
    $fs = new Filesystem();

    $this->assertEquals('logo_orange.gif', $fs->sanitizeFilename('--logö  _  __   ___   ora@@ñ--~gé--.gif'), '::sanitizeFilename() handles complex filename with specials chars');
    $this->assertEquals('coilstack', $fs->sanitizeFilename('cOiLsTaCk'), '::sanitizeFilename() converts all characters to lower case');
    $this->assertEquals('cOiLsTaCk', $fs->sanitizeFilename('cOiLsTaCk', 'default', '_', false), '::sanitizeFilename() lower case can be desactivated, passing false as the 4th argument');
    $this->assertEquals('coil_stack', $fs->sanitizeFilename('coil stack'), '::sanitizeFilename() convert a white space to a separator');
    $this->assertEquals('coil-stack', $fs->sanitizeFilename('coil stack', 'default', '-'), '::sanitizeFilename() can use a different separator as the 3rd argument');
    $this->assertEquals('coil_stack', $fs->sanitizeFilename('coil          stack'), '::sanitizeFilename() removes successive white spaces to a single separator');
    $this->assertEquals('coil_stack', $fs->sanitizeFilename('       coil stack'), '::sanitizeFilename() removes spaces at the beginning of the string');
    $this->assertEquals('coil_stack', $fs->sanitizeFilename('coil   stack         '), '::sanitizeFilename() removes spaces at the end of the string');
    $this->assertEquals('coilstack', $fs->sanitizeFilename('coil,,,,,,stack'), '::sanitizeFilename() removes non-ASCII characters');
    $this->assertEquals('coil_stack', $fs->sanitizeFilename('coil_stack  '), '::sanitizeFilename() keeps separators');
    $this->assertEquals('coil_stack', $fs->sanitizeFilename(' coil________stack'), '::sanitizeFilename() converts successive separators into a single one');
    $this->assertEquals('coil_stack.gif', $fs->sanitizeFilename('cOil Stack.GiF'), '::sanitizeFilename() lower case filename and extension');
    $this->assertEquals('copy_of_coil.stack.exe', $fs->sanitizeFilename('Copy of coil.stack.exe'), '::sanitizeFilename() keeps dots before the extension');
    $this->assertEquals('default.doc', $fs->sanitizeFilename('____________.doc'), '::sanitizeFilename() returns a default file name if filename only contains special chars');
    $this->assertEquals('default.docx', $fs->sanitizeFilename('     ___ -  --_     __%%%%__¨¨¨***____      .docx'), '::sanitizeFilename() returns a default file name if filename only contains special chars');
    $this->assertEquals('logo_edition_1314352521.jpg', $fs->sanitizeFilename('logo_edition_1314352521.jpg'), '::sanitizeFilename() returns the filename untouched if it does not need to be modified');
    $userId = rand(1, 10);
    $this->assertEquals('user_doc_'. $userId. '.doc', $fs->sanitizeFilename('亐亐亐亐亐.doc', 'user_doc_'. $userId), '::sanitizeFilename() returns the default string (the 2nd argument) if it can\'t be sanitized');
    }
}

Результаты теста: (проверено на Ubuntu с PHP 5.3.2 и MacOsX с PHP 5.3.17:

All tests pass:

phpunit -c app/ src/COil/Bundle/COilCoreBundle/Tests/Unit/Helper/FilesystemTest.php
PHPUnit 3.6.10 by Sebastian Bergmann.

Configuration read from /var/www/strangebuzz.com/app/phpunit.xml.dist

.

Time: 0 seconds, Memory: 5.75Mb

OK (1 test, 17 assertions)
person COil    schedule 07.12.2012
comment
Это предполагает ввод в основном на латинском языке. Добавьте больше символов UTF-8 из других языков, чтобы увидеть, где у вас могут возникнуть проблемы. - person Xeoncross; 07.12.2012
comment
@Xeoncross Я согласен, как сказал Кристиан, нужно сохранить идентификатор или хеш И исходное имя файла. Но эта функция предоставляет альтернативу, поскольку вы можете указать строку по умолчанию, когда процесс очистки завершается неудачно. Я добавил модульный тест для этого случая. Спасибо, что сообщили об ошибке. - person COil; 08.12.2012

Решение №1: У вас есть возможность устанавливать расширения PHP на сервере (хостинге).

Для транслитерации «почти всех языков планеты Земля» в символы ASCII.

  1. Сначала установите расширение PHP Intl. Это команда для Debian (Ubuntu): sudo aptitude install php5-intl

  2. Это моя функция fileName (создайте test.php и вставьте туда следующий код):

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Test</title>
</head>
<body>
<?php

function pr($string) {
  print '<hr>';
  print '"' . fileName($string) . '"';
  print '<br>';
  print '"' . $string . '"';
}

function fileName($string) {
  // remove html tags
  $clean = strip_tags($string);
  // transliterate
  $clean = transliterator_transliterate('Any-Latin;Latin-ASCII;', $clean);
  // remove non-number and non-letter characters
  $clean = str_replace('--', '-', preg_replace('/[^a-z0-9-\_]/i', '', preg_replace(array(
    '/\s/', 
    '/[^\w-\.\-]/'
  ), array(
    '_', 
    ''
  ), $clean)));
  // replace '-' for '_'
  $clean = strtr($clean, array(
    '-' => '_'
  ));
  // remove double '__'
  $positionInString = stripos($clean, '__');
  while ($positionInString !== false) {
    $clean = str_replace('__', '_', $clean);
    $positionInString = stripos($clean, '__');
  }
  // remove '_' from the end and beginning of the string
  $clean = rtrim(ltrim($clean, '_'), '_');
  // lowercase the string
  return strtolower($clean);
}
pr('_replace(\'~&([a-z]{1,2})(ac134/56f4315981743 8765475[]lt7ňl2ú5äňú138yé73ťž7ýľute|');
pr(htmlspecialchars('<script>alert(\'hacked\')</script>'));
pr('Álix----_Ãxel!?!?');
pr('áéíóúÁÉÍÓÚ');
pr('üÿÄËÏÖÜ.ŸåÅ');
pr('nie4č a a§ôňäääaš');
pr('Мао Цзэдун');
pr('毛泽东');
pr('ماو تسي تونغ');
pr('مائو تسه‌تونگ');
pr('מאו דזה-דונג');
pr('მაო ძედუნი');
pr('Mao Trạch Đông');
pr('毛澤東');
pr('เหมา เจ๋อตง');
?>
</body>
</html>

Эта строка является основной:

  // transliterate
  $clean = transliterator_transliterate('Any-Latin;Latin-ASCII;', $clean);

Ответ основан на этот пост.

Решение №2: у вас нет возможности устанавливать расширения PHP на сервере (хостинге).

введите описание изображения здесь

Довольно хорошая работа проделана в модуле транслитерации для CMS Drupal. Он поддерживает почти все языки на планете Земля. Я предлагаю проверить плагин репозиторий, если вы хотите иметь действительно полное решение для дезинфекции строк.

person Jasom Dotnet    schedule 12.12.2015

Этот пост, кажется, работает лучше всего из всего, что я связал. http://gsynuh.com/php-string-filename-url-safe/205

person ktamlyn    schedule 20.12.2012
comment
Этот пост очень краткий и предполагает, что все на английском. - person Xeoncross; 20.12.2012

Это хорошая функция:

public function getFriendlyURL($string) {
    setlocale(LC_CTYPE, 'en_US.UTF8');
    $string = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $string);
    $string = preg_replace('~[^\-\pL\pN\s]+~u', '-', $string);
    $string = str_replace(' ', '-', $string);
    $string = trim($string, "-");
    $string = strtolower($string);
    return $string;
} 
person joan16v    schedule 13.01.2015
comment
Выглядит плохо. \\s+ означает обратную косую черту, за которой следует один или несколько пробелов. О чем это? Кроме того, здесь используется черный список, а не белый список, игнорируя такие вещи, как CMD, null или BEL. - person Xeoncross; 13.01.2015
comment
Все еще плохо. Теперь строки типа /blog/2014-02/just-in-time не разрешены. Используйте проверенный код выше или используйте phunction код фреймворка PHP. - person Xeoncross; 13.01.2015
comment
Верно. Эта функция предназначена только для своевременной части. Может быть полезно для некоторых. - person joan16v; 14.01.2015
comment
Вы можете изменить регулярное выражение preg_replace('~[^\-\pL\pN\s]+~u', '-', $string) - person Xeoncross; 14.01.2015
comment
Потрясающий! Я также добавил: string = trim ($ string, -); - person joan16v; 15.01.2015

Это код, используемый Prestashop для очистки URL-адресов:

replaceAccentedChars

используется

str2url

убрать диакритические знаки

function replaceAccentedChars($str)
{
    $patterns = array(
        /* Lowercase */
        '/[\x{0105}\x{00E0}\x{00E1}\x{00E2}\x{00E3}\x{00E4}\x{00E5}]/u',
        '/[\x{00E7}\x{010D}\x{0107}]/u',
        '/[\x{010F}]/u',
        '/[\x{00E8}\x{00E9}\x{00EA}\x{00EB}\x{011B}\x{0119}]/u',
        '/[\x{00EC}\x{00ED}\x{00EE}\x{00EF}]/u',
        '/[\x{0142}\x{013E}\x{013A}]/u',
        '/[\x{00F1}\x{0148}]/u',
        '/[\x{00F2}\x{00F3}\x{00F4}\x{00F5}\x{00F6}\x{00F8}]/u',
        '/[\x{0159}\x{0155}]/u',
        '/[\x{015B}\x{0161}]/u',
        '/[\x{00DF}]/u',
        '/[\x{0165}]/u',
        '/[\x{00F9}\x{00FA}\x{00FB}\x{00FC}\x{016F}]/u',
        '/[\x{00FD}\x{00FF}]/u',
        '/[\x{017C}\x{017A}\x{017E}]/u',
        '/[\x{00E6}]/u',
        '/[\x{0153}]/u',

        /* Uppercase */
        '/[\x{0104}\x{00C0}\x{00C1}\x{00C2}\x{00C3}\x{00C4}\x{00C5}]/u',
        '/[\x{00C7}\x{010C}\x{0106}]/u',
        '/[\x{010E}]/u',
        '/[\x{00C8}\x{00C9}\x{00CA}\x{00CB}\x{011A}\x{0118}]/u',
        '/[\x{0141}\x{013D}\x{0139}]/u',
        '/[\x{00D1}\x{0147}]/u',
        '/[\x{00D3}]/u',
        '/[\x{0158}\x{0154}]/u',
        '/[\x{015A}\x{0160}]/u',
        '/[\x{0164}]/u',
        '/[\x{00D9}\x{00DA}\x{00DB}\x{00DC}\x{016E}]/u',
        '/[\x{017B}\x{0179}\x{017D}]/u',
        '/[\x{00C6}]/u',
        '/[\x{0152}]/u');

    $replacements = array(
            'a', 'c', 'd', 'e', 'i', 'l', 'n', 'o', 'r', 's', 'ss', 't', 'u', 'y', 'z', 'ae', 'oe',
            'A', 'C', 'D', 'E', 'L', 'N', 'O', 'R', 'S', 'T', 'U', 'Z', 'AE', 'OE'
        );

    return preg_replace($patterns, $replacements, $str);
}

function str2url($str)
{
    if (function_exists('mb_strtolower'))
        $str = mb_strtolower($str, 'utf-8');

    $str = trim($str);
    if (!function_exists('mb_strtolower'))
        $str = replaceAccentedChars($str);

    // Remove all non-whitelist chars.
    $str = preg_replace('/[^a-zA-Z0-9\s\'\:\/\[\]-\pL]/u', '', $str);
    $str = preg_replace('/[\s\'\:\/\[\]-]+/', ' ', $str);
    $str = str_replace(array(' ', '/'), '-', $str);

    // If it was not possible to lowercase the string with mb_strtolower, we do it after the transformations.
    // This way we lose fewer special chars.
    if (!function_exists('mb_strtolower'))
        $str = strtolower($str);

    return $str;
}
person Armel Larcier    schedule 19.06.2013

Есть два хороших ответа на то, чтобы заблокировать ваши данные: используйте его https://stackoverflow.com/a/3987966/971619 или это https://stackoverflow.com/a/7610586/971619

person newway    schedule 24.09.2013