Quick links: Content - sections - sub sections
EN FR

Jelix provides an installation system, which allows to execute scripts when you want to install a module inside an application, or to install the whole application.

How a module is installed

When you install a module with the installmodule or the installapp command :

  • jelix looks at the modules section inside the configuration of each entry point, and retrieve the access value of the module
  • It will launch the installation of the module for each entry points where the access value of the module is higher than 0
  • It then looks at the var/config/installer.ini.php file: it checks if the module is already installed
  • If it is already installed, it will execute all scripts of update provided by the module, corresponding to a version between the installed version and the new version of the module
  • If it is not already installed, it will execute the installation script provided by the module
  • After the installation or the update, Jelix stored the new version number of the module into installer.ini.php.

Of course, installation scripts or scripts of update are not required.

But if you provide them, **it is very important to store into the module.xml file, a good version number and the date of the release ** !

Why is an installation script called several time?

Your installation probably needs to do some modifications specifically to each entry points. But the installation system doesn't know if your script will do it, and the installation script doesn't know in advance, how many and which entry points the application have (Remember that your module could be reuse in others applications).

This is why the installation script is executed for each entry points.

However, you probably don't want to execute the same thing for each entry points, or for the same database profile. Fortunately, you have some method to know easily if you can execute some things, without knowing the number of entry points or profile configuration.

An installation script

This is a file, install.php, that you store into the install/ directory of the module.

It should contain a class, inheriting from jInstallerModule, and named {module}ModuleInstaller, where {module} should be the name of the module. Ex: mainModuleInstaller for the main module.

In this class, you need to create a method install where you'll do all things to install the module: database modifications, file copies, configuration changes etc..


<?php
class newsModuleInstaller extends jInstallerModule {

    function install() {
       if ($this->firstDbExec()) {
           $db = $this->dbConnection();
           $db->exec('CREATE TABLE...');
       }
    }
} 

You can redefine the methods preInstall and postInstall, which are executed respectively before and after the installation of all modules. In preInstall() you can prepare some data or files, or you can check that conditions to install the module are met. It not, you can create an exception which will stop the whole installation.


<?php
class newsModuleInstaller extends jInstallerModule {

    function preInstall() {
    }

    function install() {
    }

    function postInstall() {

    }
} 

jInstallerModule contains a number of properties and method which helps you to access to the database, to the configuration file etc..

Main properties or methods

Here are some of methods or properties you can use:

  • $this->dbConnection(): it returns a jDbConnection object. Don't call jDb::getConnection(), because you don't really know which is the profile used for the module.
  • $this->execSQLScript(): it executes a SQL script stored into the install/ directory (or in one of its sub directory). Indicates a simple name, and it will load the script {name}.{dbtype}.sql. for example, if you call $this->execSQLScript('sql/install') and if the database of the default profile is mysql, it will load the file sql/install.mysql.sql. You can indicate the full name of the sql script, but it should be compatible to all databases supported by your application.
  • $this->copyDirectoryContent() to copy some files, from the install directory to another place, typically to the www/ of the application
  • $this->config: the jIniMultifilesModifier object corresponding to the configuration file of the current entry point. With this object, you can retrieve content of the configuration file or to set parameters.

Warning: with $this->config you get only values of parameters which are in the mainconfig.ini.php file and the configuration file of the entrypoint. So you may miss parameters that are in localconfig.ini.php. It's probably better to retrieve the Ini modifier of the entrypoint object $this->entrypoint->localConfigIni which is a combination between mainconfig.ini.php, localconfig.ini.php and the entrypoint configuration.

Don't hesitate to read the reference API of jInstallerModule and jInstallerBase, and to look at the installation script of modules provided by Jelix. You'll certainly learn many things.

To not execute some part of your script

You notice that in the example, we call the method firstDbExec. It tells you if this is the first time that you want to access to the database, for the current database profile (some entry points can share the same database profile). Remember that your installation script is called for each entry points (read above).

You have also the method firstConfExec to know if you did already have some changes to the current configuration file (some entry points can share the same configuration file, other than mainconfig.ini.php). If it returns true, you can then access to the configuration file, by retrieving the config property, which is a jIniMultifilesModifier object.

Then you have the method firstExec. You pass a keyword to this method. And it tells you if you already call this method with this keyword (false) or not (true). You call this method to determine yourself, in some conditions, if you want to execut severalt time a part of your installation script.

Database profile used for the installation

If your installation script interacts with a database (creating a table, modifying records in some tables etc), you have to use the method dbConnection() or execSQLScript to exécute queries. By default, it uses the "default" profile. An alternate profile can be indicated in several places (listed by priority, the first has the higher priority):

  • In your install class, you call useDbProfile, by given the profile you want to use.
  • In the property defaultDbProfile of your install class, you can indicate the default profile to use for the installation of the module

Parameters for an installation

You may want to execute the installation with some parameters given by the user of your module.

Parameters for an installation script can be simple booleans, or some values. To indicate parameters, the user have to set an option in the modules section of the configuration file of the application. The name of this option is {module}.installparam. For example, for a news module, it will be news.installparam.

In this option, you can indicates simple names (it will be some booleans), or some names and values.


news.installparam = "enablecategories,defaultcategory=In the world"

Here, parameters will be a boolean named "enablecategories", and a parameter named "defaultcategory" with the value "In the world". We can imagine here that the installation script of the module "news" have the possibility to enable a "categories" management, and we could indicate the name of the default category to create.

To read parameters from the installation script, you have the getParameter method. Indicates the name of the parameter, and it will return its value (true for a boolean). If the parameter does not exists in the installparam option, it returns @@null@. Example:


  function install() {
     if ($this->getParameter('enablecategories')) {
        // here some code to create the categories table for example...
        // ...

        // now create the default category
        $defaultCategory = $this->getParameter('defaultcategory');
        if ($defaultCategory) {
           // here we could insert a record in the categories table
           // with the label given into $defaultCategory.
        }
     }
  }

Scripts to update a module

These scripts have specific file names: upgrade_{label}.php where {label} is a name which will be part of the name of the class.

Note: In jelix 1.2.5 and lower, the filename could be upgrade_to_{version}_{label}.php, where {version} should be replaced by a version number. This format is now deprecated.

These scripts should be stored into the install/ directory of the module. They will be executed in the ascendant order of the version indicated into the filename. And only those for which the version is higher than the current installed version of the module, and lower and equal than the new version of the module. Imagine that you have these scripts (with deprecated format of names):

  • upgrade_to_1.0_aaa.php (old name)
  • upgrade_zzz.php (declaring version 1.1pre.1234 in the class)
  • upgrade_foo.php (declaring version 1.1pre.1350)
  • upgrade_bar.php (declaring version 1.1)
  • upgrade_fff.php (declaring version 1.2a1)
  • upgrade_eee.php (declaring version 1.2)
  • upgrade_hello.php (declaring version 1.3)

If the current version of the module is 1.1 and the new version of the module (indicated by the new module.xml file) is 1.2, then only these scripts will be executed, in this order:

  • upgrade_fff.php
  • upgrade_eee.php

A script for update should contain a class, named {module}ModuleUpgrader_{label}, where {module} should be the module name, and {label}}, the label indicated in the filename of the script.

Because several scripts for update can be executed for the module, then class name must be different (else PHP won't like it). This is why we use a label, which should be different for each script of a module. (Jelix developers could use the version number instead of a label, but unfortunately, PHP doesn't like characters like "." or "-" in a class name).

The class should inherit from jInstallerModule, so you have exactely same behaviors and methods as in installation script. So you must implements a method install, you can call firstDbExec, getParameter etc..

As we saw, a class is doing an update for a specific version. You must indicate the version number into the property $targetVersions (which is an array), and a date in the property $date with the format 'yyyy-mm-dd' or 'yyyy-mm-dd HH:ii'.


<?php
class newsModuleUpgrader_fff extends jInstallerModule {

    public $targetVersions = array('1.2');
    public $date = '2001-06-21';

    function install() {
       // ....
    }

} 

You'll guess that you can indicate several versions in $targetVersions. It is useful when you have several branch in a project, and when an update script should be executed for several branch. With the date of the release, Jelix can determinate if, during an update, it can execute the script or not. For instance, you have a script for an update for versions 1.24 and 1.35. If the user update from the version 1.2.3 or from the version 1.3.4, the script will be called. If he update from the version 1.2.5 to the version 1.3.6, the script won't be called because the 1.2.6 version contains modifications brought by the 1.2.5 version.