php-ipc/src/Shm/SharedData.php

134 lines
3.1 KiB
PHP

<?php
namespace NoccyLabs\Ipc\Shm;
/**
* Shared data storage with a key-value interface
*
*/
class SharedData extends SharedMemory
{
private const IDX_LOCK = 0;
private const IDX_MAP = 1;
private const IDX_DATA = 2;
private $checks = [];
/**
* Acquire a lock
*
* @return void
*/
private function lock()
{
$expire = microtime(true) + 1;
while ($this[self::IDX_LOCK] !== null) {
usleep(1000);
// Forcefully continue after 1 second
if (microtime(true)>$expire) {
break;
}
}
$this[self::IDX_LOCK] = 1;
}
/**
* Release the lock
*
* @return void
*/
private function unlock()
{
$this[self::IDX_LOCK] = null;
}
/**
* Get the value of a key
*
* @param string $key
* @return mixed
*/
public function get(string $key)
{
$map = (array)$this[self::IDX_MAP];
// If the key doesn't exist, return null
if (!array_key_exists($key, $map)) {
return null;
}
$index = $map[$key];
$keyvar = $this[self::IDX_DATA + $index];
// If for some reason we don't get anything back, return null
if (!$keyvar) {
return null;
}
// Save the checksum for later and return the value
$this->checks[$key] = $keyvar[0];
return $keyvar[1];
}
/**
* Set a key to a specific value
*
* @param string $key
* @param mixed $value
* @param boolean $check
* @return boolean
*/
public function set(string $key, $value, $check=false):bool
{
// Acquire a lock and fetch the map
$this->lock();
$map = (array)$this[self::IDX_MAP];
// If the key doesn't exist in the map, we need to set it up
if (!array_key_exists($key, $map)) {
$free = 0;
// Find first free index
foreach ($map as $map_key=>$index) {
if ($index != $free) { break; }
$free++;
}
// Store the index in the map, and sort it by index.
$map[$key] = $free;
asort($map);
$this[self::IDX_MAP] = $map;
}
// Look up the key and fetch the data
$index = $map[$key];
$keyvar = $this[self::IDX_DATA + $index];
// Check the data to make sure the data hasn't changed since last read
if ($check && $keyvar && array_key_exists($key, $this->checks)) {
if ($keyvar[0] != $this->checks[$key]) {
$this->unlock();
return false;
}
}
// Update the data
$this[self::IDX_DATA + $index] = [ md5($value), $value ];
$this->checks[$key] = md5($value);
$this->unlock();
return true;
}
/**
* Check if the map contains the key. This method doesn't acquire a lock
*
* @param string $key
* @return bool
*/
public function contains(string $key):bool
{
$map = (array)$this[self::IDX_MAP];
return array_key_exists($key, $map);
}
}