mercredi 9 juillet 2014

Nouveauté PHP 5.6 - Nouvel opérateur "..."

Cet article est né du fait que je voulais essayer le nouvel opérateur "..." de PHP 5.6.
C'est un opérateur qui permet à une fonction de recevoir un nombre de paramètre indéterminé. Nous avions déjà la possibilité de créer de telle fonction en utilisant la fonction func_get_args() pour récupérer les paramètres envoyés à celle-ci. Alors j'étais curieux de voir si ce nouvel opérateur ajouterais un avantage à mon code, soit par le fait d'utiliser moins de ligne ou par le fait de rendre le code plus clair et plus lisible.

Je commence avec un premier exemple pour comparer le avant et après PHP 5.6 en implémentant une fonction coalesce en PHP. La fonction coalesce a simplement pour but de recevoir un nombre de paramètre indéterminé et de retourner la valeur du premier paramètre qui n'est pas nulle. Si aucun paramètre n'est pas nulle alors on retourne tout simplement nulle.

Exemple 1 : Avant PHP 5.6
function coalesce()
{
    $values = func_get_args();
    
    foreach ($values as $value) {
        if ($value !== null) {
            return $value;
        }
    }
    
    return null;
}

echo coalesce(null, null, 'hello world'); // output 'hello world'

Exemple 1 : Avec PHP 5.6
function coalesce(...$values)
{
    foreach ($values as $value) {
        if ($value !== null) {
            return $value;
        }
    }
    
    return null;
}

echo coalesce(null, null, 'hello world'); // output 'hello world'

Après ce premier exemple, on se rend compte qu'effectivement, la fonction avec le nouvel opérateur est plus facilement reconnaissable comme une fonction acceptant un nombre de paramètre indéterminé. La lecture du code est aussi légèrement plus aisé étant donnée que les paramètres ne semble pas venir de nulle part.

Maintenant j'ai le goût de rendre l'exemple un peu plus complexe. Je vais dériver de l'implémentation normale de la fonction coalesce pour la transformer un peu. Je désire que le premier paramètre de la fonction détermine si on accepte les valeurs vide ou non.

Exemple 2 : Avant PHP 5.6
const NULL_MODE = 1;
const EMPTY_MODE = 2;

function coalesce($mode)
{
    $values = func_get_args();
    unset($values[0]);
    
    foreach ($values as $value) {
        if (
            ($mode === NULL_MODE && $value !== null)
            || ($mode === EMPTY_MODE && !empty($value))
        ) {
            return $value;
        }
    }
    
    return null;
}

echo coalesce(NULL_MODE, null, '', 'hello world'); // output ''
echo coalesce(EMPTY_MODE, null, '', 'hello world'); // output 'hello world'

Exemple 2 : Avec PHP 5.6
const NULL_MODE = 1;
const EMPTY_MODE = 2;

function coalesce($mode, ...$values)
{
    foreach ($values as $value) {
        if (
            ($mode === NULL_MODE && $value !== null)
            || ($mode === EMPTY_MODE && !empty($value))
        ) {
            return $value;
        }
    }
    
    return null;
}

echo coalesce(NULL_MODE, null, '', 'hello world'); // output ''
echo coalesce(EMPTY_MODE, null, '', 'hello world'); // output 'hello world'

On voit très facilement que le nouvel opérateur nous facilite la vie en nous évitant d'avoir à retirer le premier paramètre que la fonction func_get_args() nous retourne. Puis tout comme l'avait prouvé le premier exemple, la signature de la fonction est beaucoup plus claire avec le nouvel opérateur.

Les exemples suivants sont mis là juste à titre de démonstration des avantages que l'on peut utiliser avec le nouvel opérateur.

On peut obliger les paramètres indéfinis à être d'un type en particulier.
function coalesce(DateTime ...$values)
{
    // implémentation ici
}

On peut également utiliser l'opérateur avec un array lorsque l'on appelle la fonction. Ce qui permet de décomposer les valeurs de l'array en multiple paramètre.

function coalesce(...$values)
{
    // implémentation ici
}

$array = [null, 'hello world'];
echo coalesce(...$array); // output 'hello world'

Donc en conclusion, il est vrai que ce nouvel opérateur rend la lecture du code plus facile, mais est-ce que les fonctions qui accepte un nombre de paramètre indéterminé sont si fréquente que cela dans la vie d'un développeur de tout les jours ... Personellement, je me suis creusé la tête un bon moment avant de trouver une fonction (coalesce) pour laquelle accepter ce genre de paramètre avait du sens. J'ai aussi pensé au console.log() en JavaScript.

Je termine sur un petit défi :) Est-ce que vous connaissez des cas types où l'usage d'une fonction acceptant un nombre de paramètre indéterminé est plus utile et compréhensible qu'une fonction acceptant un simple array !

Référence :
http://www.php.net/manual/en/migration56.new-features.php#migration56.new-features.variadics
http://www.php.net/manual/en/functions.arguments.php#functions.variable-arg-list