Jérôme Cutrona

Version statique de l'intranet de Jérôme Cutrona - Rejoindre la version dynamique 🔒

Les exemples et corrections sont potentiellement non fonctionnels.

B.U.T. Informatique - IUT de Reims - Université de Reims

Conception et tests autour de « Rock, Paper, Scissors, Lizard, Spock »

Navigation

Objectifs de la séance

  • Installer Composer
  • Créer un nouveau projet avec Composer
  • Installer PHPUnit
  • Découvrir PhpStorm
  • Installer des outils de tests statiques dans PhpStorm
  • Installer et configurer PHP Mess Detector (a.k.a. PHPMD)
  • Installer et configurer PHP Coding Standards Fixer (a.k.a. PHP CS Fixer)
  • Écrire des tests unitaires
  • Écrire des tests d'intégration
  • Prendre en main PHPUnit
  • Utiliser des Stubs
  • Utiliser des Mock objects
  • Découvrir l'analyse de couverture de code

Mise en place du projet

Quelques considérations liées à la configuration système de l'IUT

Remarque importante

Si vous travaillez sur votre ordinateur personnel, sur Linux ou sur Windows, vous pouvez placer le répertoire « tests-conception » du projet où bon vous semble. La partie qui suit concerne uniquement le travail sur les ordinateurs de l'IUT. Cela ne vous empêche pas d'en prendre connaissance.

Votre répertoire d'accueil « $HOME » se trouve sur un emplacement réseau NFS, ce qui vous permet de retrouver vos fichiers quel que soit le PC du département sur lequel vous vous connectez. Symfony utilise le sous-répertoire « var » du projet pour la gestion de son cache, des traces d'exécution et des données de session. Les traces d'exécution et plus particulièrement le cache sont des tâches générant de nombreuses entrées-sorties sur le système de fichiers, particulièrement inadaptées aux systèmes de fichiers en réseau. Il en va de même pour PhpStorm qui surveille les modifications de tous les fichiers de votre projet et ne peut pas fonctionner normalement lorsque ces fichiers sont sur un partage NFS. Afin d'améliorer les performances de votre application et d'éviter de saturer le serveur de fichiers du département informatique, vous allez travailler dans le répertoire « /working/votre_login » de la machine sur laquelle vous êtes connecté.

En fin de séance

En fin de séance, vous devez impérativement synchroniser votre dépôt distant (en ligne de commande ou avec PhpStorm)

git commit
puis
git push
faute de quoi vous perdrez votre travail entre chaque séance !

Pensez également à supprimer votre répertoire de travail /working/votre_login/tests-conception pour éviter la saturation du disque local :

rm -Rf /working/votre_login/tests-conception

En début de séance

Lorsque vous reprenez le travail la séance suivante, vous avez trois choix possibles, qui dépendent de la façon dont vous avez travaillé :

  • Mettre à jour votre dépôt local
    cd /working/votre_login/tests-conception
    git pull
  • Effacer votre dépôt local et repartir de zéro
    cd /working/votre_login
    rm -Rf /working/votre_login/tests-conception
    git clone https://iut-info.univ-reims.fr/gitlab/votre_login/tests-conception.git
    cd /working/votre_login/tests-conception
  • Réinitialiser votre dépôt local selon le dépôt distant
    cd /working/votre_login/tests-conception
    git fetch
    git reset --hard origin/main

Ensuite, dans le répertoire de votre projet, vous devez et (ré)installer les composants nécessaires à son fonctionnement :

composer install

Versionnage du projet

Pour chaque nouveau sujet de TP, vous créerez un nouveau répertoire, vous initialiserez un dépôt Git dans ce répertoire et créerez un dépôt distant sur GitLab. Vous réaliserez une opération de validation avec git commit au moins une fois par question du sujet.

Initialisation du dépôt

  • Ouvrez un terminal, utilisez la commande mkdir pour créer un nouveau répertoire tests-conception dans /working/votre_login et utilisez la commande cd pour vous placer dans le répertoire
  • Initialisez le dépôt Git du projet
    git init
  • Éditez le fichier .gitignore pour exclure les fichiers binaires de type image (JPEG, PNG, …), les fichiers qui contiennent des mots de passe ou les fichiers créés par votre éditeur de texte (*.swp pour vim, par exemple)
  • Éditez un fichier README.md dans lequel doivent figurer votre nom et votre prénom
  • Ajoutez les fichiers au dépôt
    git add .
  • Effectuez une première validation
    git commit -m "Initial commit"
  • Connectez-vous à l'application GitLab et créez un dépôt portant le même nom que votre nouveau répertoire
  • Accordez la permission « Reporter » à votre enseignant de TP
  • Associez le dépôt local et le dépôt distant
    git remote add origin https://iut-info.univ-reims.fr/gitlab/votre_login/tests-conception.git
  • Poussez le dépôt local vers le dépôt distant
    git push

Consignes

  • Vous effectuerez une validation après chaque question.
  • A la fin de chaque séance, vous effectuerez une validation. Si cette validation contient du code incomplet ou ne fonctionnant pas, mentionnez-le dans le message de validation. Vous pousserez ensuite votre travail vers le dépôt distant.
  • Ce dépôt sera utilisé par votre enseignant(e) de TP pour évaluer votre travail. Assurez-vous donc régulièrement que tous les fichiers que vous souhaitez lui rendre sont bien présents dans le dépôt. Respectez les noms des fichiers qui vous sont demandés si des consignes particulières vous sont données.
  • Le dépôt lui-même sera évalué : soignez l'écriture de vos messages.

L'aide-mémoire Git et l'aide-mémoire GitLab de Monsieur Nourrit pourront vous être utiles.

Installer et configurer Composer

Composer simplifie grandement la gestion de projets PHP. Afin d'accélérer les installations futures et de limiter l'impact sur votre quota d'espace disque, nous allons déporter le cache de Composer dans le répertoire temporaire.

Travail à réaliser

Initialiser le projet Composer

Vous allez initialiser un projet Composer pour installer des paquets et profiter de l'auto-chargement.

Attention commit = danger !

Dans cette partie, ne faites PAS de « commit » tant que cela ne vous est pas demandé. Vous devrez dans un premier temps vérifier ou compléter votre fichier « .gitignore » pour ne pas intégrer inutilement des centaines de fichiers et de répertoires à votre dépôt.

Travail à réaliser
  1. Si ce n'est pas déjà fait, installez Composer
  2. Lisez la documentation de « composer init »
  3. Dans le répertoire de votre projet, initialisez interactivement un nouveau projet Composer :
    composer init
    
                                                
      Welcome to the Composer config generator  
                                                
    
    
    This command will guide you through creating your composer.json config.
    
    Package name (<vendor>/<name>) [cutron01/tests-conception]: 
    Description []: Tests avec PHPUnit    
    Author [Jérôme Cutrona <jerome.cutrona@univ-reims.fr>, n to skip]: 
    Minimum Stability []: 
    Package Type (e.g. library, project, metapackage, composer-plugin) []: project
    License []: Copyleft
    
    Define your dependencies.
    
    Would you like to define your dependencies (require) interactively [yes]? n
    Would you like to define your dev dependencies (require-dev) interactively [yes]? n 
    Add PSR-4 autoload mapping? Maps namespace "Cutron01\TestsConception" to the entered relative path. [src/, n to skip]: n
    
    {
        "name": "cutron01/tests-conception",
        "description": "Tests avec PHPUnit",
        "type": "project",
        "license": "Copyleft",
        "authors": [
            {
                "name": "Jérôme Cutrona",
                "email": "jerome.cutrona@univ-reims.fr"
            }
        ],
        "require": {}
    }
    
    Do you confirm generation [yes]? 
    
  4. Lancez l'installation et la configuration de l'auto-chargement
    composer install
  5. Constatez l'apparition du répertoire « vendor »
  6. Vérifiez le contenu du fichier « .gitignore » afin de vous assurer que le répertoire « vendor » est bien exclu
  7. Vérifiez le contenu de l'index avec la commande :
    git status
    dont le résultat doit être :
    Sur la branche master
    Votre branche est à jour avec 'origin/master'.
    
    Modifications qui ne seront pas validées :
      (utilisez "git add <fichier>..." pour mettre à jour ce qui sera validé)
      (utilisez "git restore <fichier>..." pour annuler les modifications dans le répertoire de travail)
    	modifié :         .gitignore
    
    Fichiers non suivis:
      (utilisez "git add <fichier>..." pour inclure dans ce qui sera validé)
    	composer.json
    	composer.lock
    
    aucune modification n'a été ajoutée à la validation (utilisez "git add" ou "git commit -a")
    
    Remarque importante

    le répertoire « vendor » ne doit PAS être présent dans la précédente liste de fichiers non suivis. Si tel est le cas, votre fichier « .gitignore » est incorrect ou mal nommé.

  8. Ajoutez les fichiers à l'index Git
    git add .
  9. Enregistrez les modifications dans le dépôt :
    git commit
  10. Poussez les modifications sur le dépôt distant :
    git push
  11. Vérifiez le contenu du dépôt distant sur https://iut-info.univ-reims.fr/gitlab/

Création du projet dans PhpStorm

La gestion de nombreux fichiers dans une arborescence complexe demande un IDE adéquat. Vous utiliserez PhpStorm pour l'édition de code et la gestion globale du projet. Des greffons apportent le support de Symfony, Composer, PHPUnit, des fichiers YAML, Twig, … Certains sont préinstallés dans la configuration générale.

Vous allez créer un nouveau projet PhpStorm pour gérer vos sources et vos suites de tests.

Référez-vous au sujet Installation et configuration de PhpStorm.

Travail à réaliser
  1. Dans votre terminal et dans le répertoire du projet, lancez la commande
    phpstorm . &
    Information

    Cette commande est la façon la plus simple et la plus rapide de créer un nouveau projet PhpStorm dans un répertoire. Il n'est pas utile de la lancer une fois le projet créé puisque vos anciens projets sont proposés à l'ouverture de PhpStorm ou dans son menu « File » puis « Recent Projects ».

  2. Patientez pendant l'indexation des fichiers du projet visible dans la partie droite de la barre d'état Indexation du projet
    Information

    Il est légitime d'inclure à votre index Git le répertoire « .idea » de configuration de PhpStorm afin que vous n'ayez pas à reconfigurer votre environnement lorsque vous clonerez votre dépôt en début de séance lors d'un changement de salle. Veillez néanmoins à effectuer des commits distincts pour les modifications de ce répertoire.

Installation des outils de qualité de code

La qualité de votre code, par son style cohérent ou l'absence de problèmes potentiels, est un atout majeur pour la maintenance et la fiabilisation de vos applications. Vous avez jusqu'à présent utilisé PHP CS Fixer pour standardiser votre style de codage. Vous allez à présent découvrir PHP Mess Detector, un autre outil d'analyse statique de code, dont l'objectif est de mettre en évidence des problèmes potentiels de nommage, de conception, de taille de code ou encore de code inutile.

La qualité du code passe également par des tests unitaires, fonctionnels ou d'intégration pour analyser dynamiquement le comportement du code testé. Aussi, vous utiliserez PHPUnit et écrirez vos premiers tests unitaires pour valider le fonctionnement de votre code.

Ces trois outils, aussi puissants soient-ils, ne servent à rien si vous ne prenez pas la peine de les exécuter, de lire leurs alertes ou de suivre leurs suggestions. C'est pourquoi vous allez utiliser GrumPHP ! Cet outil de qualité de code s'appuie sur les « hooks » de Git pour se déclencher avant chaque commit et appliquer certaines tâches de contrôle. Il peut alors interrompre le commit si les tâches échouent. Il devient donc impossible d'ajouter du code de mauvaise qualité (au sens des tests mis en place) au dépôt Git local, et au dépôt distant par voie de conséquence.

PHPUnit

PHPUnit exécute les codes à travers les tests qui décrivent son comportement attendu. C'est un outil puissant et reconnu sur lequel s'appuient la plupart des outils de test dynamique de code PHP.

Travail à réaliser
  1. Recherchez le paquet de PHPUnit à l'aide de Composer :
    composer search phpunit
  2. Installez PHPUnit à l'aide de Composer
    Remarque importante

    La version 10 de PHPUnit vient juste de sortir. Afin d'éviter tout problème, vous installerez la version 9 sur laquelle ce TP a été conçu. N'oubliez pas de forcer la version à l'installation !

    composer require --dev phpunit/phpunit:^9
  3. Constatez l'apparition du répertoire « vendor/bin »
  4. Observez le contenu du répertoire « vendor/bin »
  5. Vérifiez que PHPUnit fonctionne :
    vendor/bin/phpunit --version
    PHPUnit 9.6.3 by Sebastian Bergmann and contributors.
  6. Générez un fichier de configuration avec la commande :
    vendor/bin/phpunit --generate-configuration
    PHPUnit 9.6.3 by Sebastian Bergmann and contributors.
    
    Generating phpunit.xml in /working/votre_login/tests-conception
    
    Bootstrap script (relative to path shown above; default: vendor/autoload.php): 
    Tests directory (relative to path shown above; default: tests): 
    Source directory (relative to path shown above; default: src): 
    Cache directory (relative to path shown above; default: .phpunit.cache): 
    
    Generated phpunit.xml in /working/votre_login/tests-conception.
    Make sure to exclude the .phpunit.cache directory from version control.
    
  7. Passez l'option « forceCoversAnnotation » à « false »
  8. Excluez le répertoire « .phpunit.cache » des fichiers suivis du dépôt

PHP CS Fixer

PHP CS Fixer, outil d'analyse statique de code développé par Fabien Potencier, créateur de Symfony, détecte ou corrige les défauts de style de codage de vos sources.

Travail à réaliser
  1. Placez le fichier de configuration de PHP CS Fixer (télécharger) à la racine de votre projet (le style de codage défini est ici Symfony)
    Remarque importante

    Votre navigateur risque de renommer en php-cs-fixer.dist.php, supprimant ansi le . initial. Veillez à ce que le nom du fichier soit bien .php-cs-fixer.dist.php.

  2. Référez-vous au sujet Installation et configuration de PhpStorm pour installer et configurer PHP CS Fixer
  3. Exluez le fichier .php-cs-fixer.cache du dépôt Git

PHP Mess Detector

PHP Mess Detector est un analyseur statique de code basé sur PHP Depend qui mettra en évidence des bugs, du code sous-optimal, une complexité trop élevée ou des éléments inutilisés.

Travail à réaliser
  1. Référez-vous au sujet Installation et configuration de PhpStorm pour installer et configurer PHP Mess Detector
  2. Placez le fichier de configuration de PHP Mess Detector (télécharger) à la racine de votre projet (la détection porte sur toutes les règles possibles)
    Information

    Si vous avez correctement suivi le guide d'installation de PHP Mess Detector, vous devez avoir une configuration de PHP Depend. Elle est indispensable sur les PC de l'IUT. En effet, PHP Depend génère des fichiers et répertoire de cache dans « $HOME/.pdepend », donc dans le système de fichier partagé en NFS, ce qui ralentit considérablement le temps d'analyse des fichiers du projet.

GrumPHP

GrumPHP vient parfaire votre contrôle de code en le validant avant chaque commit. Dans un cadre d'un développement logiciel avec intégration continue, il doit être impossible d'introduire du code non valide dans le dépôt central et GrumPHP permet de s'en assurer.

Remarque importante

Dans le cadre des TP, il n'est pas possible de vous interdire de soumettre du code invalide, tout particulièrement lors du « commit de fin de séance ». Il vous sera donc possible, uniquement à cette occasion, de soumettre du code non validé. Pour cela, vous utiliserez l'option « --no-verify » de git en ligne de commande qui permet de sauter les « hooks » et donc l'exécution de GrumPHP. Vous ferez alors mention de l'état invalide du code dans le message de commit.

Travail à réaliser
  1. Installez GrumPHP à l'aide de Composer
    composer require --dev phpro/grumphp
    Remarque importante

    Deux questions vous sont posées pendant l'installation.

    Répondez « y » à la question suivante :

    phpro/grumphp contains a Composer plugin which is currently not in your allow-plugins config. See https://getcomposer.org/allow-plugins
    Do you trust "phpro/grumphp" to execute code and wish to enable it now? (writes "allow-plugins" to composer.json) [y,n,d,?]

    Répondez « n » à la question suivante :

    Run composer recipes at any time to see the status of your Symfony recipes.
    Do you want to create a grumphp.yml file? [Yes]:
  2. Placez le fichier de configuration de GrumPHP (télécharger) à la racine de votre projet
  3. Observez les tâches du fichier de configuration fourni et lisez la documentation associée à chacune d'entre elles
  4. Afin de vérifier le bon fonctionnement de GrumPHP :
    1. Supprimez la description de votre projet dans « composer.json »
    2. Ajoutez le fichier « composer.json » à l'index Git :
      git add composer.json
    3. Tentez de valider votre modification :
      git commit -m "test GrumPHP"
      GrumPHP detected a pre-commit command.
      GrumPHP is sniffing your code!
      
      Running tasks with priority 0!
      ==============================
      
      Running task 1/4: composer... 
      Running task 2/4: phpcsfixer2... 
      Running task 3/4: phpmd... 
      Running task 4/4: phpunit... 
      
                   ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
                 ▄▄▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▌
               ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▄
              ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▌
             ▐▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▌
        ▄███▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▌
       █▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▌
       ▐█▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▌
         ▀█▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▌
           ▀▀▓▓▓▓▓▓▓▓▓▓▓▓█▀▀▀▀▀▀▀▀▀▀▀▀▀▀████████████▄
            ▄███████                       ██████████
           ███████▀  ▀▀▀▀▀▄      ▄▀▀▀▀▀     █████ ▀
            ▐████      ▐██        ▐██        ████▌
            ████▌                            ███
             ▌██▌           ▄▄ ▄▄           ▐███
              ███       ▄▄▄▄▄▄▄▄▄▄▄▄       ▐███
               ██▄ ▐███████████████████████████
              █▀███████████▀     ▀▀███████████
                ██████████▄███████▄███████████
               ▐█████████████████████████████
                █████████████████████████████
                 ██ █████████████████████▐██▀
                  ▀ ▐███████████████████▌ ▐▀
                      ████▀████████▀▐███
                       ▀█▌  ▐█████  ██▌
                              ██▀   ▐▀
      
             ██████████████████████████████████
             █░░░░░░▀█▀░░░░░░▀█░░░░░░▀█▀░░░░░▀█
             █░░▐█▌░░█░░░██░░░█░░██░░░█░░░██░░█
             █░░▐█▌░░█░░░██░░░█░░██░░░█░░░██░░█
             █░░▐█▌░░█░░░██░░░█░░░░░░▄█░░▄▄▄▄▄█
             █░░▐█▌░░█░░░██░░░█░░░░████░░░░░░░█
             █░░░█░░░█▄░░░░░░▄█░░░░████▄░░░░░▄█
             ██████████████████████████████████
      
      
      composer
      ========
      
      ./composer.json is valid for simple usage with Composer but has
      strict errors that make it unable to be published as a package
      See https://getcomposer.org/doc/04-schema.md for details on the schema
      # Publish errors
      - description : The property description is required
      
    4. Si le test de commit n'a pas déclenché GrumPHP, initialisez-le dans votre projet avec :
      vendor/bin/grumphp git:init
    5. Si le test de commit n'avait pas déclenché GrumPHP et que vous souhaitez défaire votre dernier commit :
      git reset --soft HEAD~1
    6. Annulez la suppression de la description de votre projet dans « composer.json » (« CTRL+Z » dans votre IDE)
Remarque importante

Si vous utilisez les fonctionnalités Git à travers PhpStorm sur Linux, vous rencontrerez certainement le message d'erreur suivant, rendant impossible la validation de la tâche « composer » :

The executable for "composer" could not be found.

Ceci est lié à l'installation de Composer dans la sous-arborescence des fichiers de l'utilisateur ainsi qu'à la configuration de l'environnement du « shell » « bash » lié à ses divers fichiers d'initialisation.

La solution la plus appropriée est de déporter la configuration de la variable « PATH » dans le fichier « $HOME/.profile ». Vous devez donc transférer les lignes concernées du fichier « $HOME/.bashrc » vers « $HOME/.profile » (que vous devez créer s'il n'existe pas dans votre compte).

Information

Scripts Composer

Pour faciliter l'invocation des divers outils de qualité de code, vous pouvez utiliser les scripts Composer. Pour gagner un peu de temps, nous vous proposons de copier les scripts fournis :

  • test:cs qui vérifie le style de codage avec PHP CS Fixer
  • fix:cs qui arrange le style de codage avec PHP CS Fixer
  • test:md qui vérifie le style de codage avec PHP Mess Detector
  • test:phpunit qui lance les tests définis avec PHPUnit
  • test qui lance tous les tests définis précédemment
  • grumphp:precommit qui lance les tâches définies avec GrumPHP sur les fichiers indexés dans Git
  • grumphp:run qui lance les tâches définies avec GrumPHP sur tous les fichiers
Travail à réaliser
  1. Ajoutez les scripts suivants au fichier composer.json :
    Information

    Il est possible d'invoquer un script Composer sans donner son nom complet, à condition que le nom tronqué conduise à un seul choix possible. À titre d'exemple, dans les scripts fournis, la commande

    composer grumphp:precommit
    peut être abrégée en
    composer grumphp:p

Configuration du projet

Avant de commencer à coder, quelques consignes seront à respecter pour respecter la structure de fichiers habituelle des projets écrits en PHP et ainsi permettre bien valider votre code avec les tests.

  • Les sources se trouvent dans un répertoire « src » dans lequel des sous-répertoires correspondent aux espaces de noms
  • Les tests se trouvent dans un répertoire « tests » dans lequel des sous-répertoires correspondent aux espaces de noms
  • La classe « MonNamespace\MaClasse » se trouve dans le fichier « src/MonNamespace/MaClasse.php »
  • Les tests de la classe « MonNamespace\MaClasse » sont faits dans la classe « Tests\MonNamespace\MaClasse » qui se trouve dans le fichier « tests/MonNamespace/MaClasseTest.php »
  • Un fichier contenant une classe ne fait pas d'inclusions (« require », …), ceci est à la charge de l'application
  • Un fichier contenant une classe de test ne fait pas d'inclusions (« require », …), ceci est à la charge du framework de tests
  • Afin de détecter les problèmes liés au type des variables, il est recommandé de passer en typage strict dans chacune de vos classes
  • Votre code doit être conforme aux standards de codage

Ces consignes impliquent la création de répertoires ainsi que la configuration de l'auto-chargement par Composer. Toutes les tâches sont réalisables dans PhpStorm et seront donc réalisées dans PhpStorm.

Travail à réaliser
  1. Vérifiez que la version de PHP indiquée dans PhpStorm soit la bonne
  2. Vérifiez que les modèles de PhpStorm intègrent le typage strict
  3. Configurez les outils d'analyse statique / de tests statiques de code PHP
  4. Créez les répertoires « src » et « tests » en effectuant un clic droit sur le projet puis « New » suivi de « Directory »
  5. Lisez la documentation de Composer sur l'auto-chargement PSR-4
  6. Ouvrez le fichier « composer.json »
  7. Ajoutez le répertoire « src » comme chemin de recherche de l'ensemble des namespaces en profitant des suggestions de PhpStorm
  8. Ajoutez le répertoire « tests » comme chemin de recherche de l'ensemble des namespaces de développement qui commencent par « Tests\ » en profitant des suggestions de PhpStorm
  9. Mettez à jour l'auto-chargement de Composer à l'aide de l'option « dump-autoload »
    composer dump-autoload
    Generating autoload files
    Generated autoload files
    
Remarque importante

L'auto-chargement PSR-4 ne nécessite PAS de refaire la commande composer dump-autoload lors de l'ajout de nouvelles classes dans le répertoire « src ».

Principe du jeu et de la réalisation

Vous allez vous intéresser au développement d'un jeu « Rock, Paper, Scissors » et ses évolutions possibles à travers l'ajout de nouveaux gestes comme dans la variante « Rock, Paper, Scissors, Lizard, Spock ». Le jeu sera exécuté en PHP ligne de commande.

Le principe de ces jeux repose sur l'opposition de deux joueurs qui choisissent chacun un geste parmi 2n+1. Chaque geste l'emporte contre n gestes et perd contre n autres gestes. Si les deux joueurs choisissent le même geste, il y a égalité.

Les combinaisons gagnantes pour « Rock, Paper, Scissors » (n=1) sont triviales : Rock Paper Scissors Les combinaisons gagnantes pour « Rock, Paper, Scissors, Lizard, Spock » (n=2) sont : Rock Paper Scissors Lizard Spock Vous pouvez consulter des variantes à 7, 9, 11, 15, 25 voire 101 gestes.

La réalisation de ces jeux va se faire, en fonction des spécifications, selon les bonnes pratiques de conception afin de permettre flexibilité, robustesse et évolutivité, avec une implémentation réfléchie et des tests garantissant le bon fonctionnement des composants et de l'ensemble.

Rock, Paper, Scissors

Pour commencer, la réalisation va se limiter à la variante la plus simple du jeu avec trois gestes.

Gestion des règles

Une partie du fonctionnement du jeu réside dans la détermination du geste gagnant. Lorsque les gestes des deux opposants sont confrontés, si le premier est gagnant, il remporte 1 point, s'il est perdant, il perd 1 point et en cas d'égalité, le résultat est nul.

Travail à réaliser
  1. Observez le diagramme de classes Diagramme des classes associées aux règles
    Le diagramme précédent est celui des classes associées aux règles.
  2. Créez l'interface et les classes avec l'assistant de PhpStorm
  3. Écrivez les méthodes de la classe « RockPaperScissors »
    • « getGestures() » donne l'ensemble des gestes
    • « compare() » retourne 1, -1 ou 0 selon que « gesture1 » gagne, perd ou est à égalité avec « gesture2 »
    • « checkGesture() » vérifie que son paramètre est un geste valide
    • « gestureIndex() » donne l'indice du geste dans le tableau « GESTURES » afin de faciliter l'évaluation de la comparaison (cette méthode n'est pas obligatoire, vous pouvez réaliser « compare() » sans elle)
  4. Écrivez les tests de la classe « RockPaperScissors »
    Remarque importante

    Les classes de test font partie de l'espace de noms « Tests » suivi de l'espace de noms de la classe qu'elles testent.

Les joueurs

Les joueurs, machine ou humain, peuvent jouer un coup valide. Diagramme des classes associées aux joueurs

Le diagramme précédent est celui des classes associées aux joueurs.
Travail à réaliser
  1. Observez le diagramme de classes
  2. Créez les classes et interface avec PhpStorm
  3. Écrivez les méthodes de la classe « Player »
  4. Écrivez la méthode de la classe « Computer » qui réalise un tirage aléatoire parmi l'ensemble des gestes connus dans les règles
  5. Écrivez les méthodes de la classe « Human »
    • « showPossibilities() » retourne l'ensemble des gestes possibles sous forme d'une chaîne de caractères
    • « prompt() » affiche les gestes possibles et invite l'utilisateur à en choisir un
    • « getInput() » effectue une lecture au clavier et la retourne
    • « playOneMove() » qui effectue toutes les opérations nécessaires pour récupérer un geste valide du joueur
  6. Écrivez les tests des classes « Human » et « Computer »
    Remarque importante

    Les classes de test font partie de l'espace de noms « Tests » suivi de l'espace de noms de la classe qu'elles testent.

    Namespaces

    La classe « Human » produisant une sortie écran, lisez la documentation concernant ce point.

Le jeu

Le jeu nécessite deux joueurs et un jeu de règles. Diagramme des classes associées au jeu

Le diagramme précédent est celui des classes associées au jeu.
Travail à réaliser
  1. Observez le diagramme de classes
  2. Créez la classe avec PhpStorm
  3. Écrivez les méthodes de la classe « Game »
    • le constructeur
    • « play() » qui lance la partie pour un certain nombre de tours minimum jusqu'à obtenir un vainqueur et qui cumule dans « score » les résultats obtenus par « playOneMove() »
    • « playOneMove() » qui correspond à un tour de jeu
    • « getScore() »
    • « winner() » qui retourne le vainqueur ou lève une « RuntimeException » s'il n'y en a pas
  4. Lisez la documentation de PHPUnit sur « onConsecutiveCalls() » ainsi que celle de PHP sur l'utilisation de « ... » pour fournir des arguments qui vous seront utiles pour simuler les coups des joueurs durant la partie de test
  5. Écrivez les tests de la classe « Game »
    Namespaces

    Les classes de test font partie de l'espace de noms « Tests » suivi de l'espace de noms de la classe qu'elles testent.

    Sortie écran

    Votre programme produisant une sortie écran qui va polluer la sortie de PHPUnit, lisez la documentation concernant ce point.

Couverture de code

Tous les composants nécessaires étant présents et testés, il est utile de vérifier la couverture de code.

Travail à réaliser
  1. Générez un rapport d'analyse de couverture de code dans le répertoire « coverage »
  2. Excluez ce répertoire coverage de votre dépôt Git
  3. Ajoutez un nouveau script Composer « test:coverage » qui efface le répertoire « coverage » avant de lancer la commande PHPUnit que vous venez d'élaborer
    Information

    À la suite de la commande d'évaluation de la couverture de code, vous pouvez ouvrir automatiquement le résultat « coverage/index.html » dans votre navigateur Web à l'aide d'une commande :

  4. Vérifiez que votre couverture de code est suffisante

Programme pour jouer

Tous les composants nécessaires étant présents, il reste à pouvoir jouer.

Travail à réaliser
  1. Créez un répertoire « bin » dans lequel vous mettrez vos programmes
  2. Dans le répertoire « bin », créez un programme « playRPS » qui commence par :
    #!/usr/bin/env php
    <?php
    Information

    La première ligne du script est le « shebang » qui indique que le fichier est un script et qu'il doit ici être exécuté par l'interpréteur « php » à trouver dans les chemins donnés par la variable d'environnement « PATH ».

    Vous pouvez lancer vos programmes avec :

    php bin/nom_programme
  3. Rendez le programme « playRPS » exécutable
    Information

    Vous pouvez à présent lancer vos programmes avec :

    bin/nom_programme
  4. Ajoutez au début du programme l'inclusion de l'auto-chargement de Composer en utilisant la constante magique « __DIR__ » pour déterminer le chemin du programme
  5. Écrivez le programme « playRPS » qui permet à un humain « Bob » de jouer contre la machine « HAL » pour 3 tours minimum, qui pourrait donner :
    bin/playRPS
    Saisissez votre geste (R, P, S) : R
    R R --> 0
    Saisissez votre geste (R, P, S) : o
    Saisissez votre geste (R, P, S) : R
    R S --> 1
    Saisissez votre geste (R, P, S) : R
    R P --> -1
    Saisissez votre geste (R, P, S) : P
    P R --> 1
    Bob won!
    

Rock, Paper, Scissors, Lizard, Spock

Vous allez reproduire ce qui vient d'être fait pour « Rock, Paper, Scissors » afin d'arriver à la création des composants nécessaires à la variante « Rock, Paper, Scissors, Lizard, Spock ».

Travail à réaliser
  1. Dupliquez la classe « RockPaperScissors » en « RockPaperScissorsLizardSpock »
  2. Complétez les gestes possibles dans les constantes de classe : « 'R' », « 'P' », « 'S' », « 'L' », « 'V' » (« 'V' » pour « Vulcan Salute » 🖖)
  3. Vous pouvez construire un tableau des gestes gagnants sur le modèle « Rock gagne face à Scissors et Lizard », …
  4. Donnez une version fonctionnelle de la méthode « compare() »
  5. Écrivez les tests permettant de le vérifier
  6. Écrivez le programme « playRPSLV » qui permet à un humain « Bob » de jouer contre la machine « HAL » pour 3 tours minimum

Généralisation et refonte

En fonction de la façon dont vous avez réalisé la variante « Rock, Paper, Scissors, Lizard, Spock », votre code est plus ou moins redondant par rapport à « Rock, Paper, Scissors ». Qu'en sera-t-il pour les variantes comportant plus de gestes ?

En réorganisant astucieusement les gestes en « Rock, Spock, Paper, Lizard, Scissors », les scores associés aux combinaisons sont :

RVPLS
R0-1-111
V10-1-11
P110-1-1
L-1110-1
S-1-1110

Dans une vision cyclique des gestes, pour un geste donné, les deux suivants l'emportent alors que les deux précédents sont battus : Rock Paper Scissors Lizard Spock

La généralisation à 2n+1 gestes donne les n gestes suivants vainqueurs et les n gestes précédents perdants. Le fonctionnement réside alors sur l'ordre des gestes qui permet de connaître les points pour chaque confrontation, ce qui va entrainer la création d'une nouvelle hiérarchie de classes.

Les gestes

Vous allez séparer les gestes des règles : Diagramme des classes associées aux gestes

Le diagramme précédent est celui des classes associées aux gestes.
Travail à réaliser
  1. Créez une interface « MyGame\Rules\Gestures\IGestures » représentant les gestes associés à des règles
  2. Ajoutez une méthode « getGestures() » à votre nouvelle interface
  3. Parcourez rapidement la documentation de « ReflectionClass » et lisez attentivement celle de sa méthode « getConstants() »
  4. Créez une classe abstraite « MyGame\Rules\Gestures\Gestures » qui implémente « IGestures » et qui aura pour but de retourner les constantes des classes qui dériveront d'elle
  5. Lisez la documentation concernant la résolution statique à la volée (« Late Static Bindings »)
  6. Donnez le code suivant à la méthode « getGestures() » :
    $reflectionClass = new ReflectionClass(static::class);
    return array_values($reflectionClass->getConstants());
  7. Testez votre classe abstraite « Gestures » en créant une classe anonyme en héritant
  8. Dérivez la classe abstraite « Gestures » en « RockPaperScissorsLizardSpockGestures »
  9. Ajoutez les constantes des gestes à votre nouvelle classe
  10. Testez « RockPaperScissorsLizardSpockGestures »

Les règles

Les règles vont également être revues : Diagramme des classes associées aux règles généralisées

Le diagramme précédent est celui des classes associées aux règles généralisées.
Travail à réaliser
  1. Créez avec PhpStorm la classe « GenericRules » qui implémente « IRules »
  2. Ajoutez à « GenericRules » un constructeur qui reçoit une instance de « IGestures » qui sera mémorisée dans une propriété d'instance
  3. En vous appuyant sur les principes énoncées au début de cette partie, donnez une version fonctionnelle des méthodes :
    • « getGestures() »
    • « checkGesture() »
    • « compare() »
    • « getGestureIndex() »
  4. Modifiez la classe « RockPaperScissorsLizardSpock » afin qu'elle hérite de « GenericRules » et qu'elle ne comporte plus qu'un constructeur
  5. Adaptez éventuellement les tests de « RockPaperScissorsLizardSpock » et vérifiez qu'ils passent
  6. Modifiez la classe « RockPaperScissors » afin qu'elle hérite de « GenericRules » et qu'elle ne comporte plus qu'un constructeur
  7. Adaptez éventuellement les tests de « RockPaperScissors » et vérifiez que votre classe les passe

Couverture de code

Tous les composants nécessaires étant présents et testés, il est utile de vérifier la couverture de code.

Travail à réaliser
  1. Générez un rapport d'analyse de couverture de code dans le répertoire « coverage »
  2. Vérifiez que votre couverture de code est suffisante
Remarque importante

Vous devrez fixer le mode de fonctionnement de Xdebug en préfixant votre commande de calcul de couverture de code par l'affectation de la variable d'environnement XDEBUG_MODE avec la valeur « coverage » :

XDEBUG_MODE=coverage vendor/bin/phpunit

RPS-9

Mettons votre code à l'épreuve de RPS-9.

Travail à réaliser
  1. Téléchargez le code de la classe de gestes RockGunWaterAirPaperSpongeHumanScissorsFireGestures.php (télécharger)
  2. Téléchargez le code de la classe de règles RockGunWaterAirPaperSpongeHumanScissorsFire.php (télécharger)
  3. Téléchargez le code de la classe de tests RockGunWaterAirPaperSpongeHumanScissorsFireTest.php (télécharger)
  4. Vérifiez que vos classes passent ces nouveaux tests