Qui ne s’est jamais demandé : à quoi peuvent bien servir les opérateurs sur les bits ? Voici un exemple : ils peuvent permettre de gérer facilement des options dans vos classes. Imaginons une classe personne qui représente une… personne :

class Person
{
    private $options;
    private $name;

    /**
     * Person constructor.
     * @param $options
     */
    public function __construct($name, $options = 0)
    {
        $this->options = $options;
        $this->name = $name;
    }

    /**
     * @return mixed
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * @return mixed
     */
    public function getOptions($binary = null)
    {
        return $binary ? sprintf("%'08b", $this->options) : $this->options;
    }
}

$nicolas = new Person('Nicolas');

Maintenant, ajoutons des options :

  • la personne peut être grande
  • la personne peut être un homme ou une femme
  • la personne peut être chauve
  • la personne peut être drôle

On utilise 4 constantes, des puissances de deux (c’est important) :

class Person
{
    const TALL = 1; // 00000001
    const FUNNY = 2; // 00000010
    const BALD = 4; // 00000100
    const MALE = 16; // 00001000
    
    //--
}

Créons un homme grand et drôle :

$nicolas = new Person('Nicolas', Person::TALL | Person::FUNNY);
echo $nicolas->getOptions(true); // 00000011

Améliorons la classe pour modifier les options après la création de l’objet (on se s’occupe que de l’option FUNNY) :

class Person
{
    //--

    /**
     * @return int
     */
    public function isFunny()
    {
        return $this->options & self::FUNNY;
    }

    public function setFunny()
    {
        $this->options |= self::FUNNY;
    }

    public function setNotFunny()
    {
        $this->options &= ~self::FUNNY;
    }

    public function toggleFunny()
    {
        $this->options ^= self::FUNNY;
    }
}

Et maintenant jouons avec nos nouvelles méthodes : on active et désactive l’option FUNNY tout en vérifiant le résultat :

$nicolas = new Person('Nicolas', Person::TALL);
$nicolas->setFunny();

// Output : Nicolas is funny
if($nicolas->isFunny()) {
    echo "{$nicolas->getName()} is funny\n";
} else {
    echo "{$nicolas->getName()} is boring\n";
}

$nicolas->setNotFunny();
// Output : Nicolas is boring
if($nicolas->isFunny()) {
    echo "{$nicolas->getName()} is funny\n";
} else {
    echo "{$nicolas->getName()} is boring\n";
}

$nicolas->toggleFunny();
// Output : Nicolas is funny
if($nicolas->isFunny()) {
    echo "{$nicolas->getName()} is funny\n";
} else {
    echo "{$nicolas->getName()} is boring\n";
}

$nicolas->toggleFunny();
// Output : Nicolas is boring
if($nicolas->isFunny()) {
    echo "{$nicolas->getName()} is funny\n";
} else {
    echo "{$nicolas->getName()} is boring\n";
}

Explication

Récapitulons un peu ce que nous devons savoir :

  • pour activer une option, on utilise l’opérande binaire OR |
  • pour désactiver une option, on utilise l’opérande binaire AND & en complément avec le NON binaire ~
  • pour inverser une option, on utilise l’opérande binaire XOR ^

Il est facile d’étendre cette astuce aux autres options, et même d’écrire une méthode setXXXX plus générique pour éviter la duplication de code à travers les options. Mais là n’est pas le sujet.

Références externes