Pionia Helpers

Who this is for

You are writing DeskFlow services and want quick access to common framework entry points — response() for Moonlight envelopes, table() for tasks / team_members, logger() for action traces — without wiring the container manually.

What you will learn

  • Which helpers to use for Moonlight responses, Porm queries, and logging
  • Boot constraints (AppRealm::create() before helpers work)
  • Where to find security, cache, async, and validation shortcuts

Before you start

Before you start
  • Booted app via bootstrap/application.php (HTTP or php pionia)
  • Services — where helpers are called from actions
  • Optional: App providers — boot-time wiring when helpers are not yet available

How it works

Pionia exposes global helpers after the application boots. They wrap AppRealm, Porm, cache, logging, and Moonlight dispatch.

flowchart LR Boot[AppRealm::create] --> Helpers[Global helpers] Helpers --> R[response] Helpers --> T[table / db] Helpers --> L[logger / report] R --> Task[TaskService actions] T --> Task L --> Task

Boot timing

Helpers like app(), realm(), and logger() are not available in bootstrap/application.php before AppRealm::create() returns. Use service providers for boot-time wiring.


DeskFlow Moonlight responses — response()

Return the standard JSON envelope from any service action:

protected function listAction(Arrayable $data): ApiResponse
{
    $rows = table('tasks')->where('status', $data->get('status', 'open'))->all();

    return response(0, 'OK', ['tasks' => $rows]);
}

protected function createAction(Arrayable $data): ApiResponse
{
    $this->mustAuthenticate();

    return response(0, 'Task created', ['id' => $newId]);
}

Related response helpers:

HelperPurpose
response($code, $message, $data?)Standard Moonlight envelope
cachedResponse() / recached()Cache action output when service caching is enabled
renderToString()Render a template to HTML without exiting the process
$html = renderToString('emails/welcome', ['name' => $member->name]);
return cachedResponse($this, response(0, 'OK', $rows), 300);

Prefer renderToString() over deprecated render() (which exits in FPM/CLI).


DeskFlow data access — table() / db()

Query Northwind tables through Porm:

$task = table('tasks')->get($id);
$members = table('team_members')->where('active', 1)->all();
$pdo = connectionManager()->connection('default');
HelperPurpose
table($name, …)Primary query builder entry
db($name, …)Alias of table()
connectionManager()Pooled PDO per process

See Database (Porm).


DeskFlow observability — logger() / report()

Trace actions and report throwables through the exception pipeline:

protected function updateAction(Arrayable $data): ApiResponse
{
    logger()->info('task.update', [
        'task_id' => $data->get('id'),
        'user' => $this->auth()?->user?->email,
    ]);

    try {
        // …
    } catch (\Throwable $e) {
        report($e);
        throw $e;
    }

    return response(0, 'Updated');
}

logger('api')->debug('Payload', $payload);
HelperPurpose
logger()Default PSR-3 channel
logger('name')Named channel via LogManager
report($throwable)Log via exception pipeline (no HTTP response)

See Logging.


Application access — app() / realm() / container()

All three return the booted AppRealm singleton (v3):

$debug = app()->environment()->get('DEBUG');
$routes = realm()->getRoutes();
$service = container()->get(MyService::class);

realm() and container() are aliases for the same instance after boot.


Moonlight dispatch — moonlight() / defer() / async()

Post-response work (same process, not a new thread):

defer(function () {
    logger()->info('Runs after the client received the JSON response');
});

Use defer() for fire-and-forget. Use async(closure) only when you need a promise (.then(), await()). On PHP 8.5+ cast unused promises: (void) async(...).

Moonlight jobs (durable / heavy work):

async('mail', 'send_welcome', ['email' => $user->email]);
moonlight()->async('mail', 'send_welcome', ['email' => $user->email]); // 202 + job_id when RR Jobs enabled
moonlight()->dispatch('member', 'list', ['limit' => 10]);             // sync programmatic call

See Background work for execution order and runtime tables.


API path helpers

HelperExample
apiBase()/api/
defaultApiVersion()v1
apiVersionPath()/api/v1/
apiPingPath()/api/v1/ping
apiCatalogPath()/api/v1/__catalog

Use these in templates and frontend config — not hard-coded /api/v1 strings. Prefer them over deprecated route() or raw baseUrl() in application code.


Environment and runtime — env() / serverPort()

$port = serverPort();              // PORT → SERVER_PORT → settings.ini → 8000
$port = serverPort(9001);          // CLI override
setEnv('RUNTIME_FLAG', '1');       // request-scoped
$jwtSecret = env('JWT_SECRET_KEY');

Security — security() and secure_*

security() returns the Pionia\Security\Security singleton. Every method has a snake_case helper.

CategoryExamples
Random / IDssecure_token(), secure_otp(), secure_uuid(), secure_ulid(), secure_password()
Passwordshash_password(), verify_password() — use PHP’s password_needs_rehash() or security()->needsRehash()
Hashingsecure_hash(), secure_hmac(), verify_hmac(), secure_equals()
Symmetric cryptoencrypt(), decrypt() (needs APP_KEY + ext-sodium)
Public-key cryptosecurity_key_pair(), encrypt_with_public_key(), rsa_encrypt()
Validatorsis_uuid(), is_ulid(), is_otp(), is_token()

Full reference: Security utilities.

$token = secure_token();
$hash = hash_password($plain);
$sealed = encrypt_with_public_key('payload', $publicKey);

Validation — rules() / validate() / validations()

rules($data, [
    'email' => 'required|email',
    'password' => 'required|password|min:8',
]);

validate('email', $this->request)->required()->email();
validate('phone', $data)->required()->rule('kenya_phone');

validations()->extend('kenya_phone', function (ValidationContext $ctx): void {
    // …
});

See Validations.


Caching — cache()

cache()->set('key', $value, 300);
cache('redis')->get('session:1');

See Caching.


Other helpers

HelperPurpose
alias()Resolve path aliases (PUBLIC_DIR, STORAGE_DIR)
addIniSection()Runtime INI section (default environment/generated.ini)
tap($value, $closure)Side effect then return original value
envKeys()List loaded .env variable names (stats page)
yesNo() / asBool()Display and env boolean coercion
router($app)Register switches in provider routes() — app switches use [app_switches]
framework(), version(), appName()Branding and core version metadata

For framework internals (providers, kernel, cache adapters), generate local reference docs with composer document:frameworkbuild/docs/.

Common mistakes

  • Calling table() or logger() inside bootstrap/application.php before AppRealm::create() returns
  • Using render() in tests or workers — prefer renderToString() to avoid unexpected process exit
  • Hard-coding /api/v1 instead of apiVersionPath() — breaks when adding v2
  • Using deprecated route() instead of router($app) in providers

What’s next

Services

Where response() and table() are used.

Logging

Channels, redaction, and report().

Database

Porm queries with table().