SPIP uitbreiden

Als je de functionaliteit van SPIP wilt uitbreiden en daaraan wilt bijdragen, is het belangrijk het volgende te onthouden: het is een hulpmiddel dat al door duizenden mensen wordt gebruikt, het heeft een toekomst, maar ook een verleden, en het is een collectief werk. Sommige keuzes zijn gemaakt, niet altijd naar tevredenheid, maar ze geven een samenhang aan de gehele code en vergemakkelijken een verdere ontwikkeling. Wanneer een eerdere keuze echt onverenigbaar is met interessante, nieuw te integreren technologie, zullen delen worden herschreven, maar dit kan enkele maanden duren, resulterend in een verlengde inconsistentie. Een objectief is dan ook zoveel mogelijk dergelijke besluiten te vermijden en te accepteren dat een nieuwe regel wordt overtreden zolang het herschrijven van dit deel van de code nog niet is vervolledigd. Het is ook duidelijk dat de stijl van programmen van plaats tot plaats kan verschillen, maar er wordt gebouwd aan een gemeenschappelijke identiteit die voortdurend in wording is, van een nuttig hulpmiddel dat het resultaat is van een levende, vrije gemeenschap.

Organisatie van de bronnen

Het gedrag van SPIP kan worden aangepast zonder de broncode te wijzigen, dankzij een zoekpad dat wordt gedefineerd met de constante SPIP_PATH. Elke uitbreiding van SPIP moet dan ook zodanig werken dat een integratietest kan worden uitgevoerd door een simpele aanpassing van dit zoekpad, waarbij de oorspronkelijk broncode SPIP onaangetast blijft. Wanneer je ervan overtuigd bent dat een bepaalde aanpassing niet op deze wijze kan worden gedaan, meld je dat in de discussiegroep spip-dev.

Bijdragen aan SPIP worden beschreven in een artikel op Spip-Contrib en de software wordt als bijlage in het artikel opgenomen, of liever op spip-zone wat een server Subversion heeft die wordt aangestuurd door Trac.

Om SPIP zo uit te breiden, moet je de organisatie en rol van mappen met bestanden goed begrijpen. Dit artikel probeert dat uit te leggen.

De root van een distributie van SPIP bevat:

-  een bestand spip.php (met alias index.php dat compatibiliteit met oudere versies garandeert), laadt het inititalisatie-programma ecrire/inc_version.php en geeft direct de hand aan het hoofdscript ecrire/public.php;

-  een map ecrire bevat uitsluitend door de server uitvoerbare bestanden (PHP en SQL);

-  één of meer mappen (afhankelijk van de versie) bevatten bestanden die door de client worden geïnterpreteerd (HTML, Javascript, StyleSheets, afbeeldingen) alsmede de layoutpatronen voor de verschillende pagina’s, de skeletten. Deze skeletten worden tweemaal geïnterpreteerd: het zijn bestanden die zijn opgebouwd in een MIME formaat, aangevuld met enkele SPIP richtlijnen die op de server worden verwerkt voordat ze in een pure MIME-vorm naar de client worden gestuurd (meestal in HTML, maaar ook in RSS, SVG, ICS, enz.).

-  bij de installatie worden vier lege mappen aangemaakt, die tijdelijke en permanente bestanden zullen gaan bevatten die noodzakelijk zijn voor het functioneren van de site.

Rol van de mappen prive en squelettes-dist en hun submappen

Deze mappen bevatten de bestanden die de presentatie van SPIP bepalen, respectievelijk voor het privé gedeelte en het publieke deel. Ze bevatten diverse submappen waarvan we hier de betekenis geven:

maprol
/ bevat skeletten. Hun naam eindigt om historische redenen op .html, maar dat bepaalt niet hun inhoud. Het volstaat om hun naam (zonder extensie) aan de parameter page aan de URL van een SPIP-site toe te voegen om het skelet uit te voeren.
De map bevat ook stylesheets (met extensie .css) die de grafische standaardweergave bepalen
formulaires/ bevat de HTML van dynamische bakens, skeletten van formulieren waarvan de PHP-code in map ecrire/balise is geplaatst
icones_barre/ bevat de afbeeldingen voor de typografische knoppenbalk
images/ afbeeldingen van het privé gedeelte
javascript/ een JavaScript map (jQuery, typografische balk, ...)
modeles/ skeletten die met #MODELE of met opmaakcodes in andere skeletten of in tekst kunnen worden opgenomen: <article6|modele>
polices/ te gebruiken lettertypes voor typografische afbeeldingen
vignettes/ miniatuurafbeeldingen voor de verschillende types documenten die aan een artikel kunnen worden gekoppeld

Rol van map ecrire en zijn submappen

De map ecrire bevat meerdere submappen met PHP bestanden die functies definiëren en soms initialiseren. De bestanden in de map zelf bevat de belangrijkste om het gedrag van SPIP te leren begrijpen en zo aan SPIP te kunnen bijdragen. Het gaat om inc_version.php, public.php en index.php.

Het bestand ecrire/inc_version.php initialiseert de constanten en de globale variabelen die benodigd zijn voor het functioneren van SPIP, met name die welke ervoor zorgen dat de verschillende platforms worden ondersteund. Eén van de eerste dingen die het doet is het uitvoeren van het bestand inc/utils.php, waarin de onmisbare functies van SPIP zijn opgenomen, een additioneel bestand mes_options.php wat de initilalisatie kan beïnvloeden zonder dat het standaardbestand inc_version.php hoeft te worden aangepast. In het bijzonder is het in dit persoonlijke bestand mogelijk om de functie spip_initialisation aan te sturen en zo meerdere gegevensmappen aan te geven voor meerder SPIP-sites die een gezamenlijke broncode gebruiken (een tweede standaard aanroepen van deze functie verderop in inc_version.php wordt hierdoor automatisch geneutraliseerd). Een ander voor SPIP onmisbare functie is find_in_path, dat het zoekpad bepaalt, alsmede include_spip wat afhangt van find_in_path, en charger_fonction wat afhangt van include_spip. Alle SPIP bestanden worden door de laatste twee functie geladen.

Het bestand ecrire/public.php, opgeroepen door spip.php heeft als belangrijkste taak het aanleveren van de pagina’s van de publieke site wanneer de HTTP query (eventueel na herschrijven) de parameter page bevat. Dit script past het skelet toe dat de naam heeft van de waarde van de parameter. Het stuurt de HTTP headings en de gevonden inhoud, beheert eventuele fouten en start eventuele achtergrondtaken met behulp van de functie cron. Bijdragen aan de publieke site van SPIP bestaat dus uit het aanleveren van nieuwe skeletten, met eventueel aangepaste stylesheets en afbeeldingen.

De andere rol van ecrire/public.php is wanneer de HTTP query het argument action bevat. Het past dan de functie charger_fonction toe met de waarde v van parameter action. Deze toepassing heeft tot doel het bestand met deze naam uit de map action te laden, waarvan de hoofdfunctie action_v_dist wordt uitgevoerd. Deze scripts schrijven gegevens weg (naar de database of naar een bestand) en sturen normaal geen resultaat terug, wat problemen met de paginaweergave voorkomt.

Het bestand index.php is het centrale toegangspunt tot de formulieren van het privé gedeelte. Het zorgt voor de identificaatie van de gebruiker, initialiseert zijn persoonlijke profiel en past de functie charger_fonction met waarde v van de parameter exec toe. Deze toepassing heeft tot doel het bestand met deze naam uit de map exec te laden, waarvan de hoofdfunctie exec_v_dist wordt gestart. Dit zal een complete stroom data resulteren, inclusie een HTTP heading. Het is dus mogelijk om SPIP eenvoudig een aanvullend PHP bestand in een submap met de naam exec uit te laten voeren, op voorwaarde dat deze submap is opgenomen in het zoekpad (in SPIP_PATH).

De map exec bevat uitsluitend bestanden die functies definiëren die direct met de URL-parameter exec kunnen worden aangeroepen. De PHP code van deze bestanden mag nooit gegevens naar de database schrijven (enkele uitzonderingen verdwijnen geleidelijk). Omgekeerd lezen deze bestanden wel uit de database om de rechten van de gebruiker te bepalen en te beslissen wat hem mag of moet worden getoond. Wanneer je SPIP bekijkt volgens het MVC-model [1] vervullen de bestanden in exec de rol van Controller. Gebruik je het ontwerppatroon (Print(Eval(Read))) van Lisp, dan is het het onderdeel Read. Uiteindelijk zal de map een verzameling skeletten bevatten, iets waaraan je moet denken bij een nieuwe bijdrage aan SPIP.

De map action, die we al eerder noemden, bevat hoofdzakelijk de scripts die naar de database schrijven. Bekijk je SPIP vanuit ontwerppatroon MVC, dan vervullen de bestanden in map action de rol van Model en in (Print(Eval(Read))) die van Eval. In dit gebeid bestaat bijdragen aan SPIP uit het schrijven van scripts en die laten uitvoeren aan de hand van formulieren gebouwd met functie generer_action_auteur die de veiligheid van die script waarborgen en de functie securiser_action gebruiken om te controleren of de gebruiker wel voldoende rechten heeft. Deze architectuur zorgt ervoor dat uitsluitend bij scripts die gegevens aanpassen naar de rechten van de gebruiker gekeken wordt: in plaats van het opnieuw berekenen van alle rechten, vergelijkt het eenvoudig of de sleutel in de argumenten dezelfde is als die welke werd berekend uit de identiteit van de aanvrager en een regelmatig veranderende waarde. Het retourneert normaal geen resultaat, dus geen HTML of functie echo (Op enkele uitzonderingen na). Ze worden wel vaak met een HTTP redirect parameter gebruikt, die vervolgens door public.php wordt verwerkt, anders stuurt deze een HTTP status 204. In het geval dat formulieren gebruik maken van de functie ajax_action_auteur, dat leidt de redirect naar het gelijknamige script in de map exec. Dit tweede script zal op zijn beurt weer een script uit de map inc gebruiken waarvan het resultaat via de functie ajax_retour Naar de client wordt teruggestuurd. Dankzij deze structuur kan SPIP op eenvoudige wijze in AJAX modus worden gebruikt.

De map inc, de best gevulde, bevat hoofdzakelijk de functies die de pagina’s opbouwen van het privé gedeelte. Die functies zijn uiteindelijk de filters die gebruikt worden door de bestanden in exec als het om skeletten gaat. Wil je SPIP beschouwen volgens de MVC architectuur, dan vervullen de bestanden in inc de rol View. Bekijk je het door de ogen van (Print(Eval(Read))) dan gaat het om het onderdeel Print. Toch bevat de map diverse functie die tot de categorie Control behoren en dat wordt aangepast. De meeste bestanden in inc worden geladen door middel charger_fonction en het uiteindelijk doel is dat dit voor alle bestanden geldt. Geen enkele functie mag nog echo gebruiken. Lever je een bijdrage aan SPIP dan wordt van je verwaxcht hiermee rekening te houden.

De map install bestaan uitsluitend uit functies die nodig zijn voor de installatie van SPIP. Elke stap kan worden geladen of vervolledigd door een andere. De hoofdfunctie van exec/install.php gebruikt deze map op dezelfde wijze als ecrire/index.php doet met de map exec.

De map urls bevat bestanden die ieder eenzelfde set functies voor een andere schrijwijze van de URL’s definiëren. Het gaat om functies die vanuit een numerieke index in een databasetabel een beter lees- en schrijfbaar adres opbouwen dan het aanroepen van het PHP-script wat door de HTTP server wordt gedaan. Het volstaat om een bestand aan deze map toe te voegen voor een nieuwe set, waarvan de naam zal worden weergegeven in het configuratiepaneel van het privé gedeelte wat de URL’s bepaalt.

De map lang bevat uitsluitend taalbestanden. Het zijn tabellen die de vertaling aangeven van alle argumenten die de functie _T, gedefinieerd in inc/utils.php kan ontvangen. De bestanden worden uitsluitend geladen door de functies van inc/lang.php. De referentiebestanden *fr* vertalen en ze een naam geven volgens de juiste conventie voegt een nieuwe taal toe.

De map charset bevat ook uitsluitend gegevenstabellen waarmee een tekenset (UTF, ISO, ASCII, HTML-entiteiten, enz.) wordt vastgelegd. Ze worden uitsluitend gebruikt door de functies inc/charsets.php. Je voegt een neiuwe tekenset toe door deze aan de map toe te voegen, maar SPIP heeft alle gangbare al in zijn basispakket opgenomen.

De map base bevat de interface-functies die SPIP kan oproepen voor de interactie tussen PHP en de SQL-server(s). In het bijzonder bevat het generieke bestand abstract_sql.php die voor deze dialoog zijn bedoeld. Om redenen van portabiliteit zijn het niet de basisfuncties van PHP die hiervoor worden gebruikt. Indeze map mag dan ook geen enkele MIME code voorkomen.

De map req bevat de effectieve doorgave van de virtuele SQL-server naar de werkelijkse servers (MySQL, PG) en aanverwante (SQLite).

De map balise bevat de PHP-bestanden die bij dynamische bakens van SPIP horen. Hun naam is een homoniem van het skelet squelettes-dist/formulaires. Wil je bijvoorbeeld de publieke site van SPIP aanvullen met een formulier F dan maak je een bestand F.html ergens in SPIP_PATH en een bestand F.php in de submap balise van zijn SPIP_PATH. De rol van dit PHP-bestand is het ontvangen van de op het formulier ingevoerde gegevens en de eventuele nieuwe weergave van het formulier bij onvolkomenheden. Het is zonder meer de meest ingewikkelde soort uitbeiding, want het onderliggende proces vereist twee uitvoeringsstappen in PHP waarvan het belangrijk is de rollen goed te begrijpen.

In de map public staan de compilers van de skeletten. Het is een gecompliceerd stuk programmatuur, maar er bestaat een interface voor het programmeren die de mogelijkheid biedt de compiler uit te breiden zonder alle details te hoeven kennen. De meest gedetailleerde uitleg hierover vind je (in het Frans) op spip-contrib.net.

De map lib bevat desubmappen van de bibliotheken die buiten SPIP werden ontwikkeld, maar die voor het gebruik vereist zijn. Systematisch wordt de bibliotheek safehtml meegeleverd die elementaire beveiligingen biedt. Om hieraan bij te dragen kijk je op de site over dit onderwerp.

Een laatste punt: de meeste bestanden in SPIP worden toegepast via charger_fonction wat een bestand laadt en zijn homonieme functie oproept (als die bestaat). Dit houdt in dat de naam van een PHP-bestand moet zijn opgebouwd uit tekens die voor de naam van een PHP-functie zijn toegestaan: je vermijdt dus het minteken, de punt, enz.

Programmeerregels

SPIP startte in het tijdperk waarin PHP de HTTP-parameters automatisch in globale variabelen omzette. Dit soort suïcidale programmeerwijze werd in PHP4 verbannen. SPIP maakte een parallelle ontwikkeling mee, maar dan met enige vertraging. De huidige code voldoet nog niet volledig aan de regels die moeten worden gevolgd, maar aan ontwikkelaars wordt gevraagd de huidige regels te respecteren en niet te wachten tot SPIP zelf helemaal bij is. Bekijk eens goed het bestand ecrire/articles.php wat waarschijnlijk het meest aan de te volgen regels voldoet.

-  Geef de voorkeur aan het programmeren in functies. De filosofie van vrije software is het te kunnen toepassen in zoveel mogelijk verschillende contexten en als gevolg moet de code zijn geschreven vanuit een optiek van hergebruik buiten zijn oorspronkelijke context. Programmeren per functie mag geen enkele globale variabele bevatten en geen gebruik maken van een echo of print wat ongewenste (neven)effecten voorkomt.

-  Voorkom zoveel mogelijk het gebruik van globale variabelen. Ze zijn de oorzaak van vele veiligheidsproblemen en maken het hergebruik onmogelijk. Gebruik een van de volgende alternatieven:

— een constante, wat als voordeel heeft dat de lezer herkent dat deze waarde in het gehele script niet zal veranderen;

— een statische variabele, wat als voordeel heeft dat de lezer begrijpt dat het om een variabele gaat met een lange levensduur, maar uitsluitend bedoeld is voor de functie die hem declareert.

-  Schrijf code die in de modus error_reporting(E_ALL) geen enkele foutmelding of waarschuwing geeft. Is het werkelijk nodig om code te schrijven die hieraan niet voldoet, gebruik dan de kunstgreep @ om zoveel mogelijk het probleemgebied in te perken en voorzie een foutmelding in de log met behulp van de functie spip_log.

-  Voeg commentaar toe over de context, niet over de tekst. Het heeft geen zin de benaming van functies en variabelen uit te leggen en helemaal niet die van standaard PHP-functies: commentaar als lus op de tabel met waardes voor een foreach dragen niet bij aan de leesbaarheid van het bestand, wel aan de grootte. Wat wel zinvol is, is om het type van argument aan te duiden (PHP is nou eenmaal een dynamische, verleidelijke programmeertaal) en hun veronderstelde eigenschap aan het begin van de functie (bijvoorbeeld: niet null). Wanneer een ingewikkelde bug wordt gecorrigeerd of erop wordt geanticipeerd, geef dan aan waarom de oorspronkelijke code onjuist was om toekomstige herhaling van die fouten tegen te gaan. Tenslotte (aangezien SPIP in het Frans ontwikkeld werd en wordt) raden we aan termen te vermijden die niet in het Franse woordenboek staan, om zo het geheel ook voor niet-Franstaligen vertaalbaar te houden.

-  Gebruik rationele benamingen voor functies en variabelen. De organisatie van de code van SPIP berust op het indelen in specifieke mappen en strikte regels rond de benoeming. Vermijdt inconsistenties zoals meertaligheid in een naam. Geef functies binnen eenzelfde bestand een gemeenschappelijke prefix of suffix die geïnspireerd is op de naam van het bestand.

-  Test op zoveel mogelijk configuraties. Vergeet niet dat SPIP moet kunnen werken op verschillende platforms, zowel voor de server als voor de client. Gebruik meerdere webbrowsers en test je software op minimaal twee van hen. Probeer, indien mogelijk, ook meerdere servers bij meerdere providers. Vereist een bepaalde server een specifieke behandeling, vermeldt dan dan expliciet inclusief versie en testdatum.

Codeerregels

SPIP besteedt zorg aan de typografische kwaliteit van de pagina’s die naar de client worden gestuurd, en ook aan zijn eigen bestanden.

De codeerregels volgen de norm PSR-2 met enkele uitzonderingen:

  • De variabelen, de functies en hun argumenten moeten worden gedeclareerd in snake_case, woorden die met een underscore zijn gekoppeld:
    function mijn_super_functie($een_argument, $ander_argument) {
    	…
    }
    
  • Gebruik de tabtoets om in te springen, want deze biedt iedereen de mogelijkheid te bepalen hoever moet worden ingesprongen in de tekstverwerker. De tabs worden aan het begin van de regel gebruikt om in te springen, spaties worden gebruikt om inhoud uit te lijnen (zie: PSR-2 - Indentation).
  • De openende accolade moet op dezelfde regel worden geplaatst als de methode. Er mag een spatie voor staan, maar niet erna (zie: PSR-2 - Methods).
    class MijnClass {
    	function een_methode() {
    		…
    	}
    }
    
  • Constanten moeten worden gedeclareerd in hoofdletters met een liggend streepje (underscore) als scheidingsteken, maar ook als eerste teken (zie: PSR-1 - Constants).
    define('_NIEUWE_CONSTANTE', true);
    
  • Aan globale variabelen, waarvan het gebruik wordt afgeraden, wordt gerefereerd met $GLOBALS["xxx"] en niet met global $xxx, omdat het eerste een visueel duidelijker onderscheid maakt.
    if (isset($GLOBALS['meta']['site_adres'])) {
    	$adresse = $GLOBALS['meta']['site_adres'];
    }
    
  • In PHP expressies wordt een spatie geplaatst tussen de binaire operators (+, =, *, AND, ...).
    $hypotenusa = sqrt(($a * $a) + ($b * $b));
    
  • Unaire operators (!, ...) mogen aan de parameter waar ze toe behoren worden vastgezet.
  • Gebruik uitsluitend dubbele aanhalingstekens wanneer dit nodig is. Heb je geen variabele ($var) of controlekarakters (\n, \r, \t...) in de reeks te evalueren, gebruik dan enkele aanhalingstekens.
    $reeks = "<a href=\"$url\">Link</a>\n";
    $reeks2 = 'Een gewone zin: ' . $andere_zaken
    

Voetnoot

[1Model-view-controller

Auteur Hanjo Gepubliceerd op: Aangepast: 21/03/23

Vertalingen: català, corsu, English, Español, français, Nederlands