Accueil du blog

Archives

CamelCase, underscore, PEAR conventions, convertir la casse des noms en PHP, classes, fonctions, méthodes, variables, constantes - Part 1

Le but de ce premier article est de mettre au point un petit convertisseur de casse, une fonction qui va nous permettre de transformer des noms communs en lowerCamelCase, etc. Nous verrons dans l'article suivant des applications pratiques.

Commentaires : 3

En termes de conventions, les débats font rage. Particulièrement en PHP, langage relativement grand public, sans règles historiques strictes de nommage des fonctions et variables. Chez PEAR cependant, ils ont tenté de recommander des conventions, consultables ici, dont nous allons nous inspirer en partie dans ce post. Il va pourtant me falloir prendre des décisions forcément un peu arbitraires pour arriver à mes fins, c'est-à-dire mettre au point un petit convertisseur de casse, dont nous verrons dans un article ultérieur quelques applications pratiques.

1. Noms de Classes

Concernant les noms de classes, il y a débat entre les tenants de la convention PEAR, reprise par Zend notamment, qui consiste à mettre une majuscule à chaque terme et a les relier par un tiret bas ou underscore (« _ » )…

Ex : Zend_Truc_Machin_Controller

… et les partisans d’une casse plus fidèle au type « camel ». La casse dite « CamelCase » (qui au passage n’a rien à voir avec le langage Caml) consiste à supprimer les espaces entre les mots tout en leur donnant chacun une majuscule.

Ex : MySuperTemplateClass

Le CamelCase peut être subdivisé en deux types : UpperCamelCase, lowerCamelCase.
Le premier commence par une majuscule, pas le second.

Ne soyez pas étonnés de voir d’autres noms à cette casse particulière, car les termes équivalents qui la désignent abondent dans la littérature informatique : camelCaps, camelBak, BiCapitalization, InterCaps, MixedCase, BumpyCaps, CamelHumpedWord, NerdCaps, WordMixing, studlyCaps, etc.

2. Noms de Méthodes ou fonctions

Historiquement, les fonctions php utilisent une casse qui relie les termes tout en minuscules par des underscores…

Ex : array_walk_recursive

…ou alors elles agrègent des termes souvent abbréviés sans distinction typographique particulière…

Ex : strtolower (comprendre « string to lower case »)

…mais sous l’influence d’autres langages populaires (C++, Java, etc.) et avec l’essor de la Programmation Orientée Objet (POO), les développeurs ont aussi adopté et fortement diffusé la casse lowerCamelCase pour les méthodes de classe et les fonctions

3. Noms de Variables

De la même manière, du point de vue strict de la casse, on retrouve dans les noms de variables les trois types principaux utilisés pour les fonctions, immédiatement précédés d’un signe dollar (« $ »)…

Ex : $mon_string_qui_claque, $strclaq, $monStringQuiClaque

4. Noms de Constantes

Pour les noms de constantes, il y a une relative unanimité (quoique). Les termes sont entièrement en majuscules et reliés par des underscores

Ex : SESSION_MAX_LIFETIME

5. Verdict

Je vais élire cinq styles de casse que je juge être les principaux et vais tenter de leur donner un nom clair, ainsi qu’une illustration et une valeur, à la manière d’une « cheat sheet », un pense-bête :

  • « Pear_Class » = We_We = 1
  • « UpperCamelCase » = WeWe = 2
  • « lowerCamelCase » = weWe = 3
  • « php_variable » = we_we = 4
  • « CONSTANT » = WE_WE = 5

6. codons!

Nous voulons obtenir une fonction caseConvert() qui puisse nous délivrer notre casse préférée en un éclair.

Pour passer d’un type de casse à l’autre, soit on code 20 fonctions, soit on utilise un bon vieux flag et un entonnoir. Le "flag" va être le nom du type de casse qu’on souhaite obtenir. L’entonnoir va donner un array des termes récupérés dont les caractéristiques sont neutralisées afin de subir ensuite un "recassage".

Donc première étape, on définit les différents types de casse disponibles en sortie (on reprend tout simplement le pense-bête) :

$pear_class = array('Pear_Class', 'We_We', 1);
$upper_cc = array('UpperCamelCase', 'WeWe', 2);
$lower_cc = array('lowerCamelCase', 'weWe', 3);
$php_var = array('php_variable', 'we_we', 4);
$constant = array('CONSTANT_CASE', 'WE_WE', 5);

Puis on récupère les termes et on neutralise leurs caractéristiques :

$string = trim($string);

if(strpos($string, ' '))
        $ary = explode(' ', $string);
elseif(strpos($string, '_'))
        $ary = explode('_', $string);
elseif(preg_match('/[a-z]+[A-Z]/', $string))
        $ary = explode('_', preg_replace('/(?<=\\w)(?=[A-Z])/',"_$1", $string));
else $ary = array($string);

Arrêtons-nous uns instant sur ces lignes.

Strpos sert à récupérer la position d’un élément dans une chaîne. Je suis normalement obligé de mettre l’opérateur « !== » dans le deuxième cas et pas le premier. Pourquoi ? 2 raisons :

  • 1. strpos commence à compter à 0, car il considère en gros la chaîne comme un array de lettres. Donc si on utilise « != false », cet opérateur ne pourra pas faire la différence entre la position zéro et l’absence de caractère. C’est pourquoi on utilise « !== ».
  • 2. Dans le premier cas, c’est impossible d’avoir un espace en premier, car on vient de faire un trim($string), dont la fonction est justement d’enlever les espaces parasites au début et à la fin des chaînes de caractères. Par contre dans le deuxième cas, il est possible d’avoir un underscore en premier.

Ici je n'utilise pas "!== false" dans le deuxième cas quand même, car nous allons voir qu'il faut traiter les cas avec l'underscore au début de manière particulière.

Explode sert à découper une chaîne à chaque fois qu’on rencontre un certain délimiteur et à mettre le tout dans un tableau.

Preg_replace sert à remplacer les éléments capturés grâce à une expression régulière (1er élément) dans la chaîne de caractère (3e élément) selon un modèle (2e élément). Le $1 représente ce que la première chaîne a capturé et indique comment faire le remplacement.

Donc à ce stade nous avons un tableau de termes prêts à être "recassés". Allons-y :

if(in_array($style, $pear_class))
        $res = implode('_', array_map('ucfirst', $ary));
elseif(in_array($style, $upper_cc))
        $res = implode('', array_map('ucfirst', $ary));
elseif(in_array($style, $lower_cc)) //en php5 >= 5.3 il existe "lcfirst", pratique
        $res = strtolower($ary[0][0]).substr(implode('', array_map('ucfirst', $ary)), 1);
elseif(in_array($style, $php_var))
        $res = implode('_', array_map('strtolower', $ary));
elseif(in_array($style, $constant))
        $res = implode('_', array_map('strtoupper', $ary));

Ici, à chaque fois, on fait deux choses :

  • 1. on vérifie si le style de casse qu’on veut est bien celui de la condition.
  • 2. on recrée la bonne casse des termes avec array_map, qui applique une fonction à tous les élélments d’un tableau et on agrège chacun d’entre eux dans une chaîne de caractères avec implode avec un agent de liaison.

Et voilà, non ? Encore un détail. On aura souvent des tableaux de noms à traiter, alors ajoutons le support des arrays ! Fastoche :

if(is_array($string))
{
        foreach($string as $str)
                $ary_str[] = caseConverter($str, $style);

        return $ary_str;
}

Et au contraire, si on a un seul mot sans marqueur particulier, il faut toujours qu’on produise une variable $ary qui soit un array pour executer la dernière partie de la fonction. Donc avant, on met :

if(empty($ary))
        $ary = array($string);

Puis dernier point, au cas où il y a un underscore au début, on retient cette caractéristique. Pourquoi? Parce que l'underscore de début peut être signifiant dans certaines conventions, comme par exemple indiquer une fonction statique ou privée ou un contenu de variable particulier.

//Si le string commence par underscore on prévient qu'il faut le restituer à la fin
if(strpos($string, '_') === 0 && $uds !== false)
{
        $string = substr($string, 1);
        $has_uds_start = 1;
}

...

if(!empty($has_uds_start)) //à la fin on restitue l'undescore du début
        $res = '_'.$res;

Si on ne souhaite vraiment pas laisser l'underscore de début, il suffit d'ajouter false dans le constructeur pour ne plus en parler.

caseConverter($myString, 'We_We_We', false);

Donc finalement on a :

function caseConverter($string, $style, $uds = true)
{
        /*Si on traite un tableau, on déclenche la récursivité (la fonction s’appelle elle-même)*/
        if(is_array($string))
        {
                foreach($string as $str)
                        $ary_str[] = caseConverter($str, $style, $uds);

                return $ary_str;
        }

        /*Notre pense-bête de flags*/
        $pear_class = array('Pear_Class', 'We_We', 1);
        $upper_cc = array('UpperCamelCase', 'WeWe', 2);
        $lower_cc = array('lowerCamelCase', 'weWe', 3);
        $php_var = array('php_variable', 'we_we', 4);
        $constant = array('CONSTANT_CASE', 'WE_WE', 5);

        $string = trim($string);

        //Si le string commence par underscore on prévient qu'il faut le restituer à la fin
        if(strpos($string, '_') === 0)
        {
                $string = substr($string, 1);
                $has_uds_start = 1;
        }

        /*on récupère les termes dans un tableau*/
        if(strpos($string, ' '))
                $ary = explode(' ', $string);
        elseif(strpos($string, '_'))
                $ary = explode('_', $string);
        elseif(preg_match('/[a-z]+[A-Z]/', $string))
                $ary = explode('_', preg_replace('/(?<=\\w)(?=[A-Z])/',"_$1", $string));
        else $ary = array($string);

        /*on applique la nouvelle casse*/
        if(in_array($style, $pear_class))
                $res = implode('_', array_map('ucfirst', $ary));
        elseif(in_array($style, $upper_cc))
                $res = implode('', array_map('ucfirst', $ary));
        elseif(in_array($style, $lower_cc)) //en php5 >= 5.3 il existe "lcfirst", pratique
                $res = strtolower($ary[0][0]).substr(implode('', array_map('ucfirst', $ary)), 1);
        elseif(in_array($style, $php_var))
                $res = implode('_', array_map('strtolower', $ary));
        elseif(in_array($style, $constant))
                $res = implode('_', array_map('strtoupper', $ary));

        if(!empty($has_uds_start) && ($uds == true)) //on restitue l'undescore du début
                $res = '_'.$res;

        return $res;
}

Et voilà !

Commentaires : 3

- Le 06/12/2014

youyou

Pas mal
J'avais pas spécialement besoin de toutes les options de ta fonction, mais ça m'a aldé à mieux maîtriser les chaînes avec de la casse
Je choperais bien ton script de capcha aussi...

- Le 31/10/2016

David Cohen

Bonjour

Je suis en train de concevoir un nouveau FRAMEWORK PHP, Je trouve cette fonction très utile
Je vous demande permission de l'intégrer à mon framework.

Merci

David Cohen

- Le 05/11/2016

Lilhoot

Faites donc, David, je vous en prie!
Ici c'est en licence Creative Commons BY-NC-SA
https://creativecommons.org/licenses/by-nc-sa/3.0/fr/
Si des aspects de votre projet sortent de ce cadre, parlons-en par mail
___
Site web : lilhoot.eu

Ecrire un commentaire

Captcha - Illisible?