LiteCommerce:Modules and Decorators

From X-Cart 4 Classic
Jump to: navigation, search

A module adds some extra functionality or changes the default LiteCommerce behavior by “runtime decorating” of the original code, so there is no need for changing or patching core php files. Modules come in .tar files and can be easily installed, removed, and deactivated. Modules serve a basic LiteCommerce customization tool, because they do not require any changes to the LiteCommerce core files and simplify upgrading customized websites.

Each module must include the classes/modules/ModuleName directory, which should contain the following files:

ModuleName.php the main module class responsible for module installation and initialization.
MANIFEST the module descsription file. A typical MANIFEST file looks like
module_id=3000
name=Promotion
description=”Promotional package: special offers, etc.”
enabled=1
version=2.0
dependencies=

The MANIFEST file specifies a unique module id, module name (no spaces or special characters allowed), description, whether the module is activated by default (1) or not (0), its version and a comma-separated list of modules this module depends on.

Note: please, contact us to obtain unique module identifier for your custom module. This reduces the risk of collisions between third-party and original modules.
install.php an optional PHP file, executing during installation.
install.sql an optional SQL patch file, executing during installation.
post-install.php an optional file, which is called just after the installation is complete and typically contains further user instructions.
classes.lst required index of all module classes. Is generated automatically with classes list builder.

The main class must be inherited from the Module and can redeclare the base Module class methods and properties. You can use the Module class description and free modules available from the LiteCommerce web site as a good example for main module class.

Modules use class inheritance to change LiteCommerce functionality. Thus a module does not change the LiteCommerce php code itself, however it inherits and overrides the kernel LiteCommerce class methods. Several modules can modify the same kernel class, so that module classes are inherited from each other, creating an inheritance chain.

Let us consider an example that illustrates this technique. The example below illustrates three of the kernel LiteCommerce classes, namely Order, Cart and OrderItem. The Cart class extends the Order, which is denoted by an arrow. An order can have a number of items, and this association is also shown by an arrow.

Figure 2.1 LiteCommerce kernel classes

Embim1.png

We also have three modules installed -- ProductOptions, Promotion, and GiftCertificates, which modify the considered classes. The table below shows which modules extend which classes.

Table 2.1 Modules that extend LiteCommerce classes.

Order Cart OrderItem
ProductOptions x
Promotion x x x
GiftCertificates x x

When LiteCommerce starts, it reads the information about all active modules and changes the class inheritance scheme so that module classes, extending the same object, are organized in a chain. The figure below represents the inheritance scheme in the given example.

Figure 2.2 Class structure when the modules are active

Embim2.png

In order to build chains dynamically, LiteCommerce reads class source files, changes the extends clause, renames the classes by appending the “__” postfix and writes the resulting PHP code in the “var/run/classes” directory for future use. For example, the following definition, appearing in the GiftCertificate module:

Example 2.1 Module inheritance order.

class Module_GiftCertificate_Order extends Order
{
...

is changed to:

class Module_GiftCertificate_Order__ extends Module_Promotion_Order__
{
...

Compiled classes are put into var/run/classes/modules/GiftCertificates/kernel. Each time you turn modules on or off, LiteCommerce rebuilds the var/run/classes files. From now on, each time you call func_new("Order"), an instance of the Module_GiftCertificate_Order__ class is created. Users can use func_new transparently to create instances. To make it even more transparent, LiteCommerce defines some functions that operate on classes, shown in the table below:

Table 2.2 LiteCommerce class manipulation functions

function name description
func_class_exists($name) checks for the class existence (no need to require() it
func_new($name[,...]) creates a new class instance
func_is_a($child, $parent) checks that the $child class extends the $parent class
func_add_decorator ($decorated, $decorator) adds the class $decorator to the inheritance chain of the class $decorated.


The module classes that replace the core classes are also called decorators

Note: There is a standard OOP design pattern called 'Decorator' which allows adding a number of decorators to a class. A decorator is a class that modifies the original class behavior, just as inherited classes, except for the fact that the number of decorators may be changed at run time by creating a decorator stack. The LiteCommerce decorator implementation differs from the standard one in that each decorator does extend the previous one, and the inheritance chain is built instead of just a decorator stack. This is possible since PHP is an interpreter and allows you to change the source code.

Once you have added a decorator to a class, it will replace the original class in future, e.g. the func_new function call will produce the decorated class instance instead of an original class instance. Decorators are typically registered in the module initialization method with the addDecorator module method, as it is shown in the listing below. Decorator classes must be declared to extend original classes.

Example 2.2 Decorating the Product class from a module

// in classes/modules/MyModule/MyModule.php

class MyModule extends Module // the module main class
{
  // module initialization method
  // called at startup
  function init()
  {
    parent::init();
    // the same as
    // func_add_decorator("Product", "Module_MyModule_Product");
    // but in a more preferred manner
    $this->addDecorator("Product", "Module_MyModule_Product");
  }
}

// in classes/modules/MyModule/kernel/Product.php

class Module_MyModule_Product extends Product
{
...
}

Of course it is possible to extend a class without replacing it, if you just inherit it and do not register a decorator.