Server Functions, Exposed Securely

Call PHP functions from your frontend as if they were local JavaScript functions. Protected by the #[Exposed] attribute.

Attribute Security

Functions are private by default. You must explicitly opt-in using PHP 8 Attributes to expose them to the client.

Built-in Auth Checks

Stop writing if (!$user) return;. Just add requiresAuth: true and the framework handles the guard.

Role Based Access

Granular control. Restrict execution to specific roles like ['admin', 'editor'] directly in the function signature.

Security & Authentication

Control who can access your backend logic using PHP Attributes.

Require Authentication
use PP\Attributes\Exposed;

// Only logged in users can call this
#[Exposed(requiresAuth: true)]
function getUserProfile() {
    return Auth::user();
}
Role Based Access (RBAC)
use PP\Attributes\Exposed;

// Implies requiresAuth: true
#[Exposed(allowedRoles: ['admin', 'editor'])]
function deletePost($args) {
    // Safe to perform admin action
    Post::delete($args->id);
}
How it works: If a user tries to call a function they don't have access to, the server rejects the request immediately. The client can also receive a redirection instruction to the login page if requiresAuth fails.

Data Flow Example

Backend (PHP)
server-side
<?php
use PP\Attributes\Exposed;

#[Exposed]
function updateUser($data) {
    // $data is an object automatically
    $email = $data->email;

    return [
        'success' => true,
        'msg' => "Updated $email"
    ];
}
?>
Frontend (JS)
client-side
<script>
    async function save() {
        const res = await pp.fetchFunction(
            'updateUser',
            { email: 'john@doe.com' }
        );

        if (res.success) {
            console.log(res.msg);
        }
    }
</script>

Real-time Streaming

Stream data from PHP to the client in real-time using Generators and Server-Sent Events (SSE).

Generator (PHP)
server-side
<?php
use PP\Attributes\Exposed;

#[Exposed]
function streamAIResponse($args) {
    yield "Connecting...";

    // Simulate long running task
    sleep(1);

    yield ['status' => 'thinking', 'progress' => 50];

    sleep(1);

    yield "Done!";
}
?>
Event Listener (JS)
client-side
<script>
    await pp.fetchFunction(
        'streamAIResponse',
        { prompt: 'Hello' },
        {
            onStream: (chunk) => {
                // Auto-parsed as JSON or String
                console.log(chunk);
            },
            onStreamComplete: () => {
                console.log('Stream finished');
            }
        }
    );
</script>

Auto-JSON Parsing

If the chunk is a valid JSON string (array or object), PulsePoint automatically parses it before firing onStream.

Native PHP Generators

No complex event loop libraries required. Just use the native yield keyword in your PHP function.

File Uploads with Real Progress

When your payload includes a File, PulsePoint automatically switches the request to multipart/form-data. To track real upload progress, pass options.onUploadProgress. Under the hood, PulsePoint uses XMLHttpRequest only for that case (because fetch() cannot report upload progress).

Upload Handler (PHP)
server-side
<?php
use PP\Attributes\Exposed;

// Receives multipart/form-data automatically when File is detected in the payload
#[Exposed(requiresAuth: true)]
function processUpload($data) {
    // $data->title, $data->description, $data->folderId ...
    // Your UploadFile / move_uploaded_file logic here

    return [
        'success' => true,
        'message' => 'Files uploaded successfully'
    ];
}
?>
Real Progress (JS)
client-side
<script>
    const [progress, setProgress] = pp.state(0);

    async function uploadFile(file) {
        const res = await pp.fetchFunction(
            'processUpload',
            {
                file,
                title: 'Document Title',
                description: 'Short description',
                folderId: 'root'
            },
            {
                onUploadProgress: ({ percent }) => {
                    // percent can be null if total is unknown
                    if (percent != null) setProgress(Math.floor(percent));
                },
                onUploadComplete: () => {
                    setProgress(100);
                }
            }
        );

        if (res.success) {
            console.log(res.message);
        }
    }
</script>
Important: Upload progress represents bytes sent from the browser to the server. If your server does heavy post-upload processing (virus scan, transcoding, indexing), the progress can reach 100% while the request is still running. For “processing progress”, pair uploads with streaming (SSE) from the backend after the file is received.

Zero-Config Multipart

If PulsePoint detects File / FileList in your payload, it auto-builds FormData. No manual encoding required.

Progress Without UI Hacks

No fake setProgress(15) or setProgress(100). The UI reflects real upload bytes via onUploadProgress.

Client API Reference

Method Signature TypeScript Definition
fetchFunction<T = any>(
  functionName: string,
  data: Record<string, any> = {},
  options: boolean | RpcOptions = false
): Promise<T | void>

// RpcOptions (Streaming + Upload Progress)
type RpcOptions = {
  abortPrevious?: boolean;

  // Streaming (SSE)
  onStream?: (chunk: any) => void;
  onStreamError?: (error: any) => void;
  onStreamComplete?: () => void;

  // Upload Progress (only when payload contains File/FileList)
  onUploadProgress?: (info: {
    loaded: number;
    total: number | null;
    percent: number | null;
  }) => void;
  onUploadComplete?: () => void;
};
  • functionName The exact name of the PHP function. Supports namespaces for static methods (e.g., User::update).
  • data Object containing arguments. Supports nested objects, arrays, and File/FileList (auto switches to multipart/form-data).
  • options.onStream Callback fired for every SSE chunk received. JSON-like chunks are automatically parsed.
  • options.onUploadProgress Fired during file upload. Receives loaded, total (or null), and percent (0–100 or null). When this handler is present, PulsePoint uses XHR for that request (enabling upload progress).
  • options.abortPrevious If true, cancels any pending requests from this caller before starting the new one.

Zero-Config File Uploads

PulsePoint detects File objects in your payload and automatically switches the content-type. No need for manual FormData construction.

Auto-Response Parsing

PHP arrays are automatically converted to JSON objects on the frontend. Strings and booleans are preserved.