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
Loading please wait ...
Before you can download the PHP files, the dependencies should be resolved. This can take some minutes. Please be patient.