SPIP 4.1

Changes brought by SPIP 4.1

SPIP 4.1 is above all a version that follows the maintained versions of PHP (7.4 to 8.1) and updates various libraries used internally. It also modifies the author authentication system.

PHP compatibility

SPIP 4.1 requires PHP 7.4 minimum, and works up to PHP 8.1.
It requires the PHP extensions: Sodium, Zlib, Zip and Phar.

Reminder: SPIP 4.0 is PHP 7.3 to 8.0.

Compatibility PHP 8.1

For PHP 8.1 compatibility, we had to correct / adapt many functions.

This is particularly true of calls to PHP functions dealing with strings, if they receive null instead of a string, to avoid deprecated : https://php.watch/versions/8.1/internal-func-non-nullable-null-deprecation

Authentication & Encryption

We take up and adapt the work of g0uz on the plugin Chiffrer.

-  It fixes some rare cases where the Javascript library used until now failed to identify the user
-  It increases security by preventing certain types of attacks.

# Login form

HTTPS is now even more strongly recommended

SPIP no longer attempts to encrypt the author’s password in Javascript from the login form, and the password therefore systematically arrives "in clear" to SPIP. It is therefore strongly recommended that your site is in HTTPS, as HTTPS ensures the security of the transmission.

As a result, the _AUTORISER_AUTH_FAIBLE constant, which forced the password to be sent in clear text, is removed.

# Pepper, salt, password hash

An author’s password is now hashed in SPIP using a pepper (a site-specific key). In addition, the password_hash PHP function takes care of finding the best hash algorithm and creates a salt in it. The PHP function password_verify is used to verify this.

The author’s rotating " aléas " are still created, but are no longer used to salt the password as PHP handles this very well. However, the additional application of a pepper is a new feature.

As before, with each successful authentication of an author, a new hash is stored in base, recreating a hash with password_hash: this applies a new salt to it then, and eventually PHP takes the opportunity to use a more powerful hash algorithm if needed.

# Encryption keys

To carry out this pepper, and other actions in SPIP, encryption keys are used and stored in config/cles.php. There are 2 by default:

  • secret_des_auth : it is used to pepper the authors’ password. So it’s a new key.
  • secret_du_site : Half of it is used to calculate the site secrecy. The other half is stored in the base in the secret_du_site entry of the table spip_meta. This site secret (combined) makes it possible to sign and/or encrypt certain parts of SPIP (ajax contexts, author actions).

When a webmaster logs in, the keys are saved in the new backup_cles field of the table spip_auteurs, by encrypting it with the author’s plaintext password.

This allows the site keys to be restored if the cles.php file has been deleted, when authenticating a webmaster.

The class \Spip\Chiffrer\SpipCles allows keys to be manipulated if required.

# Password

SPIP provides two methods that are used in ecrire/auth/spip.php

-  \Spip\Chiffrer\Password::verifier($clair, $hash, $key = null): bool
-  \Spip\Chiffrer\Password::hacher($clair, $key = null): string

The default key is that of secret_des_auth, used to pepper.

# Encrypting

Three methods are also provided for encrypting and decrypting (symmetric encryption) content.
In this we use the Sodium library (provided by default in PHP >= 7.2).

-  Encrypting::chiffrer($message, $key): ?string
-  Encrypting::dechiffrer($encoded, $key): ?string
-  Encrypting::keygen(): string

The keygen method simply produces an encryption key of the appropriate length.

The methods chiffrer() and dechiffrer() will be very slow if the transmitted key is not the right length: the encryption key is then considered to be a password requiring heavy additional processing (with sodium_crypto_pwhash).

# Signing of actions

Actions are now authenticated using hash_hmac and hash_equals (and the aléas of authors and/or the secrecy of the site)

Code quality

There is plenty of room for improvement in the quality of SPIP’s PHP code... the future can only be better ;-)

# Type declarations

In doing so for PHP 8.1, we have started to type certain function arguments and returns to detect incorrect calls as early as possible.

This is a potentially impactful change: it may create template errors where previously the error was more silent or tolerated. Similarly it could cause PHP errors in plugins or homegrown scripts on erroneous calls to these functions.

By ensuring compatibility with new versions of PHP, we will inevitably move towards more type declaration in the SPIP code as the source code evolves and becomes factorised.

The consequence will be that people developing plugins or templates will have to be more careful about the calls made on the one hand, and on the other hand we will certainly restrict new functions to single types (avoid the mixed), as well as some existing functions will probably have certain types of mixed arguments reduced (to avoid an argument getting a bit of everything as int | string | array for example), and the same goes for returns of functions.

Given the historical SPIP code, this will not be so obvious.

# Syntactic sugar

We have run the Rector tool on the SPIP 4.1 code with the PHP 7.4 configuration. The tool allows you to transform the syntax of the PHP source code by modifying certain elements that can be simplified with more recent PHP scripts. For example, using the operators ?? or ??= when possible (See the doc PHP).

That’s always a plus :-)

API changes on URLs and objects

Some call functions for calculating or decoding URLs are modified.
Older APIs are deprecated.

There are now two separate sets of functions for generating an object’s URL and for decoding a URL. This new mechanism allows you to define a function to generate a clean URL for `#URL_PAGE`.

The new functions are typed. In the following explanations, only the beginning of the signatures of the different modified functions are presented.

See the corresponding addition request for full details.

# Encoding

generer_objet_url (previously generer_url_entite)

Function generating a URL, which can be either for the public space (parameter $public = true) or for the private area ($public = false), or by default ($public = null) return a URL for the space we are currently in ...

Note: For this function the signature changes slightly.

In generer_url_entite() now deprecated:

  • The fifth parameter $public could be valued at true, false, null or 'string'. In the case of a string, it corresponded to a "connect" parameter.

These are now 2 separate parameters for generer_objet_url :

  • The fifth is ?bool $public = null (so true, false or null),
  • The sixth is string $connect = ''.

In most cases this will not affect usage and simply renaming the function should be sufficient.

- generer_url_entite($objet, $id, ...)
+ generer_objet_url($id, string $objet, ...)


Generate the URL of an object in the private area

- generer_url_ecrire_objet($objet, $id, ...)
+ generer_objet_url_ecrire($id, string $objet, ...)


Generate the absolute url to an object

- generer_url_entite_absolue($id, $entite, ...)
+ generer_objet_url_absolue($id = 0, string $entite = '', ...)

# Decoding

The URL decoding function is now called
urls_xxxx_decoder_url_dist() which avoids any confusion with the former function urls_xxxx_dist().

In addition, for a URL module that would like to be compatible with older versions of SPIP and this new convention, it is sufficient to provide a
urls_xxxx_dist() function not typed which does the routing to the 2 functions generer_url_objet() and decoder_url() of the module.

# Object


Generates a link (clickable title to url) to an object

- generer_lien_entite($id_objet, $objet, ...)
+ generer_objet_lien(int $id_objet, string $objet, ...)


To provide information about an editorial object. Used among others by #INFO_XX

- generer_info_entite($id_objet, $type_objet, $info, ...)
+ generer_objet_info($id_objet, string $type_objet, string $info, ...)

The functions for defining specific calculations for obtaining information are also renamed. For the calculated information ’xxx’: called via generer_objet_info(3, 'article', 'xxx') or #INFO_XXX{article,3}.

If these functions exist, they are used by SPIP in this case:

// xxx specifically on the 'type' object
- generer_xxx_{type}($id_objet, $type_objet, ...)
+ generer_{type}_xxx($id_objet, string $type_objet, ...)
// otherwise xxx on any object
- generer_xxx_entite($id_objet, $type_objet, $row, ...)
+ generer_objet_xxx($id_objet, string $type_objet, array $row, ...)

The old naming is deprecated.

Following this principle, the internal SPIP function for calculating the introduction has been renamed:


Generates the introduction of the object (private function to SPIP), used by #INTRODUCTION et #INFO_INTRODUCTION)

- generer_introduction_entite($id_objet, $objet, $ligne_sql, ...)
+ generer_objet_introduction(int $id_objet, string $objet, array $ligne_sql, ...)

Rewriting transmettre to .api

To facilitate and partially secure the sending of specific data (CSV, JSON) to known SPIP authors, the transmit.api URL has been set up (it replaces an old mechanism with the same objective which used a template transmettre.html).

The URL is generated with the generer_url_api_low_sec()function ("low sec" meaning "low security" here). A fixed token is created for this action (with its parameters) for the designated author and will be accepted even if the author is disconnected (meaning that anyone who owns the URL will be able to access the returned content).

Historically, this makes it possible to generate non-public files (RSS of articles proposed for publication, site statistics) which can be used by third-party tools (RSS aggregators, for example).


  • the function securiser_acces() is renamed in securiser_acces_low_sec() and defined in inc/acces.
  • We keep a filtre_securiser_acces_dist() in inc/filtres for compat of old templates
  • the function param_low_sec() is deprecated

Updates to libraries

We have updated several libraries used by SPIP.

# Javascript

  • spip/spip -> Sortable 1.14.0
  • spip/spip -> jquery Form 4.3.0
  • spip/spip -> JS Cookie 3.0.1
  • spip/statistiques -> d3 7.3.0
  • spip/statistiques -> luxon 2.3.0
  • spip/plan -> jstree 3.3.12


  • spip/compresseur -> css-tidy 2.0.0
  • spip/medias -> getid3 1.9.21
  • spip/medias -> svg-sanitizer 0.14.1

Deleting libraries

We have removed jQuery.cookie which was deprecated since SPIP 3.2 in favour of JS Cookie.

As a reminder, if you have not yet adapted your uses:

$.cookie(key) becomes Cookies.get(key)
$.cookie(key, value) becomes Cookies.set(key, value)
$.cookie(key, value, options) becomes Cookies.set(key, value, options)


The sections in the private area now also display the list of rejected articles.


We have deleted #FORMULAIRE_CONFIGURER_METAS which was deprecated from SPIP 3.0 in favour of forms #FORMULAIRE_CONFIGURER_XX. See the relevant documentation Configurer une fonctionnalité de votre site, ou un plugin .

Some adjustments

In a template, to list the tables of editorial objects, prefer #NULL (which returns null) rather than #REM (which returns an empty string).

- [(#REM|lister_tables_objets_sql)]
+ [(#NULL|lister_tables_objets_sql)]



The "Archivist" plug-in has been updated to use the PHP extensions Zip, zlib and Phar, which are now required. This has allowed us to remove the (very) old Pcl* libraries that we had been carrying around for a few years.

Note also that the plug-in comes with unit tests written with PHPUnit.


The Bigup configuration form is displayed on the advanced options page, with a slightly more meaningful wording for the title of the explanation form.


Update of the CSSTidy library to version 2.0 and correction of a bug in the compression of Javascript files on certain environments.


  • fixed a bug that was displaying the link to the forum control page for unauthorized authors.
  • normalization of the addition of a forum in base in order to pass the information through the pre/post insertion pipeline
  • fixed a bug displaying icons in RTL language
  • fixed a bug that allowed access to the private forum of the editors when it was disabled in the configuration
  • fixed a bug in the message search from the forum control page


  • improved display of long text in the modal caption
  • use of a dark theme by default in the private area
  • restored support for URLs with fragments using a selector in the `data-href-popin` attribute
  • added support for `data-(max|min)-(width|height)` and `data-(width|height)` attributes on mediabox links and restored compatibility with old `boxInline boxIframe boxWidth-xx boxHeight-xx` classes
  • fixed a bug with some remote images that were not displayed by the modal (notably SVGs), because the image has no known dimension


  • allow to customize the autolien behaviour of images depending on the media, extension, size or document_id
  • fixed a bug in the CSV file embedding model
  • removed obsolete jQuery multifile library
  • updated Sanitizer and getid3 SVG libraries
  • removed browser prefixes from the plug-in’s CSS
  • improved UX of buttons in the documents block of an object
  • fixed a bug in the code generated for the attributes of a captioned image
  • improved the template used for inserting SVG images in the text of an object
  • fixed a bug in the display of the media library when a document has a very long URL in the title or credit fields


Update of the Jstree library to version 3.3.12.


Fixed a bug that prevented the Revision Optimisation Genie from working.


  • fixed a bug that was displaying the date twice in lists of syndicated articles
  • improved site editing forms by using a placeholder in the URL fields (instead of pre-filling them with http://)
  • fixed a bug in the display of the list of sites in the side column of the private area


  • update of D3.js, d3-time-format and Luxon.js libraries
  • fixed a bug in the display of month names in Arabic statistics


  • fixed a bug in unpacking plug-in archives with the new Archivist plug-in functions
  • improved presentation of checkboxes in the plug-in list
  • fixed a bug in plug-in selection when the user was doing "check all" then "check updates
  • fixed a bug in the display of return messages from the plug-in loading form
  • always display the link to the plug-in documentation whether it is active or not
  • fixed a bug that prevented the activation of a plug-in when downloads are forbidden


  • fixed a bug that was mistakenly escaping some automatic links
  • fixed a bug in the direction of the bullets when the page contains blocks of text with different directions

URLs étendues

Following the API changes to URLs and objects, the URL functions have been rewritten to be clearer and better typed with 2 entry points for the decode/encode part.

See the corresponding add request for full details.

Templates dist

  • fixed a bug that was causing paginations to overflow on small screens
  • applying the filter |header_silencieux on public pages to respect the value of the global spip_header_silencieux
  • removal of Internet Explorer specific classes in CSS
    * fixed a bug in the display of images and SVGs on small screens

Author jack Published :

Translations : English, français