Skip to content

Instantly share code, notes, and snippets.

@tomsihap
Last active February 2, 2023 14:01
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tomsihap/6f70c5051b3cf28efb913d933df679cb to your computer and use it in GitHub Desktop.
Save tomsihap/6f70c5051b3cf28efb913d933df679cb to your computer and use it in GitHub Desktop.
PHP functions exercises

PHP : Fonctions

Schéma d'une fonction

Fonction basique

Une fonction basique est définie par le mot-clé fonction et exécute les instructions définies dans la fonction lorsque la fonction est appelée. Attention, on ne peut pas appeler une fonction avant qu'elle ait été définie :

function sayHelloWorld() {
    echo "Hello world";
}

sayHelloWorld(); // Affiche "Hello world"

Valeur de retour

Plutôt que d'afficher directement quelque chose, les fonctions doivent retourner une valeur de sorte à être bien plus flexibles ! Par exemple, on peut avoir besoin de récupérer le résultat de la fonction sans l'afficher directement. En reprenant le modèle précédent :

// 1. Je veux mettre "Hello world" dans ma variable $data puis l'ajouter à mon tableau $phrases
$data = sayHelloWorld();    // On essaie de récupérer "Hello world";
$phrases[] = $data;         // Ajout au tableau
var_dump($phrases);         // On vérifie le contenu du tableau

Le tableau est... Vide ! En effet, la fonction ne retourne rien : elle se contente d'afficher quelque chose, mais elle même ne renvoie aucune donnée. Pour fixer le problème, on indique une valeur de retour avec return. Avec l'exemple ci-dessous, notre var_dump($phrases) précédent fonctionnera :

function sayHelloWorld() {
    return "Hello world";
}

echo sayHelloWorld(); // Affiche la valeur de retour de la fonction qui est "Hello world"

Arguments de fonction

On liste les arguments dans la définition de la fonction. Déclarés ainsi, ils sont obligatoires :

function plusGrandQue($a, $b) {
    return $a >= $b ? true : false;
}

var_dump( plusGrandQue(32, 533) ); // Affiche false(bool)
var_dump( plusGrandQue(32) );      // Affiche une erreur

Typer la valeur de retour

Lorsque l'on créée une fonction, c'est pour qu'elle soit réutilisée: il faut la documenter au maximum de sorte à s'en reservir sans avoir à la relire plus tard. Selon la façon dont la fonction plusGrandQue($a, $b) est écrite, on n'a aucune idée de ce que la fonction retourne : une string ("a est plus grand que b"), un int (la valeur du plus grand) ou un bool (true si le premier est plus grand) ? On peut déclarer le type de retour ainsi :

function plusGrandQue($a, $b) : bool
{
    return $a >= $b ? true : false;
}

Typer les arguments attendus

On peut typer les arguments attendus par la fonction :

function plusGrandQue(int $a, int $b) : bool
{
    return $a >= $b ? true : false;
}

Ainsi, on ne peut comparer que des integer :

plusGrandQue(23.32, 12) // Attention, 23.32 sera converti en int, soit : 23
plusGrandQue("42", 12)  // erreur

Arguments typés ou nuls

Parfois, on n'est pas certains d'avoir une donnée à fournir dans la fonction, pour autant la fonction accepte obligatoirement un argument. Par exemple, dans le cas où une fonction d'un site e-commerce qui récupère la dernière commande d'un utilisateur : comme on n'est pas sûr d'avoir un id de dernière commande (si l'user n'a jamais commandé), on aura soit null soit un int à passer à la fonction. Pour cela, on rajoute ? devant le type de l'argument :

function lastCustomerOrder(?int $orderId) {
  var_dump($orderId);
}

Arguments facultatifs

Parfois, certains arguments sont clairement facultatifs : ils ont en fait une valeur par défaut :

function saySomething($phrase = null) {
    if ($phrase === null) {
        return "rien !";
    }
    else {
        return $phrase;
    }
}

saySomething(); // Retourne "rien !"
saySomething("hello"); // Retourne "hello"

Note sur les conditions : optimisation : pas besoin d'un else ?

Dans de nombreux cas, le else est inutile si on a un if avec un return : dans le cas ci-dessus, soit on n'a pas de phrase et on affiche rien !, soit on en a une et on l'affiche. Dans le cas où $phrase est null, return arrête la fonction immédiatement (en effet, elle retourne une valeur, et ne poursuit pas son exécution). On ne passera jamais dans le else.

Par contre, dans le cas où le if est faux, la suite sera exécutée (rappel : si if était vrai, la suite n'aurait pas été exécutée à cause du return).

En d'autres mots : dans tous les cas, on retourne return $phrase et la fonction s'arrête. Par contre, dans un cas particulier, on retourne return "rien !" et la fonction s'arrête.

Dans ce cas particulier de if avec un return, il est préférable de retirer le else !

function saySomething($phrase = null) {
    if ($phrase === null) {
        return "rien !";
    }

    return $phrase;
}

Documenter une fonction

/**
 * Permet de retourner une phrase donnée en paramètres, si le paramètre
 * est incorrect, retourne false
 * 
 * @param string $sentence Phrase à retourner
 * @return string|bool
 */
function saySomething(string $sentence) : string
{
    if (intval($sentence) === 0) {
        return $sentence;
    }

    return false;
}

Argument facultatif ou nullable ?

function test1(?int $a) {}

function test2(int $a = null) {}

La fonction test1 requiert obligatoirement un argument $a : il peut être soit de valeur null, soit un int. La fonction test2 ne requiert pas obligatoirement un argument $a : il peut être int, ou ne pas exister tout court, car par défaut la fonction le mettra égal à null.

Fonctions récursives

Une fonction récursive est une fonction qui s'appelle elle-même. On peut l'utiliser pour parcourir un tableau dont on ne connaît pas la dimension à l'avance : ainsi la fonction, de base, parcourt les valeurs du tableau, et si elle rencontre un sous-tableau, elle se relance d'elle-même afin de parcourir le sous-tableau, et ainsi de suite.

$films = [
    'The Shawshank Redemption' => [
        'annee' => 1998,
        'note' => 8.6,
        'acteurs' => [
            'john doe',
            'emma john',
            'walter doe'
        ]
    ],
    'The Godfather' => [
        'annee' => 1998,
        'note' => 8.6
    ],
    'The Dark Knight' => [
        'annee' => 1998,
        'note' => 8.6,
        'acteurs' => [
            'john doe',
            'emma john',
            'walter doe'
        ]
    ],
    'The Lord of the Rings: The Return of the King' => [
        'annee' => 1998,
        'note' => 8.6
    ],
    'Pulp Fiction',
];

function printArrayList($array)
{
    echo "<ul>";

    foreach ($array as $k => $v) {
        if (is_array($v)) {
            echo "<li>" . $k . "</li>";
            printArrayList($v);
            continue;
        }

        echo "<li>" . $v . "</li>";
    }

    echo "</ul>";
}
echo printArrayList($films);

Exercices sur les fonctions

  1. Créez une fonction qui retourne "Bonjour à tous !". Affichez le résultat avec un echo.

  2. Créez une fonction qui calcule une vitesse en mètres par secondes (m/s) : vitesse (m/s) = temps (s)/distance (m) et qui prend en argument des mètres et des secondes. Affichez la valeur dans un echo qui dit : "La durée est de xxx secondes, la distance est de xxx mètres, la vitesse est de xxx m/s".

Problèmes

Calculer la différence entre deux brins d'ADN

Des généticiens ont besoin de calculer les différences entre deux brins d'ADN représentés par exemple comme suit:

GAGCCTACTAACGGGAT
CATCGTAATGACGGCCT
^ ^ ^  ^ ^    ^^

Les différences sont représentées par ^. Par exemple ici, la réponse est 7.

Créez une fonction qui prend en paramètres obligatoires deux string et retournez le nombre de différences entre les deux brins d'ADN.

Étapes :

  1. Vérifier que les deux strings soient de la même taille
  2. Couper chaque strings en deux tableaux distincts : ces tableaux auront 1 index par lettre
  3. Trouver une fonction qui liste les différences entre deux tableaux
  4. Retourner le nombre de différences entre les deux tableaux

Correction

function difference(string $a, string $b) {

    if( strlen($a) === strlen($b) ) {
        $arr1 = str_split($a);
        $arr2 = str_split($b);
        $diff = array_diff_assoc($arr1, $arr2);
        return count($diff);
    }

    return false;
}

Ajouter des secondes à une date donnée

Des scientifiques délirants cherchent à découvrir la date qu'il sera dans 9 gigasecondes (1 000 000 000 secondes ou 10^9 secondes). Aidez-les avec une fonction !

Créez une fonction qui prend en paramètre une date au format "YYYY-MM-DD" et qui ajoute 9 gigasecondes à cette date.

Étapes :

  1. Déclarez une fonction qui prend une string au format YYYY-MM-DD
  2. Dans la fonction, créez un objet DateTime à partir de la date en argument de la fonction : $object = new DateTime($date)
  3. Cet objet DateTime est une date "évoluée" avec plein de méthodes pour travailler sur les dates !
  4. Récupérez le timestamp de l'objet (trouvez comment récupérer le timestamp d'un objet DateTime)
  5. Ajoutez à ce timestamp 9 gigasecondes (1 000 000 000 secondes)
  6. Changez le timestamp de votre objet DateTime en lui donnant la nouvelle valeur avec : $object->setTimestamp($nouvelleValeur)`
  7. Retournez l'objet au format YYYY-MM-DD (trouvez comment retourner la date d'un DateTime dans un format précis)

Solution

function addNineGigaseconds(string $date) {
    
    // On créée un objet DateTime qui nous permet de manipuler
    // des dates facilement
    $dateObject = new DateTime($date);

    // On prépare notre variable gigasecond
    // $gigasecond = pow(10,9);
    // $gigasecond = 10e9;
    $gigasecond = 1000000000;

    // On récupère le timestamp de la date donnée (en secondes)
    $givenDateTimestamp = $dateObject->getTimestamp();

    // On ajoute la gigaseconde à la date donnée
    $newDateTimestamp = $givenDateTimestamp + $gigasecond;

    // On modifie l'objet DateTime avec le nouveau timestamp
    $dateObject->setTimestamp($newDateTimestamp);

    // On prépare la nouvelle date au format Y-m-d
    $format = $dateObject->format('Y-m-d');

    // On retourne la nouvelle date
    return $format;
}

Solution 2 : version procédurale (sans POO)

function dateAddSeconds(string $date) {
    // avec strtotime(), on récupère le timestamp d'une date donnée en string
    // intval pour n'avoir que des nombres
    $date = intval(strtotime($date));
    
    // On retourne la date avec la fonction date() et son deuxième paramètre qui permet de préciser un timestamp
    return date("Y-m-d", $date += pow(10, 9));
}

Changer de l'ADN en ARN

Des scientifiques (toujours les même), ont besoin de changer de l'ADN en ARN de sorte à pouvoir créer un chat-lapin mutant. Les quatre nucléotides de l'ADN sont : l'adénine (A), la cytosine (C), la guanine (G) et la thymine (T).

The quatre nucléotides de l'ARN dont : l'adénine (A), la cytosine (C), la guanine (G) et l'uracil (U).

On convertit un brin d'ADN en ARN en remplaçant chaque nucléotide par son complément :

G -> C
C -> G
T -> A
A -> U

En utilisant la fonction strtr(), créez une fonction qui transpose de l'ADN en ARN.

Solution

function toARN(string $adn): string {
    $transposition = [
        'C' => 'G',
        'G' => 'C', 
        'T' => 'A', 
        'A' => 'U'
    ];

    return strtr($adn, $transposition);
}

Gérer le nom du robot

Les scientifiques ont réussi, ou presque, leur mutation de chat-lapin. En réalité, ils ont créé un robot simulant leur expérience. Ils ont besoin d'une fonction qui permette de gérer l'identifiant du robot (qui peut ressembler à RXW-4382 ou WSR-3455 par exemple), qui doit changer à chaque reboot de sa carte mère.

Créez une fonction qui génère un nom aléatoire pour le robot. Les conditions sont les suivantes :

  1. Respecter le format AAA-0000 (3 lettres, un tiret, 4 chiffres)
  2. Les lettres doivent être au hasard parmi R, Z, S, X, W, T
  3. Les chiffres doivent être au nombre 4

Solution

Étapes
  • Faire un tableau contenant les 6 lettres autorisées
  • Déclarer la variable $nomRobot = '';
  • Choisir au hasard 1 élément du tableau (fonction native PHP à trouver)
  • Faire une boucle pour le faire 3 fois
  • Dans la boucle : concaténer la lettre trouvée à une variable de retour
  • Concaténer un tiret
  • Générer un nombre entre 0 et 9999 et le stocker dans $nomRobotNumber
  • Gérer les cas où $nomRobotNumber ne fait que 3 chiffres :
    • [0;9] : concaténer '000' à $nomRobot
    • [10;99] : concaténer '00' à $nomRobot
    • [100:999] : concaténer '0' à $nomRobot
  • Concatène $nomRobotNumber
  • return $nomRobot
Code
function robotName() {

    // 1. Faire un tableau contenant les lettres autorisées
    $validLetters = ["R", "Z", "S", "X", "W", "T"];

    // 2. Déclarer la variable $nomRobot = '';
    $newName = "";

    // 3. Choisir au hasard 1 élément du tableau (fonction native PHP à trouver)
    // 4. Faire une boucle pour le faire 3 fois
    for ($i=0; $i < 3; $i++) {
        // une lettre aléatoire choisie par ID grâce à rand(0,5)
        // 5. Dans la boucle : concaténer la lettre trouvée à une variable de retour

        $newName.= $validLetters[rand(0, 5)];
    }

    // 6. Concaténer un tiret
    $newName.="-";

    // 7. Générer un nombre entre 0 et 9999 et le stocker dans $nomRobotNumber
    $robotNumber = rand(0, 9999);

    // 8. Gérer les cas où $nomRobotNumber ne fait que 3 chiffres :
    if ($robotNumber < 1000 && $robotNumber > 99) { // [0;9] : concaténer '000' à $nomRobot
        $newName .= "0";
    }
    elseif ($robotNumber < 100 && $robotNumber > 9) { // [10;99] : concaténer '00' à $nomRobot
        $newName .= "00";
    }
    elseif ($robotNumber < 10 ) { // [100:999] : concaténer '0' à $nomRobot
        $newName .= "000";
    };

    // 9. Concatène $nomRobotNumber
    $newName .= $robotNumber;
    
    // 10. return $nomRobot
    return $newName;
}

Endiguer la guerre des clones

Les scientifiques ont oublié quelque chose dans leur précédente demande ! Les identifiants n'étant pas uniques, des clones de chat-lapin-robots ont apparu et menacent de faire sauter le laboratoire.

Gérez l'unicité des noms dans la fonction précédente.

  • Créez un array en dehors de la fonction précécente qui contiendra les noms générés
  • Appelez cet array dans la fonction afin de comparer le nom généré au tableau contenant les noms générés. Pour appeler une variable venue de l'extérieur au sein d'une fonction, on utilise le mot clé global :
$a = 0;

function maFonction() {
    global $a;  // On importe $a qui vaut 0
    $a += 1;    // On ajoute 1
    return $a;
}
var_dump($a);   // a = 0
maFonction();   // Ajout de 1 à la globale a
var_dump($a);   // a = 1

Solution

// On prépare un tableau vide qui contiendra la liste des noms de robots édités
$namesList = [];

function robotName() {

    // On utilise la variable $namesList au sein de la fonction pour la remplir
    // à chaque appel de la fonction.
    // Comme elle vient du scope (la portée) global, on l'appelle avec global.
    // Ainsi, quand on la modifie DANS la fonction, elle est aussi modifiée en
    // dehors de la fonction.
    global $namesList;

    $validLetters = ["R", "Z", "S", "X", "W", "T"];
    $newName = "";

    for ($i=0; $i < 3; $i++) {
        $newName.= $validLetters[rand(0, 5)];
    }

    $newName.="-";
    $robotNumber = rand(0, 9999);

    if ($robotNumber < 1000 && $robotNumber > 99) {
        $newName .= "0";
    }
    elseif ($robotNumber < 100 && $robotNumber > 9) {
        $newName .= "00";
    }
    elseif ($robotNumber < 10 ) {
        $newName .= "000";
    };

    $newName .= $robotNumber;

    // On teste si le nouveau nom n'existe pas dans le tableau :
    if (!in_array($newName, $namesList)) {
        // S'il n'existe pas, on l'ajoute au tableau et on retourne le nouveau nom
        $namesList[] = $newName; // Autre écriture: array_push($namesList, $newName);
        return $newName;
    }

    // Sinon, on emmet une erreur :
    throw new Error('Vous êtes sur le point de cloner ce robot !');
}

Solution 2 (passage de variable par référence)

$robotNames = [];

// Dans les arguments de la fonction, on peut préciser un passage par référence avec & : cela indique que la variable du scope supérieur passée en paramètres de la fonction, si elle est modifiée par la fonction appelée, restera modifiée dans son scope :

function nameRobot(&$robotNames)
{
    $name = "";
    $letters = ["R", "Z", "S", "X", "W", "T"];
    while (strlen($name) < 3) {
        $name .= $letters[rand(0, count($letters) - 1)];
    }
    $name .= "-";
    while (strlen($name) < 8) {
        $name .= strval(rand(0, 9));
    }
    
    if (!in_array($name, $robotNames)) {
    
        // On ajoute à $robotNames le nouveau nom
        array_push($robotNames, $name);
        var_dump($robotNames);
        return true;
    }
    // Si il y a une correspondance (le nom existe), on relance la fonction elle-même
    nameRobot($robotNames);
}

var_dump($robotNames); // tableau vide
nameRobot($robotNames); // on lance la fonction avec $robotNames en référence
var_dump($robotNames); // tableau contenant dorénavant 1 élément !

Vérifier les circuits du robot

Les scientifiques ont réussi à implémenter des nombres premiers dans le robot, de sorte à pouvoir détecter d'où viennent les bugs. En fait, le robot émmet un nombre lorsqu'il a un bug. Pour détecter l'emplacement du bug, on trouve par quels nombres premiers il est divisible et on se reporte à la table suivante :

TABLE DE DEBUG
--------------
2       |   bras gauche
3       |   bras droit
5       |   carte mère
7       |   processeur
11      |   défluxeur de zip
13      |   moteur

Exemples :

  • 9 : divisible par 3 uniquement, la fonction doit retourner un array avec 1 élément : bras droit

  • 12 est divisible par 2 et 3, la fonction doit retourner un array avec 2 éléments : bras gauche et bras droit.

Gérer les mouvements du robot

Les testeurs des robots ont demandé une façon plus simple de diriger les robots à distance. Ils aimeraient saisir une liste de caractères correspondant aux mouvements voulus. Par exemple: 'RRLFBF'.

Table de conversion :
----------------
R | RIGHT
L | LEFT
F | FRONT
B | BACKWARDS

Créez une fonction qui prend une chaine de caractères parmi R, L, F, B et retournez un array contenant la liste des instructions à donner au robot.

Bonus : si l'utilisateur a tappé une instruction en double (RR, LL, FF, BB), à la deuxième instruction, indiquez par exemple 'RIGHT AGAIN'.

Positionnement du robot

Le robot se trouve sur un espace en 2 dimensions, et démarre à la position {0;0}, c'est à dire 0 sur l'axe X (gauche-droite) et 0 sur l'axe Y (bas-haut).

  1. Améliorez la fonction précédente pour retourner la position actuelle du robot.
  2. Améliorez la fonction précédente pour qu'elle prenne en paramètres une position de départ du robot, qui soit par défaut int x = 0; int y = 0.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment