IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage PHP Discussion :

attention avec ucfirst et les noms de fichiers ou la comparaison de casse


Sujet :

Langage PHP

  1. #1
    Membre éprouvé
    Avatar de clavier12AZQSWX
    Homme Profil pro
    Technicien maintenance
    Inscrit en
    Avril 2009
    Messages
    1 511
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Somme (Picardie)

    Informations professionnelles :
    Activité : Technicien maintenance

    Informations forums :
    Inscription : Avril 2009
    Messages : 1 511
    Par défaut attention avec ucfirst et les noms de fichiers ou la comparaison de casse
    bonjour,

    Dans les versions de php<8.2, ucfirst souffre d'une particularité (moi j’appellerai ça un bg mais bon... ya débat). Je ne sais pas quand il a été notifié, mais je le rappelle ici car j'ai cherché pendant des heures pour corriger un bug!
    Si vous faîtes un traitement du genre saisir un nom, le mettre en capitale (ucfirst/ucwords) pour télécharger un fichier du même nom ou faire une correspondance grammaticale à la casse précise, alors vous aurez le droit à un souci car ucfirst("TOTO") ne retourne pas forcément Toto suivant votre codage setlocale (donc la recherche d'un Toto.pdf sera infructueuse) !
    Des heures j'ai cherché avant avoir lu la spécificité de php8.2 :
    La conversion de la casse ne dépend désormais plus de la locale définit avec setlocale(). Seuls les caractères ASCII seront convertis.

    https://www.php.net/manual/fr/function.ucfirst.php
    https://www.php.net/manual/fr/function.ucwords.php

    Donc si vous n'êtes pas en php8.2, il ne faut pas faire ucfirst("TOTO") mais ucfirst(strtolower("TOTO")) pour ne pas souffrir de ce "bug" !

    Pour une raison que je ne connais pas, ma locale est "en_US_POSIX" et je ne connais aucune raison orthographique de la langue US pour que que ucfirst("TOTO") ne renvoie pas Toto au lieu de TOTO !
    Peut-être que la notion de "capitalisation" soit spécifique aux langues latines....et exclu les langues US...

    bonne journée, espérant avoir été utile !

  2. #2
    Candidat au Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Août 2023
    Messages
    4
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 25
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Août 2023
    Messages : 4
    Par défaut
    Hello, merci pour ce petit tuto car je venais de rencontrer le soucis effectivement.

    Bonne journée

  3. #3
    Membre chevronné
    Homme Profil pro
    Urbaniste
    Inscrit en
    Août 2023
    Messages
    387
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Autre

    Informations professionnelles :
    Activité : Urbaniste

    Informations forums :
    Inscription : Août 2023
    Messages : 387
    Par défaut
    Bonjour,

    votre analyse ne me convainc pas.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    $ php -r "echo(ucfirst('TOTO'));"
    TOTO
    $ php -v
    PHP 8.1.18 (cli) (built: Apr 11 2023 16:47:45) (NTS gcc x86_64)
    Copyright (c) The PHP Group
    Zend Engine v4.1.18, Copyright (c) Zend Technologies
    De toutes les manières,

    https://fr.wikipedia.org/wiki/UTF-8
    La principale caractéristique d'UTF-8 est qu'elle est rétro-compatible avec le standard ASCII, c'est-à-dire que tout caractère ASCII se code en UTF-8 sous forme d'un unique octet
    Du coup, on a
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    $ php -r "var_dump(unpack('C*',mb_convert_encoding('TOTO', 'utf-8')));"
    array(4) {
      [1]=>
      int(84)
      [2]=>
      int(79)
      [3]=>
      int(84)
      [4]=>
      int(79)
    }
    et

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    $ php -r "var_dump(unpack('C*',mb_convert_encoding('TOTO', 'Windows-1252')));"
    array(4) {
      [1]=>
      int(84)
      [2]=>
      int(79)
      [3]=>
      int(84)
      [4]=>
      int(79)
    }
    Strictement la même chose.

    Par contre, on aura,

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    $ php -r "var_dump(unpack('C*',mb_convert_encoding('TöTO', 'Windows-1252')));"
    array(4) {
      [1]=>
      int(84)
      [2]=>
      int(246)
      [3]=>
      int(84)
      [4]=>
      int(79)
    }
    246 dans cet encodage https://www.w3schools.com/charsets/t...i=246&ent=ouml

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    $ php -r "var_dump(unpack('C*',mb_convert_encoding('TöTO', 'utf-8')));"
    array(5) {
      [1]=>
      int(84)
      [2]=>
      int(195)
      [3]=>
      int(182)
      [4]=>
      int(84)
      [5]=>
      int(79)
    }
    195+182 en UTF-8, et non 246 car seulement les 128 premiers caractères ASCII sont "standardisés".

    Pour tester si une chaîne est encodée en utf-8 multi-octets, une manière rapide est de tester

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    php -r "echo(strlen('世界')==mb_strlen('世界')?'mono-octet':'multi-octets');"
    multi-octets



    Finalement, la doc est plutôt claire, ucfirst("TOTO") renvoi TOTO

    https://www.php.net/manual/fr/function.ucfirst.php

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    <?php
    $foo = 'bonjour tout le monde!';
    $foo = ucfirst($foo);             // Bonjour tout le monde!
     
    $bar = 'BONJOUR TOUT LE MONDE!';
    $bar = ucfirst($bar);             // BONJOUR TOUT LE MONDE!
    $bar = ucfirst(strtolower($bar)); // Bonjour tout le monde!
    Même en ressortant une version de la doc datée de 23 ans, on retrouve la même chose, en anglais.
    https://github.com/php/doc-en/blob/a...l#L27C1-L32C50
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    $foo = 'hello world!';
    $foo = ucfirst($foo);             // Hello world!
     
    $bar = 'HELLO WORLD!';
    $bar = ucfirst($bar);             // HELLO WORLD!
    $bar = ucfirst(strtolower($bar)); // Hello world!
    je sais pas, mais il se passe sûrement quelque chose puisque vous êtes au moins deux à rapporter un problème.

    Bonne journée.

  4. #4
    Expert confirmé Avatar de CosmoKnacki
    Homme Profil pro
    Justicier interdimensionnel
    Inscrit en
    Mars 2009
    Messages
    2 992
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Charente Maritime (Poitou Charente)

    Informations professionnelles :
    Activité : Justicier interdimensionnel

    Informations forums :
    Inscription : Mars 2009
    Messages : 2 992
    Par défaut
    Pour tester si une chaîne est encodée en utf-8 multi-octets, une manière rapide est de tester

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    php -r "echo(strlen('世界')==mb_strlen('世界')?'mono-octet':'multi-octets');"
    multi-octets
    Pas forcément, la valeur renvoyée par mb_strlen dépend de l'encodage interne (mb_internal_encoding). Donc si celui-ci est réglé sur UTF-8 (comme c'est le cas la plupart du temps) et qu'on lui fournit une chaîne encodée en UTF-8 et comprenant à minima 1 caractère hors de la plage ASCII, ça marchera, mais pas dans les autres cas:
    • Une chaîne ne comprenant que des caractères de la plage ASCII peut tout autant être considérée comme encodée en ISO-8859-x ou en UTF-8, vu qu'il n'y a aucune différence dans la suite d'octets qui la compose.
    • Si l'encodage interne est par exemple UTF-8 et qu'on lui passe une chaîne en UTF-16 ou en UCS-2, mb_strlen sera tout autant démunie que strlen et renverra le nombre d'octets rencontrés. Ce sont pourtant des encodages sur plusieurs octets. À noter que les erreurs d'encodage UTF-8 n'émettent pas le moindre avertissement dans ce cas précis.
    • Enfin si l'encodage interne a été changé pour une raison X ou Y pour une table de caractères quelconque, ça ne marchera pas.


    Donc dans le meilleur des cas, on sait que l'encodage interne est UTF-8 et strlen($chaine) === mb_strlen($chaine) renvoie false, et là on ne peut que déduire qu'au moins une séquence d'octets forme un caractère correct en UTF-8.
    Par contre si ce test renvoie true la situation reste indéterminée.

  5. #5
    Membre chevronné
    Homme Profil pro
    Urbaniste
    Inscrit en
    Août 2023
    Messages
    387
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Autre

    Informations professionnelles :
    Activité : Urbaniste

    Informations forums :
    Inscription : Août 2023
    Messages : 387
    Par défaut
    Bonjour,

    En effet c'est très bien vu.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    $ php -r "echo(strlen(mb_convert_encoding('世界','UCS-2','utf-8'))==mb_strlen(mb_convert_encoding('世界','UCS-2','utf-8'))?'mono-octet':'multi-octets');"
    mono-octet
    $ php -r "echo(strlen(mb_convert_encoding('世界','UCS-2','utf-8')));"
    4
    $ php -r "echo(mb_strlen(mb_convert_encoding('世界','UCS-2','utf-8')));"
    4
    $ php -r "var_dump(unpack('C*',mb_convert_encoding('世界','UCS-2','utf-8')));"
    array(4) {
      [1]=>
      int(78)
      [2]=>
      int(22)
      [3]=>
      int(117)
      [4]=>
      int(76)
    }
    Il eut fallut que j'utilise unpack('s*',... et quelque chose pour le boutisme.

    Merci pour cela.

    Bonne journée.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 12
    Dernier message: 21/03/2018, 10h56
  2. Réponses: 1
    Dernier message: 10/01/2018, 10h43
  3. [XL-2016] Populer un Tableau avec les nom de fichiers
    Par MrSph1nX dans le forum Macros et VBA Excel
    Réponses: 0
    Dernier message: 21/09/2017, 16h51
  4. [Système/Fichiers/API] Crypter les noms de fichier zippés avec TJclZipCompressArchive
    Par Pascale38 dans le forum C++Builder
    Réponses: 0
    Dernier message: 17/03/2014, 15h35
  5. [DOM] Problème d'accent sur les noms de fichier avec mon parseur
    Par ujoodha dans le forum Format d'échange (XML, JSON...)
    Réponses: 3
    Dernier message: 06/04/2006, 22h55

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo