Add VariableManager, RpcClient classes, and cache enhancements

This commit is contained in:
netkas 2024-09-30 03:00:02 -04:00
parent 38092a639e
commit e55f4d57f9
27 changed files with 606 additions and 56 deletions

View file

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="PhpTestFrameworkVersionCache">
<tools_cache>
<tool tool_name="PHPUnit">
<cache>
<versions>
<info id="Local/home/netkas/phpunit.phar" version="11.3.5" />
</versions>
</cache>
</tool>
</tools_cache>
</component>
</project>

2
.idea/php.xml generated
View file

@ -69,7 +69,7 @@
<extension name="libevent" enabled="false" />
<extension name="libsodium" enabled="false" />
<extension name="mailparse" enabled="false" />
<extension name="memcached" enabled="false" />
<extension name="memcache" enabled="false" />
<extension name="ming" enabled="false" />
<extension name="mongo" enabled="false" />
<extension name="mongodb" enabled="false" />

3
.idea/sqldialects.xml generated
View file

@ -4,6 +4,9 @@
<file url="file://$PROJECT_DIR$/src/Socialbox/Classes/Resources/database/password_authentication.sql" dialect="MariaDB" />
<file url="file://$PROJECT_DIR$/src/Socialbox/Classes/Resources/database/registered_peers.sql" dialect="MariaDB" />
<file url="file://$PROJECT_DIR$/src/Socialbox/Classes/Resources/database/sessions.sql" dialect="MariaDB" />
<file url="file://$PROJECT_DIR$/src/Socialbox/Classes/Resources/database/variables.sql" dialect="MariaDB" />
<file url="file://$PROJECT_DIR$/src/Socialbox/Managers/SessionManager.php" dialect="MariaDB" />
<file url="file://$PROJECT_DIR$/src/Socialbox/Managers/VariableManager.php" dialect="MariaDB" />
<file url="file:///var/ncc/packages/net.nosial.socialbox=1.0.0/bin/src/Socialbox/Managers/VariableManager.php" dialect="MariaDB" />
</component>
</project>

View file

@ -11,6 +11,8 @@
],
"require": {
"ext-pdo": "*",
"ext-openssl": "*"
"ext-openssl": "*",
"ext-redis": "*",
"ext-memcached": "*"
}
}

View file

@ -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).
*
@ -38,10 +45,46 @@ abstract class CacheLayer
*/
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;
}
}

View file

@ -5,6 +5,7 @@ namespace Socialbox\Classes\CacheLayer;
use Memcached;
use RuntimeException;
use Socialbox\Abstracts\CacheLayer;
use Socialbox\Classes\Configuration;
class MemcachedCacheLayer extends CacheLayer
{
@ -12,11 +13,8 @@ class MemcachedCacheLayer extends CacheLayer
/**
* 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'))
{
@ -24,9 +22,10 @@ class MemcachedCacheLayer extends CacheLayer
}
$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']);
}
}
@ -80,6 +79,25 @@ class MemcachedCacheLayer extends CacheLayer
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
*/

View file

@ -6,6 +6,7 @@ use Redis;
use RedisException;
use RuntimeException;
use Socialbox\Abstracts\CacheLayer;
use Socialbox\Classes\Configuration;
class RedisCacheLayer extends CacheLayer
{
@ -13,12 +14,8 @@ class RedisCacheLayer extends CacheLayer
/**
* 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'))
{
@ -29,10 +26,15 @@ class RedisCacheLayer extends CacheLayer
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)
@ -101,6 +103,21 @@ class RedisCacheLayer extends CacheLayer
}
}
/**
* @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
*/

View file

@ -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
{
@ -23,7 +28,14 @@ class InitializeCommand implements CliCommandInterface
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}");
@ -46,13 +58,34 @@ class InitializeCommand implements CliCommandInterface
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;
}

View file

@ -22,6 +22,18 @@ class Configuration
$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();

View file

@ -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'
);

View file

@ -0,0 +1,88 @@
<?php
namespace Socialbox\Classes;
use Socialbox\Classes\ServerResolver;
use Socialbox\Enums\StandardHeaders;
use Socialbox\Exceptions\ResolutionException;
use Socialclient\Exceptions\RpcRequestException;
class RpcClient
{
private const string CLIENT_NAME = 'Socialbox PHP';
private const string CLIENT_VERSION = '1.0';
private const string CONTENT_TYPE = 'application/json';
private string $domain;
private string $endpoint;
private string $serverPublicKey;
/**
* @throws ResolutionException
*/
public function __construct(string $domain)
{
$resolved = ServerResolver::resolveDomain($domain);
$this->domain = $domain;
$this->endpoint = $resolved->getEndpoint();
$this->serverPublicKey = $resolved->getPublicKey();
$this->clientPrivateKey = null;
}
public function getDomain(): string
{
return $this->domain;
}
public function getEndpoint(): string
{
return $this->endpoint;
}
public function getServerPublicKey(): string
{
return $this->serverPublicKey;
}
public function sendRequest(array $data)
{
$ch = curl_init($this->endpoint);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, Utilities::jsonEncode($data));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
Utilities::generateHeader(StandardHeaders::CLIENT_NAME, self::CLIENT_NAME),
Utilities::generateHeader(StandardHeaders::CLIENT_VERSION, self::CLIENT_VERSION),
Utilities::generateHeader(StandardHeaders::CONTENT_TYPE, self::CONTENT_TYPE)
]);
curl_setopt($ch, CURLOPT_HEADER, true);
$response = curl_exec($ch);
if (curl_errno($ch))
{
$statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
// Separate headers and body
$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$response_body = substr($response, $header_size);
curl_close($ch);
// Throw exception with response body as message and status code as code
throw new RpcRequestException($response_body, $statusCode);
}
$statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
// Separate headers and body
$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$response_headers = substr($response, 0, $header_size);
$response_body = substr($response, $header_size);
curl_close($ch);
}
}

View file

@ -8,6 +8,7 @@ use Socialbox\Enums\StandardHeaders;
use Socialbox\Exceptions\CryptographyException;
use Socialbox\Exceptions\DatabaseOperationException;
use Socialbox\Exceptions\RpcException;
use Socialbox\Exceptions\StandardException;
use Socialbox\Managers\SessionManager;
use Socialbox\Objects\ClientRequest;
use Socialbox\Objects\RpcRequest;
@ -85,11 +86,6 @@ class RpcHandler
try
{
if(!SessionManager::sessionExists($clientRequest->getSessionUuid()))
{
throw new RpcException('Session UUID not found', 404);
}
$session = SessionManager::getSession($clientRequest->getSessionUuid());
// Verify the signature of the request
@ -98,6 +94,10 @@ class RpcHandler
throw new RpcException('Request signature check failed', 400);
}
}
catch(StandardException $e)
{
throw new RpcException($e->getMessage(), 400);
}
catch(CryptographyException $e)
{
throw new RpcException('Request signature check failed (Cryptography Error)', 400, $e);

View file

@ -0,0 +1,52 @@
<?php
namespace Socialbox\Classes;
use Socialbox\Exceptions\ResolutionException;
use Socialbox\Objects\ResolvedServer;
class ServerResolver
{
/**
* Resolves a given domain to fetch the RPC endpoint and public key from its DNS TXT records.
*
* @param string $domain The domain to be resolved.
* @return ResolvedServer An instance of ResolvedServer containing the endpoint and public key.
* @throws ResolutionException If the DNS TXT records cannot be resolved or if required information is missing.
*/
public static function resolveDomain(string $domain): ResolvedServer
{
$txtRecords = dns_get_record($domain, DNS_TXT);
if ($txtRecords === false)
{
throw new ResolutionException(sprintf("Failed to resolve DNS TXT records for %s", $domain));
}
$endpoint = null;
$publicKey = null;
foreach ($txtRecords as $txt)
{
if (isset($txt['txt']) && str_starts_with($txt['txt'], 'socialbox='))
{
$endpoint = substr($txt['txt'], strlen('socialbox='));
}
elseif (isset($txt['txt']) && str_starts_with($txt['txt'], 'socialbox-key='))
{
$publicKey = substr($txt['txt'], strlen('socialbox-key='));
}
}
if ($endpoint === null)
{
throw new ResolutionException(sprintf("Failed to resolve RPC endpoint for %s", $domain));
}
if ($publicKey === null)
{
throw new ResolutionException(sprintf("Failed to resolve public key for %s", $domain));
}
return new ResolvedServer($endpoint, $publicKey);
}
}

View file

@ -6,7 +6,6 @@ use InvalidArgumentException;
use Socialbox\Abstracts\Method;
use Socialbox\Enums\StandardError;
use Socialbox\Exceptions\DatabaseOperationException;
use Socialbox\Exceptions\StandardException;
use Socialbox\Interfaces\SerializableInterface;
use Socialbox\Managers\SessionManager;
use Socialbox\Objects\ClientRequest;

View file

@ -4,6 +4,7 @@ namespace Socialbox\Classes;
use InvalidArgumentException;
use RuntimeException;
use Socialbox\Enums\StandardHeaders;
class Utilities
{
@ -34,6 +35,18 @@ class Utilities
return $decoded;
}
public static function jsonEncode(mixed $data): string
{
try
{
return json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR);
}
catch(\JsonException $e)
{
throw new \RuntimeException("Failed to encode json input", $e);
}
}
/**
* Encodes the given data in Base64.
*
@ -117,4 +130,9 @@ class Utilities
$e->getTraceAsString()
);
}
public static function generateHeader(StandardHeaders $header, string $value): string
{
return $header->value . ': ' . $value;
}
}

View file

@ -7,6 +7,7 @@ enum DatabaseObjects : string
case PASSWORD_AUTHENTICATION = 'password_authentication.sql';
case REGISTERED_PEERS = 'registered_peers.sql';
case SESSIONS = 'sessions.sql';
case VARIABLES = 'variables.sql';
/**
* Returns the priority of the database object
@ -17,6 +18,7 @@ enum DatabaseObjects : string
{
return match ($this)
{
self::VARIABLES => 0,
self::REGISTERED_PEERS => 1,
self::PASSWORD_AUTHENTICATION, self::SESSIONS => 2,
};

View file

@ -13,6 +13,7 @@ enum StandardHeaders : string
case SESSION_UUID = 'Session-UUID';
case FROM_PEER = 'From-Peer';
case SIGNATURE = 'Signature';
case PUBLIC_KEY = 'Public-Key';
/**
* Determines if the current instance is required based on its type.

View file

@ -0,0 +1,13 @@
<?php
namespace Socialbox\Exceptions;
use Throwable;
class ResolutionException extends \Exception
{
public function __construct(string $message = "", int $code = 0, ?Throwable $previous = null)
{
parent::__construct($message, $code, $previous);
}
}

View file

@ -13,6 +13,7 @@
use Socialbox\Enums\SessionState;
use Socialbox\Enums\StandardError;
use Socialbox\Exceptions\DatabaseOperationException;
use Socialbox\Exceptions\StandardException;
use Socialbox\Objects\SessionRecord;
use Symfony\Component\Uid\Uuid;
@ -88,6 +89,7 @@
* @param string $uuid The unique identifier of the session.
* @return SessionRecord The session record corresponding to the given UUID.
* @throws DatabaseOperationException If the session record cannot be found or if there is an error during retrieval.
* @throws StandardException
*/
public static function getSession(string $uuid): SessionRecord
{
@ -100,7 +102,7 @@
if ($data === false)
{
throw new DatabaseOperationException(sprintf('Session record %s not found', $uuid));
throw new StandardException(sprintf("The requested session '%s' does not exist"), StandardError::SESSION_NOT_FOUND);
}
// Convert the timestamp fields to DateTime objects

View file

@ -0,0 +1,151 @@
<?php
namespace Socialbox\Managers;
use PDO;
use PDOException;
use Socialbox\Abstracts\CacheLayer;
use Socialbox\Classes\Configuration;
use Socialbox\Classes\Database;
use Socialbox\Exceptions\DatabaseOperationException;
class VariableManager
{
/**
* Sets a variable in the database. If the variable already exists, its value is updated.
*
* @param string $name The name of the variable.
* @param string $value The value of the variable.
* @return void
* @throws DatabaseOperationException If the operation fails.
*/
public static function setVariable(string $name, string $value): void
{
try
{
$statement = Database::getConnection()->prepare("INSERT INTO variables (name, value) VALUES (?, ?) ON DUPLICATE KEY UPDATE value=?");
$statement->bindParam(1, $name);
$statement->bindParam(2, $value);
$statement->bindParam(3, $value);
$statement->execute();
}
catch(PDOException $e)
{
throw new DatabaseOperationException(sprintf('Failed to set variable %s in the database', $name), $e);
}
finally
{
if(Configuration::getConfiguration()['cache']['enabled'] && Configuration::getConfiguration()['cache']['variables']['enabled'])
{
if(Configuration::getConfiguration()['cache']['variables']['max'] > 0)
{
if(CacheLayer::getInstance()->getPrefixCount('VARIABLES_') >= Configuration::getConfiguration()['cache']['variables']['max'])
{
// Return early if the cache is full
return;
}
}
CacheLayer::getInstance()->set(sprintf("VARIABLES_%s", $name), $value, (int)Configuration::getConfiguration()['cache']['variables']['ttl']);
}
}
}
/**
* Retrieves the value of a variable from the database based on its name.
*
* @param string $name The name of the variable to retrieve.
* @return string The value of the variable.
* @throws DatabaseOperationException If the database operation fails.
*/
public static function getVariable(string $name): string
{
if(Configuration::getConfiguration()['cache']['enabled'] && Configuration::getConfiguration()['cache']['variables']['enabled'])
{
$cachedValue = CacheLayer::getInstance()->get(sprintf("VARIABLES_%s", $name));
if($cachedValue !== false)
{
return $cachedValue;
}
}
try
{
$statement = Database::getConnection()->prepare("SELECT value FROM variables WHERE name=?");
$statement->bindParam(1, $name);
$statement->execute();
if($statement->rowCount() === 0)
{
throw new DatabaseOperationException(sprintf('Variable with name %s does not exist', $name));
}
$result = $statement->fetch(PDO::FETCH_ASSOC);
return $result['value'];
}
catch(PDOException $e)
{
throw new DatabaseOperationException(sprintf('Failed to get variable %s from the database', $name), $e);
}
}
/**
* Checks if a variable with the specified name exists in the database.
*
* @param string $name The name of the variable to check for existence.
* @return bool Returns true if the variable exists, false otherwise.
* @throws DatabaseOperationException If the database operation fails.
*/
public static function variableExists(string $name): bool
{
if(Configuration::getConfiguration()['cache']['enabled'] && Configuration::getConfiguration()['cache']['variables']['enabled'])
{
$cachedValue = CacheLayer::getInstance()->get(sprintf("VARIABLES_%s", $name));
if($cachedValue !== false)
{
return true;
}
}
try
{
$statement = Database::getConnection()->prepare("SELECT COUNT(*) FROM variables WHERE name=?");
$statement->bindParam(1, $name);
$statement->execute();
$result = $statement->fetchColumn();
return $result > 0;
}
catch(PDOException $e)
{
throw new DatabaseOperationException(sprintf('Failed to check if the variable %s exists', $name), $e);
}
}
/**
* Deletes a variable from the database using the provided name.
*
* @param string $name The name of the variable to be deleted.
* @return void
* @throws DatabaseOperationException If the database operation fails.
*/
public static function deleteVariable(string $name): void
{
try
{
$statement = Database::getConnection()->prepare("DELETE FROM variables WHERE name=?");
$statement->bindParam(1, $name);
$statement->execute();
}
catch(PDOException $e)
{
throw new DatabaseOperationException(sprintf('Failed to delete variable %s from the database', $name), $e);
}
finally
{
if(Configuration::getConfiguration()['cache']['enabled'] && Configuration::getConfiguration()['cache']['variables']['enabled'])
{
CacheLayer::getInstance()->delete(sprintf("VARIABLES_%s", $name));
}
}
}
}

View file

@ -2,7 +2,14 @@
namespace Socialbox\Objects;
use RuntimeException;
use Socialbox\Classes\Cryptography;
use Socialbox\Enums\StandardError;
use Socialbox\Enums\StandardHeaders;
use Socialbox\Exceptions\CryptographyException;
use Socialbox\Exceptions\DatabaseOperationException;
use Socialbox\Exceptions\StandardException;
use Socialbox\Managers\SessionManager;
class ClientRequest
{
@ -95,15 +102,41 @@ class ClientRequest
return $this->headers[StandardHeaders::SIGNATURE->value];
}
/**
* @return bool
* @throws DatabaseOperationException
*/
public function verifySignature(): bool
{
$signature = $this->getSignature();
$sessionUuid = $this->getSessionUuid();
if($signature == null)
if($signature == null || $sessionUuid == null)
{
return false;
}
try
{
$session = SessionManager::getSession($sessionUuid);
}
catch(StandardException $e)
{
if($e->getStandardError() == StandardError::SESSION_NOT_FOUND)
{
return false;
}
throw new RuntimeException($e);
}
try
{
return Cryptography::verifyContent($this->getHash(), $signature, $session->getPublicKey());
}
catch(CryptographyException $e)
{
return false;
}
}
}

View file

@ -0,0 +1,25 @@
<?php
namespace Socialbox\Objects;
class ResolvedServer
{
private string $endpoint;
private string $publicKey;
public function __construct(string $endpoint, string $publicKey)
{
$this->endpoint = $endpoint;
$this->publicKey = $publicKey;
}
public function getEndpoint(): string
{
return $this->endpoint;
}
public function getPublicKey(): string
{
return $this->publicKey;
}
}

View file

@ -55,13 +55,6 @@ class RpcResponse implements SerializableInterface
return $data->toArray();
}
// If the data is an array, recursively apply this method to each element
if (is_array($data))
{
return array_map([$this, 'convertToArray'], $data);
}
// Otherwise, return the data as-is (e.g., for scalar values)
return $data;
}

View file

@ -20,6 +20,8 @@ class CryptographyTest extends TestCase
$this->assertObjectHasProperty('privateKey', $keyPair);
$this->assertIsString($keyPair->getPublicKey());
$this->assertIsString($keyPair->getPrivateKey());
print_r($keyPair);
}
/**

View file

@ -0,0 +1,21 @@
<?php
namespace Socialbox\Classes;
use PHPUnit\Framework\TestCase;
use Socialbox\Exceptions\ResolutionException;
use Socialbox\Objects\ResolvedServer;
class ServerResolverTest extends TestCase
{
/**
* Test for the function resolveDomain of the class ServerResolver
*/
public function testResolveDomain(): void
{
// successful resolution
$resolvedServer = ServerResolver::resolveDomain('n64.cc');
self::assertNotEmpty($resolvedServer->getEndpoint());
self::assertNotEmpty($resolvedServer->getPublicKey());
}
}

View file

@ -6,7 +6,6 @@ use InvalidArgumentException;
use PHPUnit\Framework\TestCase;
use Socialbox\Classes\Cryptography;
use Socialbox\Classes\Utilities;
use Socialbox\Exceptions\DatabaseOperationException;
use Socialbox\Objects\SessionRecord;
class SessionManagerTest extends TestCase
@ -27,14 +26,6 @@ class SessionManagerTest extends TestCase
$this->assertTrue(SessionManager::sessionExists($uuid));
}
public function testGetSessionWithInvalidUuid(): void
{
$uuid = 'invalid_uuid';
$this->expectException(DatabaseOperationException::class);
SessionManager::getSession($uuid);
}
public function testGetSessionWithValidUuid(): void
{
$keyPair = Cryptography::generateKeyPair();

View file

@ -0,0 +1,34 @@
<?php
namespace Socialbox\Managers;
use PDOException;
use PHPUnit\Framework\TestCase;
use Socialbox\Abstracts\CacheLayer;
use Socialbox\Exceptions\DatabaseOperationException;
use Socialbox\Managers\VariableManager;
class VariableManagerTest extends TestCase
{
/**
* Test the setter method for a variable in the VariableManager class.
*
*/
public function testSetVariable(): void
{
CacheLayer::getInstance()->clear();
VariableManager::deleteVariable('test_name');
VariableManager::setVariable('test_name', 'test_value');
$this->assertTrue(VariableManager::variableExists('test_name'));
$this->assertEquals('test_value', VariableManager::getVariable('test_name'));
VariableManager::deleteVariable('test_name');
VariableManager::deleteVariable('test_name2');
VariableManager::setVariable('test_name2', 'test_value2');
$this->assertTrue(VariableManager::variableExists('test_name2'));
$this->assertEquals('test_value2', VariableManager::getVariable('test_name2'));
VariableManager::deleteVariable('test_name2');
}
}