Chapitre 43. Développement de NOALYSS

43.1. Intro

NOALYSS a déjà plus de 22 ans , il est né bien avant la naissance des frameworks pour PHP, il était déjà fort avancé quand sont apparus symphony, Laravel, DoctorORM…​

En 2002, il était développé avec vi ou emacs, sous CVS pour la gestion des versions,en PHP3 [1] parce qu’il n’y avait pas d’outils professionnels pour travailler avec PHP , même pas un débuggueur, ou des suites de tests.

Au début, les programmeurs développaient la totalité d’un produit , quand le projet grossissait , ils regroupaient des fonctions dans des librairies.

Plus tard, ils vont non seulement réutiliser les fonctions qu’ils ont déjà développées mais ils adoptent aussi une méthode de développement, pour le nommage des fonctions, les conventions sur le code,…​ afin d’avoir un code homogène.

Et ces librairies, scripts , conventions finissent par devenir un cadre de travail (framework) qui permet d’avoir un code homogène et plus facilement abordable pour les nouveaux venus sur le projet.

Aujourd’hui, les développements en PHP sont supposés utiliser les normes PSR0/4, ces normes et frameworks sont arrivées trop tard pour nous, cela imposerait une énorme réécriture de NOALYSS et aucun gain réel si on le faisait.

Le code est réécrit au fur et à mesure des corrections et de l’évolution du programme.

Cela explique que l’organisation des fichiers et la façon de travailler avec NOALYSS , ne ressemble pas aux frameworks existants, mais on essaie d’uniformiser et de moderniser le code au fur et à mesure.

43.2. Organisation des fichiers

html

ce qui est appelable directement (modèle mvc)

include

fichier inclu par html (*.inc.php)

include/ajax

traitement pour les appels AJAX

include/template

template , écran & code html

include/lib

les classes librairies

include/class

classes métiers

include/database

mapping table, vues et object [ORM]

include/export

fichier pour confection de documents qu’on envoie (zip , csv, pdf …​)

include/ext

plugin(alias extensions)

include/sql

répertoire avec les scripts SQL pour les mises à niveau du programme

43.3. Cycle de développement : organisation des branches Git

Il existe toujours 2 branches :

stable

branche stable que tout le monde peut utiliser en production,

unstable

branche contenant tous les développements en cours, elle est réservée aux développeurs. Cette branche n’est pas à 100% testée et est fortement déconseillée en production.

Important les changements pour la base de données ne sont pas encore dans noalyss/include/sql mais dans noalyss/sql/upgrade.sql, il faut exécuter ce script afin de pouvoir utiliser la branche unstable.

43.3.1. Correction et patch

En bref, faire une branche à partir de stable, corriger le bug et publier le correctif.

Procédure

Afin de soumettre un patch , vous devez d’abord "cloner" la base de données depuis https://gitlab.com/noalyss

Ensuite vous créer votre propre branche à partir de unstable ou stable.

  • unstable si c’est un développement pour la prochaine version,

  • stable si c’est une correction de bug (pour cette branche).

Astuce Les changements pour la base de données, vont toujours dans noalyss/sql/upgrade.sql pour les dossiers et dans noalyss/sql/ac-upgrade.sql pour account_repository
Exemple 14. Exemple :

# Anonymement

git git@gitlab.com:noalyss/noalyss.git

git switch stable # ou unstable

git checkout -b mon_dev1

Corriger dans mon_dev1 , faites commit

puis simplement, générer les fichiers patchs que vous enverrez par email

git format-patch stable # OU unstable

Des fichiers patch seront générés, envoyez-les nous soit sur la mailing list (https://lists.nongnu.org/mailman/listinfo/noalyss-generale), soit sur le forum (https://forum.noalyss.eu) soit par email à dev-team@noalyss.eu

Ces patchs seront testés et intégrés dans la branche stable et unstable , éventuellement dans la branche "pre-release", s’il y en a une.

43.3.2. Nouvelle version

Quand la branche unstable est suffisamment mature et testée pour devenir la prochaine version, on créera à partir d’elle une branche appelée pre-release-xx où xx est le prochain numéro de version.

Tous les correctifs sur stable seront aussi intégrés sur cette branche en plus d'`unstable`.

Quand la nouvelle version sera publiée, la branche sera intégrée dans stable , re-testée et publiée. La branche pre-release sera alors effacée.

Note Les corrections et développement sur cette branche seront aussi portés vers unstable

43.4. Convention

43.4.1. Nommage

Les fichiers dans

include

sont destinés à être inclus et exécuter depuis le 1er script (route)

<action>.inc.php
include/ajax

Appelé depuis une procédure javascript , le nom de fichier est

ajax_<action>

43.4.2. include/database

mapping table, vues et object

<table>_SQL.class.php

43.4.3. include/template

template , écran & code html

<nom_class>-<nom-fonction>.php

43.4.4. Outil

Tout doit être documenté avec Doxygen https://www.doxygen.nl/

Tous les objets SQL doivent aussi être documentés https://www.postgresql.org/docs/current/sql-comment.html

La documentation doit être au format asciidoc , ou markdown dans le répertoire doc https://asciidoctor.org/

Utilisation de phpunit ou autre système (fichiers de test avec assert() ) pour faire des tests unitaires https://phpunit.de/index.html

43.5. Déboggage

Afin de débugger, il suffit dans le fichier noalyss/include/config.inc.php d’ajouter

define ("DEBUGNOALYSS",2);

Le niveau de déboggage est :

0

production, aucune erreur n’est affichée,

1

les erreurs et les avertissements sont affichés,

2

affichage comme le niveau 1 , mais en plus des informations sur la taille de l’écran (bande de couleur en haut), pour aider la mise au point des pages responsives , affichage des fichiers inclus, d’objets,…​

Pour le niveau 2 , on utilisera la classe \Noalyss\Dbg

Javascript

si on donne une valeur différente de 0 à DEBUGNOALYSS , en javascript on aura la variable document.debug_noalyss=true, et donc on peut faire. Cependant , il vaut mieux être économe en plaçant des info de déboggage. Il est recommandé de les supprimer après usage sauf si elles sont utiles pour aider des développements ultérieurs comme par exemple pour indiquer quel est le fichier ou la fonction appelée.

if ( document.debug_noalyss) console.debug ('test')
/*  ou  */
document.debug_noalyss && console.debug ('test')
Exemple 15. Fonctions de déboggages:

Affiche le nom du fichier inclus (à garder si possible)

\Noalyss\Dbg::echo_file(__FILE__);

Si DEBUGNOALYSS est supérieur à 1 , affiche le contenu de row dans un élément cacheable

if ( DEBUGNOALYSS>1) echo \Noalyss\Dbg::hidden_info("\$row", $row);

Si DEBUGNOALYSS est supérieur à 1, affiche l’utilisateur courant

 \Noalyss\Dbg::echo_var(1,sprintf("current user is [%s]",$cn->get_value("select current_setting('noalyss.user_login')")));

\Noalyss\Dbg contient les différentes fonctions pour afficher, il existe aussi la fonction tracedebug qui écrira dans un fichier (voir noalyss/lib/ac_common.php).

Note l’utilisateur connecté, est sauvé dans la mémoire de la base de données et peut de ce fait être utilisé dans des triggers, voir postgresql : current_setting

43.6. Principe : développement en MVC

Le développement en MVC est un développement qui suit les concepts Modèle Vue Contrôle. Il en existe 2 versions. Ici, je ne parlerai que de la première.

En pratique, je crée 3 répertoires de base /html et /include et /include/template, le premier contient les « contrôles » le second les «vues» et le troisième les « modèles ».

43.6.1. Contrôle

Un contrôle est une page php, qui en fonction de l’action demandé va inclure la page «modèle» qu’il faut.

Dans noalyss, cette page est :

do.php pour toutes les demandes de pages

export.php pour toutes les impressions (PDF, CSV, …)

ajax_misc.php pour l’ajax.[2]

Exemple : la page contrôle contient

<?php

if ( !isset ($_REQUEST['do']))  exit();

$do=$_REQUEST['do'];

if ( $do == 'auteur' ) {

    require_once('include/auteur.inc.php');

}

?>

L’explication est assez simple, si la page n’est pas appelée avec une variable do, donnée par un POST ou GET alors, le traitement s’arrête. Sinon en fonction de la valeur de la variable, il va inclure le fichier vue. Par habitude, mes fichiers vues ont toujours l’extension .inc.php.

Dans noalyss, le fichier do.php va chercher la vue à intégrer dans la base de données, ce qui améliore la sécurité mais aussi une grande souplesse pour les menus. Facile d’en ajouter, d’en retirer, de les renommer…​

Note Puisque le fichier à inclure est dans la base de données, si votre profil ne l’inclus pas, alors vous avez pas du tout accès à ce menu, cela s’étend aux impressions, il est possible d’empêcher les impressions. Les menus et profils se configurent dans [C1MENU]

43.6.2. Vue

Grâce à l’exemple vu dans Contrôle, vous avez compris ce qu’est le concept vue: c’est la présentation de votre action. Donc pour continuer l’exemple, dans le document include/auteur.inc.php, on aurait

require_once ('include/class_auteur.php');

if ( ! isset($_REQUEST['sdo']) ) exit();



$sdo=$_REQUEST['sdo'];



if ( $sdo == 'add' ) {

    $auteur=new Auteur();

    $auteur->display_form();

}

?>

Ici, comme dans contrôle, on vérifie qu’une action est bien demandée, si c’est la cas, on appelle un objet et on lui demande d’afficher quelque chose. Cet objet est ce qui est dans le concept MVC, un modèle. En fait, on dirait une espèce de sous-contrôle

43.6.3. Modèle

Eh oui, le dernier est donc le modèle. Ici ce sont deux classes, l’une va se charger de sauver, afficher, effacer les données et gère donc les relations avec la base de données et l’autre l’objet métier.

Le nom de ce fichier doit être

<nom de la classe>-<fonction>.php

Ce n’est pas toujours le cas, et cette réécriture se fait au fur et à mesure.

Il est vraiment fastidieux de construire une chaîne de caractère contenant tout le code html, surtout quand on emploie aussi du javascript et des tableaux. La solution que j’ai trouvé, je l’ai trouvé en m’inspirant de l’idée de smarty. En fait, je crée un sous-répertoire dans include appelé template, et dedans je mets le code HTML de ce qui doit être affiché

<?php
class Auteur {
    function display_form() {

    ob_start();

    include  'template/auteur-display_form.php';

    $r=ob_get_contents();

    $ob_clean();

    return $r;

    }
}
Exemple:
 Ici on inclus le fichier, comme il est en PHP, il sera
interprété par PHP, il contient surtout du code HTML et des balises PHP,
on peut aussi manipuler la chaîne retournée avec les fonctions de
chaînes comme str_replace, strcmp,... Ce qui donne à cette méthode
encore plus de souplesse.

Il est important de respecter la règle suivante: un minimum d’HTML dans le PHP et un minimum de PHP dans l’HTML. Il faudra alors utiliser beaucoup de variables avant d’utiliser les templates, il faudra aussi résister à la tentation de mettre de l’html dans la variable, ce qui n’est pas toujours possible.

Attention les variables sont définies en-dehors du fichier template. Les IDE avertissent que ces variables ne sont définies. il suffit d’ajouter un block commentaire pour supprimer cet avertissement
Exemple
<?php
/**
 * @var $auteur string contient le nom de l'auteur
 */
Important toutes les variables doivent être commentées, sauf les variables en une seule lettre (utilisée principalement pour les boucles).

43.7. La documentation

J’utilise Doxygen https://www.doxygen.nl/ afin de générer le code ce sont les TAGS brief, param…​ Cela me permet de générer une documentation; cette documentation me permet de développer plus vite et de mieux vérifier la qualité du code.

Il est recommandé de toujours documenter les variables , les fonctions, les fichiers et les classes.

Le fichier qui va router les demandes (en général index.php ) doit contenir l’explication pour le développement [3]

Exemple
<?php
// variable: $tot float total for the operation
$tot=0;

Quand une variable est héritée d’un autre fichier, à la suite d’un include ou require, afin que NetBeans (ou PhpStorm) n’indique pas une erreur.

<?php
/**
 * @var $tot float total for the operation
 */

 $tot=$tot+1;

43.8. Fichiers importants

Les fichiers à inclure sont dans la table menu_ref, cette table est accèdée à travers la table profile_menu qui décrit l’interface de l’utilisateur, ce qui automatiquement empêche un utilisateur d’accèder à un menu auquel il n’a pas accès.

Le répertoire include contient les fichiers à inclure, les noms de fichiers suivent la logique suivant :

noalyss\include/constant.php

contient les variables globales, les constantes, l’autoloader.

noalyss\include/config.inc.php

contient le paramétrage de NOALYSS, surtout l’accès aux bases de données.

noalyss\include\ajax

Action ajax, le nom de fichier commence par ajax, contient le contenu de la variable op, c’est cette variable qui va indiquer le fichier à include.[4]

fichier à inclure

le nom de fichier termine par inc.php,

fichier ORM

correspondant à une table NOM_TABLE dans la base de données:class_NOM_TABLE_sql.class.php,

fichier d’object

le nom se termine par .class.php

noalyss/log

répertoire des logs, voir fonction record_log

noalyss\include\template

ici vous avez les modèles, ce sont des fichiers contenant surtout du code HTML.

En résumé :

Les contrôleurs sont do.php, export.php, ajax_misc.php.

Pour les extensions (plugins) : do.php , ajax.php et extension.raw.php

Les modèles sont dans include/template et les vues sont dans le répertore include

43.9. La partie base de données

43.9.1. Les mises à jour

Depuis 2005, dans NOALYSS, il y a une table version qui contient les informations sur les patch de base de données qui ont été appliqués. Quand le fichier do.php est exécuté, il vérifie si DBVERSION contenu dans constant.php correspond à la version actuelle.

<?php

/* Ficher do.php */
if (DBVERSION < dossier::get_version($cn))
{
    echo '<h2 class="error" style="font-size:12px">' .
     _("Attention: la version de base de donnée est supérieure à la version du programme, vous devriez
 mettre à jour") . '</h2>';
}
if (DBVERSION > dossier::get_version($cn))
{
    echo '<h2 class="error" style="font-size:12px">' . _("Votre base de données n'est pas à jour") . '   ';
    $a = _("cliquez ici pour appliquer le patch");
    $base = dirname($_SERVER['REQUEST_URI']) . '/admin/setup.php';
    echo '<a hreF="' . $base . '">' . $a . '</a></h2>';
}

Dans le cas où DBVERSION n’est pas la valeur contenue dans la base de donnée, do.php vous propose d’appliquer le patch sur vos bases de données. Voici la fonction qui applique les patch SQL pour la base de données. Ces fichiers sont dans noalyss/include/sql/patch.

<?php
/* fichier setup.php */
//----------------------------------------------------------------------
// Upgrade the folders
//----------------------------------------------------------------------

for ($e=0;$e < $MaxDossier;$e++) {
   $db_row=Database::fetch_array($Resdossier,$e);
  echo "<h3>Patching ".$db_row['dos_name'].'</h3>';

  $name=$cn->format_name($db_row['dos_id'],'dos');

  if ( $cn->exist_database($name)> 0 )
  {
    $db=new Database($db_row['dos_id'],'dos');
    $db->apply_patch($db_row['dos_name']);
    Dossier::synchro_admin($db_row['dos_id']);

  } else
  {
      echo_warning(_("Dossier inexistant")." $name");
  }
 }

La fonction qui applique le patch est Database::apply_patch voir class Database

Tous les patchs pour la base de données se trouve dans /noalyss/include/sql/patch/ et sont numérotés dans l’ordre d’exécution. Chaque patch commence par

begin;

et termine par

insert into version (val,v_description) values (..version.,'.description..');

commit;

Ainsi, si le script échoue, la mise à jour s’arrête et annule toute ce qui a été fait dans ce script, la base de données reste ainsi dans un état cohérent. Une fois l’erreur corrigée dans votre base de données, ce script ainsi que ceux qui suivent seront appliqués quand vous appelerez à nouveau noalyss/admin/setup.php (ou noalyss/html/install.php).

Le nombre dans la table version est le nombre du script moins un, exemple : le script upgrade199.sql, correspond à la 200 dans la table version

/* upgrade199.sql */

insert into version (val,v_description) values (200,'Widget and improve menu');

43.9.2. Accèder aux données : ORM

Pour accèder aux tables ou aux vues, pour les SQL complexes , nous créons une classe qui va hériter de Table_Data_SQL qui est dérivée de Data_SQL

et qui va nous permettre de faire la même chose en très peu de ligne de code.

Cette classe fournit entre-autres les fonctions suivantes qui peuvent être surchargée

insert

pour insérer une ligne dans la base de données

delete

pour insérer une ligne dans la base de données

update

pour mettre à jour une ligne dans la base de données

verify

à surcharger, vérifie que les données sont conformes

setp

setter paramètre : nom de la colonne, valeur retourne l’objet

getp

getter paramètre : nom de la colonne , retourne la valeur de cette colonne

toString

affiche le contenu de l’objet

from_array

transforme un tableau en un objet

seek

recherche dans la table sur base d’une condition

next

retourne l’objet suivant (après seek)

check

contrôle des valeurs

Voici tout le code à taper par table, exemple pour la table stock_change.

Le fichier sera nommé table_sql.class.php , il sera dans un répertoire /database/ et la classe se nommera table_SQL

<?php
class Stock_Change_SQL extends Noalyss_SQL
{
    // Le contructeur obligatoire
    function __construct($p_id = -1)
    {
        // Façon dont les dates sont utilisées
        $this->date_format="DD.MM.YYYY";
        // Nom de la table
        $this->table = "public.stock_change";
        // nom de la clef primaire
        $this->primary_key = "c_id";

        // Structure de la table, à gauche le nom logique utilisable
        // avec les getters/setters (setp/getp) et à droite le nom de la
        // colonne
        $this->name = array(
            "id" => "c_id",
            "c_comment" => "c_comment",
            "c_date" => "c_date",
            "tech_date"=>"tech_date",
            "tech_user"=>"tech_user",
            "r_id"=>"r_id"
        );
        // Type de données
        $this->type = array(
            "c_id" => "numeric",
            "c_comment" => "text",
            "c_date" => "date",
            "tech_date"=>"date",
            "tech_user"=>"text",
            "r_id"=>"numeric"
        );
        // Les colonnes qui ne peuvent pas être changée ni par insert ni par
        // update parce leurs valeurs sont données automatiquement
        // exemple : la clef primaire qui est un numéro de séquence
        //automatiquement donné
        $this->default = array(
            "c_id" => "auto",
            "tech_date" => "auto"
        );
        global $cn;

        parent::__construct($cn, $p_id);
    }
}

43.9.3. Les tests unitaires

NOALYSS doit avoir des tests scriptés afin de ne pas devoir manuellement rejouer tous les scénarios.

Pour cela, il existe 3 méthodes utilisées.

La plus ancienne, ajouter une fonction function static test_me() dans les class . Cette fonction est appelée via le test.php

Ensuite, les scénarios : grâce à la variable LOGINPUT qui permet d’enregistrer toutes les requêtes (html, ajax ou export), dans un fichier dans le répertoire temporaire [5]

Et finalement, la plus récente avec PHPUNIT.

test-me

Les fonctions info et test_me qui sont très utiles lors des phases de déboggages. Je crée une simple page test-classe.php et j’appelle la classe; dans la fonction test_me (de la classe) je mets tout ce que je souhaite tester.

exemple :

<?php
    static function test_me()
    {
        $cn=new Database(dossier::id());
        $obj=new Periode($cn);
        $obj->set_jrn(1);
        $obj->display_form_periode();
    }
Note ces tests sont ajoutés à NOALYSS uniquement pour tester les nouvelles fonctionnalités ou pour les corrections de bugs.
Scenario

Vous pouvez enregistrer ce que vous soumettez, il faut dans config.inc.php, ajouter

   define ('LOGINPUT',true);

Ensuite il faut créer le fichier dans noalyss/html/authorized_debug , le contenu n’a pas d’importance, ce fichier peut être vide.

Ensuite, il faut aller dans votre dossier de test et faire une action dans NOALYS (une vente, un achat…​), vous devez ouvrir le fichier test.php avec comme paramètre le dossier (exemple http://localhost/noalyss/html/test.php?gDossier=14) ce que vous venez de faire a été sauvé dans le répertoire défini par $_ENV['TMP'] [6] avec un nom ressemblant à scenario-<nombre>.php, export-<nombre>.php ou plugin-export.php.

Vous devez d’abord copier ce fichier dans le répertoire noalyss/scenario/LOCAL , de préférence avec un nom plus parlant.

Si vous pointez votre browser sur noalyss/html/test.`php (après avoir créé le fichier authorized_debug) en cliquant sur le lien avec le nom de fichier vous pourrez rejouer l’action.

Astuce Vous pouvez améliorer la description en changeant l’annotation //@description: <CODE>

L’objectif étant de pouvoir tester et de rejouer facilement les actions que vous avez faites. Cela permet de débogguer plus facilement en particulier les parties ajax. Il est possible de l’utiliser pour les sorties ajax, mises au point de librarie, scénario,…​

Cela permet de tester les fichiers à include (.inc.php) , les exports , les réponses ajax, les exports en CSV ou PDF, appeler les fonctions test_me dans les classes [7],…​

Dans votre code, vous pouvez ajouter des portions de code ainsi

if (defined ('TEST_UNIT')) {
    // exécuté uniquement si appelé pour les tests unitaires
}
Les répertoires :
  • HTML : vérification pour les fichiers inclus

  • LIB : concerne les libraries

  • XML : réponse de script ajax, réponse souvent en XML

  • LOCAL : vos propres scripts, pour vos développements, ne seront pas dans GIT

Astuce Tout les sous-répertoires dans scénario, seront ajoutés. Pour faire vos propres tests il suffit de créer un répertoire, d’y placer ses fichiers et ils seront proposés dans la page test.php.
PHPUNIT

Il faut tout d’abord un fichier bootstrap qui contient le path correct pour php ainsi que les variables comme $g_user ou $cn, l’inclusion de noalyss/include/config.inc.php et de noalyss/include/constant.php


1. Voir annexe : comment contribuer ou le wiki
1. souvent latin-1 pour Excel et unicode pour libreOffice
1. vous pouvez aussi utiliser directement les postes comptables, certains postes sont si rarement utilisés qu’on peut décider de ne pas créer de fiche comme par exemple le capital souscrit
1. Voir annexe : comment contribuer ou le wiki
1. A la condition que l’administrateur lui a envoyé le QRCode à scanner
1. C’est l’accès direct AD
2. c’est le nombre avant la catégorie
1. https://wiki.noalyss.eu
1. Vérifie que vous pouvez encore déduire une facture passée pour la TVA et l’impôt
1. https://wiki.noalyss.eu
2. le 31 décembre mais cela pourrait être une autre date
3. Pour imprimer les bilans il y a aussi l’extension "Bilan Interne" ou un rapport avancé
1. Plugins standards de NOALYSS, https://gitlab.com/noalyss/noalyss-plugins
2. https://wiki.noalyss.eu
3. Plugins standards de NOALYSS, https://gitlab.com/noalyss/noalyss-plugins
1. Plugins standards de NOALYSS, https://gitlab.com/noalyss/noalyss-plugins
2. Plugins standards de NOALYSS, https://gitlab.com/noalyss/noalyss-plugins
3. Plugins standards de NOALYSS, https://gitlab.com/noalyss/noalyss-plugins
1. Il existe aussi les actifs inversés, charges inversées…​
2. pour extourner il faut entrer une date valide. Une date valide est une date qui est dans une période non fermée et dans une période du dossier
3. Plugins standards de NOALYSS, https://gitlab.com/noalyss/noalyss-plugins
4. Plugins standards de NOALYSS, https://gitlab.com/noalyss/noalyss-plugins
1. Plugins standards de NOALYSS, https://gitlab.com/noalyss/noalyss-plugins
1. Il faut d’abord créer la nouvelle année.
1. extrait de compte
2. extrait de compte
1. Plugins standards de NOALYSS, https://gitlab.com/noalyss/noalyss-plugins
1. Plugins standards de NOALYSS, https://gitlab.com/noalyss/noalyss-plugins
1. ces fichiers ont l’extension ods ou odt
1. PHP4 existait déjà, cependant on est toujours en retard sur les versions PHP afin d’être plus compatible, la partie orienté objet était très basique
2. Pour l’ajax, il existe plusieurs fichiers, ajax_misc.php est en général utilisé,ajax.php est utilisé pour les plugins.
3. NOALYSS n’est pas à 100% documenté, on l’améliore à chaque version
4. voir noalyss/html/ajax_misc.php.
5. paramètrer avec la variable $_ENV['TMP']
6. sous linux il s’agit de /tmp
7. static function test_me()