Skip to content

Commit

Permalink
Add VariableManager, RpcClient classes, and cache enhancements
Browse files Browse the repository at this point in the history
  • Loading branch information
Netkas committed Sep 30, 2024
1 parent 38092a6 commit e55f4d5
Show file tree
Hide file tree
Showing 27 changed files with 606 additions and 56 deletions.
14 changes: 0 additions & 14 deletions .idea/php-test-framework.xml

This file was deleted.

2 changes: 1 addition & 1 deletion .idea/php.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions .idea/sqldialects.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
],
"require": {
"ext-pdo": "*",
"ext-openssl": "*"
"ext-openssl": "*",
"ext-redis": "*",
"ext-memcached": "*"
}
}
43 changes: 43 additions & 0 deletions src/Socialbox/Abstracts/CacheLayer.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,15 @@

namespace Socialbox\Abstracts;

use RuntimeException;
use Socialbox\Classes\CacheLayer\MemcachedCacheLayer;
use Socialbox\Classes\CacheLayer\RedisCacheLayer;
use Socialbox\Classes\Configuration;

abstract class CacheLayer
{
private static ?CacheLayer $instance = null;

/**
* Stores a value in the cache with an associated key and an optional time-to-live (TTL).
*
Expand Down Expand Up @@ -38,10 +45,46 @@ public abstract function delete(string $key): bool;
*/
public abstract function exists(string $key): bool;

/**
* Counts the number of items that start with the given prefix.
*
* @param string $prefix The prefix to search for.
* @return int The count of items starting with the provided prefix.
*/
public abstract function getPrefixCount(string $prefix): int;

/**
* Clears all values from the cache.
*
* @return bool Returns true if the cache was successfully cleared, false otherwise.
*/
public abstract function clear(): bool;

/**
* Retrieves the singleton instance of the cache layer.
*
* @return CacheLayer The singleton instance of the cache layer.
*/
public static function getInstance(): CacheLayer
{
if (self::$instance === null)
{
$engine = Configuration::getConfiguration()['cache']['engine'];

if ($engine === 'redis')
{
self::$instance = new RedisCacheLayer();
}
else if ($engine === 'memcached')
{
self::$instance = new MemcachedCacheLayer();
}
else
{
throw new RuntimeException('Invalid cache engine specified in the configuration, must be either "redis" or "memcached".');
}
}

return self::$instance;
}
}
30 changes: 24 additions & 6 deletions src/Socialbox/Classes/CacheLayer/MemcachedCacheLayer.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,27 @@
use Memcached;
use RuntimeException;
use Socialbox\Abstracts\CacheLayer;
use Socialbox\Classes\Configuration;

class MemcachedCacheLayer extends CacheLayer
{
private Memcached $memcached;

/**
* Memcached cache layer constructor.
*
* @param string $host The Memcached server host.
* @param int $port The Memcached server port.
*/
public function __construct(string $host, int $port)
public function __construct()
{
if (!extension_loaded('memcached'))
{
throw new RuntimeException('The Memcached extension is not loaded in your PHP environment.');
}

$this->memcached = new Memcached();
if (!$this->memcached->addServer($host, $port))
$this->memcached->addServer(Configuration::getConfiguration()['cache']['host'], (int)Configuration::getConfiguration()['cache']['port']);
if(Configuration::getConfiguration()['cache']['username'] !== null || Configuration::getConfiguration()['cache']['password'] !== null)
{
throw new RuntimeException('Failed to connect to the Memcached server.');
$this->memcached->setSaslAuthData(Configuration::getConfiguration()['cache']['username'], Configuration::getConfiguration()['cache']['password']);
}
}

Expand Down Expand Up @@ -80,6 +79,25 @@ public function exists(string $key): bool
return $this->memcached->getResultCode() === Memcached::RES_SUCCESS;
}

/**
* @inheritDoc
*/
public function getPrefixCount(string $prefix): int
{
$stats = $this->memcached->getStats();
$count = 0;

foreach ($stats as $server => $data)
{
if (str_starts_with($server, $prefix))
{
$count += $data['curr_items'];
}
}

return $count;
}

/**
* @inheritDoc
*/
Expand Down
33 changes: 25 additions & 8 deletions src/Socialbox/Classes/CacheLayer/RedisCacheLayer.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,16 @@
use RedisException;
use RuntimeException;
use Socialbox\Abstracts\CacheLayer;
use Socialbox\Classes\Configuration;

class RedisCacheLayer extends CacheLayer
{
private Redis $redis;

/**
* Redis cache layer constructor.
*
* @param string $host The Redis server host.
* @param int $port The Redis server port.
* @param string|null $password Optional. The Redis server password.
*/
public function __construct(string $host, int $port, ?string $password=null)
public function __construct()
{
if (!extension_loaded('redis'))
{
Expand All @@ -29,10 +26,15 @@ public function __construct(string $host, int $port, ?string $password=null)

try
{
$this->redis->connect($host, $port);
if ($password !== null)
$this->redis->connect(Configuration::getConfiguration()['cache']['host'], (int)Configuration::getConfiguration()['cache']['port']);
if (Configuration::getConfiguration()['cache']['password'] !== null)
{
$this->redis->auth($password);
$this->redis->auth(Configuration::getConfiguration()['cache']['password']);
}

if (Configuration::getConfiguration()['cache']['database'] !== 0)
{
$this->redis->select((int)Configuration::getConfiguration()['cache']['database']);
}
}
catch (RedisException $e)
Expand Down Expand Up @@ -101,6 +103,21 @@ public function exists(string $key): bool
}
}

/**
* @inheritDoc
*/
public function getPrefixCount(string $prefix): int
{
try
{
return count($this->redis->keys($prefix . '*'));
}
catch (RedisException $e)
{
throw new RuntimeException('Failed to get the count of keys with the specified prefix in the Redis cache.', 0, $e);
}
}

/**
* @inheritDoc
*/
Expand Down
37 changes: 35 additions & 2 deletions src/Socialbox/Classes/CliCommands/InitializeCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,18 @@

namespace Socialbox\Classes\CliCommands;

use Exception;
use LogLib\Log;
use PDOException;
use Socialbox\Abstracts\CacheLayer;
use Socialbox\Classes\Configuration;
use Socialbox\Classes\Cryptography;
use Socialbox\Classes\Database;
use Socialbox\Classes\Resources;
use Socialbox\Enums\DatabaseObjects;
use Socialbox\Exceptions\DatabaseOperationException;
use Socialbox\Interfaces\CliCommandInterface;
use Socialbox\Managers\VariableManager;

class InitializeCommand implements CliCommandInterface
{
Expand All @@ -23,7 +28,14 @@ public static function execute(array $args): int
return 1;
}

print("Initializing Socialbox...\n");
Log::info('net.nosial.socialbox', 'Initializing Socialbox...');

if(Configuration::getConfiguration()['cache']['enabled'])
{
Log::verbose('net.nosial.socialbox', 'Clearing cache layer...');
CacheLayer::getInstance()->clear();
}

foreach(DatabaseObjects::casesOrdered() as $object)
{
Log::verbose('net.nosial.socialbox', "Initializing database object {$object->value}");
Expand All @@ -46,13 +58,34 @@ public static function execute(array $args): int
return 1;
}
}
catch(\Exception $e)
catch(Exception $e)
{
Log::error('net.nosial.socialbox', "Failed to initialize database object {$object->value}: {$e->getMessage()}", $e);
return 1;
}
}

try
{

if(!VariableManager::variableExists('PUBLIC_KEY') || !VariableManager::variableExists('PRIVATE_KEY'))
{
Log::info('net.nosial.socialbox', 'Generating new key pair...');

$keyPair = Cryptography::generateKeyPair();
VariableManager::setVariable('PUBLIC_KEY', $keyPair->getPublicKey());
VariableManager::setVariable('PRIVATE_KEY', $keyPair->getPrivateKey());

Log::info('net.nosial.socialbox', 'Set the DNS TXT record for the public key to the following value:');
Log::info('net.nosial.socialbox', "socialbox-key={$keyPair->getPublicKey()}");
}
}
catch(DatabaseOperationException $e)
{
Log::error('net.nosial.socialbox', "Failed to generate key pair: {$e->getMessage()}", $e);
return 1;
}

Log::info('net.nosial.socialbox', 'Socialbox has been initialized successfully');
return 0;
}
Expand Down
12 changes: 12 additions & 0 deletions src/Socialbox/Classes/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,18 @@ public static function getConfiguration(): array
$config->setDefault('database.username', 'root');
$config->setDefault('database.password', 'root');
$config->setDefault('database.name', 'test');

$config->setDefault('cache.enabled', false);
$config->setDefault('cache.engine', 'redis');
$config->setDefault('cache.host', '127.0.0.1');
$config->setDefault('cache.port', 6379);
$config->setDefault('cache.username', null);
$config->setDefault('cache.password', null);
$config->setDefault('cache.database', 0);
$config->setDefault('cache.variables.enabled', true);
$config->setDefault('cache.variables.ttl', 3600);
$config->setDefault('cache.variables.max', 1000);

$config->save();

self::$configuration = $config->getConfiguration();
Expand Down
11 changes: 11 additions & 0 deletions src/Socialbox/Classes/Resources/database/variables.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
create table variables
(
name varchar(255) not null comment 'The name of the variable'
primary key comment 'The unique index for the variable name',
value text null comment 'The value of the variable',
`read_only` tinyint(1) default 0 not null comment 'Boolean indicator if the variable is read only',
created timestamp default current_timestamp() not null comment 'The Timestamp for when this record was created',
updated timestamp null comment 'The Timestamp for when this record was last updated',
constraint variables_name_uindex
unique (name) comment 'The unique index for the variable name'
);
Loading

0 comments on commit e55f4d5

Please sign in to comment.