¿Desinfectar cadenas para que sean seguros para URL y nombre de archivo?

136

Estoy tratando de encontrar una función que haga un buen trabajo al desinfectar ciertas cadenas para que sean seguras de usar en la URL (como un mensaje de publicación) y también seguras para usar como nombres de archivo. Por ejemplo, cuando alguien sube un archivo, quiero asegurarme de eliminar todos los caracteres peligrosos del nombre.

Hasta ahora, se me ocurrió la siguiente función, que espero resuelva este problema y también permita datos UTF-8 externos.

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

¿Alguien tiene datos de muestra difíciles con los que pueda ejecutar esto, o conozca una mejor manera de proteger nuestras aplicaciones de los malos nombres?

$ is-filename permite algunos caracteres adicionales como archivos temporales vim

actualización: eliminé el personaje estrella ya que no podía pensar en un uso válido

Xeoncross
fuente
Será mejor que elimines todo excepto [\ w.-]
elias
3
Puede encontrar el Normalizador y los comentarios útiles.
Matt Gibson el

Respuestas:

57

Algunas observaciones sobre su solución:

  1. 'u' al final de su patrón significa que el patrón , y no el texto con el que coincide, se interpretará como UTF-8 (supongo que asumió esto último).
  2. \ w coincide con el carácter de subrayado. Lo incluye específicamente para los archivos, lo que lleva a la suposición de que no los quiere en las URL, pero en el código que tiene las URL se les permitirá incluir un guión bajo.
  3. La inclusión de "UTF-8 extranjero" parece depender de la localidad. No está claro si esta es la configuración regional del servidor o el cliente. De los documentos PHP:

Un carácter de "palabra" es cualquier letra o dígito o el carácter de subrayado, es decir, cualquier carácter que pueda ser parte de una "palabra" de Perl. La definición de letras y dígitos está controlada por las tablas de caracteres de PCRE, y puede variar si se realiza una coincidencia específica de la localidad. Por ejemplo, en la configuración regional "fr" (francés), algunos códigos de caracteres superiores a 128 se utilizan para letras acentuadas, y estos se corresponden con \ w.

Creando la babosa

Probablemente no debería incluir caracteres acentuados, etc. en su mensaje de publicación ya que, técnicamente, deberían estar codificados en porcentaje (según las reglas de codificación de URL) para que tenga URL de aspecto feo.

Entonces, si fuera usted, después de minúsculas, convertiría cualquier carácter 'especial' a su equivalente (por ejemplo, é -> e) y reemplazaría los caracteres que no sean [az] con '-', limitando las ejecuciones de un solo '-' como lo has hecho Hay una implementación de conversión de caracteres especiales aquí: https://web.archive.org/web/20130208144021/http://neo22s.com/slug

Desinfección en general

OWASP tiene una implementación PHP de su API de seguridad empresarial que, entre otras cosas, incluye métodos para codificar y decodificar de forma segura la entrada y salida en su aplicación.

La interfaz del codificador proporciona:

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
fuente
Tienes razón sobre mi suposición del modificador "u": pensé que era para el texto. También olvidé el modificador \ w, incluido el guión bajo. Normalmente convertiría todos los caracteres acentuados a ASCII, pero quiero que esto también funcione para otros idiomas. Estaba asumiendo que habría algún tipo de forma segura UTF-8 de que cualquier carácter de un idioma podría usarse en un slug URL o nombre de archivo para que incluso los títulos en árabe funcionen. Después de todo, Linux admite nombres de archivo UTF-8 y los navegadores deben codificar enlaces HTML según sea necesario. Muchas gracias por tu aportación aquí.
Xeoncross
Pensándolo bien, en realidad tienes razón, pero no es solo un problema con el navegador que codifica los enlaces correctamente. La forma más fácil de lograr cerca de lo que desea es asignar caracteres no ASCII a su equivalente ASCII más cercano y luego codificar con URL su enlace en el cuerpo HTML. La forma difícil es garantizar una codificación UTF-8 consistente (o UTF-16, creo que para algunos dialectos chinos) desde su almacén de datos, a través de su servidor web, capa de aplicación (PHP), contenido de la página, navegador web y no codificar sus URL (urlen) pero aún tira caracteres 'indeseables'). Esto le dará buenos enlaces y URL no codificados.
Alan Donnelly
Buen consejo. Voy a intentar crear un entorno UTF-8 puro. Luego, tomando varias cadenas de idiomas que no son ASCII, eliminaré caracteres peligrosos (./ ;: etc ...) y crearé archivos y luego enlaces HTML a esos archivos para ver si puedo hacer clic en ellos y ver si todo esto trabajos. Si no es así, probablemente tendré que volver a (raw)? Urlencode () para permitir UTF-8. Publicaré los resultados aquí.
Xeoncross
3
Creé un archivo llamado สังเวช พระปกเกศกองบู๊กู้ขึ้นใหม่.txty luego creé un archivo HTML UTF-8 con un enlace. Sorprendentemente funcionó, ¡incluso en Windows! Sin embargo, luego tuve PHP file_put_contents('สังเวช พระปกเกศกองบู๊กู้ขึ้นใหม่.txt')y no se pudo crear un nombre de archivo de bazar a partir de esa cadena. Luego intenté crearlo fopen()y obtuve el mismo nombre de archivo desordenado. Entonces, aparentemente PHP (al menos en Windows) es incapaz de crear nombres de archivo UTF-8. bugs.php.net/bug.php?id=46990&thanks=6
Xeoncross
1
Otorgo esta respuesta porque me hizo pensar más y también incluyó un enlace útil a un proyecto que nunca escuché que valga la pena analizar. Publicaré una vez que encuentre una respuesta.
Xeoncross
87

Encontré esta función más grande en el 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;
}

y este en el código de 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);
}

Actualización de septiembre de 2012

Alix Axel ha hecho un trabajo increíble en esta área. Su marco de funciones incluye varios filtros de texto y transformaciones excelentes.

Xeoncross
fuente
23
El código de WordPress no es portátil, ya que utilizaapply_filters
Kevin Mark
1
Tenga en cuenta que la versión de WordPress reemplaza /[\s-]+/con la -que es mejor que la primera versión (que reemplaza solo /\s+/) que puede causar múltiples guiones seguidos
Yotam Omer
Solo para referencia, wordpress apply_filters se puede encontrar aquí y sanitize_file_name por aquí .
Eric
¿Qué pasa con los espacios múltiples? Reemplazar
Jeffrey the Giraffe
8
La variable $ anal me suena muy aterradora con la opción force.
viljun
30

Esto debería hacer que sus nombres de archivo sean seguros ...

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

y una solución más profunda a esto es:

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

Esto supone que desea un punto en el nombre del archivo. si quieres transferirlo a minúsculas, solo usa

$clean_name = strtolower($clean_name);

para la última línea

SoLoGHoST
fuente
1
Todavía faltan algunos caracteres checos y eslovacos:'ľ' => 'l', 'Ľ' => 'L', 'č' => 'c', 'Č' => 'C', 'ť' => 't', 'Ť' => 'T', 'ň' => 'n', 'Ň' => 'N', 'ĺ' => 'l', 'Ĺ' => 'L', 'Ř' => 'R', 'ř' => 'r', 'ě' => 'e', 'Ě' => 'E', 'ů' => 'u', 'Ů' => 'U'
Jasom Dotnet
22

Prueba esto:

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

Basado en la respuesta seleccionada en este hilo: ¿ Nombre de usuario amigable de URL en PHP?

John Conde
fuente
Muy agradable: nunca he visto esto sin una tabla de traducción (como los usos de WordPress). Sin embargo, no creo que esta función sea suficiente, ya que solo traduce caracteres especiales pero no elimina los caracteres peligrosos. Tal vez se puede agregar a uno de arriba ...
Xeoncross
44
¡Decir ah! ¡Esa entidad que codifica hack es dulce! Aunque a primera vista no está nada claro cómo este método hace lo que hace. Sin embargo, hay un problema. ¿"Frédéric & Éric" no se convertirá en "Frederic amp Eric"?
Alan Donnelly
@AlanDonnelly: De hecho, he actualizado la función en mi respuesta original (verifique el enlace), trim()también debería estarlo trim($string, '-').
Alix Axel
@Xeoncross: el último preg_replace()debería eliminar todos los caracteres peligrosos.
Alix Axel
@AlixAxel, no estás en todas partes. Estaba leyendo el SDK de AWS de PHP y tenían algunos de sus códigos para UUID. El increíble código de función es difícil de superar.
Xeoncross
13

Esto no es exactamente una respuesta, ya que no proporciona ninguna solución (¡todavía!), Pero es demasiado grande para caber en un comentario ...


Hice algunas pruebas (con respecto a los nombres de archivo) en Windows 7 y Ubuntu 12.04 y lo que descubrí fue que:

1. PHP no puede manejar nombres de archivos no ASCII

Aunque tanto Windows como Ubuntu pueden manejar nombres de archivos Unicode (incluso RTL como parece) PHP 5.3 requiere hacks para tratar incluso con el viejo ISO-8859-1, por lo que es mejor mantenerlo ASCII solo por seguridad.

2. La longitud de los asuntos de nombre de archivo (especialmente en Windows)

En Ubuntu, la longitud máxima que puede tener un nombre de archivo (incluida la extensión) es 255 (sin incluir la ruta):

/var/www/uploads/123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345/

Sin embargo, en Windows 7 (NTFS) la longitud máxima que puede tener un nombre de archivo depende de su ruta absoluta:

(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 dice que:

NTFS permite que cada componente de ruta (directorio o nombre de archivo) tenga 255 caracteres de longitud.

A lo mejor de mi conocimiento (y pruebas), esto está mal.

En total (contando barras) todos estos ejemplos tienen 259 caracteres, si quitas el C:\que da 256 caracteres (¡¿no 255 ?!). Los directorios se crearon usando el Explorador y notará que se limita a usar todo el espacio disponible para el nombre del directorio. La razón de esto es permitir la creación de archivos usando la convención de nomenclatura de archivos 8.3 . Lo mismo sucede con otras particiones.

Los archivos no necesitan reservar los requisitos de longitud 8.3, por supuesto:

(255 chars) E:\12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901.txt

No puede crear más subdirectorios si la ruta absoluta del directorio principal tiene más de 242 caracteres, porque 256 = 242 + 1 + \ + 8 + . + 3. Con el Explorador de Windows, no puede crear otro directorio si el directorio principal tiene más de 233 caracteres (según la configuración regional del sistema), porque 256 = 233 + 10 + \ + 8 + . + 3; el 10aquí es la longitud de la cadenaNew folder .

El sistema de archivos de Windows plantea un problema desagradable si desea garantizar la interoperabilidad entre los sistemas de archivos.

3. Cuidado con los caracteres reservados y las palabras clave

Además de eliminar caracteres no ASCII, no imprimibles y de control , también debe volver a colocar (colocar / mover):

"*/:<>?\|

Simplemente eliminar estos caracteres podría no ser la mejor idea porque el nombre de archivo podría perder parte de su significado. Creo que, al menos, las ocurrencias múltiples de estos caracteres deberían reemplazarse por un solo guión bajo ( _), o tal vez algo más representativo (esto es solo una idea):

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

También hay palabras clave especiales que deben evitarse (como NUL), aunque no estoy seguro de cómo superar eso. Quizás una lista negra con un nombre aleatorio alternativo sería un buen enfoque para resolverlo.

4. Sensibilidad a mayúsculas y minúsculas

Esto debería ser evidente, pero si lo desea, asegúrese de que el archivo sea único en los diferentes sistemas operativos, debe transformar los nombres de los archivos en un caso normalizado, de esa manera my_file.txty My_File.txten Linux no se convertirá en el mismo my_file.txtarchivo en Windows.

5. Asegúrese de que sea único

Si el nombre del archivo ya existe, se debe agregar un identificador único a su nombre de archivo base.

Los identificadores únicos comunes incluyen la marca de tiempo UNIX, un resumen del contenido del archivo o una cadena aleatoria.

6. archivos ocultos

El hecho de que se pueda nombrar no significa que deba ...

Los puntos suelen aparecer en la lista blanca en los nombres de archivo, pero en Linux un archivo oculto está representado por un punto inicial.

7. Otras consideraciones

Si tiene que quitar algunos caracteres del nombre del archivo, la extensión suele ser más importante que el nombre base del archivo. Al permitir un número máximo considerable de caracteres para la extensión del archivo (8-16), se deben quitar los caracteres del nombre base. También es importante tener en cuenta que, en el caso improbable de tener más de una extensión larga, como por ejemplo _.graphmlz.tag.gz, _.graphmlz.tagsolo _debe considerarse como el nombre base del archivo en este caso.

8. Recursos

Calibre maneja el nombre del archivo de manera bastante decente:

Página de Wikipedia sobre el cambio de nombre de archivo y el capítulo vinculado de Uso de Samba .


Si, por ejemplo, intenta crear un archivo que infringe alguna de las reglas 1/2/3, obtendrá un error muy útil:

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

Siempre pensé que Kohana hizo un buen trabajo .

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

Los prácticos UTF8::transliterate_to_ascii()convertirán cosas como ñ => n.

Por supuesto, podría reemplazar las otras UTF8::*cosas con funciones mb_ *.

alex
fuente
5

En términos de carga de archivos, sería más seguro evitar que el usuario controle el nombre del archivo. Como ya se ha insinuado, almacene el nombre de archivo canonicalizado en una base de datos junto con un nombre elegido aleatoriamente y único que usará como el nombre de archivo real.

Usando OWASP ESAPI, estos nombres podrían generarse así:

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

Puede agregar una marca de tiempo al $ safeFilename para ayudar a garantizar que el nombre de archivo generado aleatoriamente sea único sin siquiera buscar un archivo existente.

En términos de codificación para URL, y nuevamente usando ESAPI:

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

Este método realiza la canonicalización antes de codificar la cadena y manejará todas las codificaciones de caracteres.

jah
fuente
Definitivamente, además, quitar el control del nombre de archivo a los usuarios evitará la posibilidad de 2 cargas con el mismo nombre.
CodeVirtuoso
5

Recomiendo * URLify para PHP (480+ estrellas en Github) - "el puerto PHP de URLify.js del proyecto Django. Translitera caracteres no ascii para usar en URL".

Uso básico:

Para generar babosas para 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"

?>

Para generar babosas para nombres de archivo:

<?php

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

?>

* Ninguna de las otras sugerencias coincide con mis criterios:

  • Debe ser instalable a través del compositor
  • No debería depender de iconv ya que se comporta de manera diferente en diferentes sistemas
  • Debe ser extensible para permitir anulaciones y reemplazos de caracteres personalizados
  • Popular (por ejemplo, muchas estrellas en Github)
  • Tiene pruebas

Como beneficio adicional, URLify también elimina ciertas palabras y elimina todos los caracteres no transcritos.

Aquí hay un caso de prueba con toneladas de caracteres extranjeros que se transcriben correctamente usando URLify: https://gist.github.com/motin/a65e6c1cc303e46900d10894bf2da87f

Motin
fuente
1
Gracias, eso se ve ideal para mis propósitos.
David Goodwin
5

Me he adaptado de otra fuente y agregué un par extra, tal vez un poco exagerado

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

y esta es la versión Joomla 3.3.2 de 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
fuente
4

No creo que sea seguro tener una lista de caracteres para eliminar. Prefiero usar lo siguiente:

Para nombres de archivo: use una ID interna o un hash del contenido del archivo. Guarde el nombre del documento en una base de datos. De esta manera, puede mantener el nombre de archivo original y aún encontrar el archivo.

Para parámetros de URL: se utiliza urlencode()para codificar caracteres especiales.

ZeissS
fuente
1
Estoy de acuerdo, la mayoría de los métodos enumerados aquí eliminan los caracteres peligrosos conocidos : mi método elimina todo lo que no sea un carácter seguro conocido . Como la mayoría de los sistemas codifican las URL de las publicaciones, sugiero que sigamos siguiendo este método comprobado en lugar de utilizar el código urlen inseguro UTF-8 documentado ().
Xeoncross
3

Dependiendo de cómo lo use, es posible que desee agregar un límite de longitud para proteger contra desbordamientos del búfer.

Tgr
fuente
Sí, ¡probar mb_strlen () siempre es algo importante!
Xeoncross
3

Esta es una buena manera de asegurar un nombre de archivo de carga:

$file_name = trim(basename(stripslashes($name)), ".\x00..\x20");
juegos
fuente
No estoy tan seguro de esto, porque uno .\x00..\x20puede reducirse a .\x00\x20.
Xeoncross
@Xeoncross: creo que .\x00..\x20elimina puntos y todos los caracteres entre \x00y \x20, mientras .\x00\x20que solo debería eliminar esos 3 bytes.
Alix Axel
Esta respuesta requiere más explicaciones para que se use de manera segura. No hay mucha información sobre la sintaxis exacta de charlist en la red.
Manuel Arwed Schmidt
3

Aquí está la implementación de 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));
}

Y la remove_invisible_charactersdependencia.

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

¿por qué no simplemente usar php's urlencode? reemplaza caracteres "peligrosos" con su representación hexadecimal para urls (es decir, %20para un espacio)

knittl
fuente
2
No se recomienda el carácter% para los nombres de archivo y los caracteres codificados en hexadecimal no se ven tan bien en la URL. Los navegadores pueden admitir cadenas UTF-8 que son mucho más agradables y fáciles para los idiomas que no son ASCII.
Xeoncross
podría hacer un código urlen y ENTONCES un str_replace ('% 20', '-', url)?
Francesco
2

Ya hay varias soluciones proporcionadas para esta pregunta, pero he leído y probado la mayor parte del código aquí y terminé con esta solución, que es una mezcla de lo que aprendí aquí:

La función

La función se incluye aquí en un paquete de Symfony2 pero se puede extraer para usar como PHP simple , solo tiene una dependencia con la iconvfunción que debe habilitarse:

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

Las pruebas unitarias

Lo interesante es que he creado pruebas PHPUnit, primero para probar casos extremos y para que pueda verificar si se ajusta a sus necesidades: (Si encuentra un error, no dude en agregar un caso de prueba)

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

Los resultados de la prueba: (comprobado en Ubuntu con PHP 5.3.2 y MacOsX con 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
fuente
1
Esto supone principalmente una entrada basada en latín. Agregue más caracteres UTF-8 de otros idiomas para ver dónde tendrá problemas.
Xeoncross
@ Xeoncross Estoy de acuerdo, ya que Christian dijo que uno debe guardar un Id o hash Y el nombre de archivo original. Pero esta función proporciona una alternativa, ya que puede especificar una cadena predeterminada cuando falla el proceso de desinfección. He agregado una prueba unitaria para este caso. Gracias por informar del error.
Bobina
2

Tengo títulos de entrada con todo tipo de caracteres latinos extraños, así como algunas etiquetas HTML que necesitaba traducir a un útil formato de nombre de archivo delimitado por guiones. Combiné la respuesta de @ SoLoGHoST con un par de elementos de la respuesta de @ Xeoncross y lo personalicé un poco.

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

Necesitaba agregar manualmente el carácter del guión em (-) a la matriz de traducción. Puede haber otros, pero hasta ahora mis nombres de archivo se ven bien.

Entonces:

Parte 1: ¿Los "Žurburts" de mi papá? ¡No son los mejores!

se convierte en:

parte-1-mis-papás-zurburts-ellos-no-son-los-mejores

Solo agrego ".html" a la cadena devuelta.

cbmtrx
fuente
1
Todavía faltan algunos caracteres checos y eslovacos:'ľ' => 'l', 'Ľ' => 'L', 'č' => 'c', 'Č' => 'C', 'ť' => 't', 'Ť' => 'T', 'ň' => 'n', 'Ň' => 'N', 'ĺ' => 'l', 'Ĺ' => 'L', 'Ř' => 'R', 'ř' => 'r', 'ě' => 'e', 'Ě' => 'E', 'ů' => 'u', 'Ů' => 'U'
Jasom Dotnet
1
Y sin duda muchos más. Realmente estoy tratando de averiguar si existe un conjunto ISO que incluye combinaciones de caracteres. ¿Cómo se "elige" un conjunto si el contenido exige caracteres de todos ellos? UTF-8, supongo ...
cbmtrx
Descubrí cómo transliterar cualquier cadena usando una línea de PHP : $string = transliterator_transliterate('Any-Latin;Latin-ASCII;', $string);vea mi respuesta a continuación o lea la publicación de blog vinculada.
Jasom Dotnet
1
No, lo ha leído mal: SI puede instalar extensiones PHP en su servidor (o alojamiento) :-) Aquí está la publicación .
Jasom Dotnet
1
Ah, lo tengo. Gracias @JasomDotnet: tengo mi solución actual funcionando por ahora, pero es un conjunto de caracteres limitado, por lo que vale la pena echarle un vistazo a la extensión.
cbmtrx
2

Solución n. ° 1: tiene la capacidad de instalar extensiones PHP en el servidor (hosting)

Para la transcripción de "casi todos los idiomas del planeta Tierra" a caracteres ASCII.

  1. Instale la extensión PHP Intl primero. Este es el comando para Debian (Ubuntu):sudo aptitude install php5-intl

  2. Esta es mi función fileName (crea test.php y pega el siguiente 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 línea es central:

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

Respuesta basada en esta publicación .

Solución # 2: No tiene la capacidad de instalar extensiones PHP en el servidor (hosting)

ingrese la descripción de la imagen aquí

Se realiza un trabajo bastante bueno en el módulo de transliteración para CMS Drupal. Es compatible con casi todos los idiomas del planeta Tierra. Sugiero que revise el repositorio de complementos si desea tener una solución realmente completa para la limpieza de las cadenas.

Jasom Dotnet
fuente
1

Esta es una buena función:

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
fuente
Esto se ve mal. \\s+significa una barra invertida seguida de uno o más espacios en blanco. ¿De qué se trata eso? Además, esto usa listas negras en lugar de listas blancas ignorando cosas como CMD, nulo o BEL.
Xeoncross
Todavia mal. Ahora las cadenas como /blog/2014-02/just-in-timeno están permitidas. Utilice el código probado anteriormente o utilice el phunctioncódigo de marco PHP.
Xeoncross
Así es. Esta función es solo para la parte "justo a tiempo". Podría ser útil para algunas personas.
joan16v
1
Puedes cambiar la expresión regularpreg_replace('~[^\-\pL\pN\s]+~u', '-', $string)
Xeoncross
¡Increíble! También agregué: string = trim ($ string, "-");
joan16v
0

Este es el código utilizado por Prestashop para desinfectar las URL:

replaceAccentedChars

es utilizado por

str2url

para eliminar 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
fuente
-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
fuente