PHP code example of lisachenko / php-shared-data-extension

1. Go to this page and download the library: Download lisachenko/php-shared-data-extension library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

lisachenko / php-shared-data-extension example snippets


use Lisachenko\SharedData\PersistentStore;
use ZEngine\Core;

Core::init(); // or Core::preload() from opcache.preload

$store = PersistentStore::boot();

if (!$store->has(AppConfig::class)) {
    // Expensive one-time initialization: runs ONCE per worker process
    $config = $store->persist(AppConfig::class, buildExpensiveConfig());
} else {
    // Every later request in this worker: instant recovery, zero rebuild cost
    $config = $store->get(AppConfig::class);
}

$a = $store->persist(Kernel::class, $kernel);       // graph A, ntainer;                 // first-class reference, perfectly safe

$router = new Router();
$router->container = $a->container;                 // reaches into graph A
$b = $store->persist(Router::class, $router);       // graph B shares that object

$b->container === $a->container;                    // true, in this request and every later one

$store->drop(AppConfig::class);   // true if an entry was removed, false if there was none

$config = $store->get(AppConfig::class);
$store->drop(AppConfig::class);   // RuntimeException: the request still holds ...
unset($config);
$store->drop(AppConfig::class);   // true

use Lisachenko\SharedData\PersistentStore;
use Lisachenko\SharedData\Shm\Arena;

$arena = Arena::create();                    // 64 MB by default, or SHARED_DATA_ARENA_SIZE
$store = PersistentStore::bootShared($arena);

$config = $store->persist(AppConfig::class, buildExpensiveConfig());
$address = $store->addressOf(AppConfig::class);   // eight bytes that mean the same thing
                                                  // in every process of the family
for ($worker = 0; $worker < 4; $worker++) {
    if (pcntl_fork() === 0) {
        $childStore = PersistentStore::bootShared($arena);   // recovery, no globals write
        $config     = $childStore->get(AppConfig::class);    // the SAME object, not a copy
        // ... or attach an address a sibling sent over a socket:
        // $object = $childStore->attachObject($address);
        exit(0);
    }
}

$counters = $store->persist(Counters::class, new Counters(), mutable: true);
$handle   = $store->mutableHandle($counters);

$handle->writeScalars(['hits' => 1, 'misses' => 0]);   // one critical section
$handle->writeString('lastRoute', '/checkout');        // interned in the arena, pointer swapped
$handle->writeReference('owner', $otherSharedObject);  // arena objects only

[$hits, $misses] = array_values($handle->readScalars(['hits', 'misses']));

use Lisachenko\SharedData\Ipc\{SharedChannel, ResultSlotTable, ValueCodec, WakeRegistry};
use Lisachenko\SharedData\Shm\{Arena, ArenaAllocator};

$arena     = Arena::create();
$store     = PersistentStore::bootShared($arena);
$allocator = new ArenaAllocator($arena);
$codec     = new ValueCodec($allocator, $store);
$wake      = WakeRegistry::create($arena);              // socket pairs, created PRE-FORK
$jobs      = SharedChannel::create($allocator, $codec, $wake, 64, name: 'jobs');
$results   = ResultSlotTable::create($allocator, $codec, $wake, 1024);

$slot = $results->allocateSlot();                       // a TICKET: slot index + generation
if (pcntl_fork() === 0) {
    [$job, $ok] = $jobs->recv();                        // parks on the socket, wakes on an event
    $results->complete($slot, process($job));           // writes a record, pokes the waiter
    exit(0);
}
$jobs->send($sharedObject);                             // an address, never a copy
$value = $results->await($slot)->value;                 // read straight out of shared memory
$results->releaseSlot($slot);                           // read and done with: back on the free list

$token = $channel->registerReceiver();      // null => a record is already there, take it now
// ... park the Fiber on notificationStream() in the consumer's own event loop ...
$channel->cancelReceiver($token);           // on unpark, whatever woke it

$ticket = $channel->trySendTicket($value);  // deposits only while a receiver is waiting
$done   = $channel->isTicketTaken($ticket); // the handshake completes when it is TAKEN

use Lisachenko\SharedData\Ipc\ClosureProvenance;

$closures = ClosureProvenance::create($allocator, $store);      // pre-fork, like the arena
$factor   = 3;

$record = $closures->registerSharedClosure('scale', static fn (int $n): int => $n * $factor);
$closures->markForkBarrier();                                   // registration closes here

if (pcntl_fork() === 0) {
    $scale = $closures->resolve($record);                       // or ->closure('scale')
    exit($scale(14) === 42 ? 0 : 1);                            // runs in this worker
}
$jobs->send($closures->closure('scale'));                       // travels as a record address

$store = PersistentStore::boot();          // register/reattach the persistent module
$store->persist(User::class, $o): User;    // convert + return canonical instance; the key is a NAME,
                                           // ::class by convention so get() keeps its inference
$store->persistInstance($o): User;         // per-instance graph named by its own root address:
                                           // any number of one class live at once, none upserts another
$store->attach(): array;                   // name => instance for this request (idempotent)
$store->get(User::class): ?User;           // canonical instance or null
$store->has(User::class): bool;
$store->drop(User::class): bool;           // remove the entry + reclaim what nobody shares
$store->dropInstance($o /* or address */): bool; // same, for an instance graph
$store->objectCount(): int;                // live persistent clones (shared ones counted once)
$store->detach(): void;                    // runs automatically at request shutdown

// fork-shared arena mode (opt-in)
$arena = Arena::create();                  // pre-fork, fixed size, leak-until-teardown
$store = PersistentStore::bootShared($arena);
$store->addressOf(User::class): ?int;      // the eight bytes that travel between workers
$store->attachObject($address): object;    // the receiving half, in any process of the family
$arena->watermark(): int;                  // arena bytes handed out so far
$arena->contains($address, $length): bool; // is this pointer still shared memory?

// IPC primitives (all of them live in the arena; every one has a non-blocking half)
$wake    = WakeRegistry::create($arena);              // pre-fork; sockets are inherited
$channel = SharedChannel::create($allocator, $codec, $wake, $capacity);
$channel->send($value, $timeout): bool;               // trySend() never blocks
$channel->recv($timeout): array;                      // [value, true] | [null, false]; tryRecv() too
$channel->close(): void;                              // crosses processes, drains first
$channel->notificationStream();                       // park your own event loop on this
$slots = ResultSlotTable::create($allocator, $codec, $wake, $capacity);
$slots->allocateSlot(): int;                          // a ticket: SlotTicket::indexOf()/generationOf()
$slots->complete($ticket, $value): void;              // completePanic($ticket, SharedError::capture(...))
$slots->await($ticket, $timeout): SlotResult;         // readSlot() never blocks
$slots->releaseSlot($ticket): void;                   // settled + read => back on the free list
$slots->outstanding(); $slots->highWaterMark();       // what a soak watches plateau

// shared closures (registration is the acceptance test; everything else is refused)
$closures = ClosureProvenance::create($allocator, $store);   // pre-fork
$closures->registerSharedClosure($name, $closure): int;      // returns the record address
$closures->markForkBarrier(): void;                          // closes registration for the family
$closures->closure($name): Closure;                          // resolve($address) by address
$codec = new ValueCodec($allocator, $store, $closures);      // lets registered closures travel