Nettoyer les chaînes pour les rendre sécuritaires pour les URL et les noms de fichiers?

136

J'essaie de trouver une fonction qui nettoie bien certaines chaînes afin qu'elles soient sûres à utiliser dans l'URL (comme un slug de publication) et également sûres à utiliser comme noms de fichiers. Par exemple, lorsque quelqu'un télécharge un fichier, je veux m'assurer que je supprime tous les caractères dangereux du nom.

Jusqu'à présent, j'ai proposé la fonction suivante qui, je l'espère, résout ce problème et autorise également les données UTF-8 étrangères.

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

Quelqu'un a-t-il des exemples de données délicates que je peux utiliser - ou connaît-il un meilleur moyen de protéger nos applications contre les mauvais noms?

$ is-filename autorise certains caractères supplémentaires comme les fichiers vim temporaires

mise à jour: supprimé le caractère étoile car je ne pouvais pas penser à une utilisation valide

Xeoncross
la source
Vous feriez mieux de tout supprimer sauf [\ w.-]
elias
3
Vous trouverez peut-être utile le normalisateur et ses commentaires.
Matt Gibson

Réponses:

57

Quelques observations sur votre solution:

  1. 'u' à la fin de votre motif signifie que le motif , et non le texte il correspond, sera interprété comme UTF-8 (je suppose que vous avez supposé ce dernier?).
  2. \ w correspond au caractère de soulignement. Vous l'incluez spécifiquement pour les fichiers, ce qui laisse supposer que vous ne les voulez pas dans les URL, mais dans le code que vous avez, les URL seront autorisées à inclure un trait de soulignement.
  3. L'inclusion de "UTF-8 étranger" semble dépendre de la localisation. Il n'est pas clair s'il s'agit des paramètres régionaux du serveur ou du client. À partir de la documentation PHP:

Un caractère "mot" est une lettre ou un chiffre ou le caractère de soulignement, c'est-à-dire tout caractère pouvant faire partie d'un "mot" Perl. La définition des lettres et des chiffres est contrôlée par les tables de caractères de PCRE, et peut varier si une correspondance spécifique aux paramètres régionaux a lieu. Par exemple, dans les paramètres régionaux "fr" (français), certains codes de caractères supérieurs à 128 sont utilisés pour les lettres accentuées, et ils sont mis en correspondance avec \ w.

Création de la limace

Vous ne devriez probablement pas inclure de caractères accentués, etc. dans votre slug de publication car, techniquement, ils devraient être codés en pourcentage (selon les règles de codage d'URL) afin que vous ayez des URL qui semblent laides.

Donc, si j'étais vous, après la mise en minuscules, je convertirais tous les caractères «spéciaux» en leur équivalent (par exemple é -> e) et remplacerais les caractères non [az] par «-», en se limitant aux exécutions d'un seul «-» comme vous l'avez fait. Il existe une implémentation de la conversion des caractères spéciaux ici: https://web.archive.org/web/20130208144021/http://neo22s.com/slug

Désinfection en général

OWASP dispose d'une implémentation PHP de son API de sécurité d'entreprise qui comprend entre autres des méthodes d'encodage et de décodage sécurisés des entrées et des sorties dans votre application.

L'interface de l'encodeur fournit:

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
la source
Vous avez raison sur mon hypothèse du modificateur "u" - je pensais que c'était pour le texte. J'ai également oublié le modificateur \ w, y compris le trait de soulignement. Je convertirais normalement tous les caractères accentués en ASCII - mais je veux que cela fonctionne également pour d'autres langues. Je supposais qu'il y aurait une sorte de moyen sûr UTF-8 pour que tout caractère d'une langue puisse être utilisé dans un slug d'URL ou un nom de fichier afin que même les titres arabes fonctionnent. Après tout, linux prend en charge les noms de fichiers UTF-8 et les navigateurs doivent encoder les liens HTML selon les besoins. Un grand merci pour votre contribution ici.
Xeoncross
À la réflexion, vous avez en fait raison, mais ce n'est pas seulement un problème avec le navigateur encodant correctement les liens. Le moyen le plus simple d'atteindre près de ce que vous voulez est de mapper les caractères non ASCII à leur équivalent ASCII le plus proche, puis d'encoder l'URL de votre lien dans le corps HTML. Le plus dur est d'assurer un encodage UTF-8 cohérent (ou UTF-16, je pense pour certains dialectes chinois) à partir de votre magasin de données, via votre serveur Web, la couche d'application (PHP), le contenu de la page, le navigateur Web et non l' URLencode de vos URL ( mais supprimez toujours les caractères «indésirables»). Cela vous donnera de jolis liens et URL non codés.
Alan Donnelly
Bon conseil. Je vais essayer de créer un environnement UTF-8 pur. Ensuite, en prenant plusieurs chaînes de langues non ASCII, je supprimerai les caractères dangereux (./ ;: etc ...) et créerai des fichiers, puis des liens HTML vers ces fichiers pour voir si je peux cliquer dessus et voir si tout cela travaux. Sinon, je devrai probablement revenir à (raw)? Urlencode () pour autoriser UTF-8. Je publierai les résultats ici.
Xeoncross
3
J'ai créé un fichier appelé สังเวช พระปกเกศกองบู๊กู้ขึ้นใหม่.txtpuis créé un fichier HTML UTF-8 avec un lien vers celui-ci. Étonnamment, cela a fonctionné - même sous Windows! Cependant, j'ai alors eu PHP file_put_contents('สังเวช พระปกเกศกองบู๊กู้ขึ้นใหม่.txt')et cela n'a pas réussi à créer un nom de fichier bazar à partir de cette chaîne. Ensuite, j'ai essayé de le créer avec fopen()et j'ai obtenu le même nom de fichier foiré. Donc apparemment, PHP (au moins sous Windows) est incapable de créer des noms de fichiers UTF-8. bugs.php.net/bug.php?id=46990&thanks=6
Xeoncross
1
J'attribue cette réponse parce qu'elle m'a le plus fait réfléchir et qu'elle a également inclus un lien utile vers un projet dont je n'ai jamais entendu parler et qui mérite d'être examiné. Je posterai une fois que je trouverai la réponse.
Xeoncross
87

J'ai trouvé cette fonction plus large dans le code 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;
}

et celui-ci dans le code 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);
}

Mise à jour septembre 2012

Alix Axel a fait un travail incroyable dans ce domaine. Son cadre de fonction comprend plusieurs excellents filtres de texte et transformations.

Xeoncross
la source
23
Le code WordPress n'est pas portable car il utiliseapply_filters
Kevin Mark
1
Notez que la version wordpress remplace /[\s-]+/par -qui est mieux que la première version (qui remplace uniquement /\s+/) qui peut provoquer plusieurs tirets d'affilée
Yotam Omer
Juste pour référence, wordpress apply_filters peut être trouvé ici et sanitize_file_name ici .
Eric
qu'en est-il de plusieurs espaces? Remplacer
Jeffrey the Giraffe
8
La variable $ anal me semble très effrayante avec l'option force.
viljun
30

Cela devrait sécuriser vos noms de fichiers ...

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

et une solution plus profonde à cela est:

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

Cela suppose que vous souhaitiez un point dans le nom de fichier. si vous voulez qu'il soit transféré en minuscules, utilisez simplement

$clean_name = strtolower($clean_name);

pour la dernière ligne.

SoLoGHoST
la source
1
Il manque toujours quelques caractères tchèques et slovaques:'ľ' => 'l', 'Ľ' => 'L', 'č' => 'c', 'Č' => 'C', 'ť' => 't', 'Ť' => 'T', 'ň' => 'n', 'Ň' => 'N', 'ĺ' => 'l', 'Ĺ' => 'L', 'Ř' => 'R', 'ř' => 'r', 'ě' => 'e', 'Ě' => 'E', 'ů' => 'u', 'Ů' => 'U'
Jasom Dotnet
22

Essaye ça:

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

Basé sur la réponse sélectionnée dans ce fil: Nom d'utilisateur URL convivial en PHP?

John Conde
la source
Très bien - je n'ai jamais vu cela faire sans table de traduction (comme les utilisations de wordpress). Cependant, je ne pense pas que cette fonction soit suffisante en l'état car elle ne traduit que les caractères spéciaux mais ne supprime pas les caractères dangereux. Peut-être qu'il peut être ajouté à celui ci-dessus ...
Xeoncross
4
Ha! Ce hack d'encodage d'entité est gentil! Bien qu'il ne soit pas du tout clair à première vue comment cette méthode fait ce qu'elle fait. Il y a cependant un problème. "Frédéric & Éric" ne se transformeront-ils pas en "Frédéric amp Eric"?
Alan Donnelly
@AlanDonnelly: En effet, j'ai mis à jour la fonction dans ma réponse d'origine (vérifiez le lien), cela trim()devrait aussi l'être trim($string, '-').
Alix Axel
@Xeoncross: Le dernier preg_replace()devrait supprimer tous les caractères dangereux.
Alix Axel
@AlixAxel, vous n'êtes pas partout. Je lisais juste le SDK PHP AWS et ils avaient une partie de votre code pour les UUID. Le code génial de la fonction est tout simplement difficile à battre.
Xeoncross
13

Ce n'est pas exactement une réponse car cela ne fournit aucune solution (encore!), Mais c'est trop gros pour tenir sur un commentaire ...


J'ai fait quelques tests (concernant les noms de fichiers) sur Windows 7 et Ubuntu 12.04 et j'ai découvert que:

1. PHP ne peut pas gérer les noms de fichiers non ASCII

Bien que Windows et Ubuntu puissent gérer les noms de fichiers Unicode (même ceux RTL, semble-t-il), PHP 5.3 nécessite des hacks pour gérer même l'ancien ISO-8859-1, il est donc préférable de le garder ASCII uniquement pour la sécurité.

2. La longueur du nom de fichier est importante (spécialement sous Windows)

Sur Ubuntu, la longueur maximale d'un nom de fichier (y compris l'extension de liaison) est de 255 (hors chemin):

/var/www/uploads/123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345/

Cependant, sous Windows 7 (NTFS), la longueur maximale qu'un nom de fichier peut avoir dépend de son chemin absolu:

(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

Wikipédia dit que:

NTFS permet à chaque composant de chemin (répertoire ou nom de fichier) de contenir 255 caractères.

Au meilleur de ma connaissance (et de mes tests), c'est faux.

Au total (en comptant les barres obliques), tous ces exemples ont 259 caractères, si vous supprimez le, C:\cela donne 256 caractères (et non 255?!). Les répertoires ont été créés à l'aide de l'explorateur et vous remarquerez qu'il s'interdit d'utiliser tout l'espace disponible pour le nom du répertoire. La raison en est de permettre la création de fichiers en utilisant la convention de dénomination de fichier 8.3 . La même chose se produit pour les autres partitions.

Les fichiers n'ont pas besoin de réserver les exigences de longueur 8.3 bien sûr:

(255 chars) E:\12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901.txt

Vous ne pouvez plus créer de sous-répertoires si le chemin absolu du répertoire parent contient plus de 242 caractères, car 256 = 242 + 1 + \ + 8 + . + 3. À l'aide de l'Explorateur Windows, vous ne pouvez pas créer un autre répertoire si le répertoire parent contient plus de 233 caractères (selon les paramètres régionaux du système), car 256 = 233 + 10 + \ + 8 + . + 3; le 10voici la longueur de la chaîne New folder.

Le système de fichiers Windows pose un problème désagréable si vous souhaitez assurer l'interopérabilité entre les systèmes de fichiers.

3. Méfiez-vous des caractères et mots-clés réservés

En plus de supprimer les caractères non-ASCII, non imprimables et de contrôle , vous devez également re (placer / déplacer):

"*/:<>?\|

Le simple fait de supprimer ces caractères n'est peut-être pas la meilleure idée car le nom de fichier risque de perdre une partie de sa signification. Je pense que, à tout le moins, plusieurs occurrences de ces caractères devraient être remplacées par un seul trait de soulignement ( _), ou peut-être quelque chose de plus représentatif (ce n'est qu'une idée):

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

Il y a aussi des mots-clés spéciaux qui devraient être évités (comme NUL), même si je ne sais pas comment surmonter cela. Peut-être qu'une liste noire avec un nom de secours aléatoire serait une bonne approche pour le résoudre.

4. Sensibilité aux cas

Cela devrait aller de soi, mais si vous voulez assurer l'unicité des fichiers sur différents systèmes d'exploitation, vous devez transformer les noms de fichiers en cas normalisés, de cette façon my_file.txtet My_File.txtsous Linux ne deviendront pas les deuxmy_file.txt fichier sous Windows.

5. Assurez-vous qu'il est unique

Si le nom de fichier existe déjà, un identifiant unique doit être ajouté à son nom de fichier de base.

Les identificateurs uniques communs incluent l'horodatage UNIX, un résumé du contenu du fichier ou une chaîne aléatoire.

6. Fichiers cachés

Ce n'est pas parce qu'il peut être nommé qu'il devrait ...

Les points sont généralement listés en blanc dans les noms de fichiers, mais sous Linux, un fichier caché est représenté par un point au début.

7. Autres considérations

Si vous devez supprimer certains caractères du nom de fichier, l'extension est généralement plus importante que le nom de base du fichier. En autorisant un nombre maximum considérable de caractères pour l'extension de fichier (8-16), il faut supprimer les caractères du nom de base. Il est également important de noter que dans le cas improbable d'avoir plusieurs extensions longues - telles que _.graphmlz.tag.gz-, _.graphmlz.tagseule _devrait être considérée comme le nom de base du fichier dans ce cas.

8. Ressources

Calibre gère la modification des noms de fichiers assez décemment:

Page Wikipédia sur la modification des noms de fichiers et chapitre lié à l'aide de Samba .


Si, par exemple, vous essayez de créer un fichier qui enfreint l'une des règles 1/2/3, vous obtiendrez une erreur très utile:

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

J'ai toujours pensé que Kohana avait fait du bon travail .

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

Le pratique UTF8::transliterate_to_ascii()transformera des choses comme ñ => n.

Bien sûr, vous pouvez remplacer les autres UTF8::*éléments par des fonctions mb_ *.

Alex
la source
5

En termes de téléchargement de fichiers, il serait plus sûr d'empêcher l'utilisateur de contrôler le nom du fichier. Comme cela a déjà été suggéré, stockez le nom de fichier canonisé dans une base de données avec un nom unique et choisi au hasard que vous utiliserez comme nom de fichier réel.

En utilisant OWASP ESAPI, ces noms pourraient être générés ainsi:

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

Vous pouvez ajouter un horodatage au $ safeFilename pour vous assurer que le nom de fichier généré aléatoirement est unique sans même rechercher un fichier existant.

En termes d'encodage pour URL, et encore en utilisant ESAPI:

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

Cette méthode effectue la canonisation avant d'encoder la chaîne et gérera tous les encodages de caractères.

jah
la source
Certainement - aussi, retirer le contrôle des noms de fichiers aux utilisateurs empêchera la possibilité que 2 téléchargements aient le même nom.
CodeVirtuoso
5

Je recommande * URLify pour PHP (480+ étoiles sur Github) - "le port PHP de URLify.js du projet Django. Translittère les caractères non-ascii pour une utilisation dans les URL".

Utilisation de base:

Pour générer des slugs pour les 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"

?>

Pour générer des slugs pour les noms de fichiers:

<?php

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

?>

* Aucune des autres suggestions ne correspond à mes critères:

  • Doit être installable via composer
  • Ne devrait pas dépendre de iconv car il se comporte différemment selon les systèmes
  • Doit être extensible pour permettre les remplacements et les remplacements de caractères personnalisés
  • Populaire (par exemple, de nombreuses stars sur Github)
  • A des tests

En prime, URLify supprime également certains mots et supprime tous les caractères non translittérés.

Voici un cas de test avec des tonnes de caractères étrangers translittérés correctement à l'aide d'URLify: https://gist.github.com/motin/a65e6c1cc303e46900d10894bf2da87f

Motin
la source
1
Merci - cela semble idéal pour mes besoins.
David Goodwin
5

J'ai adapté d'une autre source et ajouté quelques extra, peut-être un peu exagéré

/**
 * 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
la source
5

et ceci est la version 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
la source
4

Je ne pense pas qu'il soit sûr d'avoir une liste de caractères à supprimer. Je préférerais utiliser ce qui suit:

Pour les noms de fichiers: utilisez un ID interne ou un hachage du contenu du fichier. Enregistrez le nom du document dans une base de données. De cette façon, vous pouvez conserver le nom de fichier d'origine et toujours trouver le fichier.

Pour les paramètres d'url: utilisez urlencode()pour encoder les caractères spéciaux.

ZeissS
la source
1
Je suis d'accord, la plupart des méthodes répertoriées ici suppriment les caractères dangereux connus - ma méthode supprime tout ce qui n'est pas un caractère sûr connu . Étant donné que la plupart des systèmes slug encodent les URL de publication, je suggérerais de continuer à suivre cette méthode éprouvée plutôt que d'utiliser le code urlencode () non sécurisé UTF-8 documenté .
Xeoncross
3

Selon la façon dont vous l'utiliserez, vous souhaiterez peut-être ajouter une limite de longueur pour vous protéger contre les débordements de tampon.

Tgr
la source
Oui, tester mb_strlen () est toujours une chose importante!
Xeoncross
3

C'est un bon moyen de sécuriser un nom de fichier de téléchargement:

$file_name = trim(basename(stripslashes($name)), ".\x00..\x20");
Jeux
la source
Je n'en suis pas si sûr, car on .\x00..\x20peut se réduire à .\x00\x20.
Xeoncross
@Xeoncross: Je pense que cela .\x00..\x20supprime les points et tous les caractères entre \x00et \x20, alors que cela .\x00\x20ne devrait supprimer que ces 3 octets.
Alix Axel
Cette réponse nécessite plus d'explications pour être utilisée en toute sécurité. Pas beaucoup d'informations sur la syntaxe exacte de charlist sur le net.
Manuel Arwed Schmidt
3

Voici l'implémentation 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));
}

Et la remove_invisible_charactersdépendance.

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
la source
2

pourquoi ne pas simplement utiliser php urlencode? il remplace les caractères "dangereux" par leur représentation hexadécimale pour les URL (c'est- %20à- dire pour un espace)

tricot
la source
2
Le caractère% n'est pas recommandé pour les noms de fichiers et les caractères codés en hexadécimal ne sont pas aussi beaux dans l'URL. Les navigateurs peuvent prendre en charge les chaînes UTF-8 qui sont beaucoup plus agréables et plus faciles pour les langues non ascii.
Xeoncross
vous pourriez faire un urlencode et ALORS un str_replace ('% 20', '-', url)?
Francesco
2

Il y a déjà plusieurs solutions fournies pour cette question mais j'ai lu et testé la plupart du code ici et je me suis retrouvé avec cette solution qui est un mélange de ce que j'ai appris ici:

La fonction

La fonction est regroupée ici dans un bundle Symfony2 mais elle peut être extraite pour être utilisée comme PHP simple , elle n'a qu'une dépendance avec la iconvfonction qui doit être activée:

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

Les tests unitaires

Ce qui est intéressant, c'est que j'ai créé des tests PHPUnit, d'abord pour tester les cas de bord et ainsi vous pouvez vérifier si cela correspond à vos besoins: (Si vous trouvez un bug, n'hésitez pas à ajouter un cas de test)

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

Les résultats du test: (vérifié sur Ubuntu avec PHP 5.3.2 et MacOsX avec 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)
Bobine
la source
1
Cela suppose principalement une entrée basée sur le latin. Ajoutez plus de caractères UTF-8 d'autres langues pour voir où vous aurez des problèmes.
Xeoncross
@Xeoncross Je suis d'accord, comme Christian l'a dit, il faut enregistrer un identifiant ou un hachage ET le nom de fichier d'origine. Mais cette fonction offre une alternative car vous pouvez spécifier une chaîne par défaut lorsque le processus de nettoyage échoue. J'ai ajouté un test unitaire pour ce cas. Merci d'avoir signalé le bogue.
COil
2

J'ai des titres d'entrée avec toutes sortes de caractères latins étranges ainsi que des balises HTML que j'avais besoin de traduire dans un format de nom de fichier délimité par des tirets utile. J'ai combiné la réponse de @ SoLoGHoST avec quelques éléments de la réponse de @ Xeoncross et j'ai personnalisé un peu.

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

J'avais besoin d'ajouter manuellement le caractère tiret em (-) au tableau de traduction. Il peut y en avoir d'autres mais jusqu'à présent, mes noms de fichiers semblent bons.

Alors:

Partie 1: Les «Žurburts» de mon père? - Ce ne sont (pas) les meilleurs!

devient:

partie-1-mes-papas-zurburts-theyre-pas-le-meilleur

J'ajoute simplement ".html" à la chaîne renvoyée.

cbmtrx
la source
1
Il manque toujours quelques caractères tchèques et slovaques:'ľ' => 'l', 'Ľ' => 'L', 'č' => 'c', 'Č' => 'C', 'ť' => 't', 'Ť' => 'T', 'ň' => 'n', 'Ň' => 'N', 'ĺ' => 'l', 'Ĺ' => 'L', 'Ř' => 'R', 'ř' => 'r', 'ě' => 'e', 'Ě' => 'E', 'ů' => 'u', 'Ů' => 'U'
Jasom Dotnet
1
Et sans doute bien d'autres. J'essaie en fait de savoir s'il existe un ensemble ISO qui comprend des combinaisons de caractères. Comment «choisir» un ensemble si le contenu exige des caractères de tous? UTF-8 I'm
assuming
J'ai découvert comment translittérer n'importe quelle chaîne en utilisant une ligne de PHP : $string = transliterator_transliterate('Any-Latin;Latin-ASCII;', $string);voir ma réponse ci-dessous ou lire l'article de blog lié.
Jasom Dotnet
1
Non, vous avez mal lu: SI vous pouvez installer des extensions PHP sur votre serveur (ou hébergement) :-) Voici le post .
Jasom Dotnet
1
Ah, compris. Merci @JasomDotnet - Ma solution actuelle fonctionne pour le moment, mais c'est un jeu de caractères limité, l'extension vaut donc la peine d'être vérifiée.
cbmtrx
2

Solution n ° 1: Vous avez la possibilité d'installer des extensions PHP sur le serveur (hébergement)

Pour la translittération de «presque toutes les langues de la planète Terre» en caractères ASCII.

  1. Installez d' abord l'extension PHP Intl . Voici la commande pour Debian (Ubuntu):sudo aptitude install php5-intl

  2. Voici ma fonction fileName (créez test.php et collez-y le code suivant):

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

Cette ligne est essentielle:

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

Réponse basée sur ce post .

Solution n ° 2: Vous ne pouvez pas installer d'extensions PHP sur le serveur (hébergement)

entrez la description de l'image ici

Un bon travail est fait dans le module de translittération pour CMS Drupal. Il prend en charge presque toutes les langues de la planète Terre. Je suggère de vérifier le référentiel de plugins si vous voulez avoir des chaînes de nettoyage de solution vraiment complètes.

Jasom Dotnet
la source
1

C'est une bonne fonction:

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
la source
Cela semble mauvais. \\s+signifie une barre oblique inverse suivie d'un ou plusieurs espaces. De quoi s'agit-il? En outre, cela utilise la liste noire plutôt que la liste blanche en ignorant des éléments tels que CMD, null ou BEL.
Xeoncross
Toujours mauvais. Désormais, les chaînes comme /blog/2014-02/just-in-timene sont pas autorisées. Veuillez utiliser le code testé ci-dessus ou utiliser le phunctioncode du framework PHP.
Xeoncross
C'est vrai. Cette fonction est uniquement pour la partie "juste à temps". Cela pourrait être utile pour certaines personnes.
joan16v
1
Vous pouvez modifier l'expression régulièrepreg_replace('~[^\-\pL\pN\s]+~u', '-', $string)
Xeoncross
Impressionnant! J'ai ajouté aussi: string = trim ($ string, "-");
joan16v
0

Voici le code utilisé par Prestashop pour nettoyer les URL:

replaceAccentedChars

est utilisé par

str2url

pour supprimer les signes diacritiques

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
la source
-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);

} 
Messager de Brant
la source