Saneantes seqüências de caracteres para torná-las URL e nome de arquivo seguros?

136

Estou tentando criar uma função que faz um bom trabalho na limpeza de certas seqüências de caracteres para que elas sejam seguras para uso na URL (como uma publicação pós-lesma) e também para uso como nomes de arquivo. Por exemplo, quando alguém faz o upload de um arquivo, desejo remover todos os caracteres perigosos do nome.

Até agora, criei a seguinte função, que espero resolver esse problema e permita também dados UTF-8 estrangeiros.

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

Alguém tem dados de amostra complicados que eu possa executar contra isso - ou conhece uma maneira melhor de proteger nossos aplicativos de nomes ruins?

$ is-filename permite alguns caracteres adicionais, como arquivos temporários do vim

update: removeu o caractere estrela, pois não conseguia pensar em um uso válido

Xeoncross
fonte
É melhor você remover tudo, exceto [\ w.-]
elias
3
Você pode achar o Normalizador e os comentários úteis.
Matt Gibson

Respostas:

57

Algumas observações sobre sua solução:

  1. 'u' no final do seu padrão significa que o padrão , e não o texto correspondente, será interpretado como UTF-8 (presumo que você assumiu o último?).
  2. \ w corresponde ao caractere sublinhado. Você o inclui especificamente para arquivos que levam à suposição de que você não os deseja em URLs, mas no código que você possui, os URLs poderão incluir um sublinhado.
  3. A inclusão de "UTF-8 estrangeiro" parece ser dependente da localidade. Não está claro se esse é o código do idioma do servidor ou cliente. Nos documentos do PHP:

Um caractere "palavra" é qualquer letra ou dígito ou o caractere sublinhado, ou seja, qualquer caractere que possa fazer parte de uma "palavra" Perl. A definição de letras e dígitos é controlada pelas tabelas de caracteres do PCRE e pode variar se a correspondência específica da localidade estiver ocorrendo. Por exemplo, no código do idioma "fr" (francês), alguns códigos de caracteres maiores que 128 são usados ​​para letras acentuadas e correspondem a \ w.

Criando a lesma

Você provavelmente não deve incluir caracteres acentuados etc. na sua publicação, pois, tecnicamente, eles devem ser codificados em porcentagem (de acordo com as regras de codificação de URL), para que você tenha URLs feios.

Então, se eu fosse você, depois de usar letras minúsculas, converteria qualquer caractere 'especial' para seu equivalente (por exemplo, é -> e) e substituiria caracteres não [az] por '-', limitando a execução de um único '-' como você fez. Há uma implementação de conversão de caracteres especiais aqui: https://web.archive.org/web/20130208144021/http://neo22s.com/slug

Sanitização em geral

O OWASP possui uma implementação em PHP de sua API de Segurança Corporativa que, entre outras coisas, inclui métodos para codificação e decodificação seguras de entrada e saída em seu aplicativo.

A interface do codificador fornece:

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
fonte
Você está certo quanto à minha suposição do modificador "u" - pensei que era para o texto. Também esqueci o modificador \ w, incluindo o sublinhado. Eu normalmente converteria todos os caracteres acentuados em ASCII - mas quero que isso funcione para outros idiomas também. Eu estava assumindo que haveria algum tipo de maneira segura de UTF-8 para que qualquer caractere de um idioma pudesse ser usado em um slug de URL ou nome de arquivo para que até os títulos em árabe funcionassem. Afinal, o linux suporta nomes de arquivos UTF-8 e os navegadores devem codificar os links HTML conforme necessário. Muito obrigado pela sua contribuição aqui.
Xeoncross
Pensando bem, você está certo, mas não é apenas um problema com o navegador que codifica os links corretamente. A maneira mais fácil de atingir o que você deseja é mapear caracteres não ASCII para o equivalente ASCII mais próximo e codificar o link no corpo do URL por URL. A maneira mais difícil é garantir a codificação UTF-8 consistente (ou UTF-16, acho que para alguns dialetos chineses) do seu armazenamento de dados, através do servidor da web, camada de aplicativo (PHP), conteúdo da página, navegador da web e não codificar seus URLs ( mas ainda retira caracteres 'indesejáveis'). Isso fornecerá ótimos links e URLs não codificados.
22810 Alan Donnelly
Bom conselho. Vou tentar criar um ambiente UTF-8 puro. Então, pegando várias strings de linguagens não-ASCII, removerei caracteres perigosos (./ ;: etc ...) e crio arquivos e, em seguida, links HTML para esses arquivos para ver se posso clicar neles e ver se tudo isso trabalho. Caso contrário, provavelmente terei que voltar para (raw)? Urlencode () para permitir UTF-8. Vou postar os resultados aqui.
Xeoncross
3
Criei um arquivo chamado สังเวช พระปกเกศกองบู๊กู้ขึ้นใหม่.txte, em seguida, criei um arquivo HTML UTF-8 com um link para ele. Surpreendentemente, funcionou - mesmo nas janelas! No entanto, eu tinha o PHP file_put_contents('สังเวช พระปกเกศกองบู๊กู้ขึ้นใหม่.txt')e ele falhou ao criar um nome de arquivo de bazar a partir dessa string. Então tentei criá-lo fopen()e obtive o mesmo nome de arquivo bagunçado. Então, aparentemente, o PHP (pelo menos no Windows) é incapaz de criar nomes de arquivos UTF-8. bugs.php.net/bug.php?id=46990&thanks=6
Xeoncross
1
Eu concedo essa resposta porque ela me fez pensar mais e também incluiu um link útil para um projeto que nunca ouvi falar que vale a pena examinar. Vou postar quando encontrar a resposta.
Xeoncross
87

Eu encontrei essa função maior no código 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("~", "`", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "_", "=", "+", "[", "{", "]",
                   "}", "\\", "|", ";", ":", "\"", "'", "‘", "’", "“", "”", "–", "—",
                   "—", "–", ",", "<", ".", ">", "/", "?");
    $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;
}

e este no código 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);
}

Atualização de setembro de 2012

Alix Axel fez um trabalho incrível nesta área. Sua estrutura de funções inclui vários ótimos filtros e transformações de texto.

Xeoncross
fonte
23
O código WordPress não é portátil, pois faz uso deapply_filters
Kevin Mark
1
Note-se que os substitui a versão wordpress /[\s-]+/com -o que é melhor do que a primeira versão (que substitui apenas /\s+/) que pode causar vários traços em uma linha
Yotam Omer
Apenas para referência, o wordpress apply_filters pode ser encontrado aqui e sanitize_file_name por aqui .
22415 Eric
e quanto a vários espaços? Substitua
Jeffrey the Giraffe
8
A variável $ anal me parece muito assustadora com a opção de força.
viljun
30

Isso deve tornar seus nomes de arquivos seguros ...

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

e uma solução mais profunda para isso é:

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

Isso pressupõe que você deseja um ponto no nome do arquivo. se você quiser transferi-lo para minúsculas, use

$clean_name = strtolower($clean_name);

para a última linha.

SoLoGHoST
fonte
1
Ainda faltam alguns caracteres Checa e Eslovaca:'ľ' => 'l', 'Ľ' => 'L', 'č' => 'c', 'Č' => 'C', 'ť' => 't', 'Ť' => 'T', 'ň' => 'n', 'Ň' => 'N', 'ĺ' => 'l', 'Ĺ' => 'L', 'Ř' => 'R', 'ř' => 'r', 'ě' => 'e', 'Ě' => 'E', 'ů' => 'u', 'Ů' => 'U'
Jasom Dotnet
22

Tente o seguinte:

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

Com base na resposta selecionada neste tópico: Nome de usuário amigável do URL em PHP?

John Conde
fonte
Muito bom - nunca vi isso sem uma tabela de tradução (como o wordpress). No entanto, não acho que essa função seja suficiente, pois traduz apenas caracteres especiais, mas não remove caracteres perigosos. Talvez ele pode ser adicionado a um acima ...
Xeoncross
4
Ha! Essa entidade que codifica o hack é doce! Embora não esteja absolutamente claro à primeira vista como esse método faz o que faz. Há um problema, porém. "Frédéric & Éric" não se transformará em "Frederic amp Eric"?
22810 Alan Donnelly
@ AlanDonnelly: De fato, atualizei a função na minha resposta original (verifique o link), trim()também deve ser trim($string, '-').
Alix Axel
@Xeoncross: O último preg_replace()deve remover todos os caracteres perigosos.
Alix Axel
@AlixAxel, você não está em todos os lugares. Eu estava lendo o PHP AWS SDK e eles tinham um pouco do seu código para UUID's. O incrível código de função é difícil de bater.
Xeoncross
13

Esta não é exatamente uma resposta, pois ainda não fornece nenhuma solução, mas é grande demais para caber em um comentário ...


Fiz alguns testes (sobre nomes de arquivos) no Windows 7 e Ubuntu 12.04 e o que descobri foi:

1. O PHP não pode manipular nomes de arquivos não ASCII

Embora o Windows e o Ubuntu possam lidar com nomes de arquivos Unicode (mesmo os de RTL, ao que parece), o PHP 5.3 exige que os hacks lidem mesmo com a antiga ISO-8859-1, por isso é melhor mantê-lo ASCII apenas por segurança.

2. O comprimento dos assuntos de nome de arquivo (especialmente no Windows)

No Ubuntu, o tamanho máximo que um nome de arquivo pode ter (incluindo a extensão) é 255 (excluindo caminho):

/var/www/uploads/123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345/

No entanto, no Windows 7 (NTFS), o comprimento máximo que um nome de arquivo pode ter depende de seu caminho absoluto:

(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

A Wikipedia diz que:

O NTFS permite que cada componente do caminho (diretório ou nome do arquivo) tenha 255 caracteres.

Que eu saiba (e teste), isso está errado.

No total (contando barras), todos esses exemplos têm 259 caracteres, se você remover o C:\que fornece 256 caracteres (não 255 ?!). Os diretórios foram criados usando o Explorer e você notará que ele se impede de usar todo o espaço disponível para o nome do diretório. O motivo disso é permitir a criação de arquivos usando a convenção de nomenclatura 8.3 . O mesmo acontece com outras partições.

Os arquivos não precisam reservar os requisitos de 8,3 comprimentos, é claro:

(255 chars) E:\12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901.txt

Você não pode criar mais subdiretórios se o caminho absoluto do diretório pai tiver mais de 242 caracteres, porque 256 = 242 + 1 + \ + 8 + . + 3. Usando o Windows Explorer, você não pode criar outro diretório se o diretório pai tiver mais de 233 caracteres (dependendo da localidade do sistema), porque 256 = 233 + 10 + \ + 8 + . + 3; o 10aqui é o comprimento da string New folder.

O sistema de arquivos do Windows apresenta um problema desagradável se você deseja garantir a interoperabilidade entre os sistemas de arquivos.

3. Cuidado com caracteres reservados e palavras-chave

Além de remover caracteres não ASCII, imprimíveis e de controle , você também precisa re (colocar / mover):

"*/:<>?\|

Apenas remover esses caracteres pode não ser a melhor ideia, pois o nome do arquivo pode perder parte do seu significado. Eu acho que, no mínimo, várias ocorrências desses caracteres devem ser substituídas por um único sublinhado ( _), ou talvez algo mais representativo (isso é apenas uma ideia):

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

Também há palavras-chave especiais que devem ser evitadas (como NUL), embora eu não tenha certeza de como superar isso. Talvez uma lista negra com um nome aleatório substituto seja uma boa abordagem para resolvê-lo.

4. Sensibilidade ao Caso

Isso deve ser óbvio, mas se você quiser garantir a exclusividade dos arquivos em diferentes sistemas operacionais, deverá transformar os nomes dos arquivos em um caso normalizado, dessa forma, my_file.txte My_File.txtno Linux, os dois não se tornarão o mesmo my_file.txtarquivo no Windows.

5. Certifique-se de que é único

Se o nome do arquivo já existir, um identificador exclusivo deve ser anexado ao seu nome de arquivo base.

Identificadores exclusivos comuns incluem o registro de data e hora do UNIX, um resumo do conteúdo do arquivo ou uma sequência aleatória.

6. Arquivos ocultos

Só porque pode ser nomeado não significa que deveria ...

Os pontos são geralmente listados em nomes de arquivos, mas no Linux, um arquivo oculto é representado por um ponto inicial.

7. Outras considerações

Se você precisar remover alguns caracteres do nome do arquivo, a extensão geralmente é mais importante que o nome base do arquivo. Permitindo um número máximo considerável de caracteres para a extensão do arquivo (8-16), deve-se retirar os caracteres do nome base. Também é importante observar que, no caso improvável de ter mais de uma extensão longa - como _.graphmlz.tag.gz- _.graphmlz.tagapenas _deve ser considerado como o nome base do arquivo nesse caso.

8. Recursos

O Calibre lida com o nome de arquivo que é confuso:

Página da Wikipedia sobre o nome do arquivo desconectado e o capítulo vinculado do Using Samba .


Se, por exemplo, você tentar criar um arquivo que viole alguma das regras 1/2/3, receberá um erro muito útil:

Warning: touch(): Unable to create file ... because No error in ... on line ...
Alix Axel
fonte
11

Eu sempre achei que Kohana fez um bom trabalho nisso .

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

O prático UTF8::transliterate_to_ascii()transformará coisas como ñ => n.

Obviamente, você pode substituir os outros UTF8::*itens por funções mb_ *.

alex
fonte
5

Em termos de upload de arquivos, você seria mais seguro para impedir que o usuário controle o nome do arquivo. Como já foi sugerido, armazene o nome do arquivo canônico em um banco de dados, juntamente com um nome escolhido aleatoriamente e exclusivo que você usará como o nome do arquivo real.

Usando OWASP ESAPI, esses nomes podem ser gerados assim:

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

Você pode anexar um carimbo de data / hora ao $ safeFilename para ajudar a garantir que o nome do arquivo gerado aleatoriamente seja exclusivo, sem mesmo procurar um arquivo existente.

Em termos de codificação para URL e novamente usando ESAPI:

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

Esse método executa a canonização antes de codificar a sequência e manipula todas as codificações de caracteres.

jah
fonte
Definitivamente - também, afastar o controle do nome de arquivo dos usuários impedirá a possibilidade de 2 envios com o mesmo nome.
CodeVirtuoso
5

Eu recomendo * URLify para PHP (mais de 480 estrelas no Github) - "a porta PHP de URLify.js do projeto Django. Translitera caracteres não-ascii para uso em URLs".

Uso básico:

Para gerar slugs para URLs:

<?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"

?>

Para gerar slugs para nomes de arquivos:

<?php

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

?>

* Nenhuma das outras sugestões corresponde aos meus critérios:

  • Deve ser instalável via compositor
  • Não deve depender do iconv, pois se comporta de maneira diferente em sistemas diferentes
  • Deve ser extensível para permitir substituições e substituições de caracteres personalizados
  • Popular (por exemplo, muitas estrelas no Github)
  • Tem testes

Como bônus, o URLify também remove certas palavras e retira todos os caracteres não transliterados.

Aqui está um caso de teste com toneladas de caracteres estrangeiros sendo transliterados corretamente usando URLify: https://gist.github.com/motin/a65e6c1cc303e46900d10894bf2da87f

Motin
fonte
1
Obrigado - parece ideal para meus propósitos.
David Goodwin
5

Eu me adaptei de outra fonte e adicionei mais alguns, talvez um pouco de exagero

/**
 * 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
fonte
5

e esta é a versão Joomla 3.3.2 da 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
fonte
4

Eu não acho que ter uma lista de caracteres para remover seja seguro. Prefiro usar o seguinte:

Para nomes de arquivos: use um ID interno ou um hash do conteúdo do arquivo. Salve o nome do documento em um banco de dados. Dessa forma, você pode manter o nome do arquivo original e ainda encontrar o arquivo.

Para parâmetros de URL: use urlencode()para codificar qualquer caractere especial.

ZeissS
fonte
1
Concordo que a maioria dos métodos listados aqui remove caracteres perigosos conhecidos - meu método remove tudo que não é um caractere seguro conhecido . Como a maioria dos sistemas codifica slug URLs de post, sugiro que continuemos a seguir esse método comprovado, em vez de usar o urlencode inseguro UTF-8 documentado ().
Xeoncross
3

Dependendo de como você o usará, convém adicionar um limite de comprimento para proteger contra estouros de buffer.

Tgr
fonte
Sim, testar o mb_strlen () é sempre uma coisa importante!
Xeoncross
3

Esta é uma boa maneira de proteger um nome de arquivo de upload:

$file_name = trim(basename(stripslashes($name)), ".\x00..\x20");
jogos
fonte
Não tenho tanta certeza disso, pois um .\x00..\x20pode ser reduzido a .\x00\x20.
Xeoncross #
@ Xeoncross: Eu acho que .\x00..\x20remove pontos e todos os caracteres entre \x00e \x20, considerando que .\x00\x20deve remover apenas esses 3 bytes.
Alix Axel
Esta resposta requer mais explicações para ser usada com segurança. Não há muitas informações sobre a sintaxe exata para o charlist na rede.
Manuel Arwed Schmidt
3

Aqui está a implementação do 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));
}

E a remove_invisible_charactersdependência.

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
fonte
2

por que não simplesmente usar php's urlencode? ele substitui caracteres "perigosos" por sua representação hexadecimal por URLs (ou seja, %20por um espaço)

knittl
fonte
2
O caractere% não é recomendado para nomes de arquivos e caracteres codificados em hexadecimal não parecem tão bons no URL. Os navegadores podem suportar strings UTF-8, que são muito mais agradáveis ​​e fáceis para idiomas não-ascii.
Xeoncross
você poderia criar um código de URL e, em seguida, um str_replace ('% 20', '-', url)?
Francesco
2

Já existem várias soluções fornecidas para esta pergunta, mas li e testei a maior parte do código aqui e acabei com essa solução, que é uma mistura do que aprendi aqui:

A função

A função é empacotada aqui em um pacote Symfony2, mas pode ser extraída para ser usada como PHP simples ; ela possui apenas uma dependência da iconvfunção que deve ser ativada:

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

Os testes de unidade

O interessante é que eu criei testes PHPUnit, primeiro para testar casos extremos e, assim, você pode verificar se ele atende às suas necessidades: (Se você encontrar um bug, sinta-se à vontade para adicionar um caso de teste)

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

Os resultados do teste: (verificado no Ubuntu com PHP 5.3.2 e MacOsX com 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)
Bobina
fonte
1
Isso pressupõe principalmente entrada baseada em latim. Adicione mais caracteres UTF-8 de outros idiomas para ver onde você terá problemas.
Xeoncross
@Xeoncross Eu concordo, como Christian disse que é preciso salvar um ID ou hash E o nome do arquivo original. Mas essa função fornece uma alternativa, pois você pode especificar uma sequência padrão quando o processo de higienização falhar. Eu adicionei um teste de unidade para este caso. Obrigado por relatar o bug.
bobina
2

Tenho títulos de entrada com todos os tipos de caracteres latinos estranhos, bem como algumas tags HTML que eu precisava para traduzir em um formato de nome de arquivo útil, delimitado por traços. Combinei a resposta do @ SoLoGHoST com alguns itens da resposta do @ Xeoncross e personalizei um pouco.

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

Eu precisava adicionar manualmente o caractere traço em (-) à matriz de tradução. Pode haver outros, mas até agora meus nomes de arquivos estão com boa aparência.

Assim:

Parte 1: Os Žurburts do meu pai? - eles (não) são os melhores!

torna-se:

parte-1-meus-pais-zurburts-eles não são os melhores

Acabei de adicionar ".html" à string retornada.

cbmtrx
fonte
1
Ainda faltam alguns caracteres checos e eslovacos:'ľ' => 'l', 'Ľ' => 'L', 'č' => 'c', 'Č' => 'C', 'ť' => 't', 'Ť' => 'T', 'ň' => 'n', 'Ň' => 'N', 'ĺ' => 'l', 'Ĺ' => 'L', 'Ř' => 'R', 'ř' => 'r', 'ě' => 'e', 'Ě' => 'E', 'ů' => 'u', 'Ů' => 'U'
Jasom Dotnet 12/12
1
E sem dúvida muitos mais. Na verdade, estou tentando descobrir se existe um conjunto ISO que inclua combinações de caracteres. Como alguém "escolhe" um conjunto se o conteúdo exige caracteres de todos eles? UTF-8 Eu estou supondo ...
cbmtrx
Descobri como transliterar qualquer string usando uma linha do PHP : $string = transliterator_transliterate('Any-Latin;Latin-ASCII;', $string);Veja minha resposta abaixo ou leia a postagem do blog vinculada.
Jasom Dotnet
1
Não, você leu errado: SE você pode instalar extensões PHP no seu servidor (ou hospedagem) :-) Aqui está o post .
Jasom Dotnet
1
Ah, entendi. Obrigado @JasomDotnet --Tenho minha solução atual funcionando por enquanto, mas é um conjunto de caracteres limitado, portanto vale a pena conferir a extensão.
Cbmtrx
2

Solução 1: você pode instalar extensões PHP no servidor (hospedagem)

Para transliteração de "quase todas as línguas do planeta Terra" para caracteres ASCII.

  1. Instale a extensão PHP Intl primeiro. Este é um comando para o Debian (Ubuntu):sudo aptitude install php5-intl

  2. Esta é a minha função fileName (crie test.php e cole o seguinte código):

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

Esta linha é essencial:

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

Resposta baseada nesta postagem .

Solução 2: você não tem capacidade para instalar extensões PHP no servidor (hospedagem)

insira a descrição da imagem aqui

Um bom trabalho é feito no módulo de transliteração do CMS Drupal. Ele suporta quase todas as línguas do planeta Terra. Sugiro verificar o repositório de plug-ins se você quiser ter realmente soluções completas para higienizar a solução.

Jasom Dotnet
fonte
1

Esta é uma boa função:

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
fonte
Isso parece ruim. \\s+significa uma barra invertida seguida por um ou mais espaços em branco. O que é isso? Além disso, isso usa a lista negra em vez da lista de permissões, ignorando coisas como CMD, null ou BEL.
Xeoncross
Ainda ruím. Agora, strings como /blog/2014-02/just-in-timenão são permitidas. Por favor, use o código testado acima ou use o phunctioncódigo da estrutura PHP.
Xeoncross
Está certo. Esta função é apenas para a parte "just-in-time". Pode ser útil para algumas pessoas.
joan16v
1
Você pode alterar a regexpreg_replace('~[^\-\pL\pN\s]+~u', '-', $string)
Xeoncross
Impressionante! Eu adicionei também: string = trim ($ string, "-");
joan16v
0

Este é o código usado pelo Prestashop para limpar os URLs:

replaceAccentedChars

é usado por

str2url

remover diacríticos

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