Écrire du code

Comprenez les standards et process d’Elgg pour que vos propositions de modification soient acceptées aussi vite que possible.

Agrément de licence

En proposant un patch vous accordez de publier le code sous une licence GPLv2 et une licence MIT.

Demandes de fusion (Pull Requests)

Les Pull requests (PRs) sont le meilleur moyen de contribuer au noyau d’Elgg. L’équipe de développement les utilise y compris pour les modifications les plus mineures.

Pour de nouvelles fonctionnalités, soumettez une demande de fonctionnalité ou parlez-en avec nous en premier lieu et assurez-vous que l’équipe du noyau approuve votre proposition avant de passer beaucoup de temps sur du code.

Listes de vérification (checklists)

Utilisez ces listes de vérification réduites pour les nouveaux PRs sur github afin de garantir des contributions de haute qualité et d’aider tout le monde à comprendre le statut des PRs ouverts.

Demandes de correction de bug (bugfix PR) :

- [ ] Commit messages are in the standard format
- [ ] Includes regression test
- [ ] Includes documentation update (if applicable)
- [ ] Is submitted against the correct branch
- [ ] Has LGTM from at least one core developer

Demande de fonctionnalité (feature PR) :

- [ ] Commit messages are in the standard format
- [ ] Includes tests
- [ ] Includes documentation
- [ ] Is submitted against the correct branch
- [ ] Has LGTM from at least two core developers

Choisir une branche vers laquelle publier

Le tableau suivant suppose que la dernière version stable est la 2.1.

Type de changement

Branche vers laquelle publier

Correctif de sécurité

Ne le faites pas ! Envoyez un email à security@elgg.org pour des conseils.

Correctif de bug

1.12 (ou 2.1 si le correctif pour 1.12 est trop complexe)

Performance

2.x

Dépréciation

2.x

Fonctionnalité mineure

2.x

Fonctionnalité majeure

master (maître)

A n’importe quel changement non rétro-compatible

master (maître)

Si vous ne savez pas quelle branche choisir, demandez !

La différence entre des changements mineurs et majeurs est subjective et soumise à l’appréciation de l’équipe du noyau.

Format du message de commit

Nous exigeons un format particulier afin de permettre de publier plus souvent, avec des journaux des modifications et un historique des sources améliorés. Suivez simplement les étapes suivantes :

  1. Commencez par le type en sélectionnant la dernière catégorie qui s’applique dans cette liste :

    • docs - uniquement la documentation a été mise à jour

    • chore - (corvée) ceci comprend les remaniements (refactoring), les changements de style de code, l’ajout de tests manquants, ce qui concerne Travis, etc.

    • perf - l’objectif principal est d’améliorer la performance

    • fix - ceci corrige un bug

    • deprecate - la modification rend obsolète toute partie de l’API

    • feature - ceci ajoute une nouvelle fonctionnalité pour les utilisateurs ou les développeurs

    • security - la modification affecte une question de sécurité d’une manière ou d’une autre. Merci de ne pas pousser ce commit vers un repository public. Contactez plutôt security@elgg.org.

    Par ex., si votre commit fait des remaniements pour régler (fix) un bug, cela reste un « fix ». Cependant, si ce bug est lié à la sécurité, le type doit être « security » et vous devriez envoyer un email à security@elgg.org avant de procéder. En cas de doute, faites au mieux, et un relecteur (reviewer) vous fournira des conseils.

  2. Entre parenthèses, ajoutez le component, une courte chaîne qui décrit les sous-syst-me en train d’être modifié.

    Quelques exemples : views, i18n, seo, a11y, cache, db, session, router, <plugin_name>.

  3. Ajoutez une virgule, un espace, et un court résumé summary des modifications, qui va apparaître dans le journal des modifications (changelog).

    Aucune ligne ne devrait dépasser 100 caractères en longueur, aussi gardez un résumé concis.

    Bon résumé

    Mauvais résumé (problème)

    les propriétaires de pages (pages owners) voient leur propre bloc propriétaire sur les pages

    correction de bug (vague)

    la vue du graphique en barres n’échoue plus si “foo” n’est pas défini

    met à jour views/default/bar.php de sorte que la vue bar ne soit plus… (info redondante)

    réduit la mise en page de la rivière pour rentrer sur un iPhone

    modifie la mise en page de la rivière (vague)

    elgg_foo() gère les tableaux pour $bar

    dans elgg_foo() vous pouvez maintenant passer un tableau pour $bar et la fonction sera… (déplacez les précisions vers la description)

    supprime la couleur du lien de l’entête des commentaires dans la rivière

    corrige la bdd pour que… (info redondante)

    requiert un titre non-vide pour enregistrer les pages

    peut enregistrer des pages sans titre (résume de manière confuse l’ancien comportement)

  4. (recommandé) Sautez une ligne et ajoutez une description des modification. Incluez leur raison d’être, toute information sur la compatibilité ascendante ou descendante, et toute raison pour laquelle cette modification devait être faite d’une certaine manière. Exemple :

    Nous accélérons la migration des tables en utilisant une seule requête INSERT INTO … SELECT au lieu de ligne par ligne. Cette migration se produit durant la mise à niveau vers Elgg 1.9.

    A moins que votre modification soit triviale/évidente, une description est requise.

  5. Si le commit correspond à une demande (« issue ») Github, sautez une ligne et ajoutez Fixes # suivi par le numéro de demande. Par ex. Fixes #1234. Vous pouvez inclure de multiples demande en les séparant par des virgules.

    GitHub va fermer automatiquement la demande quand le commit est fusionné. Si vous souhaitez simplement faire référence à une demande, utilisez Refs #.

Quand c’est terminé, votre message de commit aura le format :

type(component): summary

Optional body
Details about the solution.
Opportunity to call out as breaking change.

Closes/Fixes/Refs #123, #456, #789

Voici un exemple d’un bon message de commit :

perf(upgrade): speeds up migrating remember me codes

We speed up the Remember Me table migration by using a single INSERT INTO ... SELECT query instead of row-by-row.
This migration takes place during the upgrade to 1.9.

Fixes #6204

Pour valider les messages de commit localement, vérifiez que .scripts/validate_commit_msg.php est exécutable, et faites une copie ou un lien symbolique dans le dossier .git/hooks/commit-msg.

chmod u+x .scripts/validate_commit_msg.php
ln -s .scripts/validate_commit_msg.php .git/hooks/commit-msg/validate_commit_msg.php

Réécrire des messages de commits

Si votre PR ne se conforme pas au format standard de message de commit, nous vous demanderons de le réécrire.

Pour modifier seulement le dernier commit :

  1. Amender le commit: git commit --amend (git ouvre le message dans un éditeur de texte).

  2. Changer le message et enregistrer/quitter l’éditeur.

  3. Forcez l’envoi de votre branche : git push -f your_remote your_branch (votre PR sera mis à jour).

  4. Renommez le titre du PR pour correspondre

Sinon vous pourrez avoir besoin d’effectuer un « rebase » interactif :

  1. Faites un rebase des derniers N commits: git rebase -i HEAD~N où N est le nombre. (Git va ouvrir le fichier git-rebase-todo pour modification)

  2. Pour les commits qui ont besoin d’être modifiés, modifiez pick en r (pour « reword », reformulation) et enregistrez/quittez l’éditeur.

  3. Modifiez le(s) message(s) de commit, enregistrez/quitter l’éditeur (git va prsenter un fichier pour chaque commit qui a besoin d’être reformulé).

  4. git push -f your_remote your_branch pour forcer un push de la branche (qui met à jour votre PR).

  5. Renommez le titre du PR pour correspondre

Tester

Elgg dispose de tests automatisés à la fois pour PHP et JavaScript. Toutes les nouvelles contributions doivent intégrer les tests appropriés.

Recommandations générales

Découpez les tests par comportements que vous souhaitez tester et utilisez des noms qui décrivent le comportement. Par ex. :

  • Pas si bon : Une seule grosse méthode testAdd().

  • Mieux : Méthodes testAddingZeroChangesNothing et testAddingNegativeNumberSubtracts

Efforcez-vous d’utiliser des designs à base de composants qui permettent de tester en isolement, sans de grands graphes de dépendances ou d’accès à la base de données. L’injection de dépendances est ici critique.

Tests PHP

PHPUnit

Située dans engine/tests/phpunit, c’est notre suite de tests préférée. Elle n’utilise pas d’accès à la base de données, et n’a qu’un accès superficiel à l’API des entités.

  • Si possible, nous vous encourageons à créer des composants qui sont testables dans cette suite.

  • Envisagez de séparer le stockage de votre composant de sorte que la logique métier puisse être testée ici.

  • Dépendez des classes Elgg\Filesystem\* plutôt que d’utiliser les fonctions du système de fichiers PHP.

SimpleTest

Le reste des fichiers dans engine/tests constitue notre suite d’intégration, pour tout ce qui demande un accès à la base de données ou aux APIs des entités.

  • Notre objectif à long terme est de les minimiser et de les convertir en PHPUnit

Tester les interactions entre services

Dans l’idéal, vos tests devraient construire vos propres graphes d’objet isolés pour des manipulations directes, mais ce n’est pas toujours possible.

Si votre test dépend du Fournisseur de Service Elgg (_elgg_services() retourne un Elgg\Di\ServiceProvider), réalisez que cela maintient une instance singleton pour la plupart des services qu’il fournit, et que beaucoup de services conservent également leurs propres références locales pour ces services.

Du fait de ces références locales, replacer les services sur le SP (SP = « Service Provider », Fournisseur de Service) au sein d’un test n’aura souvent pas l’effet désiré. Au lieu de cela, vous pourriez avoir besoin d’utiliser la fonctionnalité disponible dans les services eux-mêmes :

  • Les services events et hooks ont des méthodes backup() et restore().

  • Le service logger a des méthodes disable() et enable().

Tests Jasmine

Les fichiers de test doivent être nommés *Test.js et devraient être placés soit dans js/tests/ ou à côté de leurs fichiers sources dans views/default/**.js. Karma va automatiquement sélectionner les nouveaux fichiers *Test.js et exécuter ces tests.

Test générique

define(function(require) {
        var elgg = require('elgg');

        describe("This new test", function() {
                it("fails automatically", function() {
                        expect(true).toBe(false);
                });
        });
});

Effectuer les tests

Elgg utilise Karma avec Jasmine pour effectuer des tests unitaires JS.

Vous devez avoir nodejs et npm installés.

Tout d’abord installez toute les dépendances de développement :

npm install

Exécutez les tests une seule fois puis quittez :

npm test

Vous pouvez également exécuter les tests en continu pendant le développement de sorte qu’ils s’exécutent à chaque enregistrement :

karma start js/tests/karma.conf.js

Déboguer les tests JS

Vous pouvez exécuter la suite de test au sein des outils de développement de Chrome :

npm run chrome

Ceci va renvoyer une URL telle que http://localhost:9876/.

  1. Ouvrez l’URL dans Chrome, et cliquez sur « Debug ».

  2. Ouvrez les outils de développement de Chrome et l’onglet Console.

  3. Rechargez la page

Si vous modifiez un test, vous devrez quitter Karma avec Ctrl-c et le rédémarrer.

Meilleures pratiques de développement

Rendez votre code plus facile à lire, plus facile à maintenir, et plus facile à déboguer. Un usage consistant de ces recommandations signifie moins de travail de devinettes pour les développeurs, ce qui signifie des développeurs plus heureux, et plus productifs.

Développement en général

Ne Vous Répétez Pas

Si vous copiez-collez des morceaux significatifs de code, considérez qu’il y a une opportunité de réduire la duplication en introduisant une fonction, un argument supplémentaire, une vue, ou une nouvelle classe de composant.

Par ex. Si vous trouvez des vues qui sont identiques à l’exception d’une seule valeur, remaniez-les en une seule vue qui accepte une option.

Note: Dans la publicaiton d’une correction de bugs, un peu de duplication est préférable à de la refactorisation. Corrigez les bugs de la manière la plus simple possible, et refactorisez pour réduire la duplication dans la branche de la prochaine version mineure.

Adoptez SOLID et GRASP

Utilisez ces principes pour le design OO pour résoudre des problèmes en utilisant des composants couplés librement, et essayez de rendre tous les composants et le code d’intégration testables.

Les espacements sont gratuits

N’ayez pas peur d’utiliser des blocs de code séparés. Utilisez un espace unique pour séparer les paramètres des fonctions et les concaténations de chaînes.

Nom des variables

Utilisez des noms de variables auto-documentés. $group_guids est mieux que $array.

Evitez les doubles négations. Préférez $enable = true à $disable = false.

Nom des interfaces

Utilisez le motif Elgg\{Namespace}\{Name}.

N’ajoutez pas de préfixe I ou de suffixe Interface.

Nous n’utilisons aucun préfixe ou suffixe de sorte que nous sommes encouragés à :

  • nommer les classes d’implémentation de manière plus descriptive (le nom « par défaut » est déjà pris).

  • faites du typage sur les interfaces, parce que c’est la manière la plus courte et la plus simple de faire.

Nommez les implementations comme Elgg\{Namespace}\{Interface}\{Implementation}.

Fonctions

Autant que possible, ayez des focntions/méthodes qui renvoient un type unique. Utilisez des valeurs vides telle que array(), "", ou 0 pour indiquer l’absence de résultats.

Faites attention aux cas où des valeurs de retour valides (telles que "0") pourraient être interprétées comme vides.

Les fonctions qui ne déclenchent pas une exception lors d’une erreur devraient retourner false lorsqu’elles échouent.

Les fonctions qui ne renvoient qu’un booléen devraient être préfixées par is_ ou has_ (par ex., elgg_is_logged_in(), elgg_has_access_to_entity()).

Syntaxe ternaire

Acceptable seulement pour des déclarations sur une seule ligne, non imbriquée

Minimisez la complexité

Minimisez les blocs imbriqués et les chemins d’exécution distinct au sein du code. Faites un Return rapide pour réduire les niveaux imbriqués et la charge cognitive lors de la lecture du code.

Utilisez les commentaires à bon escient

Les bons commentaires décrivent le « pourquoi. » Le bon code décrit le « comment. » Par ex. :

Mauvais :

// increment $i only when the entity is marked as active.
foreach ($entities as $entity) {
        if ($entity->active) {
                $i++;
        }
}

Bon :

// find the next index for inserting a new active entity.
foreach ($entities as $entity) {
        if ($entity->active) {
                $i++;
        }
}

Ajoutez toujours un commentaire s’il n’est pas évident que quelque chose doit être fait d’une certaine manière. D’autres développeurs qui regardent le code devraient être découragés de réorganiser le code d’une manière qui le casserait.

// Can't use empty()/boolean: "0" is a valid value
if ($str === '') {
    register_error(elgg_echo('foo:string_cannot_be_empty'));
    forward(REFERER);
}

Commitez de manière efficace

  • Péchez par excès de commits atomiques qui sont précisément ciblés sur la modification d’un seul aspect du système.

  • Eviter de mélanger des modifications qui ne sont pas liées ou des modifications importants des espaces. Les commits avec de nombreux changements sont effrayants et rendent les demandes de fusion (Pull Requests) difficiles à évaluer.

  • Utilisez des outils git visuels pour façonner des diffs extrêmement précis et lisibles.

Incluez des tests

A chaque fois que c’est possible, incluez des tests unitaires pour le code que vous ajoutez ou modifiez.

Gardez les corrections de bugs simples

Évitez la tentation de refactoriser le code pour la publicaiton d’une correction de bug. Faire cela a tendance à introduire des régressions, à casser la fonctionnalité dans ce qui devrait être une version stable.

Recommandations PHP

Voici les standards de développement requis pour le noyau d’Elgg et tous les plugins associés. Les développeurs de plugins sont fortement encouragés à adopter ces standards.

Les développeurs devraient lire en premier le Guide des Standards de Développement PSR-2.

Les standards d’Elgg étendent PSR-2, mais diffèrent des manières suivantes :

  • Indentez en utilisant le caractère tabulation, pas des espaces.

  • Les parenthèses ouvrantes pour les classes, méthodes, et fonctions doivent être sur le même ligne.

  • Si une ligne dépasse 100 caractères, envisagez de la remanier (par ex. en introduisant des variables).

  • La compatibilité avec PSR-1 est encouragée, mais pas strictement requise.

Documentation

  • Incluez des commentaires PHPDoc sur les fonctions et les classes (toutes les méthodes ; les propriétés déclarées quand c’est approprié), y compris les type et description de tous les paramètres.

  • Dans les listes de déclarations @param, le début des noms de variables et des descriptions doivent être alignées.

  • Annotez les classes, méthodes, propriétés et fonctions avec @access private, à moins qu’elles ne soient prévues pour un usage public, n’aient déjà une visibilité limitée, ou soient déjà au sein d’une classe marquée comme privée.

  • Utilisez // ou /* */ pour les commentaires.

  • Utilisez seulement les commentaires // à l’intérieur du corps des fonctions et méthodes.

Nommage

  • Utilisez des traits de soulignements (« underscores ») pour séparer les mots dans les noms de fonctions, de variables, et les propriétés. Les noms de méthodes utilisent la syntaxe camelCase.

  • Les noms de fonctions pour un usage public doivent commencer par elgg_.

  • Tous les autres noms de fonctions doivent commencer par _elgg_.

  • Nommez les globales et les constantes en TOUT_MAJ (ACCESS_FRIENDS, $CONFIG).

Divers

Pour les pré-requis PHP, voyez composer.json.

N’utilisez pas les balises PHP raccourcies <? ou <%. Il est possible d’utiliser <?= dans la mesure où cela est toujours activé à partir de PHP 5.4.

Quand vous créez des chaînes de caractères avec des variables :

  • utilisez des chaînes de caractères avec des guillemets doubles

  • encadrez les variables avec des accolades seulement quand c’est nécessaire.

Mauvais (difficile à lire, mauvais usage des guillemets et des {}):

echo 'Hello, '.$name."!  How is your {$time_of_day}?";

Bon :

echo "Hello, $name!  How is your $time_of_day?";

Supprimez les espaces de fin de ligne. Un moyen simple de faire cela avant votre commit est d’exécuter php .scripts/fix_style.php à partir de la racine de l’installation.

Recommandations CSS

Utilisez des abréviations quand c’est possible

Mauvais :

background-color: #333333;
background-image:  url(...);
background-repeat:  repeat-x;
background-position:  left 10px;
padding: 2px 9px 2px 9px;

Bon :

background: #333 url(...) repeat-x left 10px;
padding: 2px 9px;

Utilisez les traits d’union « -« , pas les underscores « _ »

Mauvais :

.example_class {}

Bon :

.example-class {}

Une propriété par ligne

Mauvais :

color: white;font-size: smaller;

Bon :

color: white;
font-size: smaller;

Déclarations des propriétés

Celles-ci devraient être espacées comme ceci : propriété: valeur;

Mauvais :

color:value;
color :value;
color : value;

Bon :

color: value;

Préfixes fournisseurs (vendor)

  • Regroupez les préfixes fournisseurs pour la même propriété

  • La version la plus longue des préfixes fournisseurs en premier

  • Incluez toujours la version sans préfixe fournisseur

  • Ajoutez une ligne supplémentaire entre les groupes avec préfixes fournisseurs et les autres propriétés

Mauvais :

-moz-border-radius: 5px;
border: 1px solid #999999;
-webkit-border-radius: 5px;
width: auto;

Bon :

border: 1px solid #999999;

-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;

width: auto;

Regroupez les sous-propriétés

Mauvais :

background-color: white;
color: #0054A7;
background-position: 2px -257px;

Bon :

background-color: white;
background-position: 2px -257px;
color: #0054A7;

Recommandations JavaScript

Les mêmes standards de formatage que PHP s’appliquent.

Toutes les fonctions devraient être dans l’espace de nom elgg.

Les expressions d’une fonction devraient se terminer par un point-virgule.

elgg.ui.toggles = function(event) {
        event.preventDefault();
        $(target).slideToggle('medium');
};

APIs obsolètes

De temps en temps, des fonctions et des classes doivent être dépréciées au profit de nouveaux remplacements. Dans la mesure où les auteurs de plugins tierce-partie s’appuient sur une API cohérente, la compatibilité ascendante doit être maintenue, mais ne sera pas maintenue indéfiniment dans la mesure où les auteurs de plugins sont supposés mettre à jour leurs plugins. Afin de maintenir la compatibilité ascendante, les API obsolètes vont suivre les recommandations suivantes :

  • Les versions mineures (1.x) qui déprécient une API doivent inclure une fonction/classe d’emballage (« wrapper ») -ou tout autre moyen approprié- qui maintient la compatibilité ascendante, y compris tout bug de la fonction/classe originelle. Cette couche de compatibilité utilise elgg_deprecated_notice('...', '1.11') pour journaliser le fait que cette fonction est obsolète.

  • La révision majeure suivante (2.0) supprime la couche de compatibilité. Toute utilisation de l’API obsolète devrait être corrigé auparavant.