Compléments sur les bases de PHP

Youssef Chahir
GREYC — Université de Caen
TODO fonctions anonymes:
  • passage d'une fonction existante : string
  • use() pour faire une clôture
TODO autres :
  • type checking ⇒ mis avec la POO
  • dates
  • namespaces
  • system()
  • serialisation
  • input filters ⇒ plutôt du web

Fonctions de tri

Tri selon les valeurs :

  • La fonction sort() effectue un tri sur les valeurs des éléments d’un tableau selon un critère alphanumérique :selon les codes ASCII :
    • «a» est après «Z» et «10» est avant «9»
    • Le tableau initial est modifié et non récupérables dans son ordre original
    • Pour les tableaux associatifs les clés seront perdues et remplacées par un indice créé après le tri et commencant à 0
  • La fonction rsort() effectue la même action mais en ordre inverse des codes ASCII.
  • La fonction asort() trie également les valeurs selon le critère des codes ASCII, mais en préservant les clés pour les tableaux associatifs
  • La fonction arsort() la même action mais en ordre inverse des codes ASCII
  • la fonction natcasesort() effectue un tri dans l’ordre alphabétique non ASCII (« a » est avant « z » et « 10 » est après « 9 »)

Tri sur les clés :

  • La fonction ksort() trie les clés du tableau selon le critère des codes ASCII, et préserve les associations clé /valeur
    • La fonction krsort() effectue la même action mais en ordre inverse des codes ASCII
<?php
$tab2
=array("1622"=>"Molière","1802"=>"Hugo","1920"=>"Vian");
ksort($tab2);
echo
"Tri sur les clés de\$tab2";
foreach (
$tab2 as $cle=>$valeur){
  echo 
"l’élément a pour clé : $cle \n"
  echo 
" et pour valeur : $valeur\n";
}
?>
?>
Tri sur les clés de$tab2l’élément a pour clé : 1622 
 et pour valeur : Molière
l’élément a pour clé : 1802 
 et pour valeur : Hugo
l’élément a pour clé : 1920 
 et pour valeur : Vian
?>

Bizarreries des tableaux

Le fait que les tableaux, même numériques, soient en fait des ordered map peut causer des surprises !

<?php
$tab 
= array();
$tab[0] = "zéro";
$tab[1] = "un";
$tab[100] = "cent";
$tab[] = "je suis le dernier";
$tab[2] = "deux";
$tab[] = "où suis-je ?";
foreach (
$tab as $key => $val) {
  echo 
"$key --> $val\n";
}
0 --> zéro
1 --> un
100 --> cent
101 --> je suis le dernier
2 --> deux
102 --> où suis-je ?

  • Lors du parcours avec un foreach, seul l'ordre d'insertion dans le tableau compte.
  • La construction $tab[] = ... ajoute la valeur à une clef numérique qui est la plus grande clef numérique + 1
Attention, on ne peut pas utiliser comme indice une chaîne de caractères « contenant » un entier, car elle est convertie en entier. Une façon de l'éviter est de faire commencer la chaîne par un +. ce qui empêche d'utiliser === comme on voudrait
<?php
$tab 
= array();
$tab['0'] = 'chaîne 0';
$tab[0] = 'entier 0';
$tab['1.0'] = 'chaîne 1.0';
$tab[1.0] = 'flottant 1.0';
$tab[1.1] = 'flottant 1.1';
$tab['+1'] = 'chaîne +1';
foreach (
$tab as $key => $val) {
  echo 
"$key --> $val\n";
}
0 --> entier 0
1.0 --> chaîne 1.0
1 --> flottant 1.1
+1 --> chaîne +1

L'indice '0' est converti en 0, l'indice '100' est converti en 100, l'indice '-1' est converti en -1, mais les indices '0.0', '+1', '03', '00', '0x0', '2e2' restent des chaînes, car ce ne sont pas des « entiers décimaux valides ne commençant pas par + » (0.0 est flottant, +1 commence par +, 03 et 00 seraient en octal, 0x0 en hexa, et 2e2 un float).
détails sur la conversion des indices dans le manuel

Les tableaux sont manipulés « par copie » : dans le code suivant, la 2e ligne crée une copie du tableau !

<?php
$x 
= array('toto' => 1'tata' => 2);
$y $x;
$x['titi'] = 3;
echo 
count($x) . "\n";
echo 
count($y) . "\n";
3
2

Ce n'est pas une erreur d'utiliser un nombre ou null comme un tableau. Ça renvoie simplement toujours null.
$tab null;
var_export($tab['toto']);  // NULL
$tab 1234;
var_export($tab['toto']);  // NULL

Fonctions utiles avec les tableaux

Principales fonctions utiles avec les tableaux :
  • key_exists($key, $array) : renvoie true si le tableau $array contient la clef $key, et false sinon (il existe l'alias plus long array_key_exists)
  • in_array($val, $array, true) : renvoie true si le tableau $array contient la valeur $val, et false sinon.
    Le troisième paramètre sert à utiliser la comparaison stricte (celle de ===) plutôt que la comparaison faible (celle de ==), qui est le défaut. À ne pas oublier ! Si on l'enlève ou si on le met à false, les résultats peuvent être très surprenants…
    l'explication est que le tableau contient true et false. Toute valeur est égale soit à l'un soit à l'autre, donc in_array renverra toujours true.
  • array_search($val, $array, true) pour récupérer l'indice d'une valeur dans un tableau. Attention à la comparaison faible :
    • il faut passer true comme 3e paramètre pour que la comparaison lors de la recherche soit stricte, comme pour in_array
    • mais il faut aussi faire attention à la valeur de retour : la fonction peut renvoyer 0 ou false, ce n'est pas du tout pareil !
  • unset($array[$key]) pour supprimer un élément ; attention, il n'y a pas de redécalage des indices, utiliser array_values derrière si besoin
  • $tableau = array_count_values ($variable);
    retourne un tableau comptant le nombre d'occurrences des valeurs d'un tableau.
  • $tableau = array_diff ($var_1, $var_2, ..., $var_N);
    retourne dans un tableau contenant les valeurs différentes entre deux ou plusieurs tableaux.
  • $tableau = array_intersect ($var_1, $var_2, ..., $var_N);
    retourne un tableau contenant les enregistrements communs aux tableaux entrés en argument.
  • $tableau = array_flip ($variable);
    intervertit les paires clé/valeur dans un tableau.
  • $tableau = array_keys ($variable [, valeur]);
    retourne toutes les clés d'un tableau ou les emplacements d'une valeur dans un tableau.
  • Voir aussi array_values et éventuellement array_slice .

toutes les fonctions sur les tableaux

tuto sur la manipulation de tableaux

Fonctions anonymes

PHP permet d'utiliser des fonctions anonymes, comme en JavaScript.

On peut ainsi mettre une fonction dans une variable :
<?php
$toto 
= function () {
    echo 
"je suis anonyme\n";
};
$toto();

$titi = function ($x) {
    echo 
"\$x = $x\n";
};
$titi("salut");

$titi $toto;
$titi();
je suis anonyme
$x = salut
je suis anonyme

Cela évite de déclarer une fonction qui n'est utilisée qu'une seule fois

Particulièrement utile comme paramètre de certaines fonctions (voir la suite)

Map/Filter/Reduce sur des tableaux

  • $tableau = array_filter($variable, "fonction")
    retourne un tableau contenant les enregistrements filtrés d'un tableau à partir d'une fonction.
  • $tableau = array_map($var_1 [, $var_2, ..., $var_N], 'fonction');
    applique une fonction à un ou plusieurs tableaux.
  • $tableau = array_merge($var_1, $var_2, ..., $var_N);
    enchaîne des tableaux entrés en argument afin d'en retourner un unique.
  • $tableau = array_merge_recursive($var_1, $var_2, ..., $var_N);
    enchaîne des tableaux en conservant l'ordre des éléments dans le tableau résultant. Dans le cas de clés communes, les valeurs sont placées dans un tableau.
  • true | false = array_multisort($var, critère1, critère2 [, ..., $var_N, critère1, critère2])
    trie un ou plusieurs tableaux selon un ordre croissant ou décroissant (SORT_ASC ou SORT_DESC) et selon une comparaison alphabétique, numérique ou de chaîne de caractères (SORT_REGULAR, SORT_NUMERIC ou SORT_STRING).
  • $tableau = array_pad($variable, taille, valeur);
    recopie tout un tableau en ajustant sa taille à l'argument correspondant et en bourrant d'une valeur spécifiée les éléments vides.
<?php
function impair($var)
{
  return (
$var == 1);
}

function 
pair($var)
{
  return (
$var == 0);
}

$array1 = array ("a"=>1"b"=>2"c"=>3"d"=>4"e"=>5);
$array2 = array (6789101112);
echo 
"Impairs :\n";
print_r(array_filter($array1"impair"));
echo 
"Pairs :\n";
print_r(array_filter($array2"pair"));
?>
Impairs :
Array
(
    [a] => 1
    [c] => 3
    [e] => 5
)
Pairs :
Array
(
    [0] => 6
    [2] => 8
    [4] => 10
    [6] => 12
)

Map/Filter/Reduce sur des tableaux

Exemple d'utilisation de fonctions anonymes : filtrage ou modification des valeurs d'un tableau à l'aide d'une fonction

  • Modifier les valeurs d'un tableau : array_map($fonction, $tab) applique la fonction $fonction à chaque élément du tableau $tab et retourne le résultat (sans modifier $tab)
  • Filtrer les valeurs d'un tableau : array_filter($tab, $fonction) renvoie un tableau ne contenant que les valeurs du tableau $tab pour lesquelles la fonction $fonction a renvoyé true
<?php
$tab 
= [ 112371520, -24 ];
echo 
"Tableau initial :\n";
var_export($tab);
echo 
"\n";

$tab2 array_map(function ($value) {
  return 
$value 3;
}, 
$tab);
echo 
"Tableau modifié :\n";
var_export($tab2);
echo 
"\n";

$tab3 array_filter($tab2, function ($value) {
  return 
$value 25;
});
echo 
"Tableau résultant filtré :\n";
var_export($tab3);
echo 
"\n";

echo 
"NB: le tableau initial\n n'a pas bougé :\n";
var_export($tab);
echo 
"\n";
Tableau initial :
array (
  0 => 1,
  1 => 12,
  2 => 3,
  3 => 7,
  4 => 15,
  5 => 20,
  6 => -24,
)
Tableau modifié :
array (
  0 => 3,
  1 => 36,
  2 => 9,
  3 => 21,
  4 => 45,
  5 => 60,
  6 => -72,
)
Tableau résultant filtré :
array (
  0 => 3,
  2 => 9,
  3 => 21,
  6 => -72,
)
NB: le tableau initial
 n'a pas bougé :
array (
  0 => 1,
  1 => 12,
  2 => 3,
  3 => 7,
  4 => 15,
  5 => 20,
  6 => -24,
)

Attention, attention, l'ordre des paramètres n'est pas le même dans les deux fonctions

Dans la même famille, mais plus subtil, il existe aussi array_reduce, pour effectuer une opération sur toutes les valeurs à la fois
cependant les réductions les plus courantes ont une fonction dédiée :
  • array_sum pour la somme, array_product pour le produit
  • min et max pour le minimum et le maximum

Tableaux de tableaux

On peut bien sûr mettre des tableaux dans des tableaux, et récursivement, sans difficulté :

<?php
$couleurs 
= array(
    
"aqua" => array(0255255),
    
"black" => array(000),
    
"blue" => array(00255),
);

$couleurs["white"] = array(255255255);

// On parcourt le tableau $couleurs
foreach ($couleurs as $c => $rgb) { 
    
// Ici $rgb est lui-même un tableau
    
echo "$c = rgb($rgb[0]$rgb[1]$rgb[2])\n";
}
aqua = rgb(0, 255, 255)
black = rgb(0, 0, 0)
blue = rgb(0, 0, 255)
white = rgb(255, 255, 255)

Tableaux en paramètres des fonctions

Passer un gros tableau à une fonction peut sembler une mauvaise idée, car la sémantique des paramètres est celle du « passage par copie »

Cependant, le moteur de PHP fait cette copie paresseusement, c'est-à-dire uniquement au moment où ça devient nécessaire

si le tableau n'est jamais modifié, il n'est jamais copié !

On a parfois besoin de faire des fonctions qui modifient un tableau donné en place ; dans ce cas on peut passer en paramètre une référence vers le tableau, en ajoutant & devant le paramètre formel

<?php
function no_modif($tab) {
  
$tab[0] = 'youpi';
}
function 
yes_modif(&$tab) {
  
$tab[0] = 'youpi';
}

$x = [ 'toto''tutu' ];
echo 
"no_modif ne modifie pas le tableau :\n";
no_modif($x);
var_export($x);

echo 
"\n--------\n";
echo 
"yes_modif modifie le tableau :\n";
yes_modif($x);
var_export($x);
no_modif ne modifie pas le tableau :
array (
  0 => 'toto',
  1 => 'tutu',
)
--------
yes_modif modifie le tableau :
array (
  0 => 'youpi',
  1 => 'tutu',
)

NB : le & est à mettre seulement lors de la définition de la fonction, pas lorsqu'on l'appelle. On ne passe pas explicitement un « pointeur ».

Arguments d'une fonction

PHP supporte les fonctions à nombre d'arguments variable :

  • func_num_args() retourne le nombre d'arguments passé à la fonction
  • func_get_arg(arg_num) retourne l'argument à la position arg_num dans la liste des paramètres (à partir de 0) ; retourne false si arg_num est trop grand
  • func_get_args() retourne un tableau qui contient la liste des arguments
<?php
function foo() { 
  
$numargs=func_num_args(); $arg_list=func_get_args();
  for (
$i 0$i $numargs$i++) { 
    echo 
"L'argument $i est: " $arg_list[$i] . "\n"
  } 
}
foo(123); foo("toto");
?>
L'argument 0 est: 1
L'argument 1 est: 2
L'argument 2 est: 3
L'argument 0 est: toto

Manipulation simple de fichiers

Les fonctions suivantes sont simples à utiliser, mais pas forcément pertinentes sur de gros fichiers (on charge tout le fichier en mémoire).
  • Lire le contenu d'un fichier : file_get_contents("nomdufichier"), qui renvoie le contenu du fichier sous forme d'une chaîne de caractères.
  • Pour récupérer les lignes du fichier, on peut utiliser file("nomdufichier"), qui renvoie un tableau contenant toutes les lignes du fichier (avec les sauts de ligne).
  • Écrire un fichier : file_put_contents("nomdufichier", "contenu")
  • Ajouter du contenu à un fichier : file_put_contents("nomdufichier", "contenu", FILE_APPEND) (on utilise un paramètre optionnel de la fonction)
<?php
$file 
"demo/fichier.txt";

$contents file_get_contents($file);
echo 
"Contenu du fichier, V1 :\n$contents";

// get contents of a file line by line
$lines file($file);
foreach (
$lines as $num => $line) {
  echo 
"Ligne $num : « $line »\n";
}

Contenu du fichier, V1 :
Coucou,
je suis un fichier !

Au revoir

Ligne 0 : « Coucou,
 »
Ligne 1 : « je suis un fichier !
 »
Ligne 2 : « 
 »
Ligne 3 : « Au revoir
 »
Ligne 4 : « 
 »

Manipulation subtile de fichiers

Pour des besoins plus précis, ou pour des fichiers lourds, il existe des fonctions bas niveau c'est en fait une surcouche très fine de la bibliothèque standard de C :
  • La fonction fopen prend en 2e paramètre le mode d'ouverture du fichier : r r+ w w+ a a+
  • La fonction fread lit le contenu du fichier jusqu'à une limite donnée
  • La fonction fgets envoie la ligne courante sur laquelle se trouve le pointeur du fichier
  • La fonction fwrite permet d'écrire dans un fichier
  • La fonction fclose permet de fermer la ressource (elle est cependant fermée automatiquement à la fin du script)
<?php
$file 
"demo/fichier.txt";

// à l'ancienne
$fd fopen$file"r" );
$contents fread($fdfilesize($file));
echo 
"Contenu du fichier, V2 :\n$contents";
fclose($fd);

// get contents of a file line by line
$fd fopen$file"r" );
while (
$line fgets($fd4096)) {
  echo 
"<p>une ligne : « $line »</p>";
}
fclose($fd);
Contenu du fichier, V2 :
Coucou,
je suis un fichier !

Au revoir

une ligne : « Coucou, »

une ligne : « je suis un fichier ! »

une ligne : « »

une ligne : « Au revoir »

une ligne : « »

Expressions régulières

Plusieurs fonctions existent pour faire des recherches et remplacements dans des chaînes ou des tableaux en utilisant des expressions régulières (regex)

Le format des regex est celui de Perl («PCRE»), comme la plupart des langages modernes (notamment Python ou Java)

Voir la doc pour tous les détails sur le format des regex ou les options des fonctions

Compléments à trier

PHP en ligne de commande

function usage($msg) {
    echo 
'Error: '.$msg.PHP_EOL;
    echo 
'Usage: blabla…'.PHP_EOL;
    die(
1);
}
/* PHP_SAPI indicates the access mode */
if (PHP_SAPI !== 'cli') {
    
usage('command line only');
}
/* $argv contains the argv array from C (there is no $argc) */
if (count($argv) != 2) {
    
usage('takes one argument');
}
$mode $argv[1];

Namespaces

Si on utilise des namespaces, on a envie d'utiliser use

Mais du coup sans autoloader c'est pas très DRY :
    require_once('views/View.php');
    use views\View;

Mais ça m'embête de définir une fonction d'autoload à la main qu'il faut mettre dans tous les projets…

J'ai regardé un peu, et a priori la solution la plus simple c'est d'utiliser l'autoloader par défaut. Il suffit de mettre spl_autoload_register(); quelque part, et il va chercher le fichier de classe dans le répertoire correspondant au namespace.

Seul bémol : le nom du fichier de classe doit être tout en minuscules, sinon ça ne marche pas. (NB: Les noms de classes et de fonctions en PHP ne sont pas sensibles à la casse.) C'est pas génial, mais c'est pas dramatique non plus.

Alternative : ne pas utiliser les namespaces comme des packages, mais juste pour protéger notre espace de noms. On peut continuer à utiliser des répertoires pour grouper nos fichiers, même s'ils sont dans le même namespace.