PHPackages                             seba1rx/tabmanager - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. seba1rx/tabmanager

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

seba1rx/tabmanager
==================

Per-tab PHP session isolation — each browser tab gets its own namespaced $\_SESSION slot via a UUIDv4 header

v1.1.0(2d ago)00MITPHPPHP &gt;=8.1

Since Jun 8Pushed 2d agoCompare

[ Source](https://github.com/seba1rx/tabmanager)[ Packagist](https://packagist.org/packages/seba1rx/tabmanager)[ RSS](/packages/seba1rx-tabmanager/feed)WikiDiscussions main Synced yesterday

READMEChangelog (2)Dependencies (1)Versions (3)Used By (0)

TabManager
==========

[](#tabmanager)

**Package name:** `seba1rx/tabmanager`

TabManager provides **per-tab session isolation** for PHP applications. It ensures each browser tab maintains its own independent session state, preventing shared session data between tabs — useful for admin dashboards, multi-step forms, or any multi-tab workflow.

---

How It Works
------------

[](#how-it-works)

Each browser tab receives a unique UUIDv4 generated by the JS client and stored in `sessionStorage`. On every AJAX request the UUID is sent as the `X-TabManager-TabId` header. The PHP backend reads this header to isolate `$_SESSION` data under that UUID.

**Why a header instead of a cookie?** Cookies are shared across all tabs for the same domain — if Tab B sets a new tab-ID cookie, Tab A's cookie is overwritten immediately. The header is sent per-request with the correct UUID from `sessionStorage`, which is strictly per-tab.

**Duplicate-tab detection:** when a browser duplicates a tab it copies `sessionStorage`, so the new tab would inherit the original's UUID. On init the JS client uses the `BroadcastChannel` API to ask whether any other running tab already claims that UUID. If yes, a fresh UUID is generated before the tab registers with the server.

**Heartbeat:** while a tab is visible the JS client POSTs to the heartbeat endpoint every 30 s, keeping `last_active` accurate on the server. The heartbeat pauses when the browser hides or freezes the tab (Page Visibility API) and resumes — with an immediate beat — when the tab becomes visible again.

---

AI-assisted implementation
--------------------------

[](#ai-assisted-implementation)

The package ships with **`llms.txt`** — a structured reference document written for AI coding assistants. It covers the full integration flow, every method and configuration option, security constraints, common mistakes, and a minimal working example.

If you are implementing this package with the help of an AI assistant (Claude, Copilot, Cursor, etc.), point it at that file before asking it to write integration code:

```
Read llms.txt before implementing seba1rx/tabmanager.

```

---

Implementation Guide
--------------------

[](#implementation-guide)

### Step 1 — Install

[](#step-1--install)

```
composer require seba1rx/tabmanager
```

The Composer post-install script copies `seba1rx_tabmanagerclient.js` to your project root automatically.

---

### Step 2 — Include the JS client in your HTML

[](#step-2--include-the-js-client-in-your-html)

Add the script tag **before** your own application scripts. Optionally set configuration globals in an inline script before it.

```

        // Only needed if /tabmanager/* routes are not available in your setup.
        // For example, when using php -S without a router.
        // window.TABMANAGER_HEARTBEAT_URL = '/your/custom/heartbeat';

        // Uncomment to enable verbose JS logging during development:
        // window.TABMANAGER_DEBUG = true;

```

The client runs automatically on `DOMContentLoaded`. It assigns a UUID to the tab, detects duplicates, sets a fallback cookie, notifies the backend, and starts the heartbeat.

---

### Step 3 — Await `TabManagerClient.ready` before using the tab ID

[](#step-3--await-tabmanagerclientready-before-using-the-tab-id)

`init()` is async (it waits ~80 ms for the `BroadcastChannel` duplicate check). If your scripts read `TabManagerClient.tab.id` or call `getHeaders()` before init completes they will get `null`.

```
document.addEventListener('DOMContentLoaded', async () => {
    await TabManagerClient.ready; // wait for UUID to be confirmed

    console.log('Tab ID:', TabManagerClient.tab.id);
    // safe to use getHeaders() from here
});
```

---

### Step 4 — Add the tab header to every AJAX call

[](#step-4--add-the-tab-header-to-every-ajax-call)

Spread `TabManagerClient.getHeaders()` into the `headers` object of every `fetch` or `XMLHttpRequest` that talks to your PHP backend. This sends `X-TabManager-TabId: ` so the server knows which tab is making the request.

```
const response = await fetch('/your-endpoint', {
    method: 'POST',
    headers: {
        'Content-Type': 'application/json',
        ...TabManagerClient.getHeaders(), //  Calls that omit `getHeaders()` will fall back to the `TABMANAGER_TABID` cookie, which is shared across tabs and therefore unreliable for tab isolation.

---

### Step 5 — Use TabManager in your PHP endpoints

[](#step-5--use-tabmanager-in-your-php-endpoints)

The bootstrap file runs automatically on every `require 'vendor/autoload.php'`, registering the internal endpoints. You do not need to include or configure anything extra.

In any PHP file that handles a request from the JS client:

```
