Opérations booléennes et logique binaire en PHP

Image non disponible

Savoir manipuler des données binaires en base 2 ou en base 16 (hexadécimal) peut s'avérer nécessaire ne serait-ce que pour la compréhension d'un algorithme.
PHP propose pas mal de fonctionnalités à ce sujet, nous allons les passer en revue avec quelques exemples concrets. Commentez Donner une note à l'article (5)

Article lu   fois.

L'auteur

Site personnel

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

I. Introduction

S'il est un sujet que peu de développeurs PHP connaissent, c'est bien celui des mathématiques binaires et des opérations booléennes. C'est étonnant, car le processeur de toute machine ne pratique que ça.
Certes PHP est un langage plutôt orienté développement web, et le web n'a pas fondamentalement besoin que l'on connaisse ces principes. Mais PHP est un mortier qui sait faire beaucoup de choses, toujours est-il que tout est binaire autour de nous en informatique, et qu'à un moment ou un autre, vous aurez besoin de comprendre cette manière de voir les choses pour effectuer tout un tas d'opérations.

II. Rappels sur la représentation binaire et les bases

Les bases, en mathématiques, sont des manières de voir les choses. Nous comptons (nous humains) en base 10. Voyons un exemple.

La base 10, nous y sommes extrêmement habitués
Sélectionnez

Je pense au hasard au nombre 12974. Et bien celui-ci, exprimé en base10 est décomposable en somme de puissances de dix :
12974 = 4*10^0 + 7*10^1 + 9*10^2 + 2*10^3 + 1*10^4

Tout le monde est d'accord, impossible de me contredire ici.
La machine, elle, compte en base 2, car l'électronique sur laquelle elle est basée, gère extrêmement bien deux états: allumé-On-1 contre éteint-Off-0, au moyen de composants appelés transistors, qui pour rappels se trouvent par milliards dans les microprocesseurs modernes.
Ainsi, si je reprends la même logique avec 12974 en base 2:

La base 2, les ordinateurs n'utilisent que ça
Sélectionnez

12974 peut aussi s'exprimer en base 2 : c'est alors une somme de puissances de 2
12974 = 1*2^12 + 1*2^11 + 1*2^9 + 1*2^7 + 1*2^5 + 1*2^3 + 1*2^2 + 1*2^1

Oui bon, la base est plus petite, ceci se traduit par moins de chiffres disponibles (0 ou 1 au lieu de 0 ou 1 ou 2 ou... 9) mais en contrepartie plus de bits.
Pour creuser (convertir de base en base par exemple), voyez Wikipédia

En fait, la base 2 est plus simple que la base 10 (vous ne le croyez pas car votre cerveau est trop habitué à la base 10, c'est tout) mais il faut plus de bits pour représenter le même chiffre au final.
Tout est question de puissance, et de puissances de 2. Le processeur traite donc des opérations extraordinairement simples : elles ne peuvent être composées que de "0" ou de "1" ; et c'est pour cela qu'il en traite des millions/milliards par seconde.

II-A. Rappels sur les bits, les octets et le vocabulaire

Un bit est une unité logique d'information. Il vaut 0 ou 1 en base 2. Un octet est l'association de 8 bits et s'écrit en anglais 'one byte', attention ! (un bit en anglais se dit 'one digit'). Un processeur, aujourd'hui, sait effectuer des calculs divers et variés, mais ses opérandes ne peuvent dépasser le maximum de 32 bits (ou plutôt 64 de nos jours, tout de même) soit 4 octets.
Ainsi, toute valeur numérique supérieure au maximum que l'on peut stocker sur 32 bits (ou 64) n'est pas représentable de manière simple. Ainsi, un entier ne peut dépasser (2^32)-1 soit 4294967295 non signé (32bits).
32 bits, c'est long à représenter et 64 encore plus. On a donc souvent recours à la base 16: l'hexadécimal. La base 16 utilise 16 chiffres mais notre numération ne sait en représenter que 10: de zéro à neuf. Pour aller de 10 à 16, on utilise donc les lettres de A à F.

Encore un peu de vocabulaire, on appelle un Mot (Word) un ensemble de 16 bits (2 octets). Un Double-mot (DWORD) 32 bits et un Quatrain (QWORD) pour 64 bits. Il existe aussi le DQWORD.
En théorie, un mot doit correspondre à la longueur du bus du processeur (normalement, aujourd'hui, un mot devrait mesurer 64 bits). Mais pour des raisons historiques, le mot pèse 16 bits.

Calcul avec des octets en base 2, ici la valeur décimale 89 est représentée
valeurs décimales 128 64 32 16 8 4 2 1
valeurs (base 2) 2^7 2^6 2^5 2^4 2^3 2^2 2^1 2^0
bits 0 1 0 1 1 0 0 1
Calcul avec des octets en base 16, ici la valeur décimale 89 est représentée
valeurs décimales 4096 256 16 1
valeurs (base 16) 16^3 16^2 16^1 16^0
bits 0 0 5 9

89 en base 10 = 01011001 en base 2 ou encore 59 en base 16 aussi écrit 0x59 (l'hexadécimal est préfixé par 0x lorsqu'on l'écrit, par convention pour reconnaitre la base 16).

Reste encore la base octale: la base 8. Lorsqu'on fait ce genre de calcul souvent (langage C, assembleur ou encore électronique numérique), on arrive à convertir les bases de tête de manière très rapide (au moins jusqu'à l'octet voire jusqu'au mot). Tout est question de division/multiplication, de retenue... de mathématiques très simples.
Il reste possible pour les débutants d'utiliser des calculatrices gérant les bases. Gcalc est très bien dans le genre. Image non disponible

Dernière remarque : l'ordre des bits. On appelle le bit de poids faible celui qui possède la puissance la plus basse, inversement pour le bit de poids fort. Soit notre 89 décimal. Il donne en binaire 01011001 ou ... 10011010 ?? Dois-je exprimer le bit de poids faible en premier, ou bien le bit de poids fort ? Quelle question et quel casse-tête informatique, surtout lorsque les constructeurs (et les OS) font ce qu'ils veulent ! Le fait de préciser le bit de poids faible en premier est appelé l'ordre Little Endian, au contraire, présenter le bit de poids fort en premier représente l'ordre Big Endian.

III. Opérations binaires en PHP

PHP permet d'effectuer les opérations binaires au moyen des opérateurs sur les bits. Il possède aussi quelques fonctions qui manipulent les bases.
Les opérations logiques OR, AND, NOT et XOR sont toutes possibles via les opérateurs de bits. Attention, tout ceci suppose systématiquement le type PHP integer (exprimé souvent en hexadécimal); un transtypage sera effectué dans le cas contraire (sauf pour les chaines, PHP utilisera alors la table ASCII pour la conversion vers l'entier).

III-A. Bases et notation en PHP

PHP permet d'exprimer des entiers en base 2, 8 10 ou 16. Attention, on parle bien d'entiers, c'est différent pour les chaines.

  • Un entier exprimé en base 10 s'exprime de manière classique ;
  • Un entier exprimé en base 2 commence par 0b (à partir de PHP 5.4) ;
  • Un entier exprimé en base 8 commence par 0 ;
  • Un entier exprimé en base 16 commence par 0x ou 0X.
Exprimer les entiers dans différentes bases
Sélectionnez
<?php
$int10 = 10; /* 10 en base 10, classique */
$int8  = 012; /* 10 en base 8, attention au zéro du début qui signifie 'octal' */
$int2  = 0b110; /* 10 en base 2, cette notation n'est disponible qu'à partir de PHP5.4 */
$int16 = 0xA; /* 10 en base 16, héxadécimal */

III-B. OR, ou l'affectation d'un bit

L'opération OR met les bits de la sortie à 1 qui sont à 1 dans une des deux opérandes. Cette opération se fait au moyen de '|' en PHP :

Exemple d'un OR logique en PHP
Sélectionnez
<?php
$a = 0xA3; // 1010.0011
$b = 0x3C; // 0011.1100

$c = $a | $b; // 1011.1111 soit 0xBF ou encore 191 en décimal
OR
$a 1 0 1 0 0 0 1 1
$b 0 0 1 1 1 1 0 0
Résultat OR 1 0 1 1 1 1 1 1

Nous venons de manipuler des octets. Rappelez-vous qu'un octet représente 8 bits. Pour mieux les calculer, je les représente avec un point au milieu: '1001.0110', c'est juste pour la lisibilité et l'aide au calcul mental.
Un octet se représente donc en hexadécimal avec 2 digits: 0x00 est 0 en décimal et 0xFF est 255 en décimal.

Ne confondez surtout pas l'opérateur '|' et l'opérateur '||'. L'opérateur '|' agit sur les bits d'un entier, alors que '||' convertira les opérandes en booléen, rien à voir (en fait si, un booléen n'est rien d'autre qu'un et un seul bit : vrai (1) ou faux (0). On ne va pas aller loin avec un seul bit...).

L'opérateur OR est surtout utilisé pour affecter les bits dans un entier :

Utilisation du OR logique pour affecter les 4 bits de gauche
Sélectionnez
<?php
$a = 0x07;   // 0000.0111

// Met les 4 bits de gauche à 1 dans $a
$a |= 0xF0;  // 0000.0111 OR 1111.0000 = 1111.0111

// $a vaut ici 0xF7

L'opération |= met à 1 les bits de l'opérande de gauche étant à 1 dans l'opérande de droite. Pour les mettre plutôt à 0, il faut utiliser l'opérateur XOR (OU exclusif).

III-C. AND, ou le masque de bits

L'opération AND met les bits de la sortie à 1 qui sont à 1 dans les deux opérandes en même temps. Cette opération se fait au moyen de '&' en PHP :

Exemple d'un AND logique en PHP
Sélectionnez
<?php
$a = 0xA3; // 1010.0011
$b = 0x3C; // 0011.1100

$c = $a & $b; // 0010.0000 soit 0x20 ou encore 32 en décimal
AND
$a 1 0 1 0 0 0 1 1
$b 0 0 1 1 1 1 0 0
Résultat AND 0 0 1 0 0 0 0 0

Ne confondez surtout pas l'opérateur '&' et l'opérateur '&&'. L'opérateur '&' agit sur les bits d'un entier, alors que '&&' convertira les opérandes en booléen, rien à voir (en fait si, un booléen n'est rien d'autre qu'un et un seul bit : vrai (1) ou faux (0). On ne va pas aller loin avec un seul bit...).

L'opérateur AND est surtout utilisé pour tester les bits dans un entier, on parle de masque de bits :

Utilisation du AND logique pour créer un masque de bits
Sélectionnez
<?php
$a = 0x07;   // 0000.0111
$a &= 0xF0;  // 0000.0111 AND 1111.0000 = 0000.0000

// $a vaut ici 0x00

L'opération &= met à 1 les bits de l'opérande de gauche étant à 1 dans l'opérande de gauche ET de droite. Cela permet un masque, c'est-à-dire une sélection fine des bits sur lesquels on veut travailler.
En faisant un AND avec 0xF0, on veut travailler les 4 bits les plus à gauche de l'opérande, inversement en faisant un AND avec 0x0F, on veut travailler sur les 4 bits les plus à droite de l'opérande (et enfin un AND avec 0x3C permettra de travailler les bits 'du milieu', c'est moins commun).
Ceci est une technique fondamentale de l'informatique générale dans le traitement de l'information. Tous les processeurs et tous les programmes informatiques sans exception ont recours à un moment donné au masquage de bits, en général sur une donnée d'une longueur de 32bits (ou 64 de nos jours).
Exemple en l'air: que vous inspire la notion de 'masque de sous-réseau' ? Si vous n'avez jamais creusé cette notion, filez-y vite !

III-D. Shifting, ou les décalages des bits

Lorsqu'on travaille sur les bits, on a vu qu'on pouvait - dans un octet de 8 bits par exemple - travailler sur n'importe quel bit, simplement grâce à deux opérations élémentaires : AND et OR (restent XOR et NOT encore).
L'opération OR permet de mettre à 1 n'importe quel bit, l'opération XOR met à zéro n'importe quel bit, enfin l'opération AND permet de sélectionner par masquage certains bits dans le but de travailler avec.

OR, AND et NOT sont les trois opérations élémentaires fondamentales de l'algèbre de Boole. Il en existe d'autres remarquables qui en découlent (XOR par exemple).
Mais on peut encore s'amuser avec les bits d'une autre manière : en les décalant sur la gauche ou la droite.
L'opérateur de décalage gauche '<<', décale les bits à gauche de N rangs, déterminés par l'opérande :

Exemple de décalage de bits à gauche
Sélectionnez
<?php
$a = 0x26; // 0010.0110

// Décale les bits de $a de 2 rangs vers la gauche
$a <<= 2 // 1001.1000 soit 0x98

L'opérateur de décalage droite '>>', décale les bits à droite de N rangs, déterminés par l'opérande :

Exemple de décalage de bits à droite
Sélectionnez
<?php
$a = 0x26; // 0010.0110

// Décale les bits de $a de 2 rangs vers la droite
$a >>= 2 // 0000.1001 soit 0x09

Remarquez comme les bits sont comblés par des zéros, que ce soit à gauche, ou à droite, lorsqu'ils 'sortent' du rang. Ceci n'est pas vrai pour le bit de poids fort s'il est à 1 et décalé à droite: il est conservé.
Ceci est dû au fait que le bit de poids fort représente le signe des entiers signés, et PHP utilise des entiers signés.

Vous aurez sans doute remarqué que décaler les bits d'un rang vers la gauche, revient à multiplier la valeur décimale par 2, et inversement.
Attention, je parle de valeurs décimales non signées. Dans les valeurs signées, le bit de poids fort représente le signe, et tous les autres bits sont complémentés à 2. Je ne préfère pas m'étendre sur ce point.

Le décalage de bits va permettre de compléter l'opération de masquage (AND). En effet, dans un octet (toujours pour notre exemple), il y a 8 bits que l'on peut séparer en 2 groupes de 4 bits.
Chaque groupe peut représenter quelque chose de différent, et passer d'un groupe à l'autre est alors très simple, il suffit de décaler les bits de 4 positions:

Exemples de décalages
Sélectionnez
<?php
$a      = 0x94; // 1001.0100
$masque = 0x5; //0101

$bitsDeGauche = $a >> 4 // 0000.1001 ou plus simplement 1001

$bitsDeGaucheMasques = $bitsDeGauche & $masque; // 0001

Un autre exemple de fonction PHP permettant de lire les n bits d'un entier à partir de la position p. la position 0 est supposée le bit de poids faible (le plus à droite) :

Jouons avec les bits en PHP
Sélectionnez
<?php
/*
Lit les $n bits de $x en partant de $p
$p=0 est le bit de poids faible
*/
function readDigit($x, $p, $n)
{
    return ($x >> $p+1-$n) & ~(~0 << $n);
}

$byte = 0x9D; // 1001.1101

$digits = readDigit($byte, 5, 3); // 0000.0011

J'avoue qu'avoir fait pas mal de C facilite la compréhension d'une telle fonction. Expliquons : ($x >> $p+1-$n) déplace les $n bits à partir de $p à l'extrême droite. & ~(~0 << $n) masque les bits, ~0 est un ensemble de 1 collés, décalés de $n bits vers la gauche, donc à droite apparaissent des 0, que nous inversons avec ~, le masque est crée.
Astucieux.

III-E. Fonctions PHP relatives aux bases

PHP propose quelques fonctions liées aux bases numériques, majoritairement de conversion. Il sait nativement gérer les bases 2, 8, 10 et 16 et propose aussi des conversions vers des bases plus exotiques.
decbin()/bindec(), dechex()/hexdec(), decoct()/octdec() sont autant de fonctions dont les noms parlent d'eux-mêmes, la base 10 restant la référence.
base_convert() permet de convertir n'importe quel nombre de n'importe quelle base vers n'importe laquelle. Petite limite tout de même: pas plus que la base 36, car 36 = 26 lettres de l'alphabet + 10 chiffres. Après, on ne sait quoi utiliser comme caractère pour représenter un nombre dans la base.

Viennent ensuite les plus compliquées, mais aussi les plus utiles, j'ai nommé pack() et unpack() dont on reparlera un peu plus tard.
Enfin, on pourrait parler de printf() aussi. printf() n'effectue pas d'opérations sur les bits, mais permet de les représenter soit sous forme binaire, soit sous forme hexadécimale.

printf: un must-have lorsqu'on manipule des données binaires
Sélectionnez
<?php
$mot   = 0xF307;
printf('%b', $mot); // affiche 1111001100000111, on voit que c'est plus lisible en mettant des '.' hein?
printf et autres
Sélectionnez
<?php
$octet = decbin(3672); // 1110.0101.1000
$hexa  = 0xF2D8;

printf('%b', $hexa); // 1111.0010.1101.1000

// Affiche en hexa sur 4 digits comblés de zéros à gauche
printf('0x%04X', $octet); // 0x0E58

III-F. Utilisation des bases par PHP

Tout le monde connait le rapport d'erreur de PHP. Les constantes E_* (E_ALL, E_STRICT, E_NOTICE ...) sont des entiers qui représentent tous une puissance de 2, donc un bit particulier. Regardez plutôt leur valeur sur la documentation officielle ou en les affichant.

Ils sont codés sur 16 bits, soit 2 octets. E_ALL vaut 30719 en décimal, ce qui fait 32767 - 2048. 2048 c'est E_STRICT, donc E_ALL c'est tout sauf E_STRICT.

Calcul de E_ALL
Sélectionnez

E_ALL = (E_ERROR | E_WARNING | E_PARSE | E_NOTICE | E_CORE_ERROR | E_CORE_WARNING | E_COMPILE_ERROR | E_COMPILE_WARNING | 
         E_USER_ERROR | E_USER_WARNING | E_USER_NOTICE | E_RECOVERABLE_ERROR | E_DEPRECATED | E_USER_DEPRECATED)

Petit exemple:

Les masques du gestionnaire d'erreur PHP
Sélectionnez
<?php
$error_reporting = E_ALL | E_STRICT | ~E_NOTICE;

$error_reporting = 0x7FF | 0x800 | 0xFF7; // Soit 0xFFF ou encore 4095 en décimal
Le gestionnaire d'erreur interne de PHP (incomplet)
valeurs décimales 16384 8192 4096 2048 1024 512 256 128 64 ...
valeurs (base 2) 2^14 2^13 2^12 2^11 2^10 2^9 2^8 2^7 2^6 ...
Niveau correspondant E_USER_DEPRECATED E_DEPRECATED E_RECOVERABLE_ERROR E_STRICT E_USER_NOTICE E_USER_WARNING E_USER_ERROR E_COMPILE_WARNING E_COMPILE_ERROR ...

J'avoue que même habitué, déjà sur 2 octets c'est plus difficile à calculer de tête (encore qu'en décalant les octets et en connaissant ses tables de multiplication 2 et 16 ça va).
Donc lorsque vous affichez le rapport d'erreur en décimal, et que vous voyez par exemple 6138, avec de l'entrainement vous pourrez dire quels bits sont dedans, et donc à quelles constantes ça correspond. Reste la calculatrice sinon ! Vous pouvez aussi le demander à PHP au moyen de printf().

Les masques de bits ne sont pas utilisés que dans le rapport d'erreur de PHP, mais partout : dans PDO, dans Json, dans GD...

IV. Exemple concret

IV-A. Gestion de droits

Nous allons mettre en place un minisystème de gestion de droits d'accès à des ressources. Nous allons supposer quatre permissions, et deux drapeaux supplémentaires:

Liste des permissions
  • permission Read ;
  • permission Write ;
  • permission Lock ;
  • permission Delete.
Liste des drapeaux supplémentaires
  1. drapeau Activated ;
  2. drapeau Gold.

Nous allons coder l'ensemble de ce système sur 1 octet, donc 8 bits. Comme nous avons 6 bits à utiliser, il en restera 2 inusités pour le futur, éventuellement.

Nos constantes de droits
Sélectionnez
<?php
define READ   = 1; // ou 0x01 soit 0000.0001
define WRITE  = 2; // ou 0x02 ou encore 1 << 1 soit 0000.0010
define LOCK   = 4; // ou 0x04 ou encore 1 << 2 soit 0000.0100
define DELETE = 8; // ou 0xO8 ou encore 1 << 3 soit 0000.1000

define ACTIVE = 16; // ou 0x10 ou encore 1 << 4 soit 0001.0000
define GOLD   = 32; // ou 0x20 ou encore 1 << 5 soit 0010.0000

/*
N'avoir aucun droit, c'est être à 0x00
Avoir tous les droits, c'est être à Ox3F, soit 0011.1111

Les 2 bits de gauche sont inusités, donc l'intervalle
0x40 - 0xC0 ne sera pas utilisé
*/

Il ne reste plus que des fonctions affectant/enlevant un droit à quelqu'un, et une fonction de vérification de ce droit pour autoriser/interdire l'accès:

Quelques fonctions simples pour la gestion des droits
Sélectionnez
<?php
function allow(&$registry, $access)
{
    $registry |= $access
}

function deny(&$registry, $access)
{
    $registry ^= $access
}

function isAllowed($registry, $access)
{
    return $registry & $access;
}

Et on l'utilise:

Exemple (totalement fictif) d'utilisation de nos fonctions
Sélectionnez
<?php
$membre1 = READ | WRITE | ACTIVE;
$membre2 = 0x3F; // Tous les bits à 1

$news    = READ | GOLD;

if (isAllowed($membre1, $news)) {
    echo "Welcome to news1";
}

deny($membre2, $news);

A chacun de creuser...

IV-B. Analyse des en-têtes binaires d'une image

Dans cet exemple, nous allons faire un travail totalement binaire : nous allons décortiquer les en-têtes d'une image PNG afin d'en récupérer les métadonnées.
Pour cela, il faudra lire X octets à partir d'un certain offset dans l'en-tête (la spécification du format est ainsi indispensable), et interpréter ces octets.

Si on lit la spécification du format PNG, on s'aperçoit que les octets sont rangés de la sorte :

Une partie de la spécification PNG
Libellé Taille Remarque
Marquage universel 8 octets Obligatoirement: (décimal)137 80 78 71 13 10 26 10
Taille des données du segment 4 octets BigEndian un entier non signé
Type Segment 4 octets, ASCII Le premier DOIT être 'IHDR'
Données du Segment La taille a été précisée auparavant Pour IHDR, on aura : width(4 octets), height(4 octets) bit depth(1 octet), color type(1 octet),
compression method(1 octet), filter method(1 octet), interlace method(1 octet)

Il ne reste plus qu'à traiter tout ça. C'est simple: on ouvre le fichier en mode binaire et on consomme les 8 + 4 + 4 + 13 = 29 premiers octets que l'on va découper et interpréter (pour rappel 29 octets c'est 29*8 soit 232 bits).

 
Sélectionnez
<?php
$fp = fopen('fichierPng', 'rb');
$header = fread($fp, 29);
fclose($fp);

// ...

Ici, la problématique est que $header est de type string, pas de type int. Or en PHP, toute manipulation sur les bits ne peut se faire que sur des entiers, si on effectue de telles opérations sur des chaines, PHP se servira de la table ASCII pour comprendre ce qu'on lui dit, octet par octet. Ce n'est pas ce que l'on veut.
Il nous faut donc une fonction qui sait analyser les octets dans une chaine binaire, et en retourner quelque chose d'utile. Accueillez à bras ouvert unpack().

unpack() est exactement la fonction qu'il nous faut : elle analyse les octets dans une chaine et les transforme au format que l'on souhaite. Voyons cela:

 
Sélectionnez
<?php
$data = unpack('A8head/Nsize/Atype/Nwidth/Nheight/cdepth/ccolor/ccompress/cfilter/cinterlace' ,$header);
var_dump($data);

/*
array(10) {
  ["head"]=>
  string(8) "?PNG?"
  ["size"]=>
  int(13)
  ["type"]=>
  string(1) "I"
  ["width"]=>
  int(1212436992)
  ["height"]=>
  int(8192)
  ["depth"]=>
  int(0)
  ["color"]=>
  int(0)
  ["compress"]=>
  int(32)
  ["filter"]=>
  int(8)
  ["interlace"]=>
  int(6)
}
*/

Tous les formats de unpack() sont dans la documentation, on donne des noms à chacun des index dans le tableau de résultats et on sépare chaque donnée extraite par un '/'. Le caractère '@' permet de se déplacer à l'octet numéro xxx.

Attention, méfiez-vous bien de la taille en octets ou en bits de la donnée traitée. Si vous exprimez la mauvaise taille, PHP va manger plus ou moins de données et ne sera pas en mesure d'interpréter la donnée correctement.

Pour y voir plus clair, aidez-vous d'un éditeur hexadécimal. J'utilise hexdump -C en ligne de commandes, sinon ghex2 sous le bureau.

Toutes les manipulations que l'on vient de faire là ont déjà été réalisées, en C (langage beaucoup plus approprié pour cela), dans la libpng, utilisée par l'extension PHP ext/gd.
Il vaut toujours mieux se reposer sur une implémentation en C, généralement plus rapide et efficace, même si dans notre cas, l'utilisation de PHP n'est vraiment pas beaucoup plus lourde.

IV-C. Echange brut des valeurs de 2 variables

Aussi appelée le "XOR_swap", cette technique basée sur l'opération OU EXCLUSIF (XOR), consiste à échanger le contenu de 2 variables distinctes, sans passer par une variable intermédiaire :

XOR Swap en PHP
Sélectionnez
<?php
$a = 0x6C;
$b = 0xC8;

function xor_swap(&$v1, &$v2) {
    if ($v1 != $v2) {
    $v1 ^= $v2;
    $v2 ^= $v1;
    $v1 ^= $v2;
    }
}

xor_swap($a, $b);

printf('$a vaut maintenant 0x%2X et $b vaut 0x%2X', $a, $b);
// Affiche effectivement $a vaut 0xC8 et $b vaut 0x6C

Une technique plutôt utilisée en C à l'époque ou une variable intermédiaire de type int était trop grosse pour la mémoire (ou encore pour de l'embarqué où la mémoire est extrêmement limitée).
Beaucoup d'algorithmes de cryptographie utilisent des principes similaires (registres à décalage, Réseau de Feistel...). Il est vrai qu'en PHP c'est plus rare, ça va sans dire.

V. Conclusions

Savoir manipuler des bits et des octets est une base fondamentale de l'informatique. PHP n'est pas spécialement le langage adapté à ce cas, mais il propose tout de même quelques fonctions et opérateurs sympas, très largement empruntés du langage C (sur lequel il repose), maître incontestable dans ce domaine.
Comme nous avons pu le voir, même en PHP, il peut parfois être utile de manipuler des données binaires au niveau de l'octet, que ce soit pour manipuler PHP lui-même qui parfois vous le proposera (son rapport d'erreur par exemple), ou pour inspecter n'importe quel fichier binaire, au moyen des superbes fonctions pack() et unpack(). Tant de cas peuvent encore se présenter ...

Le langage PHP ne doit pas faire oublier les fondamentaux de l'informatique, et je conseille à chacun ne maitrisant pas ces aspects-là de se pencher dessus à temps perdu, cela permet de comprendre les piles des systèmes d'informations qui nous entourent, quels qu'ils soient. L'application la plus visible de la manipulation de bits est sans doute la couche réseau et ses protocoles (nous aurions d'ailleurs pu prendre cela comme exemple dans cet article).

L'Algèbre de Boole
Mathématiques binaires appliquées : algorithme du CRC
Les opérateurs de bits en C
Remerciements à ClaudeLELOUP pour ses relectures.

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Copyright © 2011 . Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.