D-Nada Vos développements
d'application et site web
humain et sur-mesure
contact@d-nada.com

Plume

Plume est un framework Javascript/PHP OpenSource, léger et permettant une facilité et souplesse dans le développement de vos applications Web.
Une création SASU DNada.
Mise à jour le 11/05/2020.

Sommaire

Présentation

Dans les grandes lignes, Plume permet de : Pour bénéficier de ces fonctions (et d'autres), il suffit de faire unrequire("plume.php"); en PHP et un<script src="/plume.js"></script> en HTML.

Exemple typique d'une page HTML :
<!DOCTYPE html>
<html lang="fr">
<head>

<link href="/lib/plume.css" rel="stylesheet"/>
<script src="/lib/plume.js"></script>

</head>
<body>

{.mon_template() … .}

</body>
</html>
D'un script JS :
$.start(function() {
$('mon_id').on('clic', function(event, tag) {

})
$.ajax.ma_methode(42, [1, 2, 3, 5, 7]).then(function(resultat) {

})
});
Et côté serveur PHP :
<?php
// permet d'ajouter des fonctionnalités de debugging au poste indiqué par son IP
define('PLUME_TRACE_IP', '77.0.1.2');
// spécifie une adresse où le développeur pourra recevoir des données de débugging.
define('PLUME_MAIL_REPORT', "report@ma−societe.com");
// ajoute plume au PHP
require('plume.php');
// ouvre la base de données
$base = new Base("mon−client");
// gère les méthodes asynchrones ajax (voir JS : RPC)
Template::ajax();
// affiche la page HTML et les templates (voir HTML : Template)
Template::display();
// exemple de fonction template (dans l'exemple HTML plus haut)
function template_mon_template($pattern) {

}
// exemple de méthode ajax (dans l'exemple JS plus haut)
function ajax_ma_methode($premier = 'default', $deuxieme = false, $etc = null) {

}
?>
Le 1er definePLUME_TRACE_IP Le 2è definePLUME_MAIL_REPORT

HTML

Plume apporte des fonctionnalités au niveau du HTML.

HTML : Template

Le mécanisme de template permet de relier des « marqueurs » présents en page HTML à une fonction PHP, qui générera le HTML.
Exemple{*xxx() html *} etfunction template_xxx() { /* php */ }.
Pour arriver dynamiquement à ce résultat :
<h1>Liste des satellites de Jupiter</h1>
<ol>
<li>Io</li>
<li>Europe</li>
<li>Ganymède</li>
<li>Callisto</li>
</ol>
On créé une page HTML (exemple page.html), contenant :
<h1>Liste des satellites de {planete}</h1>
<ol>
{*satellite()
<li>{nom}</li>
*}
</ol>
On code en PHP, les deux templates :
<?php
require("plume.php");
function template_planete() {
return "Jupiter";
}
function template_satellite($pattern) {
foreach (['Io', 'Europe', 'Ganymède', 'Callisto'] as $satellite)
$html .= $pattern−>html( [ 'nom' => $satellite ] );
return $html;
}
Et on termine en demandant d'afficher ce template :
Template::display("page.html");
?>
Les templates peuvent s'imbriquer.
En reprenant l'exemple précédent, on peut afficher plus d'information par satellite.
<h1>Liste des satellites de Jupiter</h1>
<ol>
<li>
Io
<ul>
<li>Diamètre: 3 643Km</li>
<li>Période: 1,769d</li>
<li>Gravité: 1,79m/s2</li>
</ul>
</li>
<li>
Europe
<ul>
<li>…</li>
</ul>
</li>
<li>
Ganymède
<ul>
<li>…</li>
</ul>
</li>
<li>
Callisto
<ul>
<li>…</li>
</ul>
</li>
</ol>
On modifie la page HTML, avec :
{*planete()
<h1>Liste des satellites de {planete}</h1>
<ol>
{+satellite()
<li>
{nom}
<ul>
{.information()
<li>{nom}: {valeur}</li>
.}
</ul>
</li>
+}
</ol>
*}
Et on code en PHP, le template « planete », son template imbriqué « satellite », et le sous-template imbriqué « information » :
<?php
require("plume.php");
$planetes = [
"Jupiter" => [
[
"Io" => [
"Diamètre" => "3 643Km",
"Période" => "1,769d",
"Gravité" => "1,79m/s2",
],
// autre satellite
],
// autre planète
],
];
function template_planete($pattern) {
global $planetes;

foreach ($planetes as $planete => $satellites) {
$html .= $pattern−>html([
'planete' => $planete,
'satellite' => $satellites
]);
}
return $html;
}
function template_satellite($pattern, $satellites) {
foreach ($satellites as $nom => $information) {
$html .= $pattern−>html([
'nom' => $nom,
'information' => $information
]);
}
return $html;
}
function template_information($pattern, $information) {
foreach ($information as $nom => $valeur) {
$html .= $pattern−>html([
'nom' => $nom,
'valeur' => $valeur
]);
}
return $html;
}
Template::display("page.html");
?>
Il est possible de faire quelques tests côté template.
{?tests()
{var_ok?test ok}{var_ko?test ko}
{var_ok!sinon ok}{var_ko!sinon ko}
{var_ok|ou alors ok} {var_ko|ou alors ko}
{{var_ko! <span> {var_ok} </span>}}
?}
<?php
function template_tests($pattern) {
return $pattern−>html([
'var_ok' => 'x',
'var_ko' => '', // ou false ou null
]);
}
?>
Affichera :
test ok
sinon ko
x ou alors ko
<span> x </span>
Enfin, on peut passer des paramètres aux fonctions template :
<span>{+parametrage(I, II, III, IV) 1:{un}, 2:{deux}, 3:{trois}, 4:{quatre} +}</span>
<?php
function template_parametrage($pattern) {
return [
'un' => $pattern−>parameter(1),
'deux' => $pattern−>parameter(2),
'trois' => $pattern−>parameter(3),
'quatre' => $pattern−>parameter(4)
]; // affichera : <span>1:I, 2:II, 3:III, 4:IV</span>
}
?>

HTML : $event

Ajout l'attribut $event au balise HTML, identique à l'attribut onevent, mais permet d'appeler la fonction JS, en passant les mêmes paramètres et contexte que la $.on
<div onclick="monClick()"></div>
Est identique à :
<div $click="monClick(this)"></div>
De plus, lors de l'appel de la méthode JS :

HTML : calendar

Ajout un nouveau type d'input pour la saisie de jour, avec popup-window associé : de à
<input type="calendar">
<!−−− interval −−−>
<input type="calendar" end="+">
<input type="calendar" begin="">

HTML : slide(s)

Ajout un nouveau type d'input de valeur sous forme d'une glissière.
Accepte les attributs suivants :

slide

Glissière à une seule valeur :
<input type="slide" min="0" max="9" rule="2" for="#slide_value_id" value="1" style="width: 200px;">
<span id="slide_value_id"></span>
valeur :

slides

La différence est au niveau des attributs : Double glissière à deux valeurs :
<input type="slides" min="1" max="9" rule="[1, 4, 7, 9]" for="#slides_value_id" value="2,3" style="width: 300px;">
<span id="slides_value_id1"></span>
<span id="slides_value_id2"></span>
valeur minimum : et maximum :

HTML : strip

Ajout d'un nouveau type d'input de sélection d'énumérés. L'attributenum peut recevoir soit une chaîne de mots, soit une liste de chaînes, soit un objet d'attribut/valeur.
<input type="strip" enum="lundi mardi mercredi jeudi vendredi" value="mardi"/>
<input type="strip" enum="['lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi']" value=""/>
<input type="strip" enum="{lun: 'lundi', mar: 'mardi', mer: 'mercredi', jeu: 'jeudi', ven: 'vendredi'}"/>

HTML : Segmented

Ajout d'un nouveau type d'input de sélection d'énumérés. L'attributenum peut recevoir soit une chaîne de mots, soit une liste de chaînes, soit un objet d'attribut/valeur.
<input type="segmented" enum="lundi mardi mercredi jeudi vendredi" value="mardi"/>
<input type="segmented" enum="['lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi']" value=""/>
<input type="segmented" enum="{lun: 'lundi', mar: 'mardi', mer: 'mercredi', jeu: 'jeudi', ven: 'vendredi'}"/>

HTML : menu

Ajout d'un menu associé à un élément HTML (input, div, …) , avec recherche saisissable (et beaucoup d'options comportementales possibles).
La création du menu est de type: Les données sont passées dans l'attribut$menu de l'élément HTML ou en retour d'ajax ou encore en retour du load (par son return ou son callback) ou open (uniquement son callback), et peuvent être de format :
  1. Tableau de liste :
    ['lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', '', 'samedi', 'dimanche']
  2. Tableau nommé :
    {lun: 'lundi', mar: 'mardi', mer: 'mercredi', jeu: 'jeudi', ven: 'vendredi', sep: '', sam: 'samedi', dim: 'dimanche'}
Chaques éléments peut être de syntaxe :
  1. Chaîne de caractère
  2. Structure pouvant contenir :
    {
    text: "texte", // l'intitulé en clair
    id: "identifiant", // l'identifiant (dans le cas d'un tableau nommé), si absent, l'id prend la valeur du texte
    disabled: boolean, // désactivé si true
    mark: "classMark", // soit false ou true (ou "check") pour indiquer un "✓", soit le nom d'une classe CSS, qui s'affichera en amont de l'intitulé
    image: "src", // la source d'une image
    key: "", // raccourci clavier éventuel
    }
Si le texte égale un signe moins (-), cet élément sera remplacé par un trait de séparation horizontal non sélectionnable<hr/>.
Les raccourcies claviers appel uniquement la méthodeshortcut sans gérer le contexte, c'est à la méthode de vérifier si l'action est possible et sur quoi elle est possible.
Ils sont toujours associés à la touche "Contrôle" sur un Windows ou un Linux, ou la touche "Commande" sur un Macintosh, et désactivé sur un mobile.
Il est possible d'associé la touche "Majuscule" en précédent le raccourci par le signe "-" et/ou la touche "Alt/Option" par le signe "+".
Certains raccourcis sont à éviter s'ils entrent en conflit avec les raccourcis du navigateur, voir carrément inactivé (raccourci "N" par exemple).
Voici le nom des touches particulières (et leurs caractères d'affichage utilisés) : Le return du "open" peut modifier l'affichage du menu, en renvoyant un tableau de :
  1. booléan : indiquant si l'élément est activé (false égale un { disabled: true} )
  2. structure : de même type que précédemment.

HTML : modal

Propose l'affichage d'une boite d'information de type modal (bloquant temporairement ou par action de l'utilisateur pour valider ce message)
Cette méthode est en mode asynchrone : elle revient immédiatement alors que le modal n'est pas encore affiché.
La réponse (le bouton appuyé par l'utilisateur) se faisant par fonction callback asynchrone passé en dernier paramètre ou par une promesse JS.
Le bouton le plus à droite est actionné par la touche retour-chariot, et celle la plus à gauche par la touche d'échappement.
<script>
// temporaire (sans gestion du retour de délai)
$.modal("Merci beaucoup.");

// information (sans gestion du bouton OK)
$.modal("Un erreur est survenue: " . error.message, "OK");

// choix à deux boutons avec retour asynchrone
$.modal("Merci de valider cette action", {cancel: "Annuler", add: "Ajouter"}, function(action) {
if (action == "add")

});

// choix à deux boutons avec retour en promesse
$.modal("Merci de valider cette action", {cancel: "Annuler", add: "Ajouter"}).then(function(action) {
if (action == "add")

});
</script>
Exemples :

JS

Plume apporte des fonctionnalités supplémentaires au Javascript et facilite le développement.
La principale est la classe$ qui est un querySelector évolué.
Mais implémente également d'autres méthodes, tel que :

JS : trace()

trace() est un alias de console.log, mais qui ne fonctionne que sur le navigateur du développeur. Il n'a aucun effet pour un usage autre que de développement. (voirPLUME_TRACE_IP)

JS : $

Pour chercher un élément dans le body de la page HTML :
  1. id (#): par son ID :$('id') ou$('#id') (attention cette 1ère syntaxe de recherche par ID ne fonctionne que pour$() et uniquement si le paramètre n'est pas entièrement en majuscule).
  2. TAGNAME: par son tagName :$('TAGNAME')
  3. class (.): par son attribut de classe :$('.class')
  4. type (?): par son attribut type :$('?type')
  5. name (@): par son attribut name :$('@name')
    • begin (@^): par son attribut name commençant par :$('@^begin')
    • end (@$): par son attribut name finissant par :$('@$end')
    • word (@*): par son attribut name contenant :$('@*word')
  6. parent (^): en remontant à son parent :$('^') ou à son 5ème parent :$('^5') à son parent récursif de tagName, type, name, … :$('^…')
    • parent include (^=): même principe que précédemment, mais en incluant l'élément, exempletrace('mon_div', $('DIV#id1 ^=DIV')); ne remontera pas et renverra le div#id1.
  7. next (+): en cherchant l'élément de même niveau situé après lui :$('+') ou 5ème élément :$('+5') ou élément de tagName, … =$('+…')
  8. previous (-): en cherchant l'élément de même niveau situé avant lui :$('') ou 5ème élément :$('−5') ou élément de tagName, … =$('−…')
Depuis un élément quelconque :element.$('') ou encore[element, …].$('').
Si l'on veut chercher une liste d'éléments :$$.('') ouelement.('') ou encore[element, …].$$('').
On peut également rechercher un de ses fils direct :$('>…').
Enfin, on peut chaîner les recherches dans la chaîne de recherche :
$$('#marqueur ^4 + DIV INPUT?text.valide@personnel[nom][]')
ou chaîner par appel successif :
$('marqueur').$('^4').$('+').$$('DIV').$$('INPUT?text.valide@personnel[nom][]')
Qui renverra un tableau de 3 éléments HTML, d'id = « un », « trois » et « sept », pour l'HTML suivant :
<body>
<article>
<section>
<div>
<fieldset>
<ul>
<li id="marqueur">
</li>
</ul>
</fieldset>
</div>
</section>
<section>
<div>
<fieldset>
<input class="valide" name="personnel[nom][]" id="un"/>
<input class="invalide" name="personnel[nom][]" id="deux"/>
<input class="important valide" name="personnel[nom][]" id="trois"/>
<input class="valide" name="personnel[prenom][]" id="quatre"/>
</fieldset>
</div>
<input type="text" class="valide" name="personnel[nom][]" id="cinq"/>
<div>
<fieldset>
<input class="invalide" name="personnel[nom][]" id="six"/>
<input class="valide" name="personnel[nom][]" id="sept"/>
<input class="valide" name="personnel[prenom][]" id="huit"/>
</fieldset>
</div>
</section>
</article>
</body>
La recherche se fait soit sur l'élément sur lequel s'applique la méthode$, soit sur document si l'on utilise directement$….
Si l'on veut rechercher spécifiquement surbody, on peut faire un$().$(xxx).
var tag = $.$('HEAD');		// renverra le <head> HTML
var tag = $('HEAD'); // renverra le <head> HTML
var tag = $().$('HEAD'); // renverra toujours null, puisque le <head> HTML est toujours placé avant le <body>
var tag = $.$('head'); // renverra le <head> HTML
var tag = $('head'); // attention à cette syntaxe qui renverra l'élément ayant un id = "head" (voir plus bas)
Attention : ces recherches se font toujours avec une syntaxeQuerySelector étendue (donc tagName, #id, …), sauf pour le$() qui peut rechercher non pas sur un tagName, mais sur un ID, si l'ID passé possèdant au moins une minuscule.
var tag = $('div');			// recherche un élément de id = "div" (sensible à la case, donc pas DIV, ni Div, ni diV, …)
var tag = $('Div'); // recherche un élément de id = "Div" (sensible à la case, donc pas DIV, ni div, ni diV, …)
var tag = $('diV'); // recherche un élément de id = "diV" (sensible à la case, donc pas DIV, ni Div, ni div, …)
var tag = $('DIV'); // recherche un élément de tagName = "div" (puisque le paramètre est entièrement en majuscule)
var tag = $.$('div'); // recherche un élément de tagName = "div"
var tag = $().$('div'); // recherche un élément de tagName = "div" (dans le body)
var tag = $$('div'); // recherche des éléments de tagName = "div"
var tag = content.$('div'); // recherche dans l'élément "content" un élément de tagName = "div"

JS : $.for()

$.for() permet d'appliquer une boucle intelligente et protégée.
$.for([], function(valeur, index) {}, this);	// itère comme un [].forEach, avec passage du this optionnel
$.for({}, function(valeur, index) {}); // itère sur les attributs d'un objet (pas les function)
$.for(NodeListe, function(valeur, index) {}); // itère même si non itérable
$.for(42, function(valeur, index) {}); // itère 42 fois
trace() est un alias de console.log, mais qui ne fonctionne que sur le navigateur du développeur. Il n'a aucun effet pour un usage autre que de développement. (voirPLUME_TRACE_IP)

JS : $.create()

Cette méthode permet de créer un nouvel élement :
$.create();				// dans le document (donc pas visible, à vous de faire un appendChild insertBefore)
$().create(); // en fin de body
$('DIV').create(); // dans un DIV existant
On peut indiquer en premier paramètre :
create("TAGNAME");			// son tagName, exemple "DIV"
create(element); // clôner un élément existant, exemple $("mon_id")
create('<tag>'); // à partir de balisage HTML
create("texte simple"); // créer simplement du texte
On peut lui attribuer en 2ème paramètres soit le nom d'une classe CSS, soit des attributs lors de sa création :
create(tag, {id: "xxx", "mon_style")
create(tag, {id: "xxx", style: {backgroundColor: 'yellow'}})
On peut spécifier en 3ème paramètre, sa position :
create(tag, false);				// soit en dernier élément si omis ou false.
create(tag, false, element); // avant un élément
create(tag, false, true); // en 1er
create(tag, false, x); // en xème élément
Si pas besoin d'attribut, passer soit{}, soitnull, soit false.

JS : $.on()

Rattache un ou plusieurs évènements à un élément.
// rattache 2 évènements
element.on("mousedown, mouseup", function(event, tag) { … }):
element.on(['click', 'keypress'], listen);
// rattache sur plusieurs élements, avec passage d'un complement de données
element.$$('DIV').on('mouseleave', function(event, tag, complement), complement);

JS : $.att()

Permet de lire ou d'assigner une valeur à un attribut.
var att = element.att('name');				// renvoi la valeur de name
var att = element.att(['name', 'id']); // renvoi un objet { name: "valeur", id: "valeur" }
element.att('name,', "valeur"); // affecter "valeur" à name
element.att({name: "valeur"}); // même chose
element.att(['name', 'class'], "valeur"); // affecte "valeur" aux attributs name, class, …
element.att(source); // clone les attributs de source à element
element.att('name,', null); // enlève l'attribut name
[tag1, tag2].att(…); // att() appliqué sur plusieurs éléments

JS : $.design()

Permet d'ajouter, enlever ou vérifier une classe CSS à un élément.
element.design("class1 classx", true);		// ajout les 2 classes à element
element.design("class1 classx", false); // enlève si présente, les 2 classes
var present = element.design("class"); // test la présence de la classe
[tag1, tag2].design(…); // design() appliqué récursivement sur le tableau

JS : $.css()

Permet de lire le style CSS calculé ou d'affecter de nouveaux styles CSS.
var css = element.css("border");				// renvoi le border calculé
var css = element.css("border margin"); // renvoi un tableau contenant le border et la margin calculé
var css = element.css(["border" , …]); // renvoi un tableau de style
var tag = element.css("border: 1px ; …"); // affecte le bording et d'autre valeur, …
var tag = element.css({border: '1px' , …}); // même principe mais passé sous forme d'un object.
var tag = element.css(["border"], source); // clone les css de la source
var tag = element.css({border: 0}, source); // clone avec des valeurs par défaut
Le nommage accepte autant la syntaxe conventionnelle kebab-case]() que la syntaxe condensée [camelCase que
Certains styles peuvent être considéré en affectation ou en lecture (en ajoutant le suffixe "i") comme des valeurs numériques.
Cela s'applique sur les styles : margin, border, padding, mais aussi tous les suffixes : top, right, bottom, left, height et width.
Idem pour la lecture des couleurs en mode hexadécimal, en finissant par le suffixe "colorx" (background-colorx, borderColorx, colorx, …).
var css = element.css("border");				// renverrait (par exemple) la chaîne "6px 12px 6px 12px"
var css = element.css("borderi"); // renverrait plutôt le tableau de valeur numérique [6, 12, 6, 12]
var css = element.css("padding−top"); // renverrait (par exemple) la chaîne "8px"
var css = element.css("paddingTopi"); // renverrait plutôt la valeur numérique 8
element.css("border", 12); // affecterait 12px sur tous les bords
element.css("margin", [12, 6, 10, 8]); // affecterait une marge de top:12px, right:6px, bottom:10px et left:8px
var css = element.css("backgroundColor"); // renverrait (par exemple) la chaîne "rgb(1, 128, 255)"
var css = element.css("backgroundColorx"); // renverrait plutôt la chaîne 0180ff00

JS : $.setPos(), $.setSize(), $.setRect()

Permet de positionner (avecstyle.left etstyle.top) et dimensionner (avecstyle.width etstyle.height) l'élément.
tag.setPos(point);			// positionnement
tag.setSize(point); // dimensionne
tag.setSize(rect); // dimensionne en utilisant la taille du rect
tag.setRect(rect); // positionnement et dimensionne
Ces méthodes utilisent des notions de $Point et de $Rect.

JS : $.position()

Permet de lire les dimensions et positions absolues d'un élément
var pos = element.position();					// renvoi les positions de l'élément
var pos = element.position($('content')); // renvoi les positions à partir de l'élément content
var pos = element.position(true); // renvoi les positions de l'élément relatif à son scroll
var pos = element.position().border.topLeft.x; // position gauche
Renvoi l'objet :
{
inner: {
topLeft, // $Point haut gauche (voir plus bas l'objet $Point)
botRight, // $Point bas droit
size, // $Point représentant la taille
},
padding: {
topLeft, // …
botRight, // …
size, // …
top, // valeur entière représentant la taille du padding−top
left, // …
bottom,
right
},
border: {
topLeft,
botRight,
size,
top,
left,
bottom,
right
},
margin: {
topLeft,
botRight,
size,
top,
left,
bottom,
right
}
}
Cette méthode utilise des notions de $Point et de $Rect.
liste des valeurs renvoyées par position()

JS : $Point

Cet objet représente un point, avec les attributs{x, y} Il permet de manipuler les points avec les méthodes suivantes :
// création
let point = new $Point(); // point de coordonnée (0,0)
let point = new $Point(x, y); // point en utilisant 2 valeurs numériques
let point = new $Point([x, y]); // fonctionne avec un tableau
let point = new $Point({x: 1, y: 2}); // fonctionne avec une simple structure
let point = new $Point({topLeft: {x: 1, y: 2}}); // fonctionne avec une structure topLeft
let point = new $Point({left: 1, top: 2}); // fonctionne avec une simple structure
let point = new $Point(event); // point à partir du clientX et clientY
let point = new $Point(point); // duplique
let point = $Point(x, y); // sucre du new $Point()
let point = point1.copy(); // duplique le point
// modification
point.add(point1, …).add(x, y); // renvoi un nouveau point, ajouté de la liste de points passés en paramètre
point.sub(point1, …).sub(x, y); // renvoi un nouveau point, enlevé de la liste de points passés en paramètre
point.move(point1, …).move(x, y); // ajout la liste de points passés en paramètre à point et le renvoi
point.back(point1, …).back(x, y); // enlève la liste de points passés en paramètre à point et le renvoi
point.rect(x1, y1, x2, y2); // réinitialise
point.min(point1, …).min(x, y); // renvoi un nouveau point contenant les coordonnées minimums entre lui et les points passés en paramètres
point.max(point1, …).min(x, y); // renvoi un nouveau point contenant les coordonnées maximums entre lui et les points passés en paramètres
point.minimize(point1, …).min(x, y); // affecte à point le point contenant les coordonnées minimums entre lui et les points passés en paramètres
point.maximize(point1, …).min(x, y); // affecte à point le point contenant les coordonnées maximums entre lui et les points passés en paramètres
point.x = 0;
point.y = 6.56;
// comparaison
point.compare(point2); // renvoi true si point a les mêmes coordonnées que point2
point.angle(point2); // renvoi false si point2 n'est pas viable, 0 s'il est égal à point, valeur en radian > 0 indiquant la direction, si différent
// utilisation
let x = point.x;
let y = point.y;
element_html.setPos(point); // positionne le top et left de l'élément
element_html.setSize(point); // positionne le height et width de l'élément
$('CANVAS').setPos(point); // positionne et dimensionne le canvas
// exemple
div.setPos($Point(event).move(point, x, y, event2, [x, y]).sub(5, 5));
valeurs renvoyé par point.angle()

JS : $Rect

Cet objet représente un rectangle, avec les attributs $Point{topLeft, botRight, size} Il permet de manipuler les rectangles avec les méthodes suivantes :
// création
let rect = new $Rect(); // rectangle vide
let rect = new $Rect(x1, y1, x2, y2); // point en utilisant 4 valeurs numériques
let rect = new $Rect([x1, y1, x2, y2]); // fonctionne avec un tableau de 2 points
let rect = new $Rect({x: 1, y: 2}, {x: 3, y: 4}); // fonctionne avec de simples structures
let rect = new $Rect(topBot); // fonctionne avec une structure topLeft, botRight
let rect = new $Rect(topSize); // fonctionne avec une structure topLeft, size
let rect = new $Rect({left: 1, top: 2, bottom: 3, right: 4}); // fonctionne avec une simple structure
let rect = new $Rect(event1, event2); // fonctionne à partir de clientX et clientY
let rect = new $Rect(rect); // duplique
let rect = $Rect(); // sucre du new $Rect()
// décalage et/ou redimensionnement
rect.add(rect1).add(point); // renvoi un nouveau rect, ajouté de rect1, …
rect.sub(rect1).sub(point); // renvoi un nouveau rect, enlevé de rect1, …
rect.move(rect1).move(point); // déplace rect, et le renvoi
rect.back(rect1).back(point); // déplace rect dans l'autre sens (comme un sub) et le renvoi
rect.include(rect1).include(point); // se transforme en rect englobant son rect et le rect1 ou point
rect.resize(width, height); // ajoute ou enlève à botRight
rect.resize(point); // ajoute ou enlève à botRight
rect.resize(rect); // ajoute ou enlève à topLeft et à botRight
rect.scale(width, height); // si positif redimensionne right ou bottom, si négatif redimensionne left ou top
rect.size = point;
// comparaison
rect.includes(rect1); // renvoi true si rect1 est totalement à l'intérieur
rect.contains(rect2); // renvoi true si rect1 est en partie ou totalement à l'intérieur
rect.excludes(rect1); // renvoi true si rect1 est totalement à l'extérieur
// utilisation
let struct = rect.tolebori(); // renvoi une structure {top, left, bottom, right}
let tl = rect.topLeft; // $Point de l'angle le plus haut à gauche
let br = rect.botRight; // $Point de l'angle le plus bas à droite
let size = rect.size; // $Point représentant la taille du rectangle
let x1 = rect.topLeft.x;
let y1 = rect.topLeft.y;
let x2 = rect.botRight.x;
let y2 = rect.botRight.y;
let width = rect.size.x;
let height = rect.size.y;
element_html.setRect(point); // positionne et dimensionne
// exemple
div.setRect($Rect(event, 50, 10).move(point, x, y, event2, [x, y]).sub(5, 5));
Le point topLeft sera toujours au dessus et sur la gauche du point botRight.

JS : $.start()

Plume lance les fonctions passées dans$.start(); lorsque la page est chargée et prête.
// Exemple d'utilisation
function demarrage() {
// les instructions qui seront executées uniquement lorsque la page sera prête.
}
$.start(demarrage);
// ou plus simplement en employant une fonction anonyme intégrée
$.start(function() {
// les instructions qui seront executées uniquement lorsque la page sera prête.
});
Il est possible de bloquer les starts, en attente de données qui pourrait venir après le load de la page, tel que les requêtes asynchrones ajax, les promesses, les database, …
Le principe est de préciser en premier paramètre le ou les flags qui sont indispensables à la bonne exécution du contenu de la fonction; puis lorsque ces flags sont opérationnels, de l'indiqueer en appelant$.start avec uniquement le flag concerné.
(Cet exemple utilise des fonctions asynchrone RFC expliquées plus bas)
// exécute une fonction en asynchrone, suite à un retour du serveur PHP
ajax.differe_ready().then(function() {
// traitement
$.start("ready"); // déverouille ce flag permettant de débloquer les starts ayant besoin de ce traitement
});
// fonction qui ne sera lancée que lorsque la page sera prête ET que le flag "ready" sera déverouillé,
// donc après le retour de l'ajax "differe_ready"
$.start("ready", function() {
});
// fonction qui ne sera lancée que lorsque la page sera prête ET que les 2 flags "ready" et "impossible" seront déverouillés,
// donc jamais, puisque le flag "impossible" n'est pas déverouillé dans cet exemple.
$.start("ready impossible", function() {
});
// alors que cette fonction sera appelée dès que la page sera prête, sans tenir compte d'aucun flag
$.start(function() {
});
Les flags sont de type chaîne de caractères, mais l'on peut également rattacher une valeur à ces flags, qui sera récupéré dans le start :
// demande d'information supplémentaire au serveur
ajax.reading().then(function(result) {
if (result.ressource)
$.start({ressource: result.ressource});
});
$.start(function() {
// attend que l'utilisateur clic sur son bouton
$.$('button#goUser').on('click', function() {
$.start({ready: true});
});
});
// lorsque ces deux évenements seront réunis, affiche le résultat dans la console
$.start("ready ressource", function(event, flags) {
console.log(flags.ready, flags.ressource);
});

JS : $.database()

Permet d'accéder à la base locale du navigateur
// connexion sur le database database1 en v1, contenant les store1, …, créés s'il le faut
var database = $.database("database1", ['store1', …], 1, function(db) {database = db; $.start('database');});
// attention : ouverture en asynchrone, il est important d'utiliser la variable qu'après appel du succes, d'où l'appel du start avec le flag 'database'
$.start('database', function() {
var record = {id: 42, nom: "Dupond"};

// enregistrement
database.write(store, record);

// lecture
database.read('store1', record.id).then(function(record) {
$('nom').value = record.nom;
}).catch(function (err) {
$.modal("Erreur sur la lecture de cet enregistrement d'id : " . record.id)
});

// ou lecture avec valeur par défaut si non trouvé
database.read('store1', record.id, {nom: ''}).then(function(record) {
$('nom').value = record.nom;
});

// lecture d'un store
database.select(store, []).then(function(list) {
$.for(list, function(record) {
// …
});
});
});
Cet exemple utilise l'itération $.for et les dialogues $.modal.

JS : extension

Plume ajoute des fonctionnalités aux objets existants.

Function.delay()

Cette méthode lance en asynchrone différé la fonction spécifiée, en lui passant le timer et éventuellement unthis.
Exemple :
function ma_fonction(parametre) {
console.log(this, parametre);
}
ma_fonction.delay($('BODY'), 3000, 42); // affichera dans 3s : <body> 42
ma_fonction.delay(500, 42); // affichera dans une demie−seconde : <window> 42

String.sprintf()

Cette méthode renvoi une chaîne formatée en utilisant les paramètres passés.
Le format est composé de caractères (qui seront affichés tel quel) et d'insertion des paramètres.
La syntaxe de ces insertions est : % position complement flag signe largeur precision type. d : affiche le paramètre sous forme d'un nombre entier. f : affiche le paramètre sous forme d'un nombre flottant. s : affiche le paramètre sous forme d'une chaîne de caractère. % : forçant d'insérer non pas un paramètre, mais le caractère pourcentage (%).
Exemples :
console.log( "(%1u, %7u, %7u, %7u)".sprintf(42, −42.24, " +42", "a 42" ) );			// affichera (42,      42,      42,       0)
console.log( "(%1d, %7d, %7d, %7d)".sprintf(42, −42.24, " +42", "a 42" ) ); // affichera (42, −42, 42, 0)
console.log( "(%1f, %7f, %7f, %7f)".sprintf(42, −42.24, " +.42", "a 42.24" ) ); // affichera (42, −42.24, 0.42, 0)
console.log( "(%1s, %7s, %7s, %7s)".sprintf(42, −42.24, " +.42", "a 42" ) ); // affichera (42, −42.24, +.42, a 42)
console.log( "(%%)".sprintf(42, −42.24, " +.42", "a 42" ) ); // affichera (%)
console.log( "(%2$x7.1f)".sprintf(42, 42.24, " +.42", "a 42" ) ); // affichera (xxx42.2)
console.log( "(%2$x−7.3f)".sprintf(42, 42.24, " +.42", "a 42" ) ); // affichera (42.240x)
console.log( "(%.3f)".sprintf(4224.2442 ) ); // affichera (4224.244)
console.log( "(%10.3s)".sprintf("abcdefghijklmnopqrstuvwxyz" ) ); // affichera ( abc)
console.log( "(%−10.3s)".sprintf("abcdefghijklmnopqrstuvwxyz" ) ); // affichera (abc )
console.log( "(%−−10.3s)".sprintf("abcdefghijklmnopqrstuvwxyz" ) ); // affichera (abc−−−−−−−)
console.log( "(%.+10.3s)".sprintf("abcdefghijklmnopqrstuvwxyz" ) ); // affichera (.......abc)
console.log( "(%.10.3s)".sprintf("abcdefghijklmnopqrstuvwxyz" ) ); // affichera (.......abc)
console.log( "(%.10.0s)".sprintf("abcd" ) ); // affichera (......abcd)
console.log( "(%.4u, %+.4u, %.4u, %+.4u)".sprintf(42, 42, −42, −42) ); // affichera (0042, +042, 0042, +042)
console.log( "(%.4d, %+.4d, %.4d, %+.4d)".sprintf(42, 42, −42, −42) ); // affichera (0042, +042, −042, −042)

Array.first()

Cette méthode renvoi le premier élément, ounull si tableau vide.

Array.second()

Cette méthode renvoi le second élément, ounull si inexistant.

Array.last()

Cette méthode renvoi le dernier élément, ounull si tableau vide.

Array.delete()

Cette méthode renvoi l'index (à partir de 0) de l'élément passé en paramètre qui a été effacé, sinon ne touche pas au tableau et renvoi -1.

Date.addDate()

Cette méthode ajoute ou enlève un nombre de jour à la date.
Exemple :
var date = new Date(2020, 0, 1, 12);
date.addDate(1);
console.log(date);
Renverra :

Date.copy()

Cette méthode renvoi une copie de la date.
Exemple :
var origine = new Date(2020, 0, 1, 12);
var reference = origine;
reference.addDate(1);
var copie = origine.copy();
reference.addDate(3);
console.log(reference, copie);
Renverra :

window.location.args

Renvoi un tableau en lecture seul, contenant la liste des variables passées au GET et en attribut, le couple variable = valeur.
Exemple avec une url de type https://www.d-nada.com/rubrique/action/index.php?nom=Dupon&prenom=André,window.location.args renverra :
{
0: "nom",
1: "André",
length: 2,
nom: "Dupon",
prenom: "André"
}

window.location.folders()

Est une méthode renvoyant un tableau indexé contenant la liste des dossiers de l'URL.
Exemple avec une url de type https://www.d-nada.com/rubrique/action/index.php?nom=Dupon&prenom=André,window.location.folders renverra :
[ 'rubrique', 'action' ]

window.navigator.name

Renvoi une chaîne contenant le nom du navigateur dans l'énuméré suivant : chrome, firefox, opera, safari, samsung, …
Exemple avec votre navigateurwindow.navigator.name renverra :

window.navigator.mobile

Renvoi unboolean indiquant si le navigateur est sur un smartphone ou tablette.
Exemple avec votre navigateurwindow.navigator.mobile renverra :

window.navigator.version

Renvoi la version
Exemple avec votre navigateurwindow.navigator.version renverra :

JS : RPC

Plume intégre un RPC afin d'appeler des fonctions serveurs (uniquement en PHP actuellement) en asynchrone depuis le JS.
Fonctions PHP, que le développeur autorise, avec passage de paramètres et retour d'une valeur.
Pour cela, la fonction doit être appelée à travers la classe$.ajax, classe, et cette fonction doit être préfixée parajax_ côté PHP.
Exemple avec côté client JS :
$.ajax.ma_methode(premier, deuxieme, etc).then(function(resultat) {
alert("Valeur de retour correct :", Array.isArray(resultat.ok) && resultat.ok.length == 1 && resultat.ok[0] === 42);
});
Et côté serveur PHP :
function ajax_ma_methode($premier = 'default', $deuxieme = false, $etc = null) {
return [ 'ok' => [42] ];
}
Template::ajax(); // à placer avant tout envoi (tel que echo ou Template::display())
On peut également lui faire passer un ou plusieurs fichiers (via un<input type="file"/>) ou une notion de timeout :
<script>
$('INPUT?file').on('change', function() {
if (! this.files.length)
return;
let file = this.files[0];
if (file.size > 100*1024*1024)
window.alert("Fichier trop volumineux.");
else
// le fichier, ou tableau de fichiers doivent toujours être en dernier paramètre (sauf si option de timeout)
$.ajax.download(42, file).then(function(resultat) {
if (resultat === true)
window.alert("Téléchargement réussi")
else
window.alert("Problème lors du téléchargement: " + resultat);
});
});
</script>
<?php
function ajax_download($quarante_deux) {
$files = $_FILES['files'];
if (! $files || count($files["tmp_name"]) == 0)
return "Fichier manquant.";
foreach ($files["tmp_name"] as $idx => $tmp) {
$name = $files["name"][$idx];
$type = $files["type"][$idx];
$size = $files["size"][$idx] / (1024*1024);
if (! preg_match('{^text/(w+)$}', $type, $type))
return "Type '$type' du fichier '$name' non pris en charge.";
if ($size > 100)
return "Taille ({$size}Mo) du fichier '$name' trop volumineux.";
if (! $file = @fopen($files["tmp_name"][$idx], 'r'))
return "Ouverture du fichier '$name' impossible.";
// fread…
fclose($file);
}
return true;
}
?>
Il existe des options, qu'il est possible de passer en dernier paramètre, en retour de la méthode$.ajax.option(). Ces options sont : Exemples :
// si la requête prend plus d'une demie−seconde, la fonction catch sera appelée
$.ajax.ma_fonction1(parametres, $.ajax.option({timeout: 500})).then(function(success) {…}).catch(function(error) {…});
// affiche visuellement la durée restante du transfert, durant l'envoi et réception de l'appel de cette méthode PHP
let progress = $('PROGRESS');
progress.value = 0; // part de zéro
progress.display = 'inherit'; // et affiche la jauge
$.ajax.ma_fonction2(file, $.ajax.option({progress: function(event) {
if (event.lengthComputable)
if (event.loaded < event.total)
progress.value = event.loaded / event.total; // augmente la jauge durant l'envoi
else
progress.value = undefined; // met en attente la jauge pendant le traitement PHP
else
if (event.loaded < event.total)
progress.value = (event.total − event.loaded) / event.total; // diminue la jauge durant la réception
else
progress.display = 'none'; // ferme la jauge lorsque terminé
}})).then(succes);
Enfin, il est possible d'employer la classe$.ajax pour lancer juste des requêtes simples asynchrones (donc sans créer de fonction PHP particulière), avec une promesse en retour :
<script>
// appel index.php avec le paramètre par1 = 42, puis appel la fonction "traitement()" avec la réponse de la requête.
$.ajax({par1: 42}).then(traitement);
// appel request.php à la racine du serveur avec post1 à 42 en POST, avec 1s maxi
$.ajax('post', "/request.php", {post1: 42}, {completion1: 2}, 1000).then(function(response, completion) {
// traitement de la réponse, avec une completion passé lors de l'appel
});
<script>
<?php
if (Template::request('post1') == 42) {
Template::send([ 'ok' => "yes" ]);
exit;
}
?>

PHP

Plume ajoute des fonctionnalités au PHP.

PHP : constante

Ces constantes permettent de personnaliser Plume :

PLUME_TRACE_IP

PLUME_TRACE_IP permet d'ajouter des fonctionnalités de debugging au poste indiqué par son IP (à chercher sur monip.org par exemple).
Il accepte : Exemple :
define('PLUME_TRACE_IP', "77.0.1.2");
define('PLUME_TRACE_IP', "77.0.1.*");
define('PLUME_TRACE_IP', "77.0.[0−3].*");
define('PLUME_TRACE_IP', ["77.0.1.2", "168.0.1.2"]);
define('PLUME_TRACE_IP', true);
define('PLUME_TRACE_IP', false);

PLUME_MAIL_REPORT

PLUME_MAIL_REPORT peut spécifier une adresse où le développeur pourra recevoir des données de débugging, de type report.
Exemple :
define('PLUME_MAIL_REPORT', "contact−technique@ma−societe.com");

PLUME_LOG

PLUME_LOG peut spécifier un dossier, où Plume cherchera à enregistrer un journal des activités SQL. Voir PHP : Log et report.
Exemple :
define('PLUME_LOG', "/www/data/log");
Par défaut, PHP va chercher un dossier :
  1. De nom "log"
  2. Se terminant par ".data/log".
  3. Se terminant par ".log".
En partant du dossier où se trouveplume.php, puis en remontant jusqu'à la racine.
Si aucun dossier n'est trouvé, le mécanisme de log n'est pas activé.
Exemple avec /www/monsite/global/plume.php :
  1. /www/monsite/global/log/
  2. /www/monsite/global.data/log/
  3. /www/monsite/global.log/
  4. /www/monsite/log/
  5. /www/monsite.data/log/
  6. /www.log/
  7. /www/log/
  8. /www.data/log/
  9. /www.log/

PHP : trace()

Procédure PHP trace() permet d'afficher des données PHP directement sur la console du navigateur, uniquement en mode développement (voirPLUME_TRACE_IP).
<?php
define('PLUME_TRACE_IP', "192.168.0.1"); // l'adresse du développeur
require('plume.php');
trace('texte'); // envoi le texte sur la console
trace('titre', $var1, $var2, …); // envoi le texte et les variables
trace_usage('titre'); // envoi la mémoire et le temps utilisé depuis le dernier trace_usage()
?>

PHP : Base

Classe PHP simplifiant la vie du développeur, par sa facilité d'utilisation, sa lisibilité et ses fonctionnalités poussées.
Tous les résultats suivants prendront cette table en exemple :
id nom ville
42 Enzo Annecy
74 Patricia La Paz
500 Yvan Annecy

Base : open

Ouverture d'une base :
$base = new Base("signature");
Base va ouvrir une base, en recherchant les informations de connexion, rattachées à la signature, dans un annuaire.
L'annuaire est un fichier qui peut être présent sur le serveur aux endroits suivants : Pour des raisons de sécurité, ce fichier "annuaire" est préfixé .php et donc de syntaxe PHP :
<?php
/*
open:signature mysql://login:password@server[/base]
sql:clé select … {x} …
value:clé constante
data:clé data
*/
?>
Ce fichier annuaire peut contenir plusieurs commandes. La commande quenew Base() recherche estopen.
open:signature	mysql://login:password@server[/base]
Pour le moment, seul MySQL est porté, les autres bases tel que Oracle ou PostgreSQL, peuvent être ajouté facilement.
Les autres commandes servent à :

Base : read

On peut lire un enregistrement avecbase−>read().
read(1 champ)
Si un seul champ demandé, renvoi une chaîne correspondant à ce champ.
$record = $base−>read("select nom from table where id = 42");
Renvoi :
"Enzo"
read(>= 2 champs)
Si plusieurs champs demandés, renvoi un tableau associatif :
$record = $base−>read("select id, * from table where id = 42");
Renvoi :
[
"id" => "42",
"nom" => "Enzo",
"ville" => "Annecy"
]

Base : select

On peut lire une liste d'enregistrement avecbase−>select().
$liste = $base−>select("select * from ma_table where id = 42 order by nom");
$liste = $base−>select("show table status like 'ESSAI%'");
select(1 champ)
Si un seul champ demandé, renvoi une liste indexée, de la valeur de ce champ.
$liste = $base−>select("select nom from table");
Renvoi :
[
"Enzo",
"Patricia",
"Yvan"
]
select(>= 2 champs)
Si plusieurs champs demandés, renvoi un tableau associatif dont la clé correspond au premier champ, et la valeur, un tableau associatif de ces champs :
$liste = $base−>select("select id, nom from table where 1");
Renvoi :
[
"42" => [ "id" => "42", "nom" => "Enzo" ],
"74" => [ "id" => "74", "nom" => "Patricia" ],
"500" => [ "id" => "500", "nom" => "Yvan" ]
]
Ce champ clé devant être unique, le champ "id" est généralement utilisé.
Exemple destructif à ne pas utiliser dans ce cas :
$liste = $base−>select("select ville, nom from table where 1");
Renvoi :
[
"500" => [ "ville" => "Annecy", "nom" => "Yvan" ],
"74" => [ "ville" => "La Paz", "nom" => "Patricia" ],
]
selecti()
Avec l'extension "i", le$base−>selecti renvoi toujours une liste indexée.
$liste = $base−>selecti("select id, nom from table where 1");
Renvoi :
[
[ "id" => "42", "nom" => "Enzo" ],
[ "id" => "74", "nom" => "Patricia" ],
[ "id" => "500", "nom" => "Yvan" ]
]
selects()
Avec l'extension "s", le$base−>selects renvoi toujours une liste indexée, dont la clé regroupe une sous-liste indexée des enregistrements ayant cette clé.
$liste = $base−>selects("select ville, * from table where 1");
Renvoi :
[
"Annecy" => [
[ "ville" => "Annecy", "id" => "42", "nom" => "Enzo" ],
[ "ville" => "Annecy", "id" => "500", "nom" => "Yvan" ],
],
"La Paz" => [
[ "ville" => "La Paz", "id" => "74", "nom" => "Patricia" ],
]
]

Base : Enregistrement

On peut enregistrer des données, en préparant en premier lieu, l'enregistrement.
set()
// on prépare l'enregistrement, champ par champ
$base−>set("champ", "valeur");
// ou en bloc
$base−>set( [ "champ" => "valeur" ] );
insert()
En forçant l'ajout d'un nouvel enregistrement.
Un seul paramètre : le nom de la table.
Si l'ajout est réussi, le retour correspond auid du nouvel enregistrement (toujours positif non-nul), sinon à false.
$id = $base−>insert("table");
update()
En modifiant un enregistrement déjà présent.
Paramètrage :
  1. Le nom de la table.
  2. La clausewhere ou le "id".
Si la clausewhere se résume à faire unid = {i}, il suffit de passer cet "id" à la place de la clausewhere :
$ok = $base−>update("table", $id);
modify()
En modifiant un enregistrement déjà présent ou en ajoutant un nouvel enregistrement.
Paramètrage :
  1. Le nom de la table.
  2. Le "id" (pas de clausewhere ).
Cela correspond plus ou moins à faire unif (read("id")) update("table", "id"); else insert("table sans le id");
delete()
Effacement d'un enregistrement.
Paramètrage :
  1. Le nom de la table
  2. La clausewhere ou le "id".
Si l'on veut effacer toute la table, il est préférable de faire undeletes Paramètrage :
  1. Le nom de la table
    $ok = $base−>deletes("table");

Base : Requêtes

On peut également faire des requêtes particulières.
desc()
Permet d'avoir la description d'un ou plusieurs champs.
desc(false)
Renvoi la liste des champs d'une table.
$champs = $base−>desc('table');
Renvoi :
[ "id", "nom", "ville" ]
desc(true)
Renvoi la description des champs d'une table.
$champs = $base−>desc('table', true);
Renvoi :
[
"id" => ['Field' => "id", 'Type' => "int(11)", 'Key' => "PRI", 'Null' => "NO", 'Default' => null, 'Extra' => "auto_increment"],
"nom" => ['Field' => "nom", 'Type' => "varchar(100)", 'Key' => "", 'Null' => "NO", 'Default' => null, 'Extra' => ""],
"ville" => ['Field' => "ville", 'Type' => "varchar(100)", 'Key' => "", 'Null' => "NO", 'Default' => null, 'Extra' => ""]
]
desc(enum)
Renvoi l'énuméré d'un champ.
$champs = $base−>desc('table', "ville");
Renvoi :
[ "Annecy", "La Paz" ]
foreignKey()
Renvoi la liste des clés étrangères des tables demanadées, regroupé par table.
Il accepte en paramètre : Les tableaux renvoyés contiennent : Exemples :
$foreign1 = $base−>foreignKey('table');
$foreign2 = $base−>foreignKey(['table', 'personnels']);
$foreigns = $base−>foreignKey();
Pourrait renvoyer :
$foreign1 = [
'nom' => [ 'name' => "table", 'source' => "nom", 'foreign' => "personnels", 'key' => "nom_personne" ],
'ville' => [ 'name' => "table", 'source' => "ville", 'foreign' => "villes", 'key' => "identifiant" ],
];
$foreign2 = [
'table' => [
'nom' => [ 'name' => "table", 'source' => "nom", 'foreign' => "personnels", 'key' => "nom_personne" ],
'ville' => [ 'name' => "table", 'source' => "ville", 'foreign' => "villes", 'key' => "identifiant" ],
],
'personnels' => [
'telephone' => [ 'name' => "personnels", 'source' => "telephone", 'foreign' => "annuaire", 'key' => "id" ],
],
];
$foreigns = [
'table' => [
'nom' => [ 'name' => "table", 'source' => "nom", 'foreign' => "personnels", 'key' => "nom_personne" ],
'ville' => [ 'name' => "table", 'source' => "ville", 'foreign' => "villes", 'key' => "identifiant" ],
],
'personnels' => [
'telephone' => [ 'name' => "personnels", 'source' => "telephone", 'foreign' => "annuaire", 'key' => "id" ],
],
'societes' => [
'directeur' => [ 'name' => "societes", 'source' => "directeur", 'foreign' => "personnels", 'key' => "id" ],
],
];
sql()
Il est possible de produire n'importe quelles requêtes SQL spécifiques.
$base−>sql('ma requete sql');
$base−>sql("alter table {w} comment {s}", $ma_table, $mon_commentaire);
trace()
Enfin, possibilité de générer des traces (envoyé directement sur la console du navigateur) :
// renvoi ['sql','result','error','time'] sur la dernière requête
$trace = $base−>trace();
// envoi sur la console.log ['sql','result','error','time'] sur la dernière requête
$base−>trace('title');
// temps maximum avant de déclencher les logs (voir plus bas "PHP : Log et report")
$base−>trace_log(0.500);
close()
Il est recommander de fermer les accès aux bases avant de terminer le programme PHP.
$base−>close();

Base : clause where

Les requêtes SQL utilisé dans read(), select(), update() et modify(), peut être rassemblé dans l'annuaire.
Exemple :
// Avec cette valeur dans l'annuaire
sql:requete_42 select * from table where 1;
// la requête :
$base−>read("requete_42");
// sera remplacé par :
$base−>read("select * from table where 1");

Cette clausewhere peut également contenir (après remplacement) des insertions, permettant de formattage et la protection de variable extérieur à la requête.
Ces insertions ont une syntaxe de une ou plusieurs lettre entre guillemets, qui sécurisera, formatera et placera les paramètres passées à la fonction, dans la requête.
$record = $base−>read("select * from table where id = {i} and nom = {s} and ville in {as}", "42", "Enzo", ["Annecy"]);
Les insertions possibles sont : Exemple :
// Avec cette valeur dans l'annuaire
value:client ma_table
// ce format SQL
$base−>read("select * from {:client} where 1 {and validate = 42} {?and name like {sb}} and level in {as}", false, "En", [−1, null, "etc", "('"%_)", ""]);
// sera remplacé par cette chaîne de caractères
$base−>read('select * from ma_table where 1 and name like
"En%" and level in ("−1", "", "etc", "('"%_)", "")');

PHP : mail

Classe PHP permettant de préparer et envoyer des mails.
// préparation du mail
$mail = new Mail();
// entête
$mail−>from("emetteur@mail.com");
$mail−>to("destinataire@mail.com");
$mail−>to(["destinataire@mail.com"]);
$mail−>cc("copie@mail.com");
$mail−>cc(["copie@mail.com"]);
$mail−>bcc("copie−caché@mail.com");
$mail−>bcc(["copie−caché@mail.com"]);
$mail−>returnPath("emetteur@mail.com");
$mail−>subject("titre du mail");
// corps du mail
$pattern = Template::extract("//message/page.html", "mail_template");
$message = Template::render($pattern−>html(['mail' => $message, 'signer' => $signature]));
$mail−>html(
"texte ou <html>");
if ! $mail−>attachment(
"/fichier.ext", "nom du fichier"))
echo
"fichier inexistant";
// envoi
$mail−>trace(
"mail"); // trace sur la console
if ($mail−>mail())
return ['success' =>
"Mail anvoyé avec succès."];
return ['error' =>
"Problème lors de l'envoi du mail."];

PHP : Markdown

Fonction PHP permettant de réaliser des markdowns (tel que la plus grosse partie des pages de ce site ou simplement cette documentation que vous lisez actuellement).

À implémenter depuis PHP :
function template_produit($pattern) {
return markdown("mon_fichier_markdown.md");
}
Où depuis un template :
<html>
{+markdown(mon_fichier_markdown)+}
</html>
L'implémentation Markdown respecte la syntaxe suivante :

Markdown : Texte

Une fin de ligne provoquera un retour à la ligne HTML<br/> automatiquement.
Sauf si la ligne finie ou commence par une balise en ligne, tel que<span>, <code>, dans ce cas, pour forcer le retour à la ligne, il faut ajouter un espace en fin de ligne.
Un ensemble de caractères permettent des mises en page (tel que# * −), pour afficher ce caractère, il suffit de placer un anti-slash avant "\" (exemple\# \* \−).
Les caractères< > et & utilisés en HTML, sont naturellement protégés, mais rien n'interdit d'insérer des balises HTML. Exemple .
Les {templates} sont gérés, en dehors des blocs de code.

Markdown : Titre

Syntaxe : le caractère dièse# en début de ligne
Formatage : encadre avec les balises<h1>, <h2>, … Exemple :
# Titre 1
## Titre 2
### Titre 3
#### Titre 4
##### Titre 5
###### Titre 6
\# Ceci n'est pas un titre
Donnera :

Titre 1

Titre 2

Titre 3

Titre 4

Titre 5
Titre 6
# Ceci n'est pas un titre

Markdown : Emphase gras

Syntaxe : le caractère astérisque* encadrant un bloc de mots
Formatage : encadre avec la balise<strong> Exemple :
Exemple de texte *en gras sur une ligne* ou
sur plusieurs *lignes
blabla blabla blabla blabla blabla
en finissant* à tout instant.
(Ceci n'est pas \*du texte en gras mais juste encadré d'astérisques\*)
Donnera :
Exemple de texte en gras sur une ligne ou
sur plusieurs lignes
blabla blabla blabla blabla blabla
en finissant
à tout instant.
(Ceci n'est pas *du texte en gras mais juste encadré d'astérisques*)

Markdown : Emphase souligné

Syntaxe : le caractère espace-souligné_ encadrant un bloc de mots
Formatage : encadre avec la balise<u> Exemple :
Exemple de texte _souligné sur une ligne_ ou
sur plusieurs _lignes
blabla blabla blabla blabla blabla
en finissant_ à tout instant.
(Ceci n'est pas \_du texte souligné mais juste encadré d'un espace souligné\_)
Donnera :
Exemple de texte souligné sur une ligne ou
sur plusieurs lignes
blabla blabla blabla blabla blabla
en finissant
à tout instant.
(Ceci n'est pas _du texte souligné mais juste encadré d'un espace souligné_)

Markdown : Emphase barré

Syntaxe : deux fois le moins encadrant un bloc de mots
Formatage : encadre avec la balise<s> Exemple :
Exemple de texte −−barré sur une ligne−− ou
sur plusieurs −−lignes
blabla blabla blabla blabla blabla
en finissant−− à tout instant.
(Ceci n'est pas \−−du texte barré mais juste encadré de 2 moins−−)
Donnera :
Exemple de texte barré sur une ligne ou
sur plusieurs lignes
blabla blabla blabla blabla blabla
en finissant
à tout instant.
(Ceci n'est pas --du texte barré mais juste encadré de 2 moins--)

Markdown : Ligne

Syntaxe : Trois caractères moins minimum en début de ligne
Formatage : transformer en balise<hr/> Exemple :
−−−
Donnera :

Markdown : Lien

Syntaxe : [texte descriptif](url)
Formatage : encadre de la balise<a>
Exemple :
Lien externe [Site de DNADA](https://www.d−nada.com) et lien interne à la page [1er chapitre](# Présentation).
Donnera :
Lien externe Site de DNADA et lien interne à la page 1er chapitre.

Les adresses mails présentes dans la partie texte (et non dans un bloc de code) sont automatiquement protégées et transformées en adresse mail HTML.
Exemple : contact@d-nada.com
Donnera :

Markdown : Image

Syntaxe : !texte alternatif Formatage : transforme avec la balise<img>
Exemple :
Image : ![image représentant une plume](exemple image.png)
Donnera :
Image :
image représentant une plume
Il est possible de définir la taille (en hauteur et/ou largeur) et de justifier la position de l'image dans le texte, avec la syntaxe suivante en fin de texte explicatif (qui ne sera supprimé de ce texte) : largeur/hauteur/centrage.
Avec :
Mais je dois vous expliquer comment est née toute cette idée erronée de dénoncer le plaisir et louer la douleur et je vais vous donner un compte rendu complet du système, et exposer les enseignements réels du grand explorateur de la vérité, le maître-constructeur de l'homme bonheur. Personne ne rejette, n'aime pas ou évite le plaisir lui-même, parce que c'est du plaisir, mais parce que ceux qui ne savent pas poursuivre le plaisir rencontrent rationnellement des conséquences extrêmement douloureuses.
image représentant une petite plume
Il n'y a pas non plus de personne qui aime ou poursuit ou désire obtenir la douleur d'elle-même, parce que c'est de la douleur, mais parce que des circonstances se produisent parfois dans lesquelles le travail et la douleur peuvent lui procurer un grand plaisir. Pour prendre un exemple trivial, lequel d'entre nous entreprend jamais un exercice physique laborieux, sauf pour en tirer un avantage ? Mais qui a le droit de trouver à redire à un homme qui choisit de jouir d'un plaisir qui n'a pas de conséquences gênantes, ou qui évite une douleur qui ne produit aucun plaisir résultant ?
Mais je dois vous expliquer comment est née toute cette idée erronée de dénoncer le plaisir et louer la douleur et je vais vous donner un compte rendu complet du système, et exposer les enseignements réels du grand explorateur de la vérité, le maître-constructeur de l'homme bonheur. Personne ne rejette, n'aime pas ou évite le plaisir lui-même, parce que c'est du plaisir, mais parce que ceux qui ne savent pas poursuivre le plaisir rencontrent rationnellement des conséquences extrêmement douloureuses.
image représentant une plume à gauche
Il n'y a pas non plus de personne qui aime ou poursuit ou désire obtenir la douleur d'elle-même, parce que c'est de la douleur, mais parce que des circonstances se produisent parfois dans lesquelles le travail et la douleur peuvent lui procurer un grand plaisir. Pour prendre un exemple trivial, lequel d'entre nous entreprend jamais un exercice physique laborieux, sauf pour en tirer un avantage ? Mais qui a le droit de trouver à redire à un homme qui choisit de jouir d'un plaisir qui n'a pas de conséquences gênantes, ou qui évite une douleur qui ne produit aucun plaisir résultant ?
Mais je dois vous expliquer comment est née toute cette idée erronée de dénoncer le plaisir et louer la douleur et je vais vous donner un compte rendu complet du système, et exposer les enseignements réels du grand explorateur de la vérité, le maître-constructeur de l'homme bonheur. Personne ne rejette, n'aime pas ou évite le plaisir lui-même, parce que c'est du plaisir, mais parce que ceux qui ne savent pas poursuivre le plaisir rencontrent rationnellement des conséquences extrêmement douloureuses.
image représentant une plume centrée
Il n'y a pas non plus de personne qui aime ou poursuit ou désire obtenir la douleur d'elle-même, parce que c'est de la douleur, mais parce que des circonstances se produisent parfois dans lesquelles le travail et la douleur peuvent lui procurer un grand plaisir. Pour prendre un exemple trivial, lequel d'entre nous entreprend jamais un exercice physique laborieux, sauf pour en tirer un avantage ? Mais qui a le droit de trouver à redire à un homme qui choisit de jouir d'un plaisir qui n'a pas de conséquences gênantes, ou qui évite une douleur qui ne produit aucun plaisir résultant ?
Mais je dois vous expliquer comment est née toute cette idée erronée de dénoncer le plaisir et louer la douleur et je vais vous donner un compte rendu complet du système, et exposer les enseignements réels du grand explorateur de la vérité.
image représentant une plume à droite
Le maître-constructeur de l'homme bonheur. Personne ne rejette, n'aime pas ou évite le plaisir lui-même, parce que c'est du plaisir, mais parce que ceux qui ne savent pas poursuivre le plaisir rencontrent rationnellement des conséquences extrêmement douloureuses. Il n'y a pas non plus de personne qui aime ou poursuit ou désire obtenir la douleur d'elle-même, parce que c'est de la douleur, mais parce que des circonstances se produisent parfois dans lesquelles le travail et la douleur peuvent lui procurer un grand plaisir. Pour prendre un exemple trivial, lequel d'entre nous entreprend jamais un exercice physique laborieux, sauf pour en tirer un avantage ? Mais qui a le droit de trouver à redire à un homme qui choisit de jouir d'un plaisir qui n'a pas de conséquences gênantes, ou qui évite une douleur qui ne produit aucun plaisir résultant ?
Mais je dois vous expliquer comment est née toute cette idée erronée de dénoncer le plaisir et louer la douleur et je vais vous donner un compte rendu complet du système, et exposer les enseignements réels du grand explorateur de la vérité, le maître-constructeur de l'homme bonheur. Personne ne rejette, n'aime pas ou évite le plaisir lui-même, parce que c'est du plaisir, mais parce que ceux qui ne savent pas poursuivre le plaisir rencontrent rationnellement des conséquences extrêmement douloureuses. Il n'y a pas non plus de personne qui aime ou poursuit ou désire obtenir la douleur d'elle-même, parce que c'est de la douleur, mais parce que des circonstances se produisent parfois dans lesquelles le travail et la douleur peuvent lui procurer un grand plaisir. Pour prendre un exemple trivial, lequel d'entre nous entreprend jamais un exercice physique laborieux, sauf pour en tirer un avantage ? Mais qui a le droit de trouver à redire à un homme qui choisit de jouir d'un plaisir qui n'a pas de conséquences gênantes, ou qui évite une douleur qui ne produit aucun plaisir résultant ?
image représentant une plume à babord
Mais je dois vous expliquer comment est née toute cette idée erronée de dénoncer le plaisir et louer la douleur et je vais vous donner un compte rendu complet du système, et exposer les enseignements réels du grand explorateur de la vérité, le maître-constructeur de l'homme bonheur. Personne ne rejette, n'aime pas ou évite le plaisir lui-même, parce que c'est du plaisir, mais parce que ceux qui ne savent pas poursuivre le plaisir rencontrent rationnellement des conséquences extrêmement douloureuses. Il n'y a pas non plus de personne qui aime ou poursuit ou désire obtenir la douleur d'elle-même, parce que c'est de la douleur, mais parce que des circonstances se produisent parfois dans lesquelles le travail et la douleur peuvent lui procurer un grand plaisir. Pour prendre un exemple trivial, lequel d'entre nous entreprend jamais un exercice physique laborieux, sauf pour en tirer un avantage ? Mais qui a le droit de trouver à redire à un homme qui choisit de jouir d'un plaisir qui n'a pas de conséquences gênantes, ou qui évite une douleur qui ne produit aucun plaisir résultant ?
Mais je dois vous expliquer comment est née toute cette idée erronée de dénoncer le plaisir et louer la douleur et je vais vous donner un compte rendu complet du système, et exposer les enseignements réels du grand explorateur de la vérité, le maître-constructeur de l'homme bonheur. Personne ne rejette, n'aime pas ou évite le plaisir lui-même, parce que c'est du plaisir, mais parce que ceux qui ne savent pas poursuivre le plaisir rencontrent rationnellement des conséquences extrêmement douloureuses. Il n'y a pas non plus de personne qui aime ou poursuit ou désire obtenir la douleur d'elle-même, parce que c'est de la douleur, mais parce que des circonstances se produisent parfois dans lesquelles le travail et la douleur peuvent lui procurer un grand plaisir. Pour prendre un exemple trivial, lequel d'entre nous entreprend jamais un exercice physique laborieux, sauf pour en tirer un avantage ? Mais qui a le droit de trouver à redire à un homme qui choisit de jouir d'un plaisir qui n'a pas de conséquences gênantes, ou qui évite une douleur qui ne produit aucun plaisir résultant ?
Mais je dois vous expliquer comment est née toute cette idée erronée de dénoncer le plaisir et louer la douleur et je vais vous donner un compte rendu complet du système, et exposer les enseignements réels du grand explorateur de la vérité, le maître-constructeur de l'homme bonheur. Personne ne rejette, n'aime pas ou évite le plaisir lui-même, parce que c'est du plaisir, mais parce que ceux qui ne savent pas poursuivre le plaisir rencontrent rationnellement des conséquences extrêmement douloureuses. Il n'y a pas non plus de personne qui aime ou poursuit ou désire obtenir la douleur d'elle-même, parce que c'est de la douleur, mais parce que des circonstances se produisent parfois dans lesquelles le travail et la douleur peuvent lui procurer un grand plaisir. Pour prendre un exemple trivial, lequel d'entre nous entreprend jamais un exercice physique laborieux, sauf pour en tirer un avantage ? Mais qui a le droit de trouver à redire à un homme qui choisit de jouir d'un plaisir qui n'a pas de conséquences gênantes, ou qui évite une douleur qui ne produit aucun plaisir résultant ?
Mais je dois vous expliquer comment est née toute cette idée erronée de dénoncer le plaisir et louer la douleur et je vais vous donner un compte rendu complet du système, et exposer les enseignements réels du grand explorateur de la vérité, le maître-constructeur de l'homme bonheur. Personne ne rejette, n'aime pas ou évite le plaisir lui-même, parce que c'est du plaisir, mais parce que ceux qui ne savent pas poursuivre le plaisir rencontrent rationnellement des conséquences extrêmement douloureuses. Il n'y a pas non plus de personne qui aime ou poursuit ou désire obtenir la douleur d'elle-même, parce que c'est de la douleur, mais parce que des circonstances se produisent parfois dans lesquelles le travail et la douleur peuvent lui procurer un grand plaisir. Pour prendre un exemple trivial, lequel d'entre nous entreprend jamais un exercice physique laborieux, sauf pour en tirer un avantage ? Mais qui a le droit de trouver à redire à un homme qui choisit de jouir d'un plaisir qui n'a pas de conséquences gênantes, ou qui évite une douleur qui ne produit aucun plaisir résultant ?
image représentant une plume à tribord

Mais je dois vous expliquer comment est née toute cette idée erronée de dénoncer le plaisir et louer la douleur et je vais vous donner un compte rendu complet du système, et exposer les enseignements réels du grand explorateur de la vérité, le maître-constructeur de l'homme bonheur. Personne ne rejette, n'aime pas ou évite le plaisir lui-même, parce que c'est du plaisir, mais parce que ceux qui ne savent pas poursuivre le plaisir rencontrent rationnellement des conséquences extrêmement douloureuses. Il n'y a pas non plus de personne qui aime ou poursuit ou désire obtenir la douleur d'elle-même, parce que c'est de la douleur, mais parce que des circonstances se produisent parfois dans lesquelles le travail et la douleur peuvent lui procurer un grand plaisir. Pour prendre un exemple trivial, lequel d'entre nous entreprend jamais un exercice physique laborieux, sauf pour en tirer un avantage ? Mais qui a le droit de trouver à redire à un homme qui choisit de jouir d'un plaisir qui n'a pas de conséquences gênantes, ou qui évite une douleur qui ne produit aucun plaisir résultant ?
Mais je dois vous expliquer comment est née toute cette idée erronée de dénoncer le plaisir et louer la douleur et je vais vous donner un compte rendu complet du système, et exposer les enseignements réels du grand explorateur de la vérité, le maître-constructeur de l'homme bonheur. Personne ne rejette, n'aime pas ou évite le plaisir lui-même, parce que c'est du plaisir, mais parce que ceux qui ne savent pas poursuivre le plaisir rencontrent rationnellement des conséquences extrêmement douloureuses. Il n'y a pas non plus de personne qui aime ou poursuit ou désire obtenir la douleur d'elle-même, parce que c'est de la douleur, mais parce que des circonstances se produisent parfois dans lesquelles le travail et la douleur peuvent lui procurer un grand plaisir. Pour prendre un exemple trivial, lequel d'entre nous entreprend jamais un exercice physique laborieux, sauf pour en tirer un avantage ? Mais qui a le droit de trouver à redire à un homme qui choisit de jouir d'un plaisir qui n'a pas de conséquences gênantes, ou qui évite une douleur qui ne produit aucun plaisir résultant ?
Mais je dois vous expliquer comment est née toute cette idée erronée de dénoncer le plaisir et louer la douleur et je vais vous donner un compte rendu complet du système, et exposer les enseignements réels du grand explorateur de la vérité, le maître-constructeur de l'homme bonheur. Personne ne rejette, n'aime pas ou évite le plaisir lui-même, parce que c'est du plaisir, mais parce que ceux qui ne savent pas poursuivre le plaisir rencontrent rationnellement des conséquences extrêmement douloureuses. Il n'y a pas non plus de personne qui aime ou poursuit ou désire obtenir la douleur d'elle-même, parce que c'est de la douleur, mais parce que des circonstances se produisent parfois dans lesquelles le travail et la douleur peuvent lui procurer un grand plaisir. Pour prendre un exemple trivial, lequel d'entre nous entreprend jamais un exercice physique laborieux, sauf pour en tirer un avantage ? Mais qui a le droit de trouver à redire à un homme qui choisit de jouir d'un plaisir qui n'a pas de conséquences gênantes, ou qui évite une douleur qui ne produit aucun plaisir résultant ?

Markdown : Liste

Syntaxe : le caractère plus pour une liste non-ordonnée (•, •, •), ou moins+ pour une liste ordonnée (1, 2, 3), en début de ligne, suivi d'un eventuel "titre :"
Formatage : encadre avec la balise<ul> ou<ol>, et encadre de<span> l'eventuel titre.
Exemple :
− titre : texte
− Action
− Aventure
− Fantastique
−− Bande−dessinée
−− Roman
−−− Stephen King
−−− H.P. Lovecarft
−−− Edgar Allan Poe
−−− J.R.R Tolkien
−− Film
−− Série
− Policier
− Suspense
+ Mercure
+ Vénus
+ Terre
++ Lune
+ Mars
++ Phobos
++ Déimos
+ Jupiter
++ Io
++ Europe
++ Ganymède
++ Callisto
+− 79 satellites
+ Saturne
+− 200 satellites
+ Uranus
+− 27 satellites
+ Neptune
+− 14 satellites
Donnera :
  • titre : texte
  • Action
  • Aventure
  • Fantastique
    • Bande-dessinée
    • Roman
  • Stephen King
- H.P. Lovecarft
  • Edgar Allan Poe
- J.R.R Tolkien
    • Film
    • Série
  • Policier
  • Suspense
  1. Mercure
  2. Vénus
  3. Terre
    1. Lune
  4. Mars
    1. Phobos
    2. Déimos
  5. Jupiter
    1. Io
    2. Europe
    3. Ganymède
    4. Callisto
    • 79 satellites
  6. Saturne
    • 200 satellites
  7. Uranus
    • 27 satellites
  8. Neptune
    • 14 satellites

Markdown : Code

Syntaxe : un caractère guillemet oblique` Formatage : encadre soit de balise<code> s'il tient sur une ligne, soit<pre> s'il tient sur plusieurs lignes
Exemple :
Exemple de `code` sur une seule ligne et
`
Exemple d'un bloc de code
sur plusieurs lignes.
`
Donnera :
Exemple decode sur une seule ligne et
Exemple d'un bloc de code
sur plusieurs lignes.
Les blocs de code ont leurs propres formatage de couleur, pour une meilleur lisibilité :
* des balises HTML (<cite><var></var></cite>).
* des mot-clés tel que if, return (<cite>)
* des $variables et Classe:: (<var>)
* des "constantes" (<samp>)
* des // commentaires (<abbr>)
Exemple :
<button name="ok"> Action </button>
// commentaire
function fonction($parametre) {
while ($parametre > 0) {
if ($parametre)
$parametre −= 1;
Template::render("<div>texte constant</div>");
}
return;
}

Markdown : Citation

Syntaxe : un ou plusieurs caractères> en début de ligne
Formatage : encadre avec la balise<blockquote> Exemple :
> Exemple de citation
>> imbriqué sur
>>> plusieurs niveaux
>> et sur
>> plusieurs lignes
> fin de la 1ère citation
> ceci n'est pas une citation
Donnera :
Exemple de citation
imbriqué sur
plusieurs niveaux
et sur
plusieurs lignes
fin de la 1ère citation
> ceci n'est pas une citation

Markdown : Sommaire

Syntaxe : !{Table of Contents} ou !{Table of Contents(de 1 à 6)}
Formatage : construit dynamiquement un sommaire, encadré de<nav>.
Exemple :
!{Table of Contents(5)}
Donnera le sommaire de cette page en affichant que les 4 premiers niveaux.

Markdown : Social

Syntaxe : ?[social](lien facebook)
Formatage : remplace par un lien rattaché à un icône propre au réseau social spécifié.
Exemple :
?[facebook](mon facebook)	?[skype](mon skype)	?[blog](url vers mon blog).
Donnera :
Facebook
mon skype
Blog
.

PHP : Log et report

Plume permet d'enregistrer automatiquement toutes les requêtes SQL et erreurs éventuelles, dans un fichier.log journalisé et archivé.zip.
Ce fichier se trouve dans un dossier sur le serveur, à créer pour activer ce mécanisme (voirPLUME_LOG), et nommé base_YYYY-MM-DD.log
L'enregistrement s'effectue uniquement si durant la requête SQL :
  1. Une erreur SQL survient
  2. Un warning SQL survient
  3. Le temps d'exécution de la requête SQL dépasse un délai bien précis, spécifié parBase.trace_log().
Ce délai pouvant être modifié en passant une autre valeur flottant, exprimée en seconde précis à la microseconde (par défaut, une demie seconde).
Exemplestrace_log(0.5) pour une demie-seconde, outrace_log(0) pour loguer toutes les requêtes.
De plus, en mode développement, une trace de l'erreur ou warning est affichée sur la console du navigateur.
Le format du fichier est une ligne = une trace, avec les colonnes suivantes, découpées par des tabulations :
  1. Date du jour : sous forme YYMMDD.
  2. Moment : sous forme HHMMSS.
  3. Utilisateur : sous forme d'un nom (extrait de$_SESSION['PHP_AUTH_USER']) suivi du caractère arobas (@) suivi de l'adresse IP de l'utilisateur.
  4. Temps : valeur flottante représentant le temps pris par la requête, en seconde précis à la microseconde.
  5. Mémoire : valeur flottante représentant la mémoire utilisés actuellement par PHP en giga octet (voirmemory_get_usage()).
  6. Résultat :
    • Soit le résultat, si un seul champ.
    • Soit le caractère dièse (#), suivi du nombre de champ ou enregistrement retourné.
    • Soit une erreur, sous forme d'un nombre positif, suivi du symbole égale (=), suivi du texte de l'erreur.
    • Soit un warning, sous forme d'un nombre négatif, suivi du symbole égale (=), suivi du texte du warning.
  7. SQL : la reqête SQL.
  8. URL : la requête URL côté navigateur.
  9. Programme : la pile de fonctions PHP, séparée par des tabulations, décomposée en différentes valeurs, séparée elles-mêmes par deux-petits-points (:) :
    • Le niveau de la pile, le 1 étant la première a avoir été lancée.
    • Le nom du fichier PHP.
    • Le numéro de la ligne dans ce fichier.
    • Le nom de la fonction PHP.
    • Les paramètres, entre parenthèse, passées à cette fonction.
Exemple :
yymmdd hhmmss	user@ip.address	time	memory	result|#lines|number=error	request	url	sub:php:line:function(args)
201231 123059 enzo@78.233.207.42 0.0006 0.0010 #18 select * from ma_table where id = 2 && enabled = 1 https://mon−site.com/rubrique/?event=235346 3:/library.php:1041:Base.select($sql, 2) 2:/lib/plume.php:688:ajax_ma_fonction('texte', 2942, 42) 1:/index.php:1485:Template.ajax()
Dans cet exemple, une requête a été faite avec succès, ce 31/12/2020 à 12h 30m 59s par "enzo" d'IP 78.233.207.42, qui a pris 0.0006s, pour 1Mo, et a renvoyé 18 enregistrements, avec la requête SQL : https://mon-site.com/rubrique/?event=235346.
Il a été lancé par la méthodeselect de la classeBase depuis la ligne 1041 du programme/library.php, lui-même, appelé parajax_ma_fonction en ligne 688 de/lib/plume.php, lui-même parTemplate.ajax appelé en ligne 1485 de/index.php.
Une synthèse des erreurs et warnings peut être envoyé automatiquement par mail journalièrement, voirPLUME_MAIL_REPORT.

Installation


Suivre les 4 étapes suivantes :
  1. Télécharger les fichiers : com.dnada.plume.zip (44Ko).
  2. Les placer sur votre serveur
  3. Les appeler côté page HTML (de préférence dans un fichier entete.html qui sera appelé dans toutes vos pages, par exemple, via un template de type {header}) :
    <!DOCTYPE html>
    <html lang="fr">
    <head>

    <link href="/lib/plume.css" rel="stylesheet"/>
    <script src="/lib/plume.js"></script>

    </head>
    <body>

    {.mon_template() .}

    </body>
    </html>
  4. et dans vos sources PHP (de préférence dans un fichier.php, qui sera inclus via un require())) :
    <?php
    define('PLUME_MAIL_REPORT', "report@ma−societe.com");
    define('PLUME_TRACE_IP', '77.0.1.2');
    require('plume.php');
    $base = new Base("mon−client");
    Template::ajax();
    Template::display();

    function template_mon_template($pattern) {

    }
    // fonction appelé en JS : $.ajax.ma_methode(par1).then(function(resultat) { }
    function ajax_ma_methode($par1 = 'default') {

    }
Et c'est tout !
Ah Si ! Dites nous ce que vous pensez de cette librairie, faites nous remontez vos remarques, bug (pas possible), améliorations (possible), ou simplement un petit merci, c'est par ici :

Copyright

Cette librairie Plume est l'œuvre et la propriété de DNada.
Même si gratuite, cette librairie ne peut être utilisée partiellement ou intégralement dans des développements rémunérées, sauf par consentement écrite de la société DNada. (Mais bon, aucune crainte à avoir, aucun refus n'a été fait jusqu'à présent).
Pour toutes demandes ou questions, merci de nous contacter :