Initial commit
This commit is contained in:
133
src/Shm/SharedData.php
Normal file
133
src/Shm/SharedData.php
Normal file
@ -0,0 +1,133 @@
|
||||
<?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->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);
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user