Skip to content

Cross Module API

Ray Fung edited this page Feb 26, 2026 · 4 revisions

Cross-Module API

The Cross-Module API allows modules to expose and consume typed commands through a provider/consumer handshake. This enables service-style communication between independent modules without tight coupling.

How It Works

The flow is:

  1. Provider registers API commands via Agent::addAPICommand() in __onInit().

  2. Consumer requests an API handle via $this->api() in __onReady() (after all modules are loaded).

  3. Framework performs a handshake: calls the provider's __onAPICall() with the consumer's ModuleInfo.

  4. If the provider grants access, the consumer receives an Emitter object and can invoke commands.

Defining an API Provider

// In controller __onInit()

public function __onInit(Agent $agent): bool

{

    // Register API commands → second argument is a closure file path (without .php)

    $agent->addAPICommand('getVersion', 'api/getVersion');

    $agent->addAPICommand('processData', 'api/processData');



    return true;

}

Then create the closure files:

// controller/api/getVersion.php

return function(): string {

    return '1.0.0';

};
// controller/api/processData.php

return function(string $data): string {

    return strtoupper($data);

};

Authorizing the Handshake

// Provider's controller

public function __onAPICall(ModuleInfo $module, string $method, string $fqdn = ''): bool

{

    // Grant access to specific modules

    return in_array($module->getCode(), [

        'demo/consumer_module',

        'demo/another_module',

    ]);

}

⚠️ Warning: Security: Always validate the caller in __onAPICall(). Returning true unconditionally allows any module to call your API.

Consuming an API

// Consumer's controller __onReady()

public function __onReady(): void

{

    $api = $this->api('demo/provider_module');



    if ($api) {

        // Invoke the provider's command

        $version = $api->getVersion();

        $result  = $api->processData('hello');

    }

}

API Class Reference

| Method | Return | Description |

| --- | --- | --- |

| __call(string $command, array $args) | mixed | Invoke a registered API command via magic method |

| has(string $command) | bool | Check if a command exists |

| getModuleCode() | string | Get the provider's module code |

Agent API Methods

| Method | Context | Description |

| --- | --- | --- |

| addAPICommand(mixed $command, ?string $path = null) | Provider (__onInit) | Register an API command mapped to a closure file path (without .php) |

| getAPI(string $moduleCode) | Consumer | Request API handle from another module |

Best Practices

  • Register API commands in __onInit(), where the Agent is available. Use __onReady() to consume APIs.

  • Use __onAPICall() to whitelist consumers by module code.

  • Always check the return value of getAPI() or $this->api() → it returns null if the handshake is denied.

  • Keep API commands focused and well-named to serve as a stable contract between modules.

← PreviousEvent System

Next → Database

Clone this wiki locally