désinfectant de chaîne pour le nom de fichier

113

Je recherche une fonction php qui nettoiera une chaîne et la rendra prête à être utilisée pour un nom de fichier. Quelqu'un en connait un pratique?

(Je pourrais en écrire un, mais j'ai peur d'oublier un personnage!)

Edit: pour enregistrer des fichiers sur un système de fichiers Windows NTFS.

user151841
la source
1
Pouvez-vous être plus précis: que se passe-t-il avec les trémas (supprimer ou convertir en caractère de base?) Que se passe-t-il avec les caractères spéciaux?
Pekka du
Pour quel système de fichiers? Ils diffèrent. Voir en.wikipedia.org/wiki/…
Gordon
Windows :) Besoin de 15 caractères.
user151841
1
Je tiens à souligner que les solutions de "liste noire" suggérées dans certaines des réponses ne sont pas suffisantes, car il est impossible de vérifier tous les caractères indésirables possibles (en plus des caractères spéciaux, il y a des caractères avec des accents et des trémas, entiers alphabets non anglais / latins, caractères de contrôle, etc. à traiter). Je dirais donc qu'une approche de «liste blanche» est toujours meilleure et que la normalisation de la chaîne (comme suggéré par le commentaire de Blair McMillan sur la réponse de Dominic Rodger) permettra une gestion naturelle de toutes les lettres avec des accents, des trémas, etc.
Sean the Bean
Un bon moyen peut-être d'utiliser des expressions régulières, voir ce script python que j'ai créé: github.com/gsscoder/normalize-fn
gsscoder

Réponses:

42

Au lieu de vous soucier d'oublier les caractères, que diriez-vous d'utiliser une liste blanche de caractères que vous êtes heureux d'utiliser? Par exemple, vous pouvez permettre juste bon vieux a-z, 0-9, _et une seule instance d'une période ( .). C'est évidemment plus limitant que la plupart des systèmes de fichiers, mais cela devrait vous protéger.

Dominic Rodger
la source
40
Pas bon pour les langues avec des trémas. Cela se traduirait par Qubec pour Québec, Dsseldorf pour Düsseldorf, etc.
Pekka
15
Vrai - mais comme je l'ai dit: "Par exemple".
Dominic Rodger
5
Ce qui peut être parfaitement acceptable pour le PO. Sinon, utilisez quelque chose comme php.net/manual/en/class.normalizer.php
Blair McMillan
3
Ce n'est en fait pas ce qui a été demandé. L'op demande une fonction pour nettoyer la chaîne, pas une alternative.
i.am.michiel
3
@ i.am.michiel, peut-être, mais étant donné que l'OP l'a accepté, je suppose qu'il l'a trouvé utile.
Dominic Rodger
157

En apportant un petit ajustement à la solution de Tor Valamo pour résoudre le problème remarqué par Dominic Rodger, vous pouvez utiliser:

// Remove anything which isn't a word, whitespace, number
// or any of the following caracters -_~,;[]().
// If you don't need to handle multi-byte characters
// you can use preg_replace rather than mb_ereg_replace
// Thanks @Łukasz Rysiak!
$file = mb_ereg_replace("([^\w\s\d\-_~,;\[\]\(\).])", '', $file);
// Remove any runs of periods (thanks falstro!)
$file = mb_ereg_replace("([\.]{2,})", '', $file);
Sean Vieira
la source
43
J'adore les accros de regex! -_ ~
AVProgrammer
2
@ iim.hlk - oui, il manquait la parenthèse d'habillage. J'ai ajouté ceux-ci maintenant. Merci!
Sean Vieira
2
il y a un défaut là-dedans, vous devriez le diviser en deux et exécuter le chèque pour ..après. Par exemple .?.finirait par l'être ... Bien que depuis que vous filtrez, /je ne vois pas comment vous pourriez exploiter cela davantage pour le moment, mais cela montre pourquoi la vérification ..est inefficace ici. Mieux encore probablement, ne remplacez pas, rejetez simplement s'il n'est pas admissible.
falstro
2
Parce qu'aucune de ces valeurs n'est illégale sur le système de fichiers Windows et pourquoi perdre plus d'informations que nécessaire? Vous pouvez changer l'expression régulière en simplement [^a-z0-9_-]si vous voulez être vraiment restrictif - ou simplement utiliser un nom généré et jeter le nom donné et éviter tous ces problèmes. :-)
Sean Vieira
3
Notez que: est illégal.
JasonXA
50

Voici comment vous pouvez nettoyer un système de fichiers comme demandé

function filter_filename($name) {
    // remove illegal file system characters https://en.wikipedia.org/wiki/Filename#Reserved_characters_and_words
    $name = str_replace(array_merge(
        array_map('chr', range(0, 31)),
        array('<', '>', ':', '"', '/', '\\', '|', '?', '*')
    ), '', $name);
    // maximise filename length to 255 bytes http://serverfault.com/a/9548/44086
    $ext = pathinfo($name, PATHINFO_EXTENSION);
    $name= mb_strcut(pathinfo($name, PATHINFO_FILENAME), 0, 255 - ($ext ? strlen($ext) + 1 : 0), mb_detect_encoding($name)) . ($ext ? '.' . $ext : '');
    return $name;
}

Tout le reste est autorisé dans un système de fichiers, donc la question est parfaitement répondue ...

... mais il pourrait être dangereux d'autoriser, par exemple, des guillemets simples 'dans un nom de fichier si vous l'utilisez plus tard dans un contexte HTML dangereux car ce nom de fichier absolument légal:

 ' onerror= 'alert(document.cookie).jpg

devient un trou XSS :

<img src='<? echo $image ?>' />
// output:
<img src=' ' onerror= 'alert(document.cookie)' />

Pour cette raison, le logiciel CMS populaire Wordpress les supprime, mais ils ne couvrent tous les caractères pertinents qu'après quelques mises à jour :

$special_chars = array("?", "[", "]", "/", "\\", "=", "<", ">", ":", ";", ",", "'", "\"", "&", "$", "#", "*", "(", ")", "|", "~", "`", "!", "{", "}", "%", "+", chr(0));
// ... a few rows later are whitespaces removed as well ...
preg_replace( '/[\r\n\t -]+/', '-', $filename )

Enfin leur liste inclut désormais la plupart des personnages qui font partie de l' URI rerserved-caractères et URL caractères dangereux liste.

Bien sûr, vous pouvez simplement encoder tous ces caractères sur la sortie HTML, mais la plupart des développeurs et moi aussi, suivons l'idiome "Mieux vaut prévenir que guérir" et les supprimer à l'avance.

Alors enfin, je suggérerais d'utiliser ceci:

function filter_filename($filename, $beautify=true) {
    // sanitize filename
    $filename = preg_replace(
        '~
        [<>:"/\\|?*]|            # file system reserved https://en.wikipedia.org/wiki/Filename#Reserved_characters_and_words
        [\x00-\x1F]|             # control characters http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx
        [\x7F\xA0\xAD]|          # non-printing characters DEL, NO-BREAK SPACE, SOFT HYPHEN
        [#\[\]@!$&\'()+,;=]|     # URI reserved https://tools.ietf.org/html/rfc3986#section-2.2
        [{}^\~`]                 # URL unsafe characters https://www.ietf.org/rfc/rfc1738.txt
        ~x',
        '-', $filename);
    // avoids ".", ".." or ".hiddenFiles"
    $filename = ltrim($filename, '.-');
    // optional beautification
    if ($beautify) $filename = beautify_filename($filename);
    // maximize filename length to 255 bytes http://serverfault.com/a/9548/44086
    $ext = pathinfo($filename, PATHINFO_EXTENSION);
    $filename = mb_strcut(pathinfo($filename, PATHINFO_FILENAME), 0, 255 - ($ext ? strlen($ext) + 1 : 0), mb_detect_encoding($filename)) . ($ext ? '.' . $ext : '');
    return $filename;
}

Tout le reste qui ne pose pas de problème avec le système de fichiers doit faire partie d'une fonction supplémentaire:

function beautify_filename($filename) {
    // reduce consecutive characters
    $filename = preg_replace(array(
        // "file   name.zip" becomes "file-name.zip"
        '/ +/',
        // "file___name.zip" becomes "file-name.zip"
        '/_+/',
        // "file---name.zip" becomes "file-name.zip"
        '/-+/'
    ), '-', $filename);
    $filename = preg_replace(array(
        // "file--.--.-.--name.zip" becomes "file.name.zip"
        '/-*\.-*/',
        // "file...name..zip" becomes "file.name.zip"
        '/\.{2,}/'
    ), '.', $filename);
    // lowercase for windows/unix interoperability http://support.microsoft.com/kb/100625
    $filename = mb_strtolower($filename, mb_detect_encoding($filename));
    // ".file-name.-" becomes "file-name"
    $filename = trim($filename, '.-');
    return $filename;
}

Et à ce stade, vous devez générer un nom de fichier si le résultat est vide et vous pouvez décider si vous souhaitez encoder des caractères UTF-8. Mais vous n'en avez pas besoin car UTF-8 est autorisé dans tous les systèmes de fichiers utilisés dans les contextes d'hébergement Web.

La seule chose que vous devez faire est d'utiliser urlencode()(comme vous le faites avec toutes vos URL) afin que le nom de fichier საბეჭდი_მანქანა.jpgdevienne cette URL en tant que votre <img src>ou <a href>: http://www.maxrev.de/html/img/%E1%83% A1% E1% 83% 90% E1% 83% 91% E1% 83% 94% E1% 83% AD% E1% 83% 93% E1% 83% 98_% E1% 83% 9B% E1% 83% 90% E1% 83% 9C% E1% 83% A5% E1% 83% 90% E1% 83% 9C% E1% 83% 90.jpg

Stackoverflow fait cela, donc je peux publier ce lien comme le ferait un utilisateur:
http://www.maxrev.de/html/img/ საბეჭდი_მანქანა. Jpg

C'est donc un nom de fichier légal complet et pas un problème comme @ SequenceDigitale.com l'a mentionné dans sa réponse .

mgutt
la source
3
Bon travail. La réponse la plus utile pour moi. +1
Oh ... La fonction fonctionne bien, mais depuis un certain temps, elle a commencé à mettre - entre chaque personnage, comme r-u-l-e-set je n'ai aucune idée pourquoi cela se produit. Bien sûr, ce n'est pas la faute de la fonction, mais simplement demander - quelle pourrait être la raison d'un tel comportement? Mauvais encodage?
1
Eh bien ... Je viens de faire un débogage et cela se produit juste après l' preg_replaceentrée filter_filename().
Après avoir supprimé ces commentaires, il a recommencé à fonctionner.
Quels commentaires avez-vous supprimés? Envoyez-moi un e-mail si cela est plus simple: gutt.it/contact.htm
mgutt
43

Qu'en est-il de l'utilisation de rawurlencode ()? http://www.php.net/manual/en/function.rawurlencode.php

Voici une fonction qui désinfecte même les caractères chinois:

public static function normalizeString ($str = '')
{
    $str = strip_tags($str); 
    $str = preg_replace('/[\r\n\t ]+/', ' ', $str);
    $str = preg_replace('/[\"\*\/\:\<\>\?\'\|]+/', ' ', $str);
    $str = strtolower($str);
    $str = html_entity_decode( $str, ENT_QUOTES, "utf-8" );
    $str = htmlentities($str, ENT_QUOTES, "utf-8");
    $str = preg_replace("/(&)([a-z])([a-z]+;)/i", '$2', $str);
    $str = str_replace(' ', '-', $str);
    $str = rawurlencode($str);
    $str = str_replace('%', '-', $str);
    return $str;
}

Voici l'explication

  1. Supprimer les balises HTML
  2. Retirer la pause / les onglets / le chariot de retour
  3. Supprimer les caractères illégaux pour le dossier et le nom de fichier
  4. Mettez la chaîne en minuscules
  5. Supprimez les accents étrangers tels que Éàû en le convertissant en entités html, puis supprimez le code et conservez la lettre.
  6. Remplacer les espaces par des tirets
  7. Encodez les caractères spéciaux qui pourraient passer les étapes précédentes et entrez le nom de fichier de conflit sur le serveur. ex. "中文 百强 网"
  8. Remplacez "%" par des tirets pour vous assurer que le lien du fichier ne sera pas réécrit par le navigateur lors de l'interrogation du fichier.

OK, certains noms de fichiers ne seront pas pertinents mais dans la plupart des cas, cela fonctionnera.

ex. Nom d'origine: "საბეჭდი-და-ტიპოგრაფიული. Jpg"

Nom de la sortie: "-E1-83-A1-E1-83-90-E1-83-91-E1-83-94-E1-83-AD-E1-83-93-E1-83-98 - E1- 83-93-E1-83-90 - E1-83-A2-E1-83-98-E1-83-9E-E1-83-9D-E1-83-92-E1-83-A0-E1-83 -90-E1-83-A4-E1-83-98-E1-83-A3-E1-83-9A-E1-83-98.jpg "

C'est mieux comme ça qu'une erreur 404.

J'espère que cela a été utile.

Carl.

SequenceDigitale.com
la source
1
Vous ne supprimez pas les caractères NULL et Control. Les ASCII de 0 à 32 doivent tous être supprimés de la chaîne.
Basil Musa du
UTF-8 est autorisé dans le système de fichiers et il est autorisé dans les URL, alors pourquoi devrait-il produire une erreur 404? La seule chose que vous devez faire est de coder l'URL http://www.maxrev.de/html/img/საბეჭდი_მანქანა.jpgà http://www.maxrev.de/html/img/%E1%83%A1%E1%83%90%E1%83%91%E1%83%94%E1%83%AD%E1%83%93%E1%83%98_%E1%83%9B%E1%83%90%E1%83%9C%E1%83%A5%E1%83%90%E1%83%9C%E1%83%90.jpgdans le code source HTML comme vous le faites si possible avec toutes vos URL.
mgutt
1
Quelques autres points: vous supprimez les balises HTML jusqu'à ce strip_tags()que vous supprimiez [<>]. Par cela strip_tags()n'est pas vraiment nécessaire du tout. Le même point sont les citations. Il ne reste plus de guillemets lorsque vous décodez avec ENT_QUOTES. Et le str_replace()ne supprime pas les espaces blancs consécutifs, puis vous utilisez strtolower()pour une chaîne multi-octets. Et pourquoi vous convertissez-vous en minuscules? Et finalement, vous n'avez attrapé aucun personnage réservé comme l'a mentionné @BasilMusa. Plus de détails dans ma réponse: stackoverflow.com/a/42058764/318765
mgutt
en est tombé amoureux!
Yash Kumar Verma
39

SOLUTION 1 - simple et efficace

$file_name = preg_replace( '/[^a-z0-9]+/', '-', strtolower( $url ) );

  • strtolower () garantit que le nom de fichier est en minuscules (car la casse n'a pas d'importance dans l'URL, mais dans le nom de fichier NTFS)
  • [^a-z0-9]+ assurera, le nom de fichier ne garde que les lettres et les chiffres
  • Remplacer les caractères invalides par '-'garde le nom de fichier lisible

Exemple:

URL:  http://stackoverflow.com/questions/2021624/string-sanitizer-for-filename
File: http-stackoverflow-com-questions-2021624-string-sanitizer-for-filename

SOLUTION 2 - pour les URL très longues

Vous voulez mettre en cache le contenu de l'URL et avez juste besoin d'avoir des noms de fichiers uniques. J'utiliserais cette fonction:

$file_name = md5( strtolower( $url ) )

cela créera un nom de fichier de longueur fixe. Le hachage MD5 est dans la plupart des cas assez unique pour ce type d'utilisation.

Exemple:

URL:  https://www.amazon.com/Interstellar-Matthew-McConaughey/dp/B00TU9UFTS/ref=s9_nwrsa_gw_g318_i10_r?_encoding=UTF8&fpl=fresh&pf_rd_m=ATVPDKIKX0DER&pf_rd_s=desktop-1&pf_rd_r=BS5M1H560SMAR2JDKYX3&pf_rd_r=BS5M1H560SMAR2JDKYX3&pf_rd_t=36701&pf_rd_p=6822bacc-d4f0-466d-83a8-2c5e1d703f8e&pf_rd_p=6822bacc-d4f0-466d-83a8-2c5e1d703f8e&pf_rd_i=desktop
File: 51301f3edb513f6543779c3a5433b01c
Philipp
la source
4
Peut-être que MD5 pourrait par un problème: soyez prudent lorsque vous utilisez des hachages avec des URL. Alors que la racine carrée du nombre skrenta.com/2007/08/md5_tutorial.html d'URL est encore beaucoup plus grande que la taille actuelle du Web si vous obtenez une collision, vous obtiendrez des pages sur Britney Spears lorsque vous attendiez des pages à propos de Bugzilla. Ce n'est probablement pas un problème dans notre cas, mais pour des milliards de pages, j'opterais pour un algorithme de hachage beaucoup plus grand tel que SHA 256 ou je l'éviterais complètement. Source: boyter.org/2013/01/code-for-a-search-engine-in-php-part-1
adilbo
15

Eh bien, tempnam () le fera pour vous.

http://us2.php.net/manual/en/function.tempnam.php

mais cela crée un nom entièrement nouveau.

Pour nettoyer une chaîne existante, limitez simplement ce que vos utilisateurs peuvent entrer et faites-en des lettres, des chiffres, un point, un trait d'union et un trait de soulignement, puis nettoyez avec une simple regex. Vérifiez quels caractères doivent être échappés ou vous pourriez obtenir de faux positifs.

$sanitized = preg_replace('/[^a-zA-Z0-9\-\._]/','', $filename);
Mark Moline
la source
13
preg_replace("[^\w\s\d\.\-_~,;:\[\]\(\]]", '', $file)

Ajoutez / supprimez plus de caractères valides en fonction de ce qui est autorisé pour votre système.

Vous pouvez également essayer de créer le fichier, puis renvoyer une erreur si elle est incorrecte.

Tor Valamo
la source
5
Cela permettrait de passer des noms de fichiers comme .., ce qui peut ou non être un problème.
Dominic Rodger
@Dom - vérifiez simplement cela séparément, car c'est une valeur fixe.
Tor Valamo
10

PHP fournit une fonction pour nettoyer un texte dans un format différent

filter.filters.sanitize

Comment :

echo filter_var(
   "Lorem Ipsum has been the industry's",FILTER_SANITIZE_URL
); 

Blockquote LoremIpsumhasbeentheindustry's

120DEV
la source
1
Bien, mais cela ne supprimerait pas les barres obliques, ce qui pourrait être un problème: Parcours de répertoires.
func0der
7

sûr: remplacez chaque séquence de NOT "a-zA-Z0-9_-" par un tiret; ajoutez vous-même une extension.

$name = preg_replace('/[^a-zA-Z0-9_-]+/', '-', strtolower($name)).'.'.$extension;
grand brochet
la source
1
Vous devez ajouter l'extension de fichier séparée par un ".": $ Name = preg_replace ('/ [^ a-zA-Z0-9 _-] + /', '-', strtolower ($ name)). '.' . $ extension;
Smith
6

L'expression suivante crée une chaîne agréable, propre et utilisable:

/[^a-z0-9\._-]+/gi

Transformer les finances d'aujourd'hui: la facturation en facturation financière d' aujourd'hui

Sampson
la source
donc un nom de fichier ne peut pas avoir un point ou un trait de soulignement, ou quelque chose comme ça?
Tor Valamo
2
@Jonathan - c'est quoi les italiques?
Dominic Rodger
@Tor, oui, désolé. Actualisé. @Dominic, mettant juste l'accent sur le texte.
Sampson
Qu'est-ce que le gism? J'obtiens "Attention: preg_replace () [function.preg-replace]: Modificateur inconnu 'g'"
user151841
1
@ user151841 Pour preg_replacel'indicateur global est implicite. Il n'y a donc pas besoin de g si preg_replace est utilisé. Lorsque nous voulons contrôler le nombre de remplacements, preg_replace a un limitparamètre pour cela. Lisez la documentation preg_replace pour en savoir plus.
rineez
6

En ajustant légèrement la solution de Sean Vieira pour permettre des points uniques, vous pouvez utiliser:

preg_replace("([^\w\s\d\.\-_~,;:\[\]\(\)]|[\.]{2,})", '', $file)
Carl Johnson
la source
2

Celles-ci peuvent être un peu lourdes, mais elles sont suffisamment flexibles pour désinfecter n'importe quelle chaîne en un "coffre-fort" en nom de fichier ou de dossier de style (ou diable, même des slugs nettoyés et autres si vous le pliez).

1) Construire un nom de fichier complet (avec un nom de secours si l'entrée est totalement tronquée):

str_file($raw_string, $word_separator, $file_extension, $fallback_name, $length);

2) Ou en utilisant simplement l'utilitaire de filtrage sans construire un nom de fichier complet (le mode strict truen'autorisera pas [] ou () dans le nom de fichier):

str_file_filter($string, $separator, $strict, $length);

3) Et voici ces fonctions:

// Returns filesystem-safe string after cleaning, filtering, and trimming input
function str_file_filter(
    $str,
    $sep = '_',
    $strict = false,
    $trim = 248) {

    $str = strip_tags(htmlspecialchars_decode(strtolower($str))); // lowercase -> decode -> strip tags
    $str = str_replace("%20", ' ', $str); // convert rogue %20s into spaces
    $str = preg_replace("/%[a-z0-9]{1,2}/i", '', $str); // remove hexy things
    $str = str_replace("&nbsp;", ' ', $str); // convert all nbsp into space
    $str = preg_replace("/&#?[a-z0-9]{2,8};/i", '', $str); // remove the other non-tag things
    $str = preg_replace("/\s+/", $sep, $str); // filter multiple spaces
    $str = preg_replace("/\.+/", '.', $str); // filter multiple periods
    $str = preg_replace("/^\.+/", '', $str); // trim leading period

    if ($strict) {
        $str = preg_replace("/([^\w\d\\" . $sep . ".])/", '', $str); // only allow words and digits
    } else {
        $str = preg_replace("/([^\w\d\\" . $sep . "\[\]\(\).])/", '', $str); // allow words, digits, [], and ()
    }

    $str = preg_replace("/\\" . $sep . "+/", $sep, $str); // filter multiple separators
    $str = substr($str, 0, $trim); // trim filename to desired length, note 255 char limit on windows

    return $str;
}


// Returns full file name including fallback and extension
function str_file(
    $str,
    $sep = '_',
    $ext = '',
    $default = '',
    $trim = 248) {

    // Run $str and/or $ext through filters to clean up strings
    $str = str_file_filter($str, $sep);
    $ext = '.' . str_file_filter($ext, '', true);

    // Default file name in case all chars are trimmed from $str, then ensure there is an id at tail
    if (empty($str) && empty($default)) {
        $str = 'no_name__' . date('Y-m-d_H-m_A') . '__' . uniqid();
    } elseif (empty($str)) {
        $str = $default;
    }

    // Return completed string
    if (!empty($ext)) {
        return $str . $ext;
    } else {
        return $str;
    }
}

Alors disons que certaines entrées utilisateur sont: .....&lt;div&gt;&lt;/div&gt;<script></script>&amp; Weiß Göbel 中文百强网File name %20 %20 %21 %2C Décor \/. /. . z \... y \...... x ./ “This name” is & 462^^ not &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = that grrrreat -][09]()1234747) საბეჭდი-და-ტიპოგრაფიული

Et nous voulons le convertir en quelque chose de plus convivial pour créer un tar.gz avec une longueur de nom de fichier de 255 caractères. Voici un exemple d'utilisation. Remarque: cet exemple inclut une extension tar.gz malformée comme preuve de concept, vous devez toujours filtrer l'ext après que la chaîne soit construite par rapport à votre (vos) liste (s) blanche (s).

$raw_str = '.....&lt;div&gt;&lt;/div&gt;<script></script>&amp; Weiß Göbel 中文百强网File name  %20   %20 %21 %2C Décor  \/.  /. .  z \... y \...... x ./  “This name” is & 462^^ not &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = that grrrreat -][09]()1234747) საბეჭდი-და-ტიპოგრაფიული';
$fallback_str = 'generated_' . date('Y-m-d_H-m_A');
$bad_extension = '....t&+++a()r.gz[]';

echo str_file($raw_str, '_', $bad_extension, $fallback_str);

Le résultat serait: _wei_gbel_file_name_dcor_._._._z_._y_._x_._this_name_is_462_not_that_grrrreat_][09]()1234747)_.tar.gz

Vous pouvez jouer avec ici: https://3v4l.org/iSgi8

Ou un Gist: https://gist.github.com/dhaupin/b109d3a8464239b7754a

EDIT: filtre de script mis à jour pour la &nbsp;place de l'espace, lien 3v4l mis à jour

Dhaupin
la source
1

Le meilleur que je connaisse aujourd'hui est la méthode statique Strings :: webalize du framework Nette.

BTW, cela traduit tous les signes diacritiques à leur base .. š => s ü => u ß => ss etc.

Pour les noms de fichiers, vous devez ajouter le point "." au paramètre de caractères autorisés.

/**
 * Converts to ASCII.
 * @param  string  UTF-8 encoding
 * @return string  ASCII
 */
public static function toAscii($s)
{
    static $transliterator = NULL;
    if ($transliterator === NULL && class_exists('Transliterator', FALSE)) {
        $transliterator = \Transliterator::create('Any-Latin; Latin-ASCII');
    }

    $s = preg_replace('#[^\x09\x0A\x0D\x20-\x7E\xA0-\x{2FF}\x{370}-\x{10FFFF}]#u', '', $s);
    $s = strtr($s, '`\'"^~?', "\x01\x02\x03\x04\x05\x06");
    $s = str_replace(
        array("\xE2\x80\x9E", "\xE2\x80\x9C", "\xE2\x80\x9D", "\xE2\x80\x9A", "\xE2\x80\x98", "\xE2\x80\x99", "\xC2\xB0"),
        array("\x03", "\x03", "\x03", "\x02", "\x02", "\x02", "\x04"), $s
    );
    if ($transliterator !== NULL) {
        $s = $transliterator->transliterate($s);
    }
    if (ICONV_IMPL === 'glibc') {
        $s = str_replace(
            array("\xC2\xBB", "\xC2\xAB", "\xE2\x80\xA6", "\xE2\x84\xA2", "\xC2\xA9", "\xC2\xAE"),
            array('>>', '<<', '...', 'TM', '(c)', '(R)'), $s
        );
        $s = @iconv('UTF-8', 'WINDOWS-1250//TRANSLIT//IGNORE', $s); // intentionally @
        $s = strtr($s, "\xa5\xa3\xbc\x8c\xa7\x8a\xaa\x8d\x8f\x8e\xaf\xb9\xb3\xbe\x9c\x9a\xba\x9d\x9f\x9e"
            . "\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3"
            . "\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8"
            . "\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf8\xf9\xfa\xfb\xfc\xfd\xfe"
            . "\x96\xa0\x8b\x97\x9b\xa6\xad\xb7",
            'ALLSSSSTZZZallssstzzzRAAAALCCCEEEEIIDDNNOOOOxRUUUUYTsraaaalccceeeeiiddnnooooruuuuyt- <->|-.');
        $s = preg_replace('#[^\x00-\x7F]++#', '', $s);
    } else {
        $s = @iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $s); // intentionally @
    }
    $s = str_replace(array('`', "'", '"', '^', '~', '?'), '', $s);
    return strtr($s, "\x01\x02\x03\x04\x05\x06", '`\'"^~?');
}


/**
 * Converts to web safe characters [a-z0-9-] text.
 * @param  string  UTF-8 encoding
 * @param  string  allowed characters
 * @param  bool
 * @return string
 */
public static function webalize($s, $charlist = NULL, $lower = TRUE)
{
    $s = self::toAscii($s);
    if ($lower) {
        $s = strtolower($s);
    }
    $s = preg_replace('#[^a-z0-9' . preg_quote($charlist, '#') . ']+#i', '-', $s);
    $s = trim($s, '-');
    return $s;
}
DnD
la source
Pourquoi voulez-vous remplacer les signes diacritiques? Utilisez simplement urlencode()avant d'utiliser le nom de fichier en tant que srcou href. Le seul système de fichiers actuellement utilisé qui a des problèmes avec UTF-8 est FATx (utilisé par XBOX): en.wikipedia.org/wiki/Comparison_of_file_systems#Limits Et je ne pense pas que cela soit utilisé par les serveurs Web
mgutt
1

Il semble que tout dépend de la question, est-il possible de créer un nom de fichier qui peut être utilisé pour pirater un serveur (ou faire d'autres dommages). Sinon, il semble que la réponse la plus simple soit d'essayer de créer le fichier là où il sera finalement utilisé (puisque ce sera le système d'exploitation de choix, sans aucun doute). Laissez le système d'exploitation régler le problème. S'il se plaint, renvoyez cette plainte à l'utilisateur en tant qu'erreur de validation.

Cela a l'avantage supplémentaire d'être portable de manière fiable, car tous les systèmes d'exploitation (j'en suis presque sûr) se plaindront si le nom de fichier n'est pas correctement formé pour ce système d'exploitation.

S'il est possible de faire des choses néfastes avec un nom de fichier, peut-être y a-t-il des mesures qui peuvent être appliquées avant de tester le nom de fichier sur le système d'exploitation résident - des mesures moins compliquées qu'un "nettoyage" complet du nom de fichier.

ReverseEMF
la source
0

une manière

$bad='/[\/:*?"<>|]/';
$string = 'fi?le*';

function sanitize($str,$pat)
{
    return preg_replace($pat,"",$str);

}
echo sanitize($string,$bad);
ghostdog74
la source
Qu'en est-il des caractères non imprimables? Il vaut mieux utiliser l'approche de la liste blanche que l'approche de la liste noire dans ce cas. Fondamentalement, n'autorisez que les noms de fichiers ASCII imprimables à l'exclusion des lettres spéciales bien sûr. Mais pour les paramètres régionaux non anglais, c'est un autre problème.
TheRealChx101
0

/et ..dans le nom de fichier fourni par l'utilisateur peut être nuisible. Vous devriez donc vous en débarrasser par quelque chose comme:

$fname = str_replace('..', '', $fname);
$fname = str_replace('/',  '', $fname);
jeu terminé
la source
C'est insuffisant! Par exemple, le nom de fichier "./.name" sortira toujours du répertoire courant. (La suppression de .. ne fait rien ici, mais la suppression de / transformera le ./. En .. et donc sortira du répertoire cible.)
cemper93
3
@ cemper93 Non, cette réponse transformera simplement la chaîne dans ..namelaquelle ne se briserait de rien. La suppression de tous les caractères de séparation de chemin devrait être suffisante pour empêcher toute traversée de répertoire. (La suppression de ..est techniquement inutile.)
cdhowie
@cdhowie Oui, mais le nom de fichier ./.devient ... Et finalement, cette réponse manque tous les autres caractères réservés au système de fichiers comme NULL. Plus dans ma réponse: stackoverflow.com/a/42058764/318765
mgutt
-4

$ fnom = str_replace ('/', '', $ fnom);

Étant donné que les utilisateurs peuvent utiliser la barre oblique pour séparer deux mots, il serait préférable de le remplacer par un tiret au lieu de NULL

user2246924
la source
Où est-il dit qu'il remplacerait par NULL? En outre, cela ne gère pas tous les caractères spéciaux.
Travis Pessetto
Oui, il y a d'autres caractères spéciaux qui doivent également être manipulés. str_replace ne sera pas la meilleure offre ici de toute façon.
Martin Kovachev