Modular Scripts

A modular script is the primary building block of the Melbis platform. Each module solves one isolated task: builds a catalog menu, renders a product card, processes a cart, or executes a background task. The entire site is assembled from these blocks.

Working with Modules in the IDE

All modules are displayed in the file tree under “Scripts, Templates” in the Workbench. Modular scripts are grouped by company and group — this makes navigation convenient even in large projects with dozens of modules.

When a module is opened in the editor, its interface consists of three parts:

PHP File Structure

A typical module looks like this:

<?php
/**
 * Function MELBIS_CATALOGE
 **/
function MELBIS_CATALOGE($mVars)
{
    // Create a template engine pointer
    $tpl = MELBIS()->TplCreate();

    // Retrieve data from the database
    $command = "SELECT id, name
                  FROM {DBNICK}_topic
                 WHERE no_visible = 0
              ORDER BY absindex";
    $menu = MELBIS()->SqlSelect(__LINE__, $command);

    // Pass data to the template engine
    MELBIS()->TplAssign($tpl, 'MENU', $menu);

    // Return the result
    return MELBIS()->TplFinal($tpl, 'main');
}

The main function is the only required part of a module. Its name matches the filename in uppercase. The parser calls exactly this function, passing the input parameters as the $mVars array.

The module may contain any number of helper functions — they are named with the main function’s name as a prefix (see the “Naming Conventions” section).

Namespaces

The prefix in function names exists for one reason: in PHP all functions live in a common space, and Price from the product card would collide with Price from the cart. A module can declare a space of its own — then the file name leaves the function names for a single line at the top, and the calls get shorter.

<?php
namespace MELBIS_STORE_CARD;

/**
 * Function Main
 **/
function Main($mVars)
{
    $price = Price($mVars['id']);
    ...
}

/**
 * Function Price
 **/
function Price($mId)
{
    ...
}

The main function here is called Main rather than the file name: the module name is already written in the namespace line, and there is no point repeating it. The parser looks for both forms — first MELBIS_STORE_CARD\Main, then the former MELBIS_STORE_CARD — so nothing changes in the template, and the module call tag stays the same.

The file’s own helper functions are called by their short name with no prefixes at all — Price($id). Calls into the engine work as before as well: PHP looks for unqualified function names first in its own space and then in the global one, so MELBIS(), count(), and any function of a flat library are visible unchanged.

Declaring it is voluntary and per-file. A module without a namespace line works exactly as it did before; flat and namespaced modules live in one store quite happily and call each other.

Calling Library Functions

The functions of an included library that has also declared a space are available by their full name:

MELBIS_INC_LOGIC\OrderCreate($order);

To write it shorter, the library is given a name inside this file — a use line under the space declaration:

namespace MELBIS_STORE_CARD;

use MELBIS_INC_LOGIC as LOGIC;

...
$version = LOGIC\OrderCreate($order);

The use line does not have to be written by hand: when you tick a library in the right-hand panel, the Workbench offers to add it itself and inserts the block right under namespace. The offer appears only for a file with a declared space, and only for libraries that have published a short name (see “Library Modules”).

A short name is an alias inside one file, not a global rule: a library that has included another library declares its own use lines in itself, and the calling module does not need to know about them. Inheriting imports is not required — files are still loaded down the tree of inclusions.

An import includes nothing. use is only a name; a library gets into memory by the tick in the manifest, as before.

What to Check When Converting a Module

The move affects more than the function declarations. Three things in the file need to be looked over by eye:

Old calls to your module from other files also need fixing after the move, but the edit is mechanical — the underscore before the function name becomes a backslash:

MELBIS_STORE_CARD_Price($id);      // before
MELBIS_STORE_CARD\Price($id);      // after

Module Workflow

Most modules follow the same pattern:

1. Create a template engine pointer:

$tpl = MELBIS()->TplCreate();

2. Retrieve data from the database using MELBIS()->Sql* methods:

// Return a flat array of a single record
$topic = MELBIS()->SqlSelectFlat(__LINE__, $command, $params);

// Return an array of records
$menu = MELBIS()->SqlSelect(__LINE__, $command, $params);

// Return an array indexed by key
$image = MELBIS()->SqlSelectEnumFlat(__LINE__, $command, 'id', $id, $params);

// Return a page of records and the total row count
$goods = MELBIS()->SqlSelectLimit(__LINE__, $command, $offset, $limit, $params);

Pagination, data writing, transactions, and table locking are described in the “Working with the Database” section.

3. Pass data to the template engine using MELBIS()->Tpl* methods:

// Pass a single value
MELBIS()->TplAssign($tpl, 'TITLE', $topic['name']);

// Pass an entire array
MELBIS()->TplAssign($tpl, $topic);

// Parse a template and place the result into a variable
MELBIS()->TplParse($tpl, 'CONTENT', 'page_index');

4. Return the result — the final parsing of the main template:

return MELBIS()->TplFinal($tpl, 'main');

The full list of Sql* and Tpl* methods is described in the corresponding documentation sections.

Input Parameters

Parameters are passed to the module through the $mVars array. How the array is formed depends on how the module was called.

If the module is called as an entry point (via MELBIS()->Run()), parameters are passed explicitly from the root script:

// In index.php:
$entry_param = [serialize($_GET), serialize($_POST)];
MELBIS()->Run('melbis_base_page', $entry_param);

The module declares get: serial, post: serial — and inside the function:

$id = (int) ( $mVars['get']['topic_id'] ?? 0 );

If the module is called from a template, parameters are passed directly in the call tag — positionally, separated by commas:

{MELBIS:melbis_store_image([ID],kDefault)}
{MELBIS:melbis_store_random(8)}
{MELBIS:melbis_cataloge_sub(,,[ID])}

Variable values are substituted in square brackets — [ID], not {ID}. Arguments are parsed by comma and by the closing parenthesis, so any “dirty” value — a name containing a comma, text with a quote, a serialized array — when substituted via {ID} will break the argument list and the module will receive garbage. Square brackets run the value through urlencode, and the parser on the receiving end performs a matching urldecode, so the content arrives intact in any form. This rule also applies to variables of the current loop row:

{#GOODS}
    {MELBIS:melbis_store_card([VAR:LANG],[ID],[PRIOR])}
{GOODS#}

Literals (kDefault, 8) are written as-is — they are not variables, there is nothing to encode.

The module must declare the corresponding parameters, for example id: int, key: str, and inside the function:

$id  = $mVars['id'];
$key = $mVars['key'];

Parameter types are declared in the field above the editor in the IDE:

Type Description
int Integer
float Floating-point number
bool Boolean value
str String
fix Fixed set of values; the first is the default. Example: mode: fix=list\|grid
serial Serialized PHP array (for passing $_GET/$_POST). If the value is empty or invalid, [] is returned — no need to check is_array in the module

Ways to Call a Module

From a template — the primary and safe method. The parser encounters a {MELBIS:module_name(...)} tag in an HTML template, runs the module, and substitutes its HTML result in place of the tag. Available for any module without restrictions.

As an entry point — a call via MELBIS()->Run() in the root script. The URL can be anything — the key criterion is the call via Run. To allow such a call, the “Entry Point Module” option must be enabled in the IDE’s right panel. Without this flag, the parser will refuse to run the module directly. Entry points are typically page router modules, form handlers, and cron tasks.

Module Nesting

The HTML fragment returned by a module may contain call tags for other modules — to any depth: the parser processes the entire chain on its own. A breakdown using the demo store page as an example is provided in the “Storefront Architecture” section.

Libraries and Tables

The right panel of the IDE displays all available library modules (inc). To connect a library, simply check the checkbox — the parser will automatically load it before running the current module, making all its functions available.

The “Tables” tab displays two lists: - Left — the database tables directly accessed by the current module. The developer marks the required tables. - Right — tables from connected libraries (informational only, cannot be changed).

This data is used by the caching system: when data in a tracked table changes, the platform knows which modules’ caches need to be invalidated. See the “Caching” section for details.

Utility Methods

In addition to working with templates and the database, a module occasionally needs platform utility calls — the current module’s name, calling its own function, the path to an uploaded file. These are covered in the “Utility Methods” section.

Utility Methods

Besides working with templates and the database, a module now and then needs the platform’s utility calls — the name of the current module, calling a function of its own, the path to an uploaded file. They are collected in the “Utility Methods” section.