Commit 79a02a54 authored by Hannes Peterseim's avatar Hannes Peterseim
Browse files

inital commit

parents
Pipeline #1 canceled with stages
# *YAMS* means *Y*et *A*nother *M*emory *S*tore
YAMS v 1.0
2021 by Hannes Peterseim
https://www.petit-souris.de
##introduction
YAMS is an small in memory data store, completely written in PHP.
It comes with an server component and an client libary for an simple use in your PHP Scripts without the need of installing big products like redis or memcache.
The whole project has an size of some kilobytes.
You can use it as an very fast in memory caching layer for small data of any type. Perfect for an real fast session storing.
##installation
Copy the server script (server.php) to an save place and run it as an service with "php server.php &".
Standard port will be 5000, but you can define an own port via "php server.php port &" the server listens only on 127.0.0.1:port.
Do not forget to check the amount of memory an script can use, this defines the maximum size of the array the the data will be hold.
Copy the client script (client.php) to the place where you need it.
You can test everything with the supplied test script via "php test.php".
For your other use include it into your scripts via "use hp/yams/client;" if there is an autoloader or use "require('pathtothescript/client.php');" instead.
Create a new instance, connect to the server, get, set values as you need.
##commands
command examples based on ``$yamsClient = new client();``
###connect
connects to the server
connect(ip, port);
####parameters :
ip : string '127.0.0.1'
port : int 5000 (should be the same port the server listens)
####return :
boolean
true if connection was successful, false if connection failed
####example
$yamsClient->connect('127.0.0.1', 5000);
###stats
delivers some server stats from the server
stats();
####return :
array
with the following keys :
stores = number of stores
elements = number of elements overall
memory = memory usage in bytes
stores = detailed store data
####example
$yamsClient->stats();
###addstore
creates an dedicated memory store
addstore(store);
####parameters :
store : string 'choosedstore'
####return :
boolean
true if the key could be created, false if not
####example
$yamsClient->addstore('choosedstore');
###existstore
detemines wethder an dedicated memory exists
existstore(store);
####parameters :
store : string 'choosedstore'
####return :
boolean
true if the key could be deleted, false if not
####example
$yamsClient->existstore('choosedstore');
###delstore
deletes an dedicated memory store
delstore(store);
####parameters :
store : string 'choosedstore'
####return :
boolean
true if the key could be deleted, false if not
####example
$yamsClient->delstore('choosedstore');
###exist
determines wether an key exists in the given store
exist(key, store);
####parameters :
key : string 'choosedkey'
store : string 'choosedstore' (optional, if not setted, standard store will be used)
####return :
boolean
true if the key exists, false if not
####example
$yamsClient->exist('choosedkey','choosedstore');
###get
gets the value of an given key from the given store
get('choosedkey','choosedstore');
####parameters :
key : string 'abc'
store : string 'choosedstore' (optional, if not setted, standard store will be used)
####return :
string|boolean
string if the key exists, false if not
####example
$yamsClient->get('choosedkey');
###set
sets the value of an given key in a given store
set(key, value, store);
####parameters :
key : string 'choosedkey'
value : any value
store : string 'choosedstore' (optional, if not setted, standard store will be used)
####return :
boolean
true if the key could be setted, false if not
####example
$yamsClient->set('choosedkey', 'choosedvalue', 'choosedstore');
###del
deletes the given key and it's value from the given store
del(key, store);
####parameters :
key : string 'choosedkey'
store : string 'choosedstore' (optional, if not setted, standard store will be used)
####return :
boolean
true if the key could be deleted, false if not
####example
$yamsClient->del('choosedkey', 'choosedstore');
###inc
increments the value of an given key in the given store by 1
inc(key, store);
####parameters :
key : string 'choosedkey'
store : string 'choosedstore' (optional, if not setted, standard store will be used)
####return :
integer|boolean
integer value of the key if increment was sucessful, false if not
####example
$yamsClient->inc('choosedkey', 'choosedstore');
###dec
decrements the value of an given key in the given store by 1
dec(key);
####parameters :
key : string 'choosedkey'
store : string 'choosedstore' (optional, if not setted, standard store will be used)
####return :
integer|boolean
integer value of the key if decrement was sucessful, false if not
####example
$yamsClient->dec('choosedkey', 'choosedstore');
<?php
#################################
# YAMS v1.0 #
# 2021 by Hannes Peterseim #
# more can be found on #
# https://www.petit-souris.de #
# #
# licensed under GPL 3.0 #
# https://www.gnu.org/licenses #
#################################
namespace hp\yams;
/**
* Class server
* @package hp\yams
*/
class server
{
/**
* @var array
*/
private $memMap = [];
/**
* @var false|resource
*/
private $socket;
/**
* @var array
*/
private $clients = [];
/**
* server constructor.
*
* @param $address
* @param $port
*/
public function __construct($address, $port)
{
echo "YAMS Server v 1.0\n";
echo "2021 by Hannes Peterseim\n";
echo "https://www.petit-souris.de\n\n";
echo "creating socket...";
if (($this->socket = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) === false) {
echo "\nCouldn't create socket" . socket_strerror(socket_last_error()) . "\n";
exit;
}
echo "ok\n";
echo "binding socket on port " . $port . " for IP " . $address . "...";
if (@socket_bind($this->socket, $address, $port) === false) {
echo "\nBind Error " . socket_strerror(socket_last_error($this->socket)) . "\n";
exit;
}
echo "ok\n";
echo "listening...";
if (@socket_listen($this->socket, 5) === false) {
echo "\nListen Failed " . socket_strerror(socket_last_error($this->socket)) . "\n";
exit;
}
echo "ok\n";
echo "initializing standard memory store...";
$this->memMap[md5('standard')] = ['name' => 'standard', 'elements' => 0, 'values' => []];
echo "ok\n";
echo memory_get_usage(true) . " Bytes of memory allocated\n";
echo memory_get_usage() . " Bytes of memory used\n";
echo "ready to be used...\n";
$this->listen();
}
/**
* listen on given sockets
*
* @return void
*/
private function listen()
{
do {
$read = [];
$read[] = $this->socket;
$read = array_merge($read, $this->clients);
$write = NULL;
$except = NULL;
$tv_sec = 5;
if (socket_select($read, $write, $except, $tv_sec) < 1) {
continue;
}
if (in_array($this->socket, $read)) {
if (($msgsock = socket_accept($this->socket)) === false) {
echo "socket_accept() error: " . socket_strerror(socket_last_error($this->socket)) . "\n";
break;
}
$this->clients[] = $msgsock;
}
foreach ($this->clients as $key => $client) {
if (in_array($client, $read)) {
if (false === ($buffer = socket_read($client, 1048576 , PHP_BINARY_READ))) {
echo "socket_read() error: " . socket_strerror(socket_last_error($client)) . "\n";
socket_clear_error($client);
break 2;
}
if (!$buffer = trim($buffer)) {
continue;
}
$input = json_decode($buffer, true);
if (isset($input['key'])) {
if ($input['key'] != '') {
$input['key'] = md5($input['key']);
} else {
$input['key'] = '';
}
}
if (isset($input['store'])) {
if ($input['store'] != '') {
$input['store'] = md5($input['store']);
} else {
$input['store'] = md5('standard');
}
}
if ($input['command'] == 'close') {
$this->write($client, json_encode(['return' => 'ok']));
unset($this->clients[$key]);
socket_close($client);
break;
}
if ($input['command'] == 'exist') {
$this->write($client, $this->exist($input['key'], $input['store']));
}
if ($input['command'] == 'get') {
$this->write($client, $this->get($input['key'], $input['store']));
}
if ($input['command'] == 'set') {
$this->write($client, $this->set($input['key'], $input['value'], $input['store']));
}
if ($input['command'] == 'del') {
$this->write($client, $this->del($input['key'], $input['store']));
}
if ($input['command'] == 'stats') {
$this->write($client, $this->stats());
}
if ($input['command'] == 'inc') {
$this->write($client, $this->inc($input['key'], $input['store']));
}
if ($input['command'] == 'dec') {
$this->write($client, $this->dec($input['key'], $input['store']));
}
if ($input['command'] == 'addstore') {
$this->write($client, $this->addstore($input['memstore']));
}
if ($input['command'] == 'existstore') {
$this->write($client, $this->existstore($input['store']));
}
if ($input['command'] == 'delstore') {
$this->write($client, $this->delstore($input['memstore']));
}
}
}
} while (true);
socket_close($this->socket);
}
/**
* write output to the given socket
*
* @param $client
* @param $msg
*/
private function write ($client, $msg)
{
$msg = $msg . 'endofcommunication';
socket_write($client, $msg, strlen($msg));
}
/**
* determines wether an key exists in a given store
*
* @param string $key
* @param string $store
* @return false|string
*/
private function exist ($key, $store)
{
if (isset($this->memMap[$store]['values'][$key])) {
$msg = json_encode(['return' => 'ok', 'value' => true]);
} else {
$msg = json_encode(['return' => 'ok', 'value' => false]);
}
return $msg;
}
/**
* gets the value of an given key in a given store
*
* @param string $key
* @param string $store
* @return false|string
*/
private function get ($key, $store)
{
if ($key != '' && isset($this->memMap[$store]['values'][$key])) {
$msg = json_encode(['return' => 'ok', 'value' => $this->memMap[$store]['values'][$key]['value']]);
} else {
$msg = json_encode(['return' => 'ok', 'value' => '']);
}
return $msg;
}
/**
* sets the value for an given key in a given store
*
* @param string $key
* @param string $value
* @param string $store
* @return false|string
*/
private function set ($key, $value, $store)
{
if ($key != '') {
$this->memMap[$store]['values'][$key] = ['value' => $value];
$this->memMap[$store]['elements']++;
$msg = json_encode(['return' => 'ok']);
} else {
$msg = json_encode(['return' => 'nok', 'error' => 'no key given']);
}
return $msg;
}
/**
* deletes an given key in a given store
*
* @param string $key
* @param string $store
* @return false|string
*/
private function del ($key, $store)
{
if ($key != '' && isset($this->memMap[$store]['values'][$key])) {
$this->memMap[$store]['elements']--;
unset($this->memMap[$store]['values'][$key]);
}
return json_encode(['return' => 'ok']);
}
/**
* collect some statistics
*
* @return false|string
*/
private function stats ()
{
$allStores = 0;
$allElements = 0;
$stores = [];
foreach($this->memMap as $store) {
$allElements = $allElements + $store['elements'];
$allStores++;
$stores[] = ['store' => $store['name'], 'elements' => $store['elements']];
}
return json_encode(['return' => 'ok', 'value' => ['stores available' => $allStores, 'elements available' => $allElements, 'memory used' => memory_get_usage(true), 'stores' => $stores]]);
}
/**
* increment the value of an given key in a given store
*
* @param string $key
* @param string $store
* @return false|string
*/
private function inc ($key, $store)
{
if ($key != '' && isset($this->memMap[$store]['values'][$key]) && is_numeric($this->memMap[$store]['values'][$key]['value'])) {
$this->memMap[$store]['values'][$key]['value']++;
$msg = json_encode(['return' => 'ok', 'value' => $this->memMap[$store]['values'][$key]['value']]);
} else {
$msg = json_encode(['return' => 'ok', 'value' => '']);
}
return $msg;
}
/**
* decrement the value of an given key in a given store
*
* @param string $key
* @param string $store
* @return false|string
*/
private function dec ($key, $store)
{
if ($key != '' && isset($this->memMap[$store]['values'][$key])&& is_numeric($this->memMap[$store]['values'][$key]['value'])) {
$this->memMap[$store]['values'][$key]['value']--;
$msg = json_encode(['return' => 'ok', 'value' => $this->memMap[$store]['values'][$key]['value']]);
} else {
$msg = json_encode(['return' => 'ok', 'value' => '']);
}
return $msg;
}
/**
* adds an store to the memMap
*
* @param string $store
* @return false|string
*/
private function addstore ($store)
{
if (!isset($this->memMap[md5($store)])) {
$this->memMap[md5($store)] = ['name' => $store, 'elements' => 0, 'values' => []];
$msg = json_encode(['return' => 'ok']);
} else {
$msg = json_encode(['return' => 'nok']);
}
return $msg;
}
/**
* determines wether an store exists
*
* @param string $store
* @return false|string
*/
private function existstore ($store)
{
if (isset($this->memMap[$store])) {
$msg = json_encode(['return' => 'ok', 'value' => true]);
} else {
$msg = json_encode(['return' => 'ok', 'value' => false]);
}
return $msg;
}
/**
* adds an store to the memMap
*
* @param string $store
* @return false|string
*/
private function delstore ($store)
{
if (isset($this->memMap[md5($store)])) {
unset($this->memMap[md5($store)]);
$msg = json_encode(['return' => 'ok']);
} else {
$msg = json_encode(['return' => 'nok']);
}
return $msg;
}
}
if (isset($_SERVER[1])) {
if ($_SERVER[1] == 'help') {
echo "php server.php mode port\n";
echo "mode : help|listen -> standard listen\n";
echo "port : int between 450 and 65000 -> standard 5000\n";
}
if ($_SERVER[1] == 'version') {
echo "YAMS Server v 1.0\n";
echo "2021 by Hannes Peterseim\n";
echo "https://www.petit-souris.de\n";
}
}
if (isset($_SERVER[2])) {
if (!is_int($_SERVER[2]) || $_SERVER[2] < 450 || $_SERVER[2] > 65000) {
echo "port " . $_SERVER[2] . " not possible\n";
echo "port should be int and between 450 and 65000\n";
}
}
set_time_limit(0);
ob_implicit_flush();
$address = '127.0.0.1';
$port = 5000;
if (isset($_SERVER[2]) && is_int($_SERVER[2])) {
$port = $_SERVER[2];
}
new server($address, $port);
<?php
#################################
# YAMS v1.0 #
# 2021 by Hannes Peterseim #
# more can be found on #
# https://www.petit-souris.de #
# #
# licensed under GPL 3.0 #
# https://www.gnu.org/licenses #
#################################
namespace hp\yams;
require('client.php');
/**
* Class test
* @package hp\yams
*/
class test
{
/**
* @var client
*/
private $client = null;
/**
* @var string
*/
private static $teststore = 'teststore';
/**
* @var string
*/
private static $testkey = 'testkey';
/**
* @var string
*/
private static $testvalue = 'testvalue';
/**
* @var int
*/
private $start = 0;
/**
* test constructor.
*/
public function __construct ()
{
$this->client = new client();
echo 'connect test...';
$this->start();
$result = $this->client->connect();
$this->end();
if ($result === true) {
echo 'successful' . "\n";
} else {
echo 'failed' . "\n";
}
}
/**
* stats function test
*
* @retrun void
*/
public function statstest ()
{
echo 'stats test...';
$this->start();
$result = $this->client->stats();
$this->end();
if (is_array($result)) {
echo 'successful' . "\n";
} else {
echo 'failed' . "\n";
}
}
/**
* set function test
*
* @retrun void
*/
public function settest ()
{