SPIP

[ar] [ast] [bg] [br] [ca] [co] [cpf] [cs] [da] [de] [en] [eo] [es] [eu] [fa] [fon] [fr] [gl] [id] [it] [ja] [lb] [nl] [oc] [pl] [pt] [ro] [ru] [sk] [sv] [tr] [uk] [vi] [zh] Espace de traduction

Download

Extending SPIP

August 2010 — updated on : 8 February

All the versions of this article:

If you’re thinking about extending SPIP and specifically intend to contribute to the kernel, there are a few salient points to consider: SPIP is a tool already used by thousands of people, it has a history and a planned road map, and is a widely collaborative project with a global audience. Certain choices have been made, not always easily, but which have led to an overall consistency in the code, which makes moving forward a much simpler task. Whenever an earlier choice reveals itself to be clearly incompatible with a new technology that would be useful to integrate, a refactoring of the code is commenced but which may take several months to complete, entailing a prolonged inconsistency until it’s done. It’s obvious why such decisions should be avoided where possible, and accept that occasionally a new rule will be violated, with the refactoring of that section still not having been completed. It’s also true that even if programming styles vary a little from one place to another, an overall internally coherent system is in constant evolution, and a useful and widely used software is not derived from religious application of some sainted procedures, but results from organic growth from a lively and open community.


Organisation of the code sources

Since version 1.9 was released, it has been made possible to modify SPIP’s behaviour without altering its kernel code, principally due to the use of the access paths defined by the SPIP_PATH global constant. All SPIP extensions should use this mechanism so that integration testing can be detachable from the system as a whole by simply redefining that access path, with the SPIP core distribution essentially being read only. If you are convinced that a certain desired behaviour can not be achieved using this mechanism, please discuss your requirements and thoughts before proceeding through the spip-dev discussion list.

Contributions to SPIP should always be described with an article on Spip-Contrib, and their constituent contents made available as documents attached to that article, or (and even better) stored in the spip-zone which provides a Subversion code server provisioned by Trac.

In order to extend SPIP in this way, it is necessary to fully understand how the various directories nominated in the SPIP path are organised and the roles played by the files assigned to each of these directories. This is the purpose of this article, but you might well also benefit from reading the "release notes" article describing the SPIP path system that was issued when SPIP 1.9 was initially released.

The root directory of the SPIP distribution essentially contains:

-  a file called spip.php, aliased as index.php, to handle backwards compatibility with prior versions, and which loads up the ecrire/inc_version.php initialisation file, and then immediately passing control on to the main script: ecrire/public.php;

-  a directory called ecrire which contains only files that can be interpreted on the server side (PHP and SQL);

-  one or more (depending on the version) directories containing files that can be interpreted on the client side (HTML, JavaScript, style sheets, images in various formats) as well as the formatting templates known as squelettes. These squelettes/templates are interpreted on both server and client sides: these are files compiled in a MIME format, and supplemented with SPIP directives which are processed on the server side in order to send the client a text file that is purely MIME (normally HTML, but also RSS, SVG, ICS, etc).

-  four directories which are empty upon installation, which will contain the temporary and permanent data necessary for maintaining the web site.

The roles of the prive and squelettes-dist directories and their sub-directories

These folders contain the files that determine the presentation of SPIP’s output, for the private and public zones respectively. Prior to SPIP 2.0, these two directories were one and the same, and were then named as dist. They contain a large number of sub-directories, summaries of which are detailed here below:

directoryrole
/ contains the squelette templates. The template files are suffixed with .html for historical reasons but this does not imply anything about their contents. You can supply any file without that extension as the page parameter for the URL of a SPIP site and it will be used just like a squelette template that did have the extension.

This directory also contains the stylesheets (with the .css extension) defining the default styling for the pages

formulaires/ contains the html section of the dynamic tags, form templates which use PHP code stored in the ecrire/balise directory
icones_barre/ contains the images used to illustrate the typographic toolbar in the private zone
images/ images used in the private zone
javascript/ JavaScript libraries (jQuery, typographical toolbar, ...)
modeles/ squelette templates that can be called using the #MODELE tag or with shortcuts in the form of <article6|model_name>
polices/ the fonts that can be used with typographical (textual) images
vignettes/ standard thumbnails for the types of document that can be associated with an article

The roles of the ecrire directory and its sub-directories

The ecrire directory contains several sub-directories which contain the PHP files defining the functions and sometimes, but quite rarely, triggering certain initialisation tasks when they are loaded (these exceptions will soon be eliminated). The files located in the main directory which are the most important ones to understand as regards making your own contributions to SPIP are: inc_version.php, public.php and index.php.

The ecrire/inc_version.php file initialises the global constants and variables necessary for SPIP’s normal operations, specifically those that ensure its portability over the various supporting platforms. Fairly early on when it is loaded, it includes the inc/utils.php file where all of the essential SPIP files are defined, and then another file not part of the standard SPIP distribution called mes_options.php, which is used to modify the initialisation task without needing to modify the inc_version.php file. In particular, it is possible in this customised file to call the spip_initialisation function in order to define the data directories and thereby enable you to run several distinct SPIP web sites using a single distribution kernel (the standard call to this function, further down in the inc_version.php file, will automatically be neutralised so that it does not run twice). Another essential SPIP function is find_in_path, which uses the access paths as well as include_spip which relies on find_in_path, and charger_fonction which relies on include_spip. All of SPIP’s files are loaded by one of these two functions.

The ecrire/public.php file, called by spip.php, has the critical role of provisioning the public web pages required whenever an HTTP request contains the page parameter (with URL rewriting possibly intervening beforehand). This script then applies the squelette template which bears the same name as that parameter. It returns the HTTP headers and calculated contents, handles any possible errors that may occur, and runs the background tasks using the internal SPIP cron function. Contributing to the SPIP public zone therefore consists of supplying new templates, and also possibly additional stylesheet rules and images that they may reference.

The other role of the ecrire/public.php directory concerns cases where the HTTP requests includes the action argument. It then applies the charger_fonction function with the v value of that action parameter. This application will cause the loading of the file with the same name in the action directory, which has a principal function named action_v_dist which is thereby invoked. These scripts principally perform write operations (to the database or to an external file) and generally do not return any results, thereby escaping any issues with page formatting.

The index.php file is the central access file to the forms in the private zone. It authenticates the site visitor, initialise that user’s personal data, and applied the charger_fonction function to the v value of the exec parameter. This application will cause the loading of the file with the same name from the execdirectory, which will have a principal function called exec_v_dist which is then invoked. That function then has the task to deliver all of the output data, includes the HTTP headers. It is therefore possible to extend SPIP simply by adding a PHP file into a sub-directory named exec that is listed in the list of directories stored in the SPIP_PATH global variable.

The exec directory only contains the files that define the functions directly callable using the exec URL parameter. The PHP code in these files must never attempt to write to the database (the few remaining exceptions to this rule are currently being eliminated). On the other hand, it frequently accesses the database in read mode in order to verify the rights of the requester and to determine the data to be displayed. Looking at SPIP as a Model-View-Controller architecture, the exec files perform the role of the Controller. Looking at SPIP using Lisp’s (Print(Eval(Read))) architecture, it performs the Read segment. Eventually, this directory ought to become a directory of squelette templates. It is requested of new contributions to SPIP to consider this objective when the contributions are being coded.

The action directory, which has already been mentioned, essentially contains all of the scripts that access the database in write mode. Looking at SPIP as a Model-View-Controller architecture, the action files fill the role of the Model. From the point of view of the Lisp (Print(Eval(Read))) architecture, it is the Eval section. Here again, any contributions to SPIP consist of writing such scripts and calling them from the forms built using the generer_action_auteur function, which ensures the security access of those scripts, which itself will invoke the securiser_action function to verify the access rights of the requester. This architecture enables the calculation of these rights only when constructing the forms that call the write mode access scripts: rather than repeatedly recalculating all of these rights, these scripts simply verify that the key passed amongst the arguments is the same as that which they recalculated based on the other arguments, the requester’s identity, and a random value that is periodically refreshed. These scripts do not generally return any results, so therefore also don’t generally return any HTML code nor any call to the echo function (any remaining exceptions are currently being eliminated). On the other hand, they are often called with an HTTP parameter called redirect, which requests a redirection which will then be automatically triggered by public.php, which itself will retrn an HTTP 204 status message if that parameter is absent. In the case of forms constructed with the ajax_action_auteur function, that redirection leads to the script of the same name in the exec directory. This second script often only entails loaded the file with the same name from the inc directory, calling that file’s main function, and returning that function’s result to the client via the ajax_retour function. In this way it is very easy to extend SPIP in AJAX mode using this pre-constructed infrastructure.

The inc directory, the one with the most content, essentially contains the functions used to build the private zone pages returned to the client, these functions which ought to later become filters used by the exec files when those files are all squelette templates. Looking at SPIP as a Model-View-Controller architecture, the inc files fill the role of the View. From the point of view of the Lisp (Print(Eval(Read))) architecture, it is the Print section. Nevertheless, this directory also contains a lot of functions that are actually more relevant to the Control function and which should therefore really be reorganised. Most of the inc files are loaded via the charger_fonction function, and in time this will be the case for all of them. None of the functions in this directory are supposed to use echo. Contributions to SPIP are requested to respect these rules from now on.

The install directly only contains the functions necessary for SPIP installation. Each step may be overloaded or supplemented by others, the principal function exec/install.php using this directory following the same principle as ecrire/index.php does with the exec directory.

The urls directory contains the files that each define the same set of URL rewriting functions. Based on the numeric identifiers in a given table in the database, these are functions which calculate a name which is easier to read and write than the direct PHP script call that is actually executed by the HTTP server for that numeric index and that table. Here again, you only need to add a file into this directory to obtain a new set of rewrite rules, the name of which will be displayed for selection in the private zone configuration panel that deals with the different types of URLs (prior to SPIP 2.0, this panel did not exist in the private zone and these files were referenced instead by the type_urls global variable).

The lang directory only contains data files, PHP arrays which supply the language specific translations for the "idioms" used for all of SPIP’s hosted languages, and for all the arguments that the _T function (defined in inc/utils.php) is expected to be passed. These files are loaded only by the inc/lang.php functions. By translating the reference files suffixed with fr into similarly named files suffixed with another language code is all that is required to make SPIP available in another language.

The charset directory also only contains data files, PHP arrays used to convert from one character encoding to another (utf, iso, ascii, html entities, etc). They are exclusively read by the functions in inc/charsets.php. Once again, just by adding a file into the directory makes it possible to declare a new encoding to SPIP, but SPIP actually already offers all encodings in common use, so adding such a file will be a rare event indeed.

The base directory contains the interface functions between PHP and the SQL servers that SPIP can access. In particular, the generic abstract_sql.php file contains the functions that should be used to communicate the the SQL servers, thereby avoiding any need to use the PHP database functions which would negatively impact the portability of any code. There should, of course, not be any MIME code in this directory.

The req directory contains the effective ports of SPIP’s virtual SQL server to the real world servers (MySQL, PG) and similar others (SQLite).

The balise directory contains the PHP files associated with SPIP’s dynamic tags. Their names are synonymous with the squelettes-dist/formulaires squelette template. Supplementing the SPIP public zone with a form F consists of creating an F.html file in your SPIP_PATH and an F.php file in a balise sub-directory of your SPIP_PATH. The role of the PHP file is to accept the entries requested by this form, and to possible redisplay it to request corrections to invalid data entries. It is without doubt the type of contribution to the public zone which is the most difficult to provision, since the accompanying mechanism requires twoo PHP execution passes, with different roles that must be understood completely. Before this mechanism existed, the strategy for developing forms consisted of writing templates that contained their own PHP instructions. it is still possible to do this, but the result won’t be very efficient since the results will never be cached; and this would still not allow you to avoid understanding the two PHP passes intrinsic to the operation.

The public directory contains the squelette template compiler. This is a fairly complicated section of code, but since SPIP 1.8, 1.8.1 it has benefited from a programming interface which makes the compiler totally extendable without requiring the coder to understand all of its internal details.

The lib directory contains the library sub-directories developed outside the scope of SPIP which upon which it depends. At present, the only one supplied systematically is the safehtml library which supplies security tools for scriptable pages. To contribute to this section, please contact the separate dedicated site.

Final point: most of the SPIP files are used via the charger_fonction function, which loads an file and calls the function of the same name which ought to be defined in that file. This means that the PHP file name must be composed only of characters which are acceptable in a PHP function name: this means that you should avoid using the minus sign, the period sign, etc.

Programming rules

SPIP was started at a time when PHP automatically transformed global variables in to HTTP parameters. This suicidal style of programming was completely abandoned by PHP4. SPIP has followed a parallel evolution, but a little delayed in time. Even though the current code does not necessarily follow the rules that follow below, it is requested of all contributors that they respect them from here on in, without expecting SPIP to have completely abandoned the practice itself in a few odd locations. As a guide, carefully examining the ecrire/articles.php file is useful as it is currently the closest adherent to the specifications detailed below.

-  Try to opt for writing functions. The philosophy of open source software is to be used in a maximum of different contexts, and so the code should be written as much as possible with a view to being reused outside of its initial development environment. Writing functions will no reference to any global variables and not executing any calls to echo or print will guarantee a silent loading and calls that do not have any undesirable side effects.

-  Avoid the use of global variables wherever possible. They are responsible for numerous security holes and make the code virtually impossible to be reused. Alternatives to them are:

— constants, which have the advantage of indicating to the reader that their values will not change throughout the entire execution of the script;

— static variables, which have the advantage of indicating to the reader that they relate to values of long life duration but are only of interest to the function that declares them.

-  Write code that does not generate any error or warning in error_reporting(E_ALL) mode. This makes it easier to update in the event of unintentionally undefined variables. If you really need to run the code outside of this mode, use the @ construct and limit the problematic code as much as possible, and opt for an error message into the log files using the spip_log function.

-  Comment on the context, not the text. It serves nothing to paraphrase the names of its variables and functions, nor the PHP functions described in its manual: comments such as loop through the table of values before a foreach statement only serve to uselessly increase the size of the files. On the other hand, it’s a good idea to indicate the argument types (PHP being a dynamically typed language, they can often be difficult to ascertain implicitly) and their presumed properties upon entry to the function (e.g. not null). When a difficult bug is corrected or expected, explain why the initial code was incorrect in order to avoid any later code refactoring reintroducing the fault under the guise of an attempt to optimise the code in question. Finally, SPIP has been developed in French, so try to avoid using terms not generally available in French dictionaries so that it is easier for non-native French speakers to understand what you have written (i.e. avoid local slang).

-  Name your functions and variables sensibly. The organisation of the SPIP code is originally based on breaking it up into dedicated directories with strict naming conventions, but we still try to avoid inconsistencies like including multilingualism within a given name. Functions within the same file will tend to have a common prefix or suffix that is inspired by the name of the file itself.

-  Test your code on a maximum number of different configurations. do not overlook that SPIP should work on all its supported platforms, both in terms of servers and clients. You will necessarily have several browsers deployed on your machine, and you should test your code using at least two of them. Wherever possible, try it out on several different hosts as well. Whenever a particular platform requires unusual coding techniques, detail them explicitly, specifying the date and version number the test was executed.

Coding rules

SPIP is concerned with the typographical quality of the pages that it sends out to HTTP clients, so it is therefore also concerned with the quality of its own internal files.

Coding rules follow the norm PSR-2 with some exceptions and additions listed below:

  • The variables, functions and their arguments should be declared snake_case (ref. PSR-1 - Global View).
    1. function ma_super_fonction($un_argument, $autre_argument) {
    2.         …
    3. }

    Use the tab character to indent, because it allows to freely choose indentation depth the options of its text editor. Tabs must be used beginning of the line for indentation, spaces only for the alignment of content (ref. PSR-2 - Indenting)

  • The opening brace must be on the same line as the method name, as control structures. And must have a space before and after any space (ref. PSR-2 - Methods).
    1. class MaClasse {
    2.         function une_methode() {
    3.                 …
    4.         }
    5. }
  • The constants must be declared in capitals with underscore separator and prefixed with an underscore (ref. PSR-1 - Constants).
    1. define('_NOUVELLE_CONSTANTE', true);
  • Global variables, whose use is not recommended, are referenced by$GLOBALS["xxx"] and not by a declaration global $xxx, because it allows to be sure the reference is used and easily tell the difference when reading the code.
    1. if (isset($GLOBALS['meta']['adresse_site'])) {
    2.         $adresse = $GLOBALS['meta']['adresse_site'];
    3. }
  • In expressions PHP leave a space either side of binary operators (+, =, *, AND, ...).
    1. $hypothenuse = sqrt(($a * $a) + ($b * $b));
  • Unary operators (!, ...) Must be bonded to the parameter to which they apply.
  • Use double quotes only when necessary. If you do not have a variable ($ var) or control character (\ n \ r \ t ...) to evaluate in the string, use single quotes.
    1. $chaine = "<a href=\"$url\">Lien</a>\n";
    2. $chaine2 = 'Une simple phrase : ' . $autre_chose

Show the template of this page Site powered by SPIP | Translation area | Private area