PHP moderne

Quelques notions un peu plus avancées en PHP qu’il est bon de connaître et d’utiliser à bon escient !

Espaces de nommage en PHP

Tout d’abord, quelques notions sur les espaces de nommage en PHP qui ressemblent un peu à ceux du C++ ou aux packages Java avec quelques différences de notation.

Utiliser un espace de nommage en PHP

<?php
  // on déclare l'usage d'un espace de nommage
  use Symfony\Component\HttpFoundation\Request;
  $request = Request::create('/controleur.php');
  // au lieu de :
  $response = new \Symfony\Component\HttpFoundation\Request('/controleur.php');

Créer un espace de nommage en PHP

On déclare un Espace de nommage en PHP avec le mot clef namespace. Supposons de plus qu’une méthode d’une classe de cet espace de nommage émette une exception.

<?php
  namespace fr\iut45\roza;
  class Calcule
  {
      public function divise(a,b)
      {
          if ( b ===0) throw new Exception('division par zéro !');
          // ...
      }
  }

A quel espace de nommage appartient Exception ?? Il faut être plus précis et indiquer qu’on fait bien référence à la classe Exception de l’espace de nommage global ou racine :

<?php
  namespace fr\iut45\roza;
  class Calcule
  {
      public function divise(a,b)
      {
          if ( b ===0) throw new \Exception('division par zéro !');
          // ...
      }
  }

Classes, fonctions anonymes et closures en PHP

Fonctions anonymes et lambdas en PHP

On peut utiliser des fonctions anonymes et des lambdas en PHP !

<?php

// anonymous function = fonction anonyme
$carres = array_map(function($n){ return pow($n,2); }, [1,2,3]);
print_r($carres);
// avec des lambdas à la manière de python ou des "fat arrow" JS
$carres = array_map(fn($n): int => pow($n,2) , [1,2,3,4]);
print_r($carres);

Resultat brut html :

Array
(
    [0] => 1
    [1] => 4
    [2] => 9
)
Array
(
    [0] => 1
    [1] => 4
    [2] => 9
    [3] => 16
)

Classes anonymes en PHP

On peut également utiliser des classes anonymes en PHP, à la manière de Java :

<?php

interface Logger {
    public function log(string $msg);
}

class Application {
    private $logger;

    public function getLogger(): Logger {
         return $this->logger;
    }

    public function setLogger(Logger $logger) {
         $this->logger = $logger;
    }
}

$app = new Application;
$app->setLogger(new class implements Logger {
    public function log(string $msg) {
        echo $msg;
    }
});

var_dump($app->getLogger());

Resultat brut html :

object(Logger@anonymous)#2 (0) {
}

Attacher un état à une closure en PHP

On utilise le mot-clef use dans un autre sens ici, pour attacher un état à une closure implémentée dans une fonction anonyme :

<?php
  $app = new Micro(); //Phalcon ou new Silex\Application() (Silex)
  $app['debug'] = true;
  $app->get('/api/contact/{id}',
          function($id) use ($app) {
              $ami = get_friend_by_id($id);
              if (!$ami) $app->abort(404, "Contact inexistant");
              else return json_encode($ami,JSON_PRETTY_PRINT);
          });

Serveur Web embarqué de PHP

On peut le lancer depuis le répertoire de son choix

php -S localhost:8000

Pour y accéder depuis d’autres machines

php -S 0.0.0.0:8000

Avec une conf personnalisée

php -S 0.0.0.0:8000 -c myphp.ini

Avec un routeur principal

php -S 0.0.0.0:8000 index.php

Autres fonctionnalités spécifiques à PHP 7 et PHP 8

Support Unicode

<?php

$special1 = "\u{9998}";
$special2 = "\u{9999}";
echo "$special1 \n";
echo "$special2 \n";

Resultat brut html :

馘 
香

Contrôle des types de paramètres

Ajouter éventuellement la directive declare(strict_types=1) mais pas obligatoire sur php récent …

<?php
declare(strict_types=1); // pas obligatoire (PHP récent)

function essai(int $param){
    return $param + 1;
}

echo essai(2);

echo essai("2"); // provoque une erreur TypeError

Resultat brut

3PHP Fatal error:  Uncaught TypeError: essai(): Argument #1 ($param) must be of type int, string given, called in /Users/roza/work/iut/prog/PHP/php-depot/source/exemples/ex-PHP7-PHP8/strictTypes.php on line 10 and defined in /Users/roza/work/iut/prog/PHP/php-depot/source/exemples/ex-PHP7-PHP8/strictTypes.php:4
Stack trace:
#0 /Users/roza/work/iut/prog/PHP/php-depot/source/exemples/ex-PHP7-PHP8/strictTypes.php(10): essai('2')
#1 {main}
  thrown in /Users/roza/work/iut/prog/PHP/php-depot/source/exemples/ex-PHP7-PHP8/strictTypes.php on line 4

Fatal error: Uncaught TypeError: essai(): Argument #1 ($param) must be of type int, string given, called in /Users/roza/work/iut/prog/PHP/php-depot/source/exemples/ex-PHP7-PHP8/strictTypes.php on line 10 and defined in /Users/roza/work/iut/prog/PHP/php-depot/source/exemples/ex-PHP7-PHP8/strictTypes.php:4
Stack trace:
#0 /Users/roza/work/iut/prog/PHP/php-depot/source/exemples/ex-PHP7-PHP8/strictTypes.php(10): essai('2')
#1 {main}
  thrown in /Users/roza/work/iut/prog/PHP/php-depot/source/exemples/ex-PHP7-PHP8/strictTypes.php on line 4

Contrôle des types de retour

On peut aussi déclarer le type de retour d’une fonction :

<?php

function maintenant0(): DateTime
{
    return new DateTime('now');
}

function maintenant(): DateTime
{
    return 3;// inavlide
}

function maintenant2(): DateTime
{
    return "aujourd'hui"; //invalide
}

function foo(): DateTime 
{ 
    return null; // invalide
}

var_dump(maintenant0());//correct
var_dump(maintenant());//TypeError
var_dump(maintenant2());//TypeError
var_dump(foo());//TypeError

Resultat brut :

object(DateTime)#1 (3) {
  ["date"]=>
  string(26) "2023-06-27 14:48:45.807875"
  ["timezone_type"]=>
  int(3)
  ["timezone"]=>
  string(3) "UTC"
}
PHP Fatal error:  Uncaught TypeError: maintenant(): Return value must be of type DateTime, int returned in /Users/roza/work/iut/prog/PHP/php-depot/source/exemples/ex-PHP7-PHP8/returnType.php:10
Stack trace:
#0 /Users/roza/work/iut/prog/PHP/php-depot/source/exemples/ex-PHP7-PHP8/returnType.php(24): maintenant()
#1 {main}
  thrown in /Users/roza/work/iut/prog/PHP/php-depot/source/exemples/ex-PHP7-PHP8/returnType.php on line 10

Fatal error: Uncaught TypeError: maintenant(): Return value must be of type DateTime, int returned in /Users/roza/work/iut/prog/PHP/php-depot/source/exemples/ex-PHP7-PHP8/returnType.php:10
Stack trace:
#0 /Users/roza/work/iut/prog/PHP/php-depot/source/exemples/ex-PHP7-PHP8/returnType.php(24): maintenant()
#1 {main}
  thrown in /Users/roza/work/iut/prog/PHP/php-depot/source/exemples/ex-PHP7-PHP8/returnType.php on line 10

Tableaux constants

<?php
define('POISSONS', [
    'sole',
    'sardine',
    'lotte'
]);

echo POISSONS[1].PHP_EOL; // affiche 'sardine'

Resultat brut :

sardine

Types scalaires en PHP

Les types scalaires permettent de représenter un nombre d’arguments variables stockés dans un tableau. Ici on a plus précisément un tableau de tableaux d’entiers et on souhaite renvoyer la somme avoir en résultat un tableau contenant les sommes des entiers de chaque tableau.

avec fonction anonyme

<?php
/* avec fonction anonyme */
function arraysSum(array ...$arrays): array
{
    return array_map(function(array $array): int {
        return array_sum($array);
    }, $arrays);
}

print_r(arraysSum([1,2,3], [4,5,6], [7,8,9]));

Resultat brut :

Array
(
    [0] => 6
    [1] => 15
    [2] => 24
)

avec fonction lambda

<?php
/* avec fonction lambda */
function arraysSum(array ...$arrays): array
{
    return array_map(fn($array): int => array_sum($array), $arrays);
}

print_r(arraysSum([1,2,3], [4,5,6], [7,8,9]));

Resultat brut :

Array
(
    [0] => 6
    [1] => 15
    [2] => 24
)

avec fonction nommée

<?php
/* en passant le nom de la fonction à array_map */
function arraysSum(array ...$arrays): array
{
    return array_map('array_sum', $arrays);
}

print_r(arraysSum([1,2,3], [4,5,6], [7,8,9]));

Resultat brut :

Array
(
    [0] => 6
    [1] => 15
    [2] => 24
)

Union types, opérateur null-safe et interface Stringable

En PHP 8 et supérieur on peut utiliser des Union types. Pour l’exemple, on reprend notre petite classe étudiant :

<?php
/** Classe Etudiant en PHP */

class Etudiant{
  /** Identification unique d'un etudiant */
  protected $etudiant_id;
  /** Nom de l'etudiant */
  protected $nom;
  /** Date de naissance de l'etudiant */
  protected $naissance;

  public function __construct($id, $nom,$naissance){
    $this->etudiant_id = (int)$id;  // cast vers integer
    $this->nom = (string)$nom;     // cast vers string
    $this->naissance= (int)$naissance;  // cast vers date(timestamp)
  }

  /**
   * Fonction de comparaison simplifiee entre etudiants
   * == comparera id, nom et naissance 
   */
  public function equals(etudiant $etudiant){
    return ($this->getId() == $etudiant->getId());
  }

  public function getId(){
    return $this->etudiant_id;
  }

  public function getNom(){
    return $this->nom;
  }

  public function getNaissance(){
    return $this->naissance;
  }

  public function __toString(){
  	setlocale(LC_TIME, "fr_FR");
  	$ne=strftime('%A %d %B %Y',$this->naissance);
    return 'etudiant: id=' . $this->getId().', nom='.$this->getNom()." $ne";
  }
}

Union de types

<?php
// UnionTypes PHP8
function somme(int|float $a, int|float $b ): int|float {
    return $a + $b;
}

echo somme(2,3) . ' ' . gettype(somme(2,3)) . PHP_EOL;
echo somme(2.5, 3.5) . ' ' .gettype(somme(2.5,3.5)) . PHP_EOL;
// Déclenche une TypeError
//echo somme('a','b') . PHP_EOL;

require('etudiant.php');
function nom(Etudiant|null $etu){
    // ou function nom(?Etudiant $etu)
    if (!empty($etu))
        return strtoupper($etu->getNom());
    else 
        return 'inconnu';
}

$etu = new Etudiant(234,"Talon",time());
echo nom($etu) . PHP_EOL;
echo nom(NULL) . PHP_EOL;

Resultat brut :

5 integer
6 double
TALON
inconnu

Union de types et null safe operator

<?php
require('etudiant.php');
function nom(Etudiant|null $etu){
    return $etu?->getNom() ;
}

$etu = new Etudiant(234,"Talon",time());
echo nom($etu) . PHP_EOL;  // Talon
echo nom(NULL) . PHP_EOL; // aucun affichage

// on peut aussi chainer les appels et faire des choses du type
$pays = $session?->user?->getAddresse()?->pays;

Union de types et interface Stringable

En PHP au moins 8, on peut utiliser l’interface Stringable que son réputés implémenter par défaut tous les objets qui offrent une méthode __toString() pour par exemple proposer en paramètre d’une fonction quelque chose qui est soit un string soit Stringable !

<?php
require('etudiant.php');
// on peut passer en argument un string ou quelque chose de Stringable
function maj(string|Stringable $chose)
{
    if (gettype($chose) =='string'){
        $res = $chose;
    }
    else {
        $res = $chose->__toString();
    }
    return strtoupper($res);
}

$etu = new Etudiant(1234,"Talon",time());
echo maj($etu) . PHP_EOL;
echo maj('bonjour') . PHP_EOL;

Resultat brut :

PHP Deprecated:  Function strftime() is deprecated in /Users/roza/work/iut/prog/PHP/php-depot/source/exemples/ex-PHP7-PHP8/etudiant.php on line 40

Deprecated: Function strftime() is deprecated in /Users/roza/work/iut/prog/PHP/php-depot/source/exemples/ex-PHP7-PHP8/etudiant.php on line 40
ETUDIANT: ID=1234, NOM=TALON MARDI 27 JUIN 2023
BONJOUR