Skip to content

DotBridge

DotBridge is a key component of the DotApp Framework, ensuring secure and efficient communication between server-side PHP and client-side JavaScript via AJAX requests. It allows you to define PHP functions callable from the front-end, manage inputs, and protect communication from unauthorized access or abuse. DotBridge is designed to provide flexibility, security, and easy integration of dynamic features into web applications.

1.1 What is DotBridge?

DotBridge in the DotApp Framework is a class Dotsystems\App\Parts\Bridge that serves as a bridge between back-end PHP logic and front-end JavaScript actions. It enables calling PHP functions from HTML elements (e.g., buttons, form inputs) through encrypted AJAX requests. Bridge calls POST to the current page URL. Its primary role is to simplify client-server communication while ensuring it is verified, encrypted, and protected against attacks such as CSRF or request replay.

Bridge functions are registered in a module with Bridge::listen() or Router::bridge() (controller string).

1.2 Key Features

DotBridge offers a wide range of features that simplify the development of secure and dynamic applications:

  • Secure Communication: Uses data encryption, key verification, and CRC checks to protect requests.
  • Calling PHP Functions: Define PHP functions callable from JavaScript with support for before and after callbacks.
  • Validation Filters: Real-time input validation (e.g., email, URL, password) with visual feedback on the client side.
  • Rate Limiting: Ability to set request limits per time (rateLimit(seconds,clicks)).
  • One-Time Keys: Support for oneTimeUse and regenerateId for enhanced security.
  • Flexibility: Supports various events (e.g., click, keyup) and dynamic inputs from HTML.
  • Chaining: Method chaining for cleaner code on both PHP and JavaScript sides.

1.3 Basic Operating Principles

DotBridge handles communication between the client and server in the following steps:

  1. Generates a unique session key and registers PHP functions on the server side via Bridge::listen() or Router::bridge().
  2. In HTML, events (e.g., {{ dotbridge:on(click)="fnName" }}) and inputs (e.g., {{ dotbridge:input="name" }} or short dotbridge="name") are defined and linked to PHP functions.
  3. When an event is triggered (e.g., a click), an AJAX POST is sent to the bound page URL (default: the current page) with encrypted data.
  4. The server verifies the key, decrypts the data, checks request limits, and executes the requested PHP function.
  5. The result is returned as a JSON response, which JavaScript can further process.

Communication is safeguarded with data encryption, key verification, and limits to prevent abuse or unauthorized access.

Example of Basic Usage:

<button {{ dotbridge:on(click)="sayHello" }}>Click me</button>
        

use Dotsystems\App\Parts\Bridge;
use Dotsystems\App\Parts\Router;

$urls = ['/hello', '/hello/'];
Bridge::listen($urls, "sayHello", function ($request) {
    return ["status" => 1, "message" => "Hello from the server!"];
}, Router::STATIC_ROUTE);
        

Upon clicking the button, the PHP function sayHello is called and returns a JSON response with the message "Hello from the server!".

2. Getting Started

This chapter guides you through the basics of working with DotBridge in the DotApp Framework – from initialization to defining your first function, adding a front-end event, and handling the response in JavaScript.

Register handlers with Router::bridge(['/path','/path/'], 'fnName', 'Examples:Forms@submit3!', Router::STATIC_ROUTE) or Bridge::listen($urls, 'fnName', $callback, Router::STATIC_ROUTE) in the module’s initialize().

2.1 Initializing DotBridge

DotBridge starts with the application. A unique session key is stored in _bridge.key. Register page handlers from a module with Bridge::listen() or Router::bridge(). Bridge AJAX requests POST to the current page URL; the template modifier url(/path) binds a call to another URL (invalid bound URL: HTTP 403, error_code 6). Include /assets/dotapp/dotapp.js on pages that use bridge events.

2.2 Defining a PHP Function

On the server side, register a function with Bridge::listen(). The callback receives a $request object; payload fields are in $request->data(true)['data']. Return a string or an array (e.g. ['ok' => true, 'message' => '...']) — the framework wraps it as JSON { status: 1, body: <your return> }. Place handlers in a module’s initialize() or a module controller method.

Example:

use Dotsystems\App\Parts\Bridge;
use Dotsystems\App\Parts\Router;

$urls = ['/contact', '/contact/'];
Bridge::listen($urls, "sendMessage", function ($request) {
    $data = $request->data(true)['data'];
    $message = $data["user.message"] ?? "No message";
    return ["ok" => true, "message" => "Message received: " . $message];
}, Router::STATIC_ROUTE);
    

The sendMessage function is now ready to be called from the front-end and will return a JSON response with the received message.

2.3 Adding a Front-End Event

In HTML, use the attribute {{ dotbridge:on(event)="functionName(params)" }} to link an event (e.g., click, keyup) to a PHP function. Define inputs with {{ dotbridge:input="name" }} or the short form dotbridge="name" on plain HTML elements. Add modifiers such as rateLimit(60,5) or oneTimeUse to control behavior.

Example:

<input type="text" {{ dotbridge:input="user.message" }}>
<button {{ dotbridge:on(click)="sendMessage(user.message)" rateLimit(60,5) }}>Send</button>
    

When the button is clicked, the value from the user.message input is sent to the sendMessage function with a limit of 5 requests per 60 seconds.

2.4 Handling the Response in JavaScript

On the client side, include /assets/dotapp/dotapp.js and use $dotapp().bridge() to define before and after callbacks. The after callback receives body (your PHP return value). Use onResponseCode with $dotapp().parseReply() to read framework error envelopes (status_txt on HTTP 400/403/429).

Example:

$dotapp().bridge("sendMessage", "click")
    .before(function () {
        $dotapp("button").html("Sending...");
    })
    .after(function (body) {
        $dotapp("button").html("Done!");
        alert(body.message || body);
    })
    .onResponseCode(function (status, text) {
        var reply = $dotapp().parseReply(text);
        alert((reply && reply.status_txt) ? reply.status_txt : "Failed");
    }, 429);
    

Before sending, the button text changes to "Sending...", and after receiving the response, it displays the message from the PHP function. Rate-limit and other framework errors are handled via onResponseCode and parseReply.

3. Advanced Usage

This chapter covers advanced features of DotBridge, such as validation filters, rate limiting, method chaining, and working with dynamic data. These tools enable the creation of more robust and secure applications with greater control over behavior.

3.1 Validation Filters

DotBridge provides built-in validation filters for real-time input validation on the client side. Filters like email, url, phone, or password apply regular expressions and visual feedback (CSS classes) based on input validity. They are used in HTML via the dotbridge-result="0" dotbridge-input="name" attribute.

Available Arguments:

  • filter: Name of the filter (e.g., email).
  • start_checking_length: Minimum number of characters to start validation.
  • class_ok: CSS class for valid input.
  • class_bad: CSS class for invalid input.
Example:

<input type="text" {{ dotbridge:input="user.email(email, 5, 'valid-email', 'invalid-email')" }}>
    

The user.email input starts validation after 5 characters. If the email is valid, the valid-email class is added; if invalid, invalid-email.

3.2 Rate Limiting and Security Mechanisms

DotBridge allows you to limit the number of requests using the rateLimit(seconds,clicks) parameters, protecting the application from abuse. Additional security features include oneTimeUse (single-use key) and regenerateId (key regeneration after each use).

Usage:

  • rateLimit(seconds,clicks): Maximum of clicks requests per seconds. Repeat the modifier for multiple windows (e.g. rateLimit(60,2) rateLimit(3600,5)).
  • oneTimeUse: Key is valid for only one use.
  • regenerateId: Generates a new key after each call.
  • url(path): POST target URL (default: current page).
  • internalID(id): Assign a stable internal ID to the bridge object.
  • expireAt(timestamp): Expire the bridge key at a Unix timestamp.
Example:

<button {{ dotbridge:on(click)="submitForm" rateLimit(60,10) rateLimit(3600,100) }}>Submit</button>
<button {{ dotbridge:on(click)="submitForm" oneTimeUse }}>Submit</button>
<button {{ dotbridge:on(click)="submitForm" regenerateId }}>Submit</button>
    

1. Example - The button allows a maximum of 10 clicks per minute, 100 per hour.

2. Example - The button allows only one click, and the listener is removed.

3. Example - The button regenerates its ID on each click.

3.3 Method Chaining

In PHP, register with Bridge::listen(); optional before() / after() chain on that call. In JavaScript, use $dotapp().bridge() with before() and after().

Example:

use Dotsystems\App\Parts\Bridge;
use Dotsystems\App\Parts\Router;

$urls = ['/process', '/process/'];
Bridge::listen($urls, "processData", function ($request) {
    $data = $request->data(true)['data'];
    return ["result" => trim($data["value"] ?? "")];
}, Router::STATIC_ROUTE)
->before(function ($request) {
    return null;
})
->after(function ($request) {
    return null;
});
    

// JavaScript
$dotapp().bridge("processData", "click")
    .before(function () {
        $dotapp(".trigger").addClass("loading");
    })
    .after(function (body) {
        $dotapp(".trigger").removeClass("loading");
        console.log(body.result);
    });
    

The handler trims input on the server; before and after hooks receive $request. On the client, a loading state is toggled around the call.

3.4 Working with Dynamic Data

DotBridge allows sending and processing dynamic data from multiple inputs defined in HTML. Inputs use {{ dotbridge:input="name" }} or the short form dotbridge="name" and arrive in $request->data(true)['data'] on the server.

Example:

<input type="text" {{ dotbridge:input="user.name" }}>
<input type="text" {{ dotbridge:input="user.email" }}>
<button {{ dotbridge:on(click)="saveUser(user.name, user.email)" }}>Save</button>
    

use Dotsystems\App\Parts\Bridge;
use Dotsystems\App\Parts\Router;

$urls = ['/account', '/account/'];
Bridge::listen($urls, "saveUser", function ($request) {
    $data = $request->data(true)['data'];
    $name = $data["user.name"] ?? "";
    $email = $data["user.email"] ?? "";
    return ["ok" => true, "message" => "User $name ($email) saved"];
}, Router::STATIC_ROUTE);
    

Upon clicking, the values from the user.name and user.email inputs are sent to the saveUser function and returned in the response.

4. Best Practices and Tips

This chapter provides recommendations and tips for effectively and securely using DotBridge. It covers security optimization, debugging issues, and integration with other parts of the DotApp Framework.

4.1 Optimizing Security

Security is a critical aspect when using DotBridge. The following recommendations will help minimize risks and ensure robust communication:

  • Use rate limiting: Set rateLimit(seconds,count) for actions sensitive to repeated calls (e.g. rateLimit(60,2) rateLimit(3600,5) for multiple windows).
  • Enable one-time keys: For critical operations (e.g., form submission), use oneTimeUse or regenerateId to prevent reuse of the same key.
  • Validate inputs: Combine validation filters with additional server-side checks (e.g., filter_var()) to ensure consistent validation.
  • Monitor sessions: Regularly check and clean old keys in _bridge.objects to avoid memory overflow.
  • Use encryption: Leverage the built-in DotApp encryption (encrypt(), decrypt()) for sensitive data in communication.
Example:

<button {{ dotbridge:on(click)="secureAction" rateLimit(60,2) oneTimeUse }}>Execute</button>
    

use Dotsystems\App\Parts\Bridge;
use Dotsystems\App\Parts\Router;

$urls = ['/secure', '/secure/'];
Bridge::listen($urls, "secureAction", function ($request) {
    return ["ok" => true, "message" => "Action executed securely"];
}, Router::STATIC_ROUTE);
    

This example limits the action to 2 calls per 60 seconds and allows only one use of the key.

4.2 Debugging and Troubleshooting

While working with DotBridge, you may encounter errors. Here are common issues and their solutions:

  • CRC check failed (error_code 1): Verify that the data sent from the front-end hasn’t been modified. Check the integrity of the JavaScript code and network requests.
  • Bridge key does not match (error_code 2): Ensure the session key (_bridge.key) matches what the client sends. This could be due to an expired session.
  • Function not found (error_code 3): Confirm that the function is registered with Bridge::listen() or Router::bridge() and that the name matches the HTML call.
  • Rate limit exceeded (error_code 4): You’ve exceeded the set limit. Adjust rateLimit(seconds,count) or inform the user to wait.

Tip: Enable debugging in DotApp and inspect bridge POST responses in the browser’s developer tools (Network tab). The default POST target is the current page URL.

4.3 Integration with Other Parts of DotApp

DotBridge is designed to work seamlessly with other DotApp components, such as Router, Request, and the database. Integration allows you to build complex applications with minimal effort.

Integration Examples:

  • With Router: Use Router::bridge($urls, $fnName, 'Module:Controller@method!', Router::STATIC_ROUTE) to bind handlers to page URLs. Signature: ($url, $function_name, $callback, $static = false).
  • With Request: Handlers receive $request; payload fields are in $request->data(true)['data'].
  • With Database: You can insert front-end data with DB::module('RAW').
Example with Database:

<input type="text" {{ dotbridge:input="user.email" }}>
<button {{ dotbridge:on(click)="saveEmail(user.email)" }}>Save</button>
    

use Dotsystems\App\Parts\Bridge;
use Dotsystems\App\Parts\DB;
use Dotsystems\App\Parts\Router;

$urls = ['/notes', '/notes/'];
Bridge::listen($urls, "saveEmail", function ($request) {
    $data = $request->data(true)['data'];
    $email = $data["user.email"] ?? "";
    if (filter_var($email, FILTER_VALIDATE_EMAIL)) {
        DB::module('RAW')->q(function ($qb) use ($email) {
            $qb->insert('helloworld_notes', ['email' => $email]);
        })->execute(
            function ($result, $db, $exec) {},
            function ($error) {}
        );
        return ["ok" => true, "message" => "Email saved"];
    }
    return ["ok" => false, "message" => "Invalid email"];
}, Router::STATIC_ROUTE);
    

Upon clicking, the email is validated and saved to the database, returning a success or error response.