Module parse_bif_obj

Source
Expand description

§{:obj; … :}

This feature is experimental.

Executes an external script (Python or PHP via PHP-FPM) and processes its output. The script receives parameters and can access the template schema.

JSON obj file

{:obj; fileobj.json :}

JSON obj inline:

{:obj;
    {
        "engine": "python",
        "file": "script.py",
        "params": {},
        "callback": "main",
        "template": "template.ntpl"
    }
:}

JSON obj template inline:

{:obj;
    {
        "engine": "python",
        "file": "script.py",
        "params": {},
        "callback": "main"
    }
    >>
    {:;local::varname:}
:}

The idea is to use a script that has its own template to assign values to the variables of that template. In a JSON file, an object with its properties, script to execute, template, etc., is defined.

Example Object:

{
    "engine": "python",          // Optional, "python" (default) or "php"
    "file": "script.py",         // Required, path to script
    "schema": false,             // Optional, default false
    "schema_data": "__test-nts", // Optional, default none
    "venv": "/path/to/.env",     // Optional, overrides config default
    "fpm": "unix:/run/php/php-fpm.sock", // Optional for PHP, overrides config default
    "params": {},                // Optional, parameters passed to the script
    "callback": "main",          // Optional, default "main"
    "template": "template.ntpl"  // Optional, template to process the result
}

The keys “file”, “params”, “venv”, “fpm”, “template” and “schema_data” accept variables {:;varname:}

Examples:

{
    "venv": "{:;VENV_PATH:}",
    "fpm": "{:;PHP_FPM_ENDPOINT:}"
}

Example Script:

def main(params=None):
    schema = globals().get('__NEUTRAL_SCHEMA__')
    schema_data = globals().get('__NEUTRAL_SCHEMA_DATA__')
    return {
        "data": {
            "varname": "Hello from Python!"
        }
    }

__NEUTRAL_SCHEMA__ (requires "schema": true in object) is read-only for accessing the schema. Access to __NEUTRAL_SCHEMA__ can be slow, it is faster to use parameters.

__NEUTRAL_SCHEMA_DATA__ (requires "schema_data": "...") sends only one resolved value from schema data:

  • Global data:
    • "schema_data": "varname"
    • "schema_data": "varname->key->..."
  • Local data:
    • "schema_data": "local::varname"
    • "schema_data": "local::varname->key->..."

If the key does not exist, __NEUTRAL_SCHEMA_DATA__ is None.

PHP script example (via PHP-FPM):

<?php
function main($params = []) {
    $schema = $GLOBALS['__NEUTRAL_SCHEMA__'] ?? null;
    $schema_data = $GLOBALS['__NEUTRAL_SCHEMA_DATA__'] ?? null;
    return [
        "data" => [
            "varname" => "Hello from PHP!"
        ]
    ];
}

For PHP:

  • "engine": "php"
  • "fpm" can be:
    • "unix:/run/php/php-fpm.sock"
    • "tcp://127.0.0.1:9000"
    • "127.0.0.1:9000"
  • If "fpm" is not set, default is config.obj_php_fpm.

Global defaults are read from schema config:

{
  "config": {
    "obj_python_venv": "",
    "obj_php_venv": "",
    "obj_php_fpm": "unix:/run/php/php-fpm.sock"
  }
}

For distributable objects/plugins, avoid hardcoding "venv" or "fpm" in the object JSON. Let each installation define them in config.

It must return a dictionary where the variables are set in the format:

{
    "data": {
        "varname": "value",
        "arrname": {
            "key": "value"
        }
    }
}

The variables are set in the template as “locals” {:;local::varname:}

Example for the previous script:

{:obj; { "file": "script.py" } >>
    {:;local::varname:}
:}

Output:

Hello from Python!

We can define a template in the object, in the latter case they are summed, first the template defined in the object will be shown and then the inline one.

§Modifiers:

{:^obj; ... :}
{:+obj; ... :}

§Modifier: ^ (upline)

Removes previous whitespaces.

§Modifier: + (scope)

For more details about the “+” see “modifiers”.

§No flags

§Examples

Basic usage with file:

{:obj; objfile.json :}

Inline configuration with parameters:

{:obj;
    {
        "file": "scripts/hello.py",
        "params": {
            "name": "World"
        }
    }
:}

{:obj;
    {
        "file": "scripts/hello.py",
        "params": {
            "name": "{:;varname:}"
        }
    }
:}

PHP-FPM usage:

{:obj;
    {
        "engine": "php",
        "file": "scripts/hello.php",
        "fpm": "unix:/run/php/php-fpm.sock",
        "params": {
            "name": "World"
        }
    }
:}

Using schema_data:

{:obj;
    {
        "file": "scripts/data.py",
        "schema_data": "__test-arr-nts"
    }
:}

{:obj;
    {
        "file": "scripts/data.py",
        "schema_data": "__test-obj-nts->level1-obj"
    }
:}

{:obj;
    {
        "file": "scripts/data.py",
        "schema_data": "local::array->text"
    }
:}

Nested path examples:

{:obj;
    {
        "file": "scripts/data.py",
        "schema_data": "__test-obj-nts->level1-obj->level2-obj->level2"
    }
:}

{:obj;
    {
        "file": "scripts/data.py",
        "schema_data": "local::nested-obj->Lorem->Ipsum->Dolor->Sit->Amet"
    }
:}

Using template with script output:

{:obj;
    {
        "file": "scripts/data.py",
        "template": "templates/view.ntpl"
    }
:}

§Limitations

Python is slow and executing Python as a subprocess is even slower, use “{:cache;” when possible.

In practice, PHP via FPM is usually faster than Python for obj calls. PHP via FPM still adds socket overhead per call, so use “{:cache;” when possible.

It is not the same to use “obj” to replace multiple variables than to, for example, create a complete form, in the first case performance will be affected until it is unacceptable, in the second case the loss is likely not noticeable.