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 </h1>
<ol>
{*satellite()
<li></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 </h1>
<ol>
{+satellite()
<li>

<ul>
{.information()
<li>: </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()

sinon oksinon ko
ou alors ok ou alors 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:, 2:, 3:, 4: +}</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="0" max="9" rule="[0, 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 : popup menu

Ajout d'un popup menu associé à un élément HTML (input, div, …) , avec recherche saisissable (et beaucoup d'options comportementales possibles).
Les données sont de type statique :
<input $popup="[ value1, … ]"/>
<div $popup="{ key1: value1, … }">légende</div>
Où sous forme de requête vers le serveur :
<span $popup="mon_popup">mon popup</span>
<?php
function ajax_mon_popup($value) {
return ['lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi', '', 'samedi', 'dimanche'];
}
?>
Où gérées dynamiquement en JS :
<span $popup="un_popup">un popup</span>
<script>
var un_popup = {
load: function(trigger, name, callback) {
ajax.mon_popup(callback);
},
open: function(trigger, target, name, replacing) {
},
change: function(key, value) {
},
close: function(key, value, changed, trigger, name) {
},
option: {
contiguous: false, // si false, la recherche se fait sur chaque caractère, si true, elle se fait en continue
full: false, // si true, ne fait aucune recherche
closeness: false, // si true, l'effacement du dernier caractère, referme le menu
shifting: false, // si true, change la valeur lors des sélections, si false: ne change que si validé
free: false, // si true, saisie libre
essential: false, // si true, ne peut rester vide (toujours false si obstruct == true)
obstruct: false, // si true, vide si non−choisi (toujours false si free == true)
event: 13, // ex: change: true, keyup: 13, click: true
}
};
</script>

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. 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. par son tagName :$('TAGNAME')
  3. par son attribut de classe :$('.class')
  4. par son attribut type :$('?type')
  5. par son attribut name :$('@name')
  6. par son attribut name commençant par :$('@^begin')
  7. par son attribut name finissant par :$('@$end')
  8. par son attribut name contenant :$('@*word')
  9. en remontant à son parent :$('^') ou à son 5ème parent :$('^5') à son parent récursif de tagName, type, name, … :$('^…')
  10. en cherchant l'élément de même niveau situé après lui :$('+') ou 5ème élément :$('+5') ou élément de tagName, … =$('+…')
  11. 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 2 éléments HTML, d'id = « un » et « trois », 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>
</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 des attributs lors de sa création :
create(tag, {id: "xxx", style: {backgroundColor: 'yellow'}})
On peut spécifier en 3ème paramètre, sa position :
create(tag, {});			// soit en dernier élément si omis ou false.
create(tag, {}, element); // avant un élément
create(tag, {}, true); // en 1er
create(tag, {}, x); // en xème élément

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 name"valeur" à name, class, …
element.att(source); // clone les attributs de source à element
element.att('name,', null); // efface name
[tag1, tag2].att(…); // att() appliqué sur le tableau

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 : $.position

Permet de lire les dimensions et positions absolues d'un élément
var pos = element.position();			// renvoi les informations d'éléments
var pos = element.position(content); // renvoi à partir de content
var pos = element.position().left; // position gauche
Renvoi l'objet :
{
top, // position haute absolue visible (comprenant la bordure)
left, // position gauche absolue visible
bottom, // position basse absolue visible
right, // position droite absolue visible

height, // hauteur visible (bordure et padding inclus)
width, // largeur visible

head, // top − marginTop
foot, // bottom + marginBottom

leading, // left − marginLeft
trailing, // right + marginRight

tall, // heigth + padding + margin
wide, // width + padding + margin

padding: {top, left, bottom, right},
border: {top, left, bottom, right},
margin: {top, left, bottom, right}
}
notice des valeurs

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() {

});
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, et d'indiquer que ces flags sont opérationnels en appelant$.start en ne passant que le flag.
// exécute une fonction en asynchrone, suite à un retour du serveur PHP
ajax.differe_indispensable().then(function() {
// traitement
$.start("indispensable"); // dévérouille 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 "indispensable" sera dévérouillé, donc après le retour de l'ajax "differe_indispensable"
$.start("indispensable", function() {
});
// fonction qui ne sera (jamais) lancée que lorsque la page sera prête *et* que les 2 flags "indispensable" et "obligatoire" seront dévérouillés
$.start("indispensable obligatoire", function() {
});
// alors que cette fonction sera appelée dès que la page sera prête, sans tenir compte d'aucun flag
$.start(function() {
});

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) {
// …
});
});
});

JS : extension

Plume ajoute des fonctionnalités aux objets existants.

Function.timeout

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.timeout(5000, 42); // affichera dans 5s : <window>, 42
ma_fonction.timeout($('BODY'), 3000, 42); // affichera dans 3s : <body>, 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. 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.

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();
On peut employer la classe$.ajax pour lancer des requêtes asynchrones, avec une promesse en retour :
// 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
$.ajax('post',
"/request.php", {post1: 42}, {completion1: 2}).then(function(response, completion) {
// traitement de la réponse
});
Et interception et réponse côté PHP :
<?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 … …
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]
Les autres commandes servent à :

Base : read

On peut lire un enregistrement avecbase−>read().
read(1)
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)
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().
select(1)
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)
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 = , 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. La clausewhere ou le "id".
Cela correspond plus ou moins à faire unif (read("where_ou_id")) update("table", "where_ou_id"); else insert("table");

Base : Requêtes

On peut également faire des requêtes particulières.
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");
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" ]
sql()
Il est possible de produire toutes autres requêtes SQL spécifique (mais sans protection, ni formatage).
$base−>sql('ma requete sql');
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 =  and nom =  and ville in ", "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 } and level in ", 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).

À 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és 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és mais juste encadré d'un espace souligné_)

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.

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(4)}
Donnera le sommaire de cette page en affichant que les 4 premiers niveaux.

Markdown : Social

Syntaxe : ?social 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 1.7.3.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 !

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 de la société DNada.
Pour toutes demandes ou questions, merci de nous contacter : contact@d-nada.com