1. Résumé de "Clean Code" par Robert C. Martin

📖️️Traduction de https://gist.github.com/cedrickchee/55ecfbaac643bf0c24da6874bf4feb08(https://gist.github.com/cedrickchee/55ecfbaac643bf0c24da6874bf4feb08)


Un résumé des principales idées du livre "Clean Code : A Handbook of Agile Software Craftsmanship" de Robert C. Martin (alias Oncle Bob).

Le code est propre s'il peut être compris facilement - par tous les membres de l'équipe. Un code propre peut être lu et amélioré par un développeur autre que son auteur original. Avec la compréhensibilité viennent la lisibilité, la modifiabilité, l'extensibilité et la maintenabilité.

1.1. Règles générales

1.1.1 Suivez les conventions

* Restez simple et stupide. Le plus simple est toujours le mieux. Réduisez la complexité autant que possible.
* Règle des scouts. Laissez le terrain de camping plus propre que vous ne l'avez trouvé.
* Toujours trouver la cause profonde. Cherchez toujours la cause profonde d'un problème.
* Suivez le principe de la moindre surprise.
* Ne vous répétez pas (DRY).
* Ne pas passer outre les sécurités.

1.2. Règles de conception

* Gardez les données configurables (ex. : constantes) à un niveau élevé. Elles doivent être faciles à modifier.
* Préférez le polymorphisme à if/else ou switch/case.
* Séparer le code multithreading.
* Empêcher la sur-configurabilité.
* Utiliser l'injection de dépendances.
* Suivre la loi de Déméter. Une classe ne doit connaître que ses dépendances directes.

1.3. Conseils de compréhensibilité

* Soyez cohérent (consistent). Si vous faites quelque chose d'une certaine manière, faites toutes les choses similaires de la même manière.
* Utilisez des noms de variables explicites.
* Encapsulez les conditions limites. Il est difficile de garder une trace des conditions limites. Mettez le traitement de ces conditions à un seul endroit.
* Préférez les objets aux types primitifs.
* Évitez les dépendances logiques. N'écrivez pas de méthodes qui fonctionnent correctement en fonction de quelque chose d'autre dans la même classe.
* Évitez les conditions négatives.

1.4. Règles relatives aux noms

* Choisissez des noms descriptifs et non ambigus.
* Faites des distinctions significatives.
* Utilisez des noms prononçables.
* Utilisez des noms faciles à chercher (évitez les noms très courts, très récurrents).
* Remplacer les nombres magiques par des constantes nommées.
* Évitez les codages. Ne pas ajouter de préfixes ou d'informations de type.

1.5. Règles de fonctions

* Petites.
* Elles ne font qu'une chose et doivent la faire bien.
* Utilisez des noms descriptifs.
* Préférez moins d'arguments. Pas plus de 2 arguments si possible.
* N'ont pas d'effets de bord (ie. se limite à ce qu'elles sont censées faire).
* Ne pas utiliser d'arguments de type "flag(https://martinfowler.com/bliki/FlagArgument.html)". Diviser la méthode en plusieurs méthodes indépendantes qui peuvent être appelées depuis le client sans ce type d'argument.

1.6. Règles de commentaires

Essayez toujours de vous expliquer dans le code. Si ce n'est pas possible, prenez votre temps pour écrire un bon commentaire.
* Ne soyez pas redondant (par exemple : i++ ; // incrémente i).
* N'ajoutez pas de commentaires évidents. (par exemple: ->save() // sauvegarde la donnée).
* N'utilisez pas d'accolades fermantes (par exemple : } // fin de fonction).
* Ne commentez pas le code mort. Supprimez-le plutôt.
* Utiliser les commentaires pour expliquer l'intention.
* Utiliser les commentaires pour clarifier le code.
* Utiliser les commentaires pour avertir des conséquences.

1.7. Structure du code source

* "Séparez les concepts verticalement."
* "Le code connexe doit apparaître verticalement dense."
* Déclarez les variables à proximité de leur utilisation.
* Les fonctions dépendantes doivent être proches.
* Les fonctions similaires doivent être proches.
* Placez les fonctions dans le sens de la descente.
* Gardez les lignes courtes.
* N'utilisez pas l'alignement horizontal.
* Utilisez les espaces blancs pour associer des éléments connexes et dissocier des éléments faiblement connexes.
* Ne rompez pas l'indentation.

1.8. Objets et structures de données

* Cachez la structure interne.
* Préférez les structures de données.
* Évitez les structures hybrides (moitié objet et moitié données).
* Doivent être petites.
* Ne font qu'une seule chose.
* Un petit nombre de variables d'instance. Si votre classe a trop de variables d'instance, alors elle fait probablement plus d'une chose.
* La classe de base ne doit rien savoir de ses dérivés.
* Mieux vaut avoir plusieurs fonctions que de passer du code dans une fonction pour sélectionner un comportement.
* Préférez les méthodes non statiques aux méthodes statiques.

1.9. Tests

* Une assertion par test.
* Rapide.
* Indépendant.
* Répétable.
* Auto-validation.
* Rapide.
* Lisible.
* Facile à exécuter.
* Utilisez un outil de couverture.

1.10. Senteurs du code

* Rigidité. Le logiciel est difficile à modifier. Une petite modification entraîne une cascade de modifications ultérieures.
* Fragilité. Le logiciel se casse en de nombreux endroits à cause d'une seule modification.
* Immobilité. Vous ne pouvez pas réutiliser des parties du code dans d'autres projets en raison des risques encourus et de l'effort élevé.
* Complexité inutile.
* Répétition inutile.
* Opacité. Le code est difficile à comprendre.

1.11. Gestion des erreurs

Ne mélangez pas la gestion des erreurs et le code.
Utilisez des exceptions au lieu de renvoyer des codes d'erreur.
Ne retournez pas null, ne passez pas null non plus.
Lancez les exceptions avec le contexte.

2. Code Smell



📖️️Lire ceci directement : https://fr.wikipedia.org/wiki/Code_smell

3. Les codes à éviter

Lorsqu'on débute en PHP, on commence par écrire du code en suivant les mauvaises pratiques.
La plupart du temps, ce code est un héritage de celui qu'on pouvait écrire en 2005. A cette époque, le PHP balbutiait encore. En 2020, ce code ne devrait plus exister, car il est toujours possible de suivre les bonnes pratiques même avec du PHP procédural.

3.1. Utiliser le or die

On rencontre l'utilisation du 'or die' lorsque le développeur copie/colle un exemple d'utilisation du mysql_connect. On retrouve alors ce genre de ligne :

$bd = mysql_connect($nomserveur, $login, $pass) or die("Connexion échouée");

3.1.1 Pourquoi il ne faut pas utiliser le or die

Il n'est pas conseillé de forcer l'arrêt de l'interpréteur PHP.
Dans le cas d'une communication entre un serveur et un client, le client, lorsqu'il fait une requête HTTP s'attend à avoir une réponse du serveur. Ici, il reçoit la chaine de caractère "Connexion échouée". Non seulement le client ne devrait pas connaitre ce genre d'information, mais en plus il ne peut pas savoir ce qu'il doit en faire.

3.1.2 Ce qu'il faut faire

Il faut :
1. arrêter d'utiliser le die (sauf éventuellement pour débugguer) ;
2. utiliser le retour du mysql_connect pour gérer l'erreur de façon plus censée :
2.1 mettre à jour le journal d'évenement (log) en indiquant l'erreur (date/ip/contexte) ;
2.2 retourner une erreur au client dans un format structuré (ex: json) associée à un code d'erreur HTTP, ici cela peut être le code 500 puisque l'erreur vient manifestement du serveur WEB.

3.2. Utiliser un die/exit dans une fonction

Ce type de code introduit un danger :

function connect_bd(nomserveur, $login, $pass)
{
    $db = mysql_connect($nomserveur, $login, $pass);
    if (!$db) {
        die("Connexion échouée");
    }

    return $db;
}

3.2.1 Pourquoi ?

Utiliser un exit ou un die dans une fonction rend complexe la gestion des erreurs.

3.2.2 Alternative

On peut lancer une exception au niveau de la fonction afin de récupérer plus proprement l'erreur.
Voici un exemple :

function connect_bd(nomserveur, $login, $pass)
{
    $db = mysql_connect($nomserveur, $login, $pass);
    if (!$db) {
        throw new \Exception("Connexion échouée");
    }

    return $db;
}

Note : \Exception peut ici être remplacée par une classe plus spécifique (ex: "MySQLException").

3.3. Utiliser le tag ?>

Lorsque le tag PHP est fermé, tout ce qui le suit est tout de même retourné par le serveur WEB.
C'est-à-dire que des caractères blancs supplémentaires peuvent être retournés et interprétés par le navigateur comme du HTML.
Or, le HTML, c'est avant tout une syntaxe à base de balises à respecter, et quand on utilise le ?>, on ne contrôle plus ce qui est retourné par le serveur WEB. Dans la grande majorité des cas, cela est sans conséquence ; le résultat est interprété comme du HTML invalide et la page s'affiche tout de même. Dans la minorité des cas, cela a de graves conséquences ; cela peut entrainer des problèmes d'affichage (dans le cas où un <pre/> a été ouvert par exemple) ou même des erreurs JS du côté du client (le JS pense recevoir un objet JSON et il reçoit des caractères supplémentaires).

3.3.1 Ce qu'il faut faire

Il ne faut pas utiliser le tag ?>. Le serveur doit interpréter les espaces et les retours à la ligne comme du code PHP.
Il faut respecter les deux règles suivantes :
1. Ne jamais mélanger du HTML et du PHP dans le même fichier ;
2. N'utiliser qu'une et une seule fois le tag <?php par fichier .php ;

3.4. Ne pas espacer les symboles +,*,-,/,=

C'est surtout une question de confort de lecture et de norme de codage.

3.5. Mélanger du HTML et du PHP dans le même fichier

Le net regorge d'exemples où dans un fichier .PHP, on ouvre et on ferme des balises PHP pour écrire du HTML.
Si l'on souhaite construire plusieurs modèles de page selon le site web, autant isoler dès le départ les morceaux de templates dans des fichiers séparés.

3.6. Utiliser le double égal '=='

Alors, le double égal ne prend pas en compte le typage fort et ça c'est un problème.
Car toutes ces choses là retourne TRUE :

<?php
0 == false;
1 == 1.0;
true == 'chataigne';
!null == true;

Ceci peut génèrer des anomalies. En pratique, on compare une donnée qui vient de la base de données, d'un système de cache ou d'un formulaire avec une valeur en dur dans le code, et c'est souvent un drame car le code peut avoir un comportement non prévu par la suite si le test retourne soit disant TRUE.

3.6.1 Ce qu'il faut faire

Utiliser le triple égal '==='.

3.7. Utiliser un God Object

Copie de la page Wikipedia : God object(https://fr.wikipedia.org/wiki/God_object)

Un God object est, dans le domaine de la programmation orientée objet, un objet qui reconnaît trop de choses ou fait trop de choses. Le god object est un exemple d'antipattern (ou anti-patron).

Le principe général de la programmation structurée est de s'attaquer à un problème important en le divisant en plus petits problèmes à résoudre (stratégie de diviser pour régner). Une fois chacun des petits problèmes résolus, le problème général est automatiquement réglé. Ainsi, il n'y a qu'un objet auquel il doit être connu ou renseigné : lui-même. De la même façon, il n'y a qu'un seul ensemble de problèmes auquel un objet doit se confronter : l'ensemble des siens propres.

La programmation « god object » ne suit pas cette approche. Au lieu de cela, la plus grande partie du programme consiste en un seul bloc qui est renseigné sur tout et maintient constamment à jour données ou informations sur le programme, et fournit la plupart des fonctions et des algorithmes qui utilisent ces données. Du fait que cet objet supporte et organise tellement d'informations à lui seul, il joue un rôle identique à celui d'un dieu. Au lieu de blocs de programme communicant indépendamment entre eux et sans intermédiaire, les autres parties du programme sont dépendants du god object pour communiquer et prendre leurs informations. Comme le god object est référencé par tout le reste de la programmation, la maintenance de celle-ci devient très difficile, y compris dans les plus ordonnés des programmes.

Un god object est la version « orientée-objet » de l'incapacité à concevoir correctement les sous-programmes dans un langage de programmation procédural, ou d'utiliser trop de variables globales pour y stocker des informations sur l'état du programme à un moment donné (comme les drapeaux).

Bien que la création d'un god object soit considérée comme une mauvaise pratique de programmation, cette technique est à l'occasion utilisée dans les environnements critiques de programmation (comme les microcontrôleurs), où le gain dans la vitesse d'exécution et la centralisation du contrôle sont des facteurs plus importants que la facilité de maintenance et l'élégance de la programmation.

3.8. Dupliquer du code

Copie de la page Wikipedia : Duplication de code(https://fr.wikipedia.org/wiki/Duplication_de_code)

La duplication de code en programmation informatique est une erreur courante de conception de logiciels où une suite d'instructions similaires (voire identiques) existe en plusieurs endroits du code source d'un logiciel.

La duplication de code arrive à la suite de la programmation par copier-coller. C'est une erreur classique de débutants en programmation informatique ; cependant, cette erreur touche également les développeurs confirmés.

Le code dupliqué pose des problèmes de maintenance dont l'importance augmente avec la quantité de code dupliqué. Plus le code est dupliqué, plus il y a de code à maintenir. Par exemple :

si le code copié a une erreur de programmation, cette erreur se répète à chaque copier-coller, et corriger cette erreur nécessite de modifier tous les endroits où le code est dupliqué ;
si le code dupliqué doit évoluer, il faut alors modifier tous les endroits où le code est dupliqué pour y parvenir ;
un code dupliqué peut masquer des différences minimes, mais essentielles, qui existent avec une autre portion de code similaire.
L'antipattern correspondant est l'erreur de copier/coller : Il s'agit d'un copier-coller de code, où le code collé n'a pas été adapté à la portion de code environnante ce qui entraîne des incohérences. Cela arrive généralement à cause d'un défaut de vérification de la part du programmeur. La meilleure solution étant de factoriser les parties communes au lieu de les dupliquer.

La technique permettant d'éviter la duplication de code est la factorisation du code et celle permettant de s'en débarrasser est le réusinage du code.
Test Driven Development est un processus de développement permettant d'éviter le code dupliqué tout en disposant d'un code bien testé.
DRY (Don't repeat yourself! en anglais, ou Ne vous répétez pas) est un principe de programmation encourageant à éviter la duplication de code (dans laquelle le programmeur se répète).
Des logiciels tels PMD, permettent d'identifier le code dupliqué dans une base de code.

3.8.1 Utiliser la règle de trois

Copie de la page Wikipedia : Règle de trois(https://fr.wikipedia.org/wiki/R%C3%A8gle_de_trois_(programmation_informatique))

La règle de trois est une règle empirique de refactorisation de code pour décider quand des morceaux de code similaires doivent être refactorisés pour éviter la duplication de code. Cette règle indique que deux instances de code similaire ne nécessitent pas de refactorisation, mais lorsqu'un code similaire est utilisé trois fois, il doit être extrait dans une nouvelle procédure. La règle a été popularisée par Martin Fowler dans Refactoring 1 et attribuée à Don Roberts.

La duplication est considérée comme une mauvaise pratique en programmation car elle rend le code plus difficile à maintenir. Lorsque la règle codée dans un morceau de code répliqué change, celui qui gère le code devra le changer correctement à tous les endroits.

Cependant, le choix d'une conception appropriée pour éviter la duplication pourrait bénéficier de plus d'exemples pour voir les modèles. Tenter une refactorisation prématurée risque de sélectionner une mauvaise abstraction, ce qui peut entraîner un code pire lorsque de nouvelles exigences émergent 2 et devront éventuellement être à nouveau refactorisées.

La règle implique que le coût de la maintenance l'emporte certainement sur le coût de la refactorisation et une mauvaise conception potentielle lorsqu'il y a trois copies, et peut-être ou non s'il n'y a que deux copies.

4. Les conventions de nommage

C'est le genre de sujet qui fait couler beaucoup de sang entre pas mal de développeurs, c'est un peu dommage car ce serait plus intéressant qu'ils débattent plutôt à propos des RFC, histoire que la controverse s'élève au dessus des préférences personnelles.

Le PHP est surement encore décrié car le langage lui même n'a pas respecté à ses débuts de convention de nommage, notamment au niveau de ses fonctions.
On retrouve ainsi des noms de fonctions comme strtolower (flatcase), str_pos (snake_case) et d'autres curiosités comme bin2hex (le 2 voulant dire to) et cal_to_jd
Concernant le nommage des classes (PascalCase) et de leurs méthodes (camelCase), rien à signaler.

4.1. camelCase

Il est souvent confondu avec le PascalCase alors que la première lettre du premier mot doit être en majuscule.
Le camelCase est utilisé en PHP pour le nom des méthodes de classe.
Tous les mots sont attachés, la première lettre de chaque mot dès le deuxième mot est en majuscule.

Exemple: doSomething

4.2. PascalCase

Le PascalCase ou StudlyCaps est actuellement la convention de nommage à utiliser pour le nom des classes en PHP.

Tous les mots sont attachés, la première lettre de chaque mot est en majuscule.

Exemple : ChickenIterator : nom de classe composé des deux mots chicken et iterator.

4.3. snake_case

Le snake_case est utilisé dans certains noms de fonctions natives de PHP.

Tous les mots sont séparés par un underscore, les lettres de chaque mot sont en minuscule.

Exemple : chicken_iterator

4.4. kebab-case

Le kebab-case n'est pas utilisé en PHP, mais est utilisé en HTML pour le nommage des classes ou dans le nom des URLs.

Tous les mots sont séparés par un tiret, les lettres de chaque mot sont en minuscule.

Exemple : chicken-iterator

4.5. flatcase

Tous les mots sont attachés, les lettres qui les composent sont en minuscule.

Exemple: strtolower
# UPPER_CASE
L'UPPER_CASE est utilisé en PHP pour les noms des constantes.
Les mots sont séparés par des underscore, les lettres qui les composent sont en majuscule.
Pendant un moment, c'était aussi conventionnel de nommer les colonnes de ses bases de données en UPPER_CASE. Cela dérive actuellement plutôt vers du snake_case.

Exemple : STR_PAD_RIGHT

4.6. WikiCase

Rarement entendu autour d'une table, c'est du PascalCase où il est imposé que chaque lettre majuscule ne soit jamais suivie par une autre lettre majuscule.

Ex: CreateAJob est interdit et doit être remplacé par CreateJob

5. Les normes

Voici les normes que le projet doit suivre, exemples à l'appui.
On adaptera bien sûr les règles aux contraintes de la version de PHP utilisée sur les serveurs de production.

5.1. Exemple d'une classe

<?php

namespace App\Feature;

use App\Service;

class MyClass implements MyInterface
{
    public function myMethod(): bool
    {
        if () {

        }

        while() {

        }

        do {

        } while ();

        return true;
    }
}

Plusieurs règles sont à respecter :
* l'emplacement des accolades
** à la ligne pour la déclaration de la classe et de ses méthodes
** même ligne pour les conditions
* l'emplacement des espaces :
** entre le if/do/while/for/foreach/switch et les parenthèses
* l'emplacement des retours à la ligne :
** retour à la ligne avant le return/if/while/do/switch

* le nom des classes : en StudlyCase (PSR-1)
* le nom des méthodes : en camelCase (PSR-1)
* la visibilité la moins permissive possible est toujours indiquée, que ce soit pour les méthodes/constantes/attributs
* le type d'attribut/argument est toujours indiqué, le type mixed est interdit
* le type de retour des méthodes est toujours indiqué, même le void, le type mixed est interdit
* utiliser le mot clef static partout où cela est possible
* toute classe doit implémenter une interface directement ou par héritage
* toujours utiliser le cas default dans un switch/match

5.2. Plus de code procédural

Mis à part celui de l'autoload, le projet doit être codé en full POO :
* Plus de fonctions seules : on n'utilise que des méthodes de classes.
* Ne pas utiliser de variable globales mise à part les superglobales

5.3. Utilisation des superglobales

Les superglobales ($GLOBALS, $_SERVER, $_GET, $_POST, $_FILES, $_COOKIE, $_SESSION, $_REQUEST, $_ENV) ne doivent être utilisées qu'au plus une et une seule fois dans le projet.
Il est strictement interdit d'en modifier directement la valeur.

5.4. Templating

Ne pas écrire de HTML dans un fichier PHP, utiliser un moteur de template ou créer sa propre solution.
Ne pas utiliser de fonctions natives de sortie standard plus d'une fois dans l'ensemble du projet.

5.5. Fonctions natives PHP

Autant que faire ce peut, ne pas utiliser plus d'une fois les fonctions natives de PHP dont le rôle est très précis (ex: htmlentities, json_decode...) ou dont le nombre d'arguments facultatifs est important.
Les encapsuler si besoin en définissant des classes à responsabilité unique (ex: class Templater, méthode escape($htmlContent)).

Bien sûr, le but n'est pas de faire une classe par fonction PHP...

6. Acronymes de bonnes pratiques

6.1. SOLID

* Single responsibility principle (Responsabilité unique)
* Open/closed principle (Ouvert/fermé )
* Liskov substitution principle (Substitution de Liskov )
* Interface segregation principle (Ségrégation des interfaces)
* Dependency inversion principle (Inversion des dépendances)

6.2. KISS

Keep It Simple, Stupid (garde ça simple, idiot)

🧙‍♂️️Cette phrase peut être détournée et utilisée pour des motifs non valables.
Le KISS concerne de mon point de vue uniquement la facilité d'utilisation du logiciel, et non sa conception technique.


6.3. DRY

Don't Repeat Yourself (Ne vous répétez pas)

📖️️Wikipédia : https://fr.wikipedia.org/wiki/Ne_vous_répétez_pas(https://fr.wikipedia.org/wiki/Ne_vous_r%C3%A9p%C3%A9tez_pas)


6.4. MoSCoW

* Must have this (doit être fait)
* Should have this if at all possible (devrait être fait dans la mesure du possible)
* Could have this if it does not affect anything else (pourrait être fait dans la mesure où cela n'a pas d'impact sur les autres tâches)
* Won't have this time but would like in the future (ne sera pas fait cette fois mais sera fait plus tard)

6.5. YAGNI

You Ain't Gonna Need It (vous n'en aurez pas besoin)

📖️️Wikipédia : https://fr.wikipedia.org/wiki/YAGNI(https://fr.wikipedia.org/wiki/YAGNI)


6.6. GRASP

General Responsibility Assignment Software Patterns (lignes directrices pour l'attribution de la responsabilité des classes et des objets en conception orientée objet)


# Loi de Demeter
La Loi de Déméter pour les fonctions requiert que toute méthode M d'un objet O peut simplement invoquer les méthodes des types suivants d'objets :

* O lui-même
* les paramètres de M
* les objets que M crée/instancie
* les objets membres de O

📖️️Wikipédia : https://fr.wikipedia.org/wiki/Loi_de_Déméter(https://fr.wikipedia.org/wiki/Loi_de_D%C3%A9m%C3%A9ter)


# Loi de Leblanc
Later Equals Never

Utilisé en particulier pour cibler les @TODO qui font partie du code pendant des années. Il est en effet assez rare que le @TODO soit traité par un autre développeur que celui d'origine.


7. PSR-1: Basic Coding Standard

7.1. Introduction

Cette toute première PSR toujours valide, la bien nommée PSR-1(https://www.php-fig.org/psr/psr-1/) pose le décor : il est vivement recommandé lorsqu'on fait du PHP de faire du POO, cela ne semble par contre pas obligatoire (voir la conclusion).

7.2. Résumé des règles

* Les fichiers PHP doivent uniquement utiliser la balise <?php
* Les fichiers doivent uniquement être encodés en UTF-8 sans entête BOM
* Les fichiers doivent soit être déclaratifs (constances, classes, fonctions), soit être à effet de bord (modifie des fichiers, appels des services), jamais les deux en même temps
* Les namespaces et classes doivent respecter la convention de nommage de la PSR-4
* Les noms des classes sont en StudlyCaps
* Les noms des constantes de classes sont toujours en majuscule et espacé si besoin par des underscores.
* Les noms des méthodes doivent être déclarés en camelCase.

7.3. Les règles une par une

7.3.1 Les balises PHP

7.3.1.1 Une et une seule balise

Bien qu'il existe d'autres balises, une seule balise doit être utilisée, la balise <?php
La balise ?> ne doit pas être utilisée:
* Le code HTML est de toute manière toujours séparé du code PHP
* cela évite de laisser des caractères en fin de fichier interprété comme des caractères HTML

7.3.1.2 Exemple d'utilisation

Tous les fichiers .php de votre projet doivent commencer par cette ligne suivie d'un retour à la ligne :

<?php

7.3.2 UTF-8 sans BOM

D'où la nécessite d'écrire du code avec des outils dédiés (exit donc le wordpad de Windows par exemple).
Le BOM est une entête de fichier pratiquement invisible lorsque l'on code. Au moment de l'appel au serveur, ce caractère est le premier que le navigateur voit lorsqu'il pense récupérer le contenu de la page (avant donc le <!DOCTYPE ou <html>).
Comme ce caractère est en dehors de toute balise meta qui indiquerait l'encodage de la page, le navigateur va faire comme il souhaite puisqu'il considère que la page html n'est pas valide, tous les caractères sont alors affichés en non UTF-8.

7.3.2.1 Exemple d'IDE

Voici plusieurs outils qui ont la notion d'encodage et de BOM:
Sûrs :
* PHPStorm 2020.3
* Geany 1.36

Probables :
* NodePad++
* Visual Studio
* SublimeText
* Atom
* Eclipse

Peu probables :
* Wordpad
* Gedit
* Kate
* Mousepad
* Leafpad
* Pluma
* KWrite et Kedit
* Nedit/TEA ?

Certains outils de traitement de texte en ligne de commande n'injectent pas d'entête BOM :
* emacs (et XEdit)
* vim (et Gvim)
* nano

7.3.3 Fichiers purement déclaratifs ou à effet de bord (side-effect)

7.3.3.1 Fichier PHP avec effet de bords == PHP procédural

Si un fichier PHP, lors de son execution provoque ceci,on considère qu'il a des effets de bord :
* génèrer une sortie (avec echo, print_r et autres)
* lire, créer ou modifier un fichier (file_put_contents, fwrite et autres)
* modifier la configuration de PHP (set_ini)
* utiliser un require/include, interdit de toute manière en dehors des choses comme l'autoloader
* faire appel à un service externe (HTTP, FTP, n'importe quoi)
* génèrer des erreurs
* modifier des variables globales ou statiques

Ce qui est globalement très souvent le cas lorsque l'on fait du procédural, à moins de faire du code qui ne fait rien.

7.3.3.2 Fichier PHP sans effet de bords == PHP POO

En bref, un fichier déclaratif ne doit rien faire sauf déclarer, c'est à dire :
* tous les fichiers de classes/interfaces sont déclaratifs
* tous les fichiers PHP qui déclarent uniquement des fonctions (utilisation du mot clef function), des constantes (utilisation du mot clef define)

Cependant, il y a certaines ambiguités, aujourd'hui nous avons tendance à créer des classes de type Controller ou même Command.
Ces deux là remplacent dans le concept les fichiers de type command.php et index.php que l'on manipulait en non POO.

En d'autres termes, la PSR-1 est clairement du côté de l'usage de la POO. Les fichiers PHP de type procédurale sont à oublier dans le code source de votre application.

7.3.4 Une execution dépend forcément d'un fichier écrit en PHP procédural

Il ne faut pas penser qu'une application en PHP ne peut tourner qu'avec du POO vu que ce n'est que du déclaratif, car il y a forcément au moins dans votre projet un fichier quelque part écrit en procédural pour executer tout ce beau monde.
Ex: via composer 1.10, le fichier autoload.php est celui qui se charge d'executer tout ce petit monde.

Donc, pour faire simple et respecter cette règle :
* tous les fichiers PHP de votre projet sont en POO use/class/interface/namespace et sont donc déclaratif
* le seul et unique fichier de type side-effect est l'autoload, bien souvent géré par Composer ou par un autre Framework

/!\ ce qui est appelé effet de bord est le fait que la ligne s'execute, pas qu'elle soit utilisée dans une définition.
Par exemple, on peut très bien définir une méthode dans une classe qui ferait un appel à un service externe.
Mais le fait de charger cette classe ne doit effectivement pas executer la ligne en question, sauf si c'est demandé par un fichier executif.

7.3.4.1 Un fichier PHP == Une seule classe ou interface

Bien que dans sa cuisine interne d'optimisation, le framework que vous utilisez fait une fusion de toutes les déclarations. Lors du développement, il faut créer autant de fichier que de classe/interface.

7.3.5 L'autoloading

Fini le temps où les fichiers PHP sont importés dans son code via des require ou des include. Chaque fichier PHP doit déclarer en entête son namespace (cf la PSR-4 pour le nommage) et importer les autres fichiers via des use.
Il y a des exceptions : l'autoloader est le seul à pouvoir faire des includes, idem pour un système personnalisé de mise en cache du code PHP.

7.3.6 Nommage des classes

Le nom des classes et des fichiers de classe doivent être en StudlyCaps, c'est à dire que tous les mots sont reliés ensemble sans espace et commencent chacun par une majuscule.
Ex: SecurityController pour le nom de la classe et SecurityController.php pour le nom du fichier.

7.3.7 Nommage des constantes

Toutes les constantes doivent être définies dans des classes ou des interfaces.
En static car il n'est pas être nécessaire d'instancier la classe.

Pendant un temps la visibilité unique des constantes était public, on peut désormais changer cette visibilité.

7.3.7.1 Exemple de déclaration de constante

Voilà comment on peut définir des constantes :

<?php
namespace App;

class MyClass
{
    public static const MY_STRING_CONST = 'valeur';
    protected static const MY_BOOL_CONST = true;
    private static const MY_INT_CONST = 8;
    public static const MY_FLOAT_CONST = 8.08;
}

7.3.7.2 Exemple d'utilisation d'une constante dans un namespace commun

Voilà comment on peut appeler une constante public (ne pas oublier le use en entête) :

<?php
namespace App;

echo MyClass::MY_STRING_CONST;

7.3.7.3 Exemple d'utilisation d'une constante dans un namespace différent

Si la classe est dans un autre namespace, par exemple elle est déclarée dans la classe App\MyClass et est appelée en procédural dans le namespace Walt, il faut l'indiquer dans un use.
<?php
namespace Walt;

use App\MyClass;

echo MyClass::MY_STRING_CONST;

7.4. Conclusion : POO obligatoire ou non ?

Alors, est il possible de faire du PHP sans faire du POO en respectant la PSR-1 ? Je pense que la réponse est Oui.

Imaginons deux fichiers :
* index.php : uniquement du procédural (pas de définition), fait un require_once('lib.php');
* lib.php : uniquement des définitions de fonctions.

index.php :

<?php
require_once('lib.php');
myFunction();


lib.php :

<?php
function myFunction()
{
    // Peu importe, du code.
}


A priori, ce n'est pas de la POO mais cela respecte les différentes règles de la PSR-1.

On peut même aller plus loin en créant autant de librairies que l'on souhaite avec des noms plus judicieux et les injecter avec d'autres require.

La PSR-1 ne rend donc pas obligatoire la POO mais l'incite fortement vu que l'accent est fortement mis sur l'application de la PSR-4.

8. PSR-4: Autoloader


8.1. Pourquoi la PSR-0 a été remplacée par la PSR-4 ?

La réponse est toute simple, la PSR-0 introduisait la notion de namespace en PHP mais cela ne concernait que les vendors du projet et non pas le projet lui même.
Ce qui est en soi assez dommage de ne pas pouvoir utiliser l'élégance des namespaces au sein du code de son application.

9. Que faire des vieilles applications ?

9.1. Le problème

9.1.1 Que veut dire le terme Legacy ?

Legacy est un mot anglais qui veut uniquement dire héritage. En informatique, on appelle en général Legacy le code source original du projet, bien souvent avant l'implémentation d'un framework.
Mais cela pourrait très bien faire référence au code de la veille, il faut que celui-ci soit considéré comme obsolète et qu'il soit toujours executé en production.

9.1.2 Traits typiques d'une application Legacy

Alors, la vieille application que tout le monde connait, c'est celle-ci :
* du PHP procédural ;
* une page web générée par des echo ou des ouvertures et fermetures de tags php ;
* des ouvertures / fermetures d'accès à la base de données dans chaque fichier ;
* des variables globales qui portent des noms évocateurs comme mysql_password default_admin_name ;
* du copier/coller de fonctions mélangeant des noms de variables comme $a, $b, $tartiflette... ;
* des appels par référence utilisés sans justification ;
* de la mise en cache gérée un peu partout pour tout et n'importe quoi ;
* des "scripts cron" qui réparent les erreurs des "scripts web" ;
* des librairies en version 0.4.0-modifie-par-luc-le-dev-parti-depuis-3-ans compatible avec une version particulière de PHP ;
* du franglais car les développeurs ont trouvé un compromis : chacun fait comme il veut ;
* les tests sont faits manuellement avec le navigateur web.

9.1.3 Les problèmes qu'engendre une application Legacy

Tout le monde peut les lister :
* fuite des développeurs ;
* charge de maintenance technique (des bugs, des rattrapages) ;
* clients mécontents, ambiance morose au travail ;
* évolutions fonctionnelles très difficiles à implémenter ;
* fonctions métiers floues.

9.1.4 Est-ce si grave ?

La vrai question à se poser est celle-ci :
Combien cette application rapporte à l'entreprise VS combien cette application coûte à l'entreprise ?
Même si l'entreprise dépense énormement d'argent pour maintenir tout ce beau monde en vie, est-ce qu'elle gagne tout de même de l'argent ? La réponse est souvent affirmative :tant que les clients sont là et que l'application répond dans les grandes lignes à leurs demandes, cela suffit pour vendre.

9.2. Comportement 1 : tout refaire et tout de suite

9.2.1 Pourquoi la refonte totale n'est pas forcément judicieuse

Avant d'envisager de tout casser pour reconstruire, il faut d'abord répondre à toutes ces questions :
* Combien ça va prendre de temps de faire un code iso-fonctionnalité et donc combien d'argent l'entreprise doit dépenser pour ça ?
* Y a-t-il vraiment un gain financier derrière (on fait rarement une refonte au sein d'une société pour la gloire) ?
** Cela peut-il réduire le temps de chargement des pages ?
** Cela peut+il augmenter le nombre de clients et donc de ventes ?
** Cela va-t-il réduire la charge en maintenance technique et du SAV auprès du client ?
** Cela va-t-il réduire la charge du SAV auprès du client ?

Enfin pour résumer :
* Est-ce que l'entreprise va gagner plus d'argent après cette refonte ? Combien ? Quand ?

Si vous ne pouvez pas répondre à cette dernière question, chiffres à l'appui, est-ce que vous pensez réellement que l'entreprise va prendre aveuglément le risque de dépenser l'argent que lui rapporte la vente de ses produits via l'application actuelle ?

9.2.2 Avec ou sans refonte, il faut toujours penser à l'après

Dans un monde utopique et merveilleux où vous refaites tout gratuitement, avez-vous penser à la suite ?
* qui gérera les futurs bugs et combien cela coutera ?
* qui modifiera le futur code et a-t-on la garantie qu'il ne va pas réduire la qualité de la refonte ?
* qui mettra à jour la version du framework/des librairies/du langage ?
* est ce que l'application sera toujours utilisée ?

9.2.3 Remplaçons le framework maison par un framework populaire : la petite histoire

Voici un exemple concret : en 2011, une entreprise spécialiste dans la vente en ligne de DVD a fait le choix d'utiliser symfony 1.4 pour refaire son application mère.

Tout porte à croire que le framework va faire des miracles, après tout il est dans le top 5 des frameworks français.

Bilan plusieurs années plus tard :
* le framework a mal été mis en place
** les développeurs n'avaient pas été formés
** certaines pratiques controversées de code ont été maintenues
* la maintenance de l'application est terrible
** la documentation sur cette version du framework n'existe quasiment plus
** la communauté s'est tournée vers des solutions plus récentes
** aucun développeur du marché ne veut travailler sur le code source
* l'entreprise pleure :
** elle a perdu de l'argent (temps de conception/développement/test...)
** un à plusieurs développeurs sont partis, aussi bien ceux à l'ère pré-framework que ceux à l'ère post-framework

Et à ceux qui dise "il fallait le mettre à jour en version 2.0 au bon moment", mettez-vous une seconde à la place de l'entreprise : "je ne comprends pas, je viens de payer ce projet qui a pris 1 an plus de 100 000€ et maintenant tu me dis qu'il faut encore 6 mois pour le mettre à jour ? Et demain ? ".

Quelle est la véritable erreur ? Concevoir son application en mélangeant le code métier ET le code technique.

Cette petite histoire nous apprends deux choses :
* il peut être dangereux d'utiliser un framework sans avoir à l'esprit que le code du projet devrait pouvoir être indépendant de celui-ci
* un projet legacy 2020 n'est pas forcément la même chose qu'un projet legacy 2010

9.2.4 On ne peut pas tout changer du jour au lendemain

Quand un développeur arrive sur un projet, son avis se résume souvent à ceci :
* le modèle de données doit être refait
* les applications doivent être refaites

Il faut alors prendre de la hauteur sur tout ceci. Si tout *marchouille* à son arrivée, c'est au prix de plusieurs années de travail, il est réellement illusoire de penser que le nouveau développeur va tout changer du jour au lendemain, car il lui faudra déjà plusieurs semaines pour assimiler toutes les règles métiers : sachant que la plupart du temps ces règles sont écrites et modifiées sur des documents ou des emails dissiminés sur plusieurs comptes utilisateurs, le développeur les découvrira bien souvent dans le code des anciennes applications.
Idem pour le modèle de données, il est intrinséquement lié au code source, chaque colonne a sa raison d'exister ou non et répond parfois à des règles énigmatiques.

9.2.5 Garder sa vieille application VS la remplacer

Quid de la TMA et du SAV ?
Malheureusement, un mauvais projet, c'est de la maintenance en plus. Là il faut aussi prendre deux minutes pour poser les choses :
- quelles sont les anomalies remontées ? Leurs fréquences ? Leurs coûts ?
* S'il suffit à l'entreprise de payer une personne 10€ pour débloquer une fois sur cent un client qui en rapporte 2000€ et que cela arrive une fois par jour sur l'application principale de la boite, quel est concrètement l'interet financier de payer un développeur 100000€/an (si on compte l'urssaf, la mutuelle, les chèques cadeaux et tout le tintoin) pour tout refaire ? Et bien en supposant que le coût de la TMA et du SAV est de 0€ (idéalement utopique), voilà deux calculs :
** cas de figure 1 : (CA - coût de SAV/TMA) = 2000*100 - 10*365 = 196 350€ de marge
-> au bout de 10 ans, en supposant une progression de 0% : 1 963 500€
** cas de figure 2 : (CA - coût du nouveau projet - coût de SAV/TMA) = 2000*100 - 100000 - 0 = 100 000€ de marge
-> au bout de 10 ans, en supposant une progression de 0% : (marge de la 1iere année + marge des 9 années suivantes) = 100 000 + 200 000*9 = 1 900 000€ de marge sur 10 ans

Conclusion ? on perd plus d'argent dans le cas de figure 2, en refaisant tout, que dans le cas 1, en payant de façon -certes parfois ingrates- quelqu'un pour maintenir le code source d'origine.

Bien sûr, si on change les chiffres de cette histoire, le cas de figure 2 peut être plus rentable. Et c'est là que c'est compliqué pour tout le monde : alors que le développeur pense ça serait forcément mieux d'avoir un projet de qualité, l'entreprise va surtout demander avant toute chose le prix que ça coutera pour le gain que ça rapportera (moins les coûts de maintenance qui ne seront jamais nulle pour autant).
Tout est donc qu'une question de mesure encore une fois, il n'y a pas de réponse magique et ferme car chaque cas est unique mais les questions qu'il faut se poser sont par contre toujours les mêmes :
* combien ça coute aujourd'hui à l'entreprise de garder cette solution technique et combien elle lui rapporte ?
* combien ça couterait de changer la solution technique et combien ça rapporterait sur le court/moyen/long terme ?

En supposant bien sûr que les clients soient toujours là, mais ça c'est encore un autre problème. (ex: le site d'ecommerce spécialiste des DVDs a bien fait de payer une nouvelle version de son site web au début des années 2010, la vente de DVDs en france décline de plus de 10% chaque année...).

9.3. Comportement 2 : améliorer l'existant au fur et à mesure, développer de nouvelles applications

Par existant, je nomme l'ensemble du code source de la société, applications Legacy ou non.

9.3.1 Faire avec l'ancien code

L'ancien code, vous pouvez toujours l'améliorer au fil de l'eau pour qu'il soit plus simple à comprendre/maintenir/modifier pour les générations futures, même dans une ancienne version du langage ou du framework.
Vous pouvez en outre être stratégique et n'améliorer que les bouts de code qui génèrent le plus de problèmes (bugs/lenteurs)

Par améliorations, voici ce que je préconise :
* réduiser le nombre de lignes de code
** en identifiant les endroits dupliqués
** en identifiant le code mort
** en créant des fonctions de refactorisation : attention à ne pas faire des choses trop complexes par ailleurs
* appliquer autant que possible les normes de code de la dernière version du langage/framework
* améliorer les performances des pages les plus critiques pour le chiffre d'affaire (ex: paiement du panier)

9.3.1.1 Qu'est ce que des choses trop complexes ?

Par complexe, j'entends que si vous remplacez quelques lignes de codes HTML facilement maintenables par n'importe qui par du code qui va génerer ces lignes avec des règles de plus en plus bizarres pour prendre en compte tous les cas, votre nouveau code, même des dizaines de fois moins long que le précédent sera plus difficile à comprendre, donc plus couteux à terme par l'entreprise.

Notamment :
* un code qui génère des formulaires HTMLs
* un code qui génère des bouts de codes PHP
* un code qui met en cache la terre entière pour optimiser sur le moment une lenteur

9.3.2 Produire du code utile

Donc que faut-il faire ? La réponse est simple : se concentrer sur du code de qualité qui *paye*.
* Faire du code de qualité : toujours garder un projet dont le code respecte les bonnes pratiques. Faire un code métier autonome du framework.
* Faire du code qui paye : travailler sur les projets qui rapporte de l'argent à l'entreprise car c'est une partie de celui-ci qui est reversé aux salariés (même dans le cas où il y a des associés vénaux).

En outre, si un développeur produit du code de qualité, il sera surement plus heureux.

9.3.3 Que faire si le métier demande une ou plusieurs nouvelles fonctionnalités

Si la nouvelle fonctionnalité demandée par le métier rentre dans le cadre d'une ancienne application, il faut savoir choisir entre deux solutions :
* faire avec les "normes" de l'application pour ajouter son bout de code
* faire un code de qualité en dehors de l'application (ex: via un web service) et l'appeler via l'application comme on peut

Si le métier demande beaucoup de nouvelles fonctionnalités, il peut s'avérer pertinent de proposer une nouvelle application qui adoptera de meilleurs principes.

Dans les deux cas, la question est toujours la même : combien cela va couter à l'entreprise sur le court/moyen/long terme ?

10. Utilisation des exceptions en PHP

Voici une interprétation de la page 15 Best Practices for Exception Handling(http://codebuild.blogspot.com/2012/01/15-best-practices-about-exception.html). Certaines best practices se complètent, il ne me semble pas nécessaire d'en citer 15.

10.1. Ne pas utiliser les exceptions pour gérer des erreurs métiers

Toutes les erreurs métiers doivent être prévues et gérées via des conditions classiques, on évitera donc d'écrire quelque chose comme catch (FunctionalException $e).

10.2. Eviter l'usage du \Exception

Le nom de l'exception doit être clair, on évite d'utiliser la classe \Exception, trop générale. Donc on évite le catch (\Exception $e) même dans le cas où on utiliserait une librairie/composant qui retournerait beaucoup d'exception (ex: Doctrine).
Evidemment, si la librairie retourne une instance d'\Exception, il n'y a pas le choix que de la catcher.cher

10.2.1 Exemples de noms clairs

* MysqlConnectionException
* NullEntityException
* EmptyFileException
* DeniedAccessException
* HttpException (Eventuellement, on peut définir des exceptions pour chaque code d'erreur HTTP)
En plus d'avoir un nom clair, chaque exception doit être correctement documentée.

10.3. Ne pas utiliser autre chose que des exceptions en cas d'erreurs techniques

On évite de retourner des nombres négatifs par exemple selon les erreurs. Mieux vaut privilégier des exceptions claires et non ambigues.

10.3.1 Exemples de mapping pour une lecture de fichier

* -1 -> FileNotFoundException
* -2 -> FileNotReadableException
* -3 -> UnsupportedFileException
* -4 -> TooBigFileException (dans le cas d'une limite à l'écriture par exemple)

10.4. Eviter de faire uniquement un throw dans un catch

Alors, ce genre de code est inutile :

try {
    // Peu importe
} catch (AnyException $e) {
    throw $e;
}

Soit le catch sert à quelque chose (on logguerait l'erreur par exemple), soit s'il ne sert à rien, il n'a pas de raison d'être.
De la même façon, ce n'est pas pertinent non plus de transformer le type d'exception en un autre type d'exception, autant laisser passer l'exception d'origine.
Eventuellement, il peut être par contre intéressant de compléter le message d'erreur de l'exception afin d'aider à la résolution du problème.

10.4.1 Eviter de ne rien faire dans le catch

Alors, ignorer l'exception, c'est souvent une fausse bonne idée. On évitera donc ce code :

try {
    // Peu importe
} catch (AnyException $e) {
    // Sifflote
}

10.5. Faire de l'héritage autant que faire ce peut

Plutôt que de faire hériter toutes ses exceptions avec \Exception, cela peut être intéressant de les réunir au sein de leurs domaines respectifs (ex: FileNotFoundException extends FileException, NotFoundHttpException extends HttpException, UnauthorizedUserException extends UserException...).

10.6. Logger les exceptions selon leur gravité

Selon les exceptions catchés, il peut être intéressant d'inscrire dans les logs les erreurs en fonction de leur gravité (DEBUG/NOTICE/FATAL).
On évite par contre de logger plusieurs fois la même erreur.

10.6.1 Exemples de niveau

DEBUG : Fichier non initialisé, Utilisateur n'existant pas encore
NOTICE : Time out sur la récupération d'une donnée non essentielle en cache par exemple
FATAL : erreur de connexion à la BDD, moteur de recherche, file d'attente

10.7. Utiliser le finally

Rarement utilisé, il peut être parfois être pratique pour libérer des ressources par exemple.

10.8. Eviter les try/catch dans des boucles

On peut catcher 1 fois mille erreurs plutôt que catcher mille fois 1 erreur.

10.9. Faire plusieurs try/catch plutôt qu'un seul

Bon cela se défend car personnellement je préfère fois un seul try/catch que plusieurs d'affilée.
L'idée est qu'il y ait peu d'instructions dans le try afin d'évaluer plus facilement d'où proviennent les erreurs.

10.10. User de code d'erreurs

Dans l'idéal, chaque exception est accompagnée d'un code d'erreur qui simplifiera la maintenance de l'application, ne serait-ce que dans l'étude statistique des problèmes courants.

10.11. Enfin, ne pas en abuser

Si pour afficher une seule page web, on risque de tomber sur l'une des 200 exceptions possibles, c'est que manifestement, la page est peut-être trop complexe.
Au-delà de 10 exceptions possibles pour un seul appel HTTP, je pense que cela devient ingérable.