Oczyszczanie ciągów, aby były bezpieczne dla adresu URL i nazwy pliku?

136

Próbuję wymyślić funkcję, która dobrze radzi sobie z oczyszczaniem niektórych ciągów, tak aby można je było bezpiecznie używać w adresie URL (jak post slug), a także bezpiecznie używać jako nazw plików. Na przykład, gdy ktoś przesyła plik, chcę się upewnić, że usunę wszystkie niebezpieczne znaki z nazwy.

Do tej pory wymyśliłem następującą funkcję, która, mam nadzieję, rozwiązuje ten problem i zezwala również na zagraniczne dane 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');
}

Czy ktoś ma jakieś skomplikowane przykładowe dane, z którymi mogę się temu zapoznać - lub zna lepszy sposób ochrony naszych aplikacji przed złymi nazwami?

$ is-filename pozwala na dodatkowe znaki, takie jak pliki temp vim

aktualizacja: usunąłem znak gwiazdy, ponieważ nie mogłem wymyślić prawidłowego użycia

Xeoncross
źródło
Lepiej usuń wszystko oprócz [\ w.-]
elias
3
Może się przydać Normalizer i komentarze na jego temat.
Matt Gibson,

Odpowiedzi:

57

Kilka uwag na temat twojego rozwiązania:

  1. „u” na końcu twojego wzorca oznacza, że wzorzec , a nie tekst, do którego pasuje, zostanie zinterpretowany jako UTF-8 (zakładam, że założyłeś ten drugi?).
  2. \ w dopasowuje znak podkreślenia. Umieszczasz go specjalnie dla plików, co prowadzi do założenia, że ​​nie chcesz ich w adresach URL, ale w kodzie, który masz, adresy URL będą mogły zawierać podkreślenie.
  3. Wydaje się, że włączenie „obcego UTF-8” zależy od ustawień lokalnych. Nie jest jasne, czy jest to lokalizacja serwera czy klienta. Z dokumentacji PHP:

Znak „słowa” to dowolna litera, cyfra lub znak podkreślenia, czyli dowolny znak, który może być częścią „słowa” w języku Perl. Definicja liter i cyfr jest kontrolowana przez tabele znaków PCRE i może się różnić, jeśli ma miejsce dopasowywanie specyficzne dla lokalizacji. Na przykład w ustawieniach regionalnych „fr” (francuski) niektóre kody znaków większe niż 128 są używane dla liter akcentowanych i są one dopasowywane przez \ w.

Tworzenie ślimaka

Prawdopodobnie nie powinieneś umieszczać znaków akcentowanych itp. W swoim poście, ponieważ technicznie rzecz biorąc, powinny być zakodowane w procentach (według reguł kodowania adresu URL), więc będziesz mieć brzydko wyglądające adresy URL.

Tak więc na twoim miejscu po małych literach zamieniłbym wszystkie znaki „specjalne” na ich odpowiedniki (np. É -> e) i zamieniłbym znaki inne niż [az] na „-”, ograniczając się do ciągów pojedynczego „-” tak jak zrobiłeś. Istnieje implementacja konwersji znaków specjalnych tutaj: https://web.archive.org/web/20130208144021/http://neo22s.com/slug

Ogólnie odkażanie

OWASP ma implementację PHP Enterprise Security API, która obejmuje między innymi metody bezpiecznego kodowania i dekodowania danych wejściowych i wyjściowych w Twojej aplikacji.

Interfejs kodera zapewnia:

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

Alan Donnelly
źródło
Masz rację co do mojego założenia co do modyfikatora „u” - myślałem, że to dla tekstu. Zapomniałem też o modyfikatorze \ w zawierającym podkreślenie. Zwykle zamieniłbym wszystkie znaki akcentowane na ASCII - ale chcę, aby działało to również w innych językach. Zakładałem, że będzie jakiś bezpieczny sposób UTF-8, aby dowolny znak języka mógł zostać użyty w slugu URL lub nazwie pliku, tak aby działały nawet arabskie tytuły. W końcu linux obsługuje nazwy plików UTF-8, a przeglądarki powinny kodować linki HTML w razie potrzeby. Wielkie dzięki za Twój wkład.
Xeoncross
Po zastanowieniu masz rację, ale nie jest to tylko problem z poprawnym kodowaniem łączy przez przeglądarkę. Najłatwiejszym sposobem osiągnięcia tego, co chcesz, jest odwzorowanie znaków innych niż ASCII na ich najbliższy odpowiednik ASCII, a następnie zakodowanie adresu URL w treści HTML. Trudnym sposobem jest zapewnienie spójnego kodowania UTF-8 (lub UTF-16, myślę, że w przypadku niektórych chińskich dialektów) z magazynu danych, poprzez serwer internetowy, warstwę aplikacji (PHP), zawartość strony, przeglądarkę internetową, a nie kod urlenkodu adresów URL ( ale nadal usuwaj `` niepożądane '' znaki). To da ci ładne niekodowane linki i adresy URL.
Alan Donnelly
Dobra rada. Spróbuję stworzyć czyste środowisko UTF-8. Następnie, biorąc kilka ciągów z języków innych niż ASCII, usunę niebezpieczne znaki (./ ;: itd ...) i utworzę pliki, a następnie linki HTML do tych plików, aby zobaczyć, czy mogę je kliknąć i zobaczyć, czy to wszystko Pracuje. Jeśli nie, prawdopodobnie będę musiał wrócić do (raw)? Urlencode (), aby zezwolić na UTF-8. Opublikuję wyniki tutaj.
Xeoncross
3
Utworzyłem plik o nazwie, สังเวช พระปกเกศกองบู๊กู้ขึ้นใหม่.txta następnie utworzyłem plik HTML UTF-8 z linkiem do niego. Zadziwiająco działało - nawet w oknach! Jednak miałem wtedy PHP file_put_contents('สังเวช พระปกเกศกองบู๊กู้ขึ้นใหม่.txt')i nie udało się utworzyć nazwy pliku bazaru z tego ciągu. Potem próbowałem go utworzyć za pomocą fopen()i otrzymałem tę samą pomieszaną nazwę pliku. Najwyraźniej PHP (przynajmniej w systemie Windows) nie jest w stanie tworzyć nazw plików UTF-8. bugs.php.net/bug.php?id=46990&thanks=6
Xeoncross
1
Udzielam tej odpowiedzi, ponieważ skłoniła mnie do myślenia, a także zawierała przydatny link do projektu, o którym nigdy nie słyszałem, i który jest wart uwagi. Jednak opublikuję, gdy znajdę odpowiedź.
Xeoncross
87

Znalazłem tę większą funkcję w kodzie Chyrpa :

/**
 * 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("~", "`", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "_", "=", "+", "[", "{", "]",
                   "}", "\\", "|", ";", ":", "\"", "'", "‘", "’", "“", "”", "–", "—",
                   "—", "–", ",", "<", ".", ">", "/", "?");
    $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;
}

a ten w kodzie 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);
}

Aktualizacja wrzesień 2012

Alix Axel wykonał niesamowitą pracę w tej dziedzinie. Jego struktura funkcyjna zawiera kilka świetnych filtrów i transformacji tekstu.

Xeoncross
źródło
23
Kod WordPress nie jest przenośny, ponieważ wykorzystujeapply_filters
Kevin Mark
1
Należy zauważyć, że zastępuje wersję WordPressie /[\s-]+/z -co jest lepsze niż w pierwszym wariancie (który zastępuje tylko /\s+/), które mogą powodować wiele kresek rzędu
Yotam Omer
Tylko w celach informacyjnych wordpress apply_filters można znaleźć tutaj, a sanitize_file_name tutaj .
Eric
a co z wieloma spacjami? Zastąp
Jeffrey the Giraffe
8
Zmienna $ anal brzmi dla mnie bardzo przerażająco z opcją siły.
viljun,
30

To powinno sprawić, że twoje nazwy plików będą bezpieczne ...

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

a głębszym rozwiązaniem tego problemu jest:

// 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);

Zakłada się, że chcesz mieć kropkę w nazwie pliku. jeśli chcesz, aby były przenoszone na małe litery, po prostu użyj

$clean_name = strtolower($clean_name);

dla ostatniej linii.

SoLoGHoST
źródło
1
Wciąż brakuje niektórych czeskich i słowackich postaci:'ľ' => 'l', 'Ľ' => 'L', 'č' => 'c', 'Č' => 'C', 'ť' => 't', 'Ť' => 'T', 'ň' => 'n', 'Ň' => 'N', 'ĺ' => 'l', 'Ĺ' => 'L', 'Ř' => 'R', 'ř' => 'r', 'ě' => 'e', 'Ě' => 'E', 'ů' => 'u', 'Ů' => 'U'
Jasom Dotnet
22

Spróbuj tego:

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

Na podstawie odpowiedzi wybranej w tym wątku: Przyjazna nazwa użytkownika w PHP?

John Conde
źródło
Bardzo fajnie - nigdy nie widziałem tego bez tabeli tłumaczeń (jak używa Wordpress). Jednak uważam, że ta funkcja nie wystarczy, ponieważ tłumaczy tylko znaki specjalne, ale nie usuwa niebezpiecznych znaków. Może można to dodać do jednego powyżej ...
Xeoncross
4
Ha! Ten hack do kodowania bytu jest słodki! Chociaż na pierwszy rzut oka nie jest jasne, jak ta metoda robi to, co robi. Jest jednak problem. Czy „Frédéric & Éric” nie zamieni się w „Frederic amp Eric”?
Alan Donnelly
@AlanDonnelly: Rzeczywiście, zaktualizowałem funkcję w mojej oryginalnej odpowiedzi (sprawdź link), trim()powinno też być trim($string, '-').
Alix Axel
@Xeoncross: Ostatni preg_replace()powinien usunąć wszystkie niebezpieczne znaki.
Alix Axel
@AlixAxel, po prostu nie jesteś wszędzie. Właśnie czytałem PHP AWS SDK i mieli trochę twojego kodu dla UUIDów. Niesamowity kod funkcji jest po prostu trudny do pokonania.
Xeoncross
13

To nie jest dokładna odpowiedź, ponieważ nie dostarcza żadnych rozwiązań (jeszcze!), Ale jest zbyt duża, aby zmieścić się w komentarzu ...


Przeprowadziłem kilka testów (dotyczących nazw plików) na Windows 7 i Ubuntu 12.04 i odkryłem, że:

1. PHP nie obsługuje nazw plików spoza ASCII

Chociaż zarówno Windows, jak i Ubuntu mogą obsługiwać nazwy plików Unicode (nawet te RTL, jak się wydaje), PHP 5.3 wymaga hacków, aby poradzić sobie nawet ze zwykłym starym ISO-8859-1, więc lepiej jest zachować ASCII tylko dla bezpieczeństwa.

2. Długość nazwy pliku ma znaczenie (szczególnie w systemie Windows)

W systemie Ubuntu maksymalna długość, jaką może mieć nazwa pliku (łącznie z rozszerzeniem) to 255 (bez ścieżki):

/var/www/uploads/123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345/

Jednak w systemie Windows 7 (NTFS) maksymalna długość nazwy pliku zależy od jego ścieżki bezwzględnej:

(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

Wikipedia podaje, że:

NTFS dopuszcza, aby każdy składnik ścieżki (katalog lub nazwa pliku) miał długość 255 znaków.

Według mojej najlepszej wiedzy (i testów) jest to błędne.

W sumie (licząc ukośniki) wszystkie te przykłady mają 259 znaków, jeśli pozbędziesz się tego, C:\co daje 256 znaków (nie 255 ?!). Katalogi zostały utworzone przy użyciu Eksploratora, a zauważysz, że ogranicza się on do wykorzystania całego dostępnego miejsca na nazwę katalogu. Powodem tego jest umożliwienie tworzenia plików przy użyciu rozszerzenia konwencji nazewnictwa plików 8.3 . To samo dzieje się z innymi partycjami.

Pliki nie muszą oczywiście rezerwować wymagań dotyczących długości 8.3:

(255 chars) E:\12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901.txt

Nie można utworzyć więcej podkatalogów, jeśli bezwzględna ścieżka katalogu nadrzędnego ma więcej niż 242 znaki, ponieważ 256 = 242 + 1 + \ + 8 + . + 3. Używając Eksploratora Windows, nie możesz utworzyć innego katalogu, jeśli katalog nadrzędny ma więcej niż 233 znaki (w zależności od ustawień regionalnych systemu), ponieważ 256 = 233 + 10 + \ + 8 + . + 3; 10tutaj jest długość łańcuchaNew folder .

System plików Windows stwarza nieprzyjemny problem, jeśli chcesz zapewnić współdziałanie między systemami plików.

3. Uważaj na zastrzeżone znaki i słowa kluczowe

Oprócz usuwania znaków spoza ASCII, niedrukowalnych i kontrolnych , musisz również ponownie (umieść / przenieś):

"*/:<>?\|

Samo usunięcie tych znaków może nie być najlepszym pomysłem, ponieważ nazwa pliku może stracić na znaczeniu. Myślę, że przynajmniej wielokrotne występowanie tych znaków powinno być zastąpione pojedynczym podkreśleniem ( _), a może czymś bardziej reprezentatywnym (to tylko pomysł):

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

Istnieją również specjalne słowa kluczowe, których należy unikać (np. NUL), Chociaż nie jestem pewien, jak to przezwyciężyć. Być może czarna lista z przypadkową nazwą zastępczą byłaby dobrym podejściem do rozwiązania tego problemu.

4. Wrażliwość wielkości liter

To powinno być oczywiste, ale jeśli chcesz, aby zapewnić unikalność plików w różnych systemach operacyjnych, powinieneś przekształcić nazwy plików do znormalizowanej wielkości, w ten sposób my_file.txti My_File.txtna Linuksie nie będą oba takie samemy_file.txt plikiem w systemie Windows.

5. Upewnij się, że jest wyjątkowy

Jeśli nazwa pliku już istnieje, do podstawowej nazwy pliku należy dołączyć unikalny identyfikator .

Typowe unikalne identyfikatory obejmują sygnaturę czasową systemu UNIX, skrót zawartości pliku lub losowy ciąg.

6. Ukryte pliki

To, że można go nazwać, nie oznacza, że ​​powinno ...

Kropki są zwykle wyświetlane na biało w nazwach plików, ale w systemie Linux ukryty plik jest reprezentowany przez wiodącą kropkę.

7. Inne kwestie

Jeśli musisz usunąć niektóre znaki z nazwy pliku, rozszerzenie jest zwykle ważniejsze niż podstawowa nazwa pliku. Dopuszczając znaczną maksymalną liczbę znaków dla rozszerzenia pliku (8-16) należy usunąć znaki z nazwy bazowej. Należy również zauważyć, że w mało prawdopodobnym przypadku posiadania więcej niż jednego długiego rozszerzenia - takiego jak _.graphmlz.tag.gz- _.graphmlz.tagtylko _w tym przypadku należy traktować jako nazwę bazową pliku.

8. Zasoby

Calibre całkiem przyzwoicie radzi sobie ze zniekształcaniem nazw plików:

Strona Wikipedii dotycząca zniekształcania nazw plików i połączonego rozdziału z Korzystanie z Samby .


Jeśli na przykład spróbujesz utworzyć plik, który narusza którąkolwiek z zasad 1/2/3, otrzymasz bardzo przydatny błąd:

Warning: touch(): Unable to create file ... because No error in ... on line ...
Alix Axel
źródło
11

Zawsze myślałem, że Kohana całkiem nieźle się spisała .

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);
}

Poręczny UTF8::transliterate_to_ascii()zmieni rzeczy takie jak ñ => n.

Oczywiście możesz zastąpić inne UTF8::*rzeczy funkcjami mb_ *.

Alex
źródło
5

Jeśli chodzi o przesyłanie plików, najbezpieczniej byłoby uniemożliwić użytkownikowi kontrolowanie nazwy pliku. Jak już wspomniano, przechowuj kanonizowaną nazwę pliku w bazie danych wraz z losowo wybraną i unikalną nazwą, której będziesz używać jako rzeczywistej nazwy pliku.

Używając OWASP ESAPI, nazwy te można wygenerować w ten sposób:

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

Możesz dołączyć sygnaturę czasową do $ safeFilename, aby upewnić się, że losowo wygenerowana nazwa pliku jest unikalna, nawet bez sprawdzania istniejącego pliku.

Jeśli chodzi o kodowanie adresów URL i ponownie przy użyciu ESAPI:

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

Ta metoda przeprowadza kanonizację przed zakodowaniem ciągu i obsługuje wszystkie kodowania znaków.

jah
źródło
Zdecydowanie - również odebranie użytkownikom kontroli nad nazwami plików zapobiegnie możliwości przesłania dwóch plików o tej samej nazwie.
CodeVirtuoso,
5

Polecam * URLify dla PHP (ponad 480 gwiazdek na Github) - "port PHP URLify.js z projektu Django. Transliteracja znaków innych niż ASCII do użycia w adresach URL".

Podstawowe użycie:

Aby wygenerować informacje o błędach dla adresów 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"

?>

Aby wygenerować informacje o błędach dla nazw plików:

<?php

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

?>

* Żadna z pozostałych sugestii nie spełniła moich kryteriów:

  • Powinien być zainstalowany za pośrednictwem kompozytora
  • Nie powinien zależeć od iconv, ponieważ zachowuje się inaczej w różnych systemach
  • Powinien być rozszerzalny, aby umożliwić nadpisywanie i zastępowanie niestandardowych znaków
  • Popularne (na przykład wiele gwiazdek na Github)
  • Ma testy

Dodatkowo, URLify usuwa również określone słowa i usuwa wszystkie znaki niepoddane transliteracji.

Oto przypadek testowy z tonami obcych znaków poprawnie transliterowanych za pomocą URLify: https://gist.github.com/motin/a65e6c1cc303e46900d10894bf2da87f

Motin
źródło
1
Dzięki - to wygląda idealnie do moich celów.
David Goodwin
5

Zaadaptowałem z innego źródła i dodałem kilka dodatkowych, może trochę przesadzonych

/**
 * 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;
}
John Magnolia
źródło
5

a to jest wersja Joomla 3.3.2 z 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));
}
cedric.walter
źródło
4

Nie sądzę, aby posiadanie listy znaków do usunięcia nie było bezpieczne. Wolałbym raczej użyć następujących:

W przypadku nazw plików: użyj wewnętrznego identyfikatora lub skrótu zawartości pliku. Zapisz nazwę dokumentu w bazie danych. W ten sposób możesz zachować oryginalną nazwę pliku i nadal go znaleźć.

W przypadku parametrów adresu URL: służy urlencode()do kodowania wszelkich znaków specjalnych.

ZeissS
źródło
1
Zgadzam się, większość wymienionych tutaj metod usuwa znane niebezpieczne postacie - moja metoda usuwa wszystko, co nie jest znaną bezpieczną postacią. Ponieważ większość systemów koduje adresy URL postów, sugerowałbym dalsze stosowanie tej sprawdzonej metody zamiast używania udokumentowanego niebezpiecznego kodu urlencode UTF-8 ().
Xeoncross
3

W zależności od tego, jak będziesz go używać, możesz chcieć dodać limit długości, aby chronić przed przepełnieniem bufora.

Tgr
źródło
Tak, testowanie mb_strlen () jest zawsze ważne!
Xeoncross
3

To dobry sposób na zabezpieczenie nazwy pliku do przesłania:

$file_name = trim(basename(stripslashes($name)), ".\x00..\x20");
Gry
źródło
Nie jestem tego taki pewien, bo do jednego .\x00..\x20można się sprowadzić .\x00\x20.
Xeoncross
@Xeoncross: Myślę, że to .\x00..\x20usuwa kropki i każdy znak między \x00a \x20, podczas gdy .\x00\x20powinno usuwać tylko te 3 bajty.
Alix Axel
Ta odpowiedź wymaga więcej wyjaśnień, aby można ją było bezpiecznie używać. Niewiele informacji na temat dokładnej składni listy znaków w sieci.
Manuel Arwed Schmidt
3

Oto implementacja CodeIgnitera.

/**
 * 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));
}

I remove_invisible_characterszależność.

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;
}
Kevin Mark
źródło
2

dlaczego po prostu nie użyć php urlencode? zastępuje „niebezpieczne” znaki ich reprezentacją szesnastkową dla adresów URL (np. %20dla spacji)

knittl
źródło
2
Znak% nie jest zalecany w nazwach plików, a znaki zakodowane szesnastkowo nie wyglądają tak dobrze w adresie URL. Przeglądarki mogą obsługiwać łańcuchy znaków UTF-8, które są znacznie ładniejsze i łatwiejsze w przypadku języków innych niż ASCII.
Xeoncross
możesz zrobić kod urlenkod, a WTEDY str_replace ('% 20', '-', url)?
Francesco,
2

Istnieje już kilka rozwiązań dla tego pytania, ale przeczytałem i przetestowałem większość kodu tutaj i skończyło się na tym rozwiązaniu, które jest mieszanką tego, czego się tutaj nauczyłem:

Funkcja

Funkcja jest dołączona tutaj w pakiecie Symfony2, ale można ją wyodrębnić do użycia jako zwykły PHP , ma tylko zależność od iconvfunkcji, która musi być włączona:

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;
    }
}

Testy jednostkowe

Co ciekawe, stworzyłem testy PHPUnit, najpierw do testowania skrajnych przypadków, więc możesz sprawdzić, czy pasuje do twoich potrzeb: (Jeśli znajdziesz błąd, możesz dodać przypadek testowy)

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');
    }
}

Wyniki testu: (sprawdzone na Ubuntu z PHP 5.3.2 i MacOsX z 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)
Cewka
źródło
1
Zakłada się, że dane wejściowe są oparte głównie na łacinie. Dodaj więcej znaków UTF-8 z innych języków, aby zobaczyć, gdzie będziesz mieć problemy.
Xeoncross
@Xeoncross Zgadzam się, jak powiedział Christian, należy zapisać identyfikator lub skrót ORAZ oryginalną nazwę pliku. Ale ta funkcja zapewnia alternatywę, ponieważ możesz określić domyślny ciąg, gdy proces czyszczenia nie powiedzie się. Dodałem test jednostkowy dla tego przypadku. Dzięki za zgłoszenie błędu.
COil
2

Mam tytuły wpisów z wszelkiego rodzaju dziwnymi znakami łacińskimi, a także kilka tagów HTML, które musiałem przetłumaczyć na przydatny format nazwy pliku rozdzielany myślnikami. Połączyłem odpowiedź @ SoLoGHoST z kilkoma elementami z odpowiedzi @ Xeoncross i trochę dostosowałem.

    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;
}

Musiałem ręcznie dodać znak myślnika (-) do tablicy tłumaczenia. Mogą być inne, ale jak dotąd moje nazwy plików wyglądają dobrze.

Więc:

Część 1: „Žurburts” mojego taty? - są (nie) najlepsze!

staje się:

część-1-moi-tatusiowie-zurburty-nie-najlepsi

Po prostu dodaję „.html” do zwróconego ciągu.

cbmtrx
źródło
1
Wciąż brakuje niektórych czeskich i słowackich postaci:'ľ' => 'l', 'Ľ' => 'L', 'č' => 'c', 'Č' => 'C', 'ť' => 't', 'Ť' => 'T', 'ň' => 'n', 'Ň' => 'N', 'ĺ' => 'l', 'Ĺ' => 'L', 'Ř' => 'R', 'ř' => 'r', 'ě' => 'e', 'Ě' => 'E', 'ů' => 'u', 'Ů' => 'U'
Jasom Dotnet
1
I bez wątpienia o wiele więcej. Właściwie to próbuję dowiedzieć się, czy istnieje zestaw ISO zawierający kombinacje znaków. Jak „wybrać” jeden zestaw, jeśli zawartość wymaga od nich wszystkich znaków? UTF-8 Zakładam ...
cbmtrx
Dowiedziałem się, jak transliterować dowolny ciąg przy użyciu jednej linii PHP : $string = transliterator_transliterate('Any-Latin;Latin-ASCII;', $string);Zobacz moją odpowiedź poniżej lub przeczytaj link do wpisu na blogu.
Jasom Dotnet
1
Nie, źle przeczytałeś: JEŚLI możesz zainstalować rozszerzenia PHP na swoim serwerze (lub hostingu) :-) Oto post .
Jasom Dotnet
1
Ach, rozumiem. Dzięki @JasomDotnet - moje obecne rozwiązanie działa na razie, ale jest to ograniczony zestaw znaków, więc warto sprawdzić rozszerzenie.
cbmtrx
2

Rozwiązanie nr 1: Masz możliwość instalowania rozszerzeń PHP na serwerze (hosting)

Do transliteracji „prawie każdego języka na Ziemi” do znaków ASCII.

  1. Najpierw zainstaluj rozszerzenie PHP Intl . To jest polecenie dla Debiana (Ubuntu):sudo aptitude install php5-intl

  2. To jest moja funkcja fileName (utwórz test.php i wklej następujący kod):

<!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>

Ta linia jest rdzeniem:

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

Odpowiedz na podstawie tego postu .

Rozwiązanie nr 2: Nie masz możliwości instalowania rozszerzeń PHP na serwerze (hosting)

wprowadź opis obrazu tutaj

Całkiem niezła robota jest wykonana w module transliteracji dla CMS Drupal. Obsługuje prawie każdy język na Ziemi. Proponuję sprawdzić repozytorium wtyczek, jeśli chcesz mieć naprawdę kompletne ciągi czyszczące rozwiązanie.

Jasom Dotnet
źródło
1

To dobra funkcja:

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;
} 
joan16v
źródło
Źle to wygląda. \\s+oznacza ukośnik odwrotny, po którym następuje jedna lub więcej białych znaków. O czym to jest? Ponadto wykorzystuje to czarną listę zamiast białej listy, ignorując takie rzeczy jak CMD, null lub BEL.
Xeoncross
Wciąż zły. Teraz ciągi takie jak /blog/2014-02/just-in-timenie są dozwolone. Skorzystaj z testowanego kodu powyżej lub użyj phunctionkodu struktury PHP.
Xeoncross
Zgadza się. Ta funkcja dotyczy tylko części „just in time”. Może się przydać niektórym osobom.
joan16v
1
Możesz zmienić wyrażenie regularnepreg_replace('~[^\-\pL\pN\s]+~u', '-', $string)
Xeoncross
Niesamowite! Dodałem też: string = trim ($ string, "-");
joan16v
0

Oto kod używany przez Prestashop do oczyszczania adresów URL:

replaceAccentedChars

jest używany przez

str2url

aby usunąć znaki diakrytyczne

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;
}
Armel Larcier
źródło
-4
// CLEAN ILLEGAL CHARACTERS
function clean_filename($source_file)
{
    $search[] = " ";
    $search[] = "&";
    $search[] = "$";
    $search[] = ",";
    $search[] = "!";
    $search[] = "@";
    $search[] = "#";
    $search[] = "^";
    $search[] = "(";
    $search[] = ")";
    $search[] = "+";
    $search[] = "=";
    $search[] = "[";
    $search[] = "]";

    $replace[] = "_";
    $replace[] = "and";
    $replace[] = "S";
    $replace[] = "_";
    $replace[] = "";
    $replace[] = "";
    $replace[] = "";
    $replace[] = "";
    $replace[] = "";
    $replace[] = "";
    $replace[] = "";
    $replace[] = "";
    $replace[] = "";
    $replace[] = "";

    return str_replace($search,$replace,$source_file);

} 
Brant Messenger
źródło