From e55f4d57f91174efffa8d8ac84084d86eeb9f48d Mon Sep 17 00:00:00 2001 From: netkas Date: Mon, 30 Sep 2024 03:00:02 -0400 Subject: [PATCH] Add VariableManager, RpcClient classes, and cache enhancements --- .idea/php-test-framework.xml | 14 -- .idea/php.xml | 2 +- .idea/sqldialects.xml | 3 + composer.json | 4 +- src/Socialbox/Abstracts/CacheLayer.php | 43 +++++ .../CacheLayer/MemcachedCacheLayer.php | 30 +++- .../Classes/CacheLayer/RedisCacheLayer.php | 33 +++- .../Classes/CliCommands/InitializeCommand.php | 37 ++++- src/Socialbox/Classes/Configuration.php | 12 ++ .../Classes/Resources/database/variables.sql | 11 ++ src/Socialbox/Classes/RpcClient.php | 88 ++++++++++ src/Socialbox/Classes/RpcHandler.php | 10 +- src/Socialbox/Classes/ServerResolver.php | 52 ++++++ .../Classes/StandardMethods/CreateSession.php | 1 - src/Socialbox/Classes/Utilities.php | 18 +++ src/Socialbox/Enums/DatabaseObjects.php | 2 + src/Socialbox/Enums/StandardHeaders.php | 1 + .../Exceptions/ResolutionException.php | 13 ++ src/Socialbox/Managers/SessionManager.php | 4 +- src/Socialbox/Managers/VariableManager.php | 151 ++++++++++++++++++ src/Socialbox/Objects/ClientRequest.php | 35 +++- src/Socialbox/Objects/ResolvedServer.php | 25 +++ src/Socialbox/Objects/RpcResponse.php | 7 - tests/Socialbox/Classes/CryptographyTest.php | 2 + .../Socialbox/Classes/ServerResolverTest.php | 21 +++ .../Socialbox/Managers/SessionManagerTest.php | 9 -- .../Managers/VariableManagerTest.php | 34 ++++ 27 files changed, 606 insertions(+), 56 deletions(-) delete mode 100644 .idea/php-test-framework.xml create mode 100644 src/Socialbox/Classes/Resources/database/variables.sql create mode 100644 src/Socialbox/Classes/RpcClient.php create mode 100644 src/Socialbox/Classes/ServerResolver.php create mode 100644 src/Socialbox/Exceptions/ResolutionException.php create mode 100644 src/Socialbox/Managers/VariableManager.php create mode 100644 src/Socialbox/Objects/ResolvedServer.php create mode 100644 tests/Socialbox/Classes/ServerResolverTest.php create mode 100644 tests/Socialbox/Managers/VariableManagerTest.php diff --git a/.idea/php-test-framework.xml b/.idea/php-test-framework.xml deleted file mode 100644 index ea6eea8..0000000 --- a/.idea/php-test-framework.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/php.xml b/.idea/php.xml index 1a158d8..6e91387 100644 --- a/.idea/php.xml +++ b/.idea/php.xml @@ -69,7 +69,7 @@ - + diff --git a/.idea/sqldialects.xml b/.idea/sqldialects.xml index 5b5c311..12d6855 100644 --- a/.idea/sqldialects.xml +++ b/.idea/sqldialects.xml @@ -4,6 +4,9 @@ + + + \ No newline at end of file diff --git a/composer.json b/composer.json index 2136802..c76565e 100644 --- a/composer.json +++ b/composer.json @@ -11,6 +11,8 @@ ], "require": { "ext-pdo": "*", - "ext-openssl": "*" + "ext-openssl": "*", + "ext-redis": "*", + "ext-memcached": "*" } } \ No newline at end of file diff --git a/src/Socialbox/Abstracts/CacheLayer.php b/src/Socialbox/Abstracts/CacheLayer.php index 72d0f07..e825def 100644 --- a/src/Socialbox/Abstracts/CacheLayer.php +++ b/src/Socialbox/Abstracts/CacheLayer.php @@ -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 @@ 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; + } } \ No newline at end of file diff --git a/src/Socialbox/Classes/CacheLayer/MemcachedCacheLayer.php b/src/Socialbox/Classes/CacheLayer/MemcachedCacheLayer.php index a67502a..8095ae1 100644 --- a/src/Socialbox/Classes/CacheLayer/MemcachedCacheLayer.php +++ b/src/Socialbox/Classes/CacheLayer/MemcachedCacheLayer.php @@ -5,6 +5,7 @@ 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 @@ public function __construct(string $host, int $port) } $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 @@ 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 */ diff --git a/src/Socialbox/Classes/CacheLayer/RedisCacheLayer.php b/src/Socialbox/Classes/CacheLayer/RedisCacheLayer.php index 1fcb787..1715e51 100644 --- a/src/Socialbox/Classes/CacheLayer/RedisCacheLayer.php +++ b/src/Socialbox/Classes/CacheLayer/RedisCacheLayer.php @@ -6,6 +6,7 @@ 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 @@ 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) @@ -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 */ diff --git a/src/Socialbox/Classes/CliCommands/InitializeCommand.php b/src/Socialbox/Classes/CliCommands/InitializeCommand.php index 8a71ad7..9cf5b64 100644 --- a/src/Socialbox/Classes/CliCommands/InitializeCommand.php +++ b/src/Socialbox/Classes/CliCommands/InitializeCommand.php @@ -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 @@ 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}"); @@ -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; } diff --git a/src/Socialbox/Classes/Configuration.php b/src/Socialbox/Classes/Configuration.php index 5bc47a0..648e439 100644 --- a/src/Socialbox/Classes/Configuration.php +++ b/src/Socialbox/Classes/Configuration.php @@ -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(); diff --git a/src/Socialbox/Classes/Resources/database/variables.sql b/src/Socialbox/Classes/Resources/database/variables.sql new file mode 100644 index 0000000..29b865d --- /dev/null +++ b/src/Socialbox/Classes/Resources/database/variables.sql @@ -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' +); \ No newline at end of file diff --git a/src/Socialbox/Classes/RpcClient.php b/src/Socialbox/Classes/RpcClient.php new file mode 100644 index 0000000..31dbcc0 --- /dev/null +++ b/src/Socialbox/Classes/RpcClient.php @@ -0,0 +1,88 @@ +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); + } + } \ No newline at end of file diff --git a/src/Socialbox/Classes/RpcHandler.php b/src/Socialbox/Classes/RpcHandler.php index 810c3d7..df24724 100644 --- a/src/Socialbox/Classes/RpcHandler.php +++ b/src/Socialbox/Classes/RpcHandler.php @@ -8,6 +8,7 @@ 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 @@ public static function getClientRequest(): ClientRequest 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 @@ public static function getClientRequest(): ClientRequest 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); diff --git a/src/Socialbox/Classes/ServerResolver.php b/src/Socialbox/Classes/ServerResolver.php new file mode 100644 index 0000000..58bf1ff --- /dev/null +++ b/src/Socialbox/Classes/ServerResolver.php @@ -0,0 +1,52 @@ +getTraceAsString() ); } + + public static function generateHeader(StandardHeaders $header, string $value): string + { + return $header->value . ': ' . $value; + } } \ No newline at end of file diff --git a/src/Socialbox/Enums/DatabaseObjects.php b/src/Socialbox/Enums/DatabaseObjects.php index 5ca0b51..d0037e3 100644 --- a/src/Socialbox/Enums/DatabaseObjects.php +++ b/src/Socialbox/Enums/DatabaseObjects.php @@ -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 @@ public function getPriority(): int { return match ($this) { + self::VARIABLES => 0, self::REGISTERED_PEERS => 1, self::PASSWORD_AUTHENTICATION, self::SESSIONS => 2, }; diff --git a/src/Socialbox/Enums/StandardHeaders.php b/src/Socialbox/Enums/StandardHeaders.php index 37841fe..9d707de 100644 --- a/src/Socialbox/Enums/StandardHeaders.php +++ b/src/Socialbox/Enums/StandardHeaders.php @@ -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. diff --git a/src/Socialbox/Exceptions/ResolutionException.php b/src/Socialbox/Exceptions/ResolutionException.php new file mode 100644 index 0000000..d17a840 --- /dev/null +++ b/src/Socialbox/Exceptions/ResolutionException.php @@ -0,0 +1,13 @@ +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)); + } + } + } +} \ No newline at end of file diff --git a/src/Socialbox/Objects/ClientRequest.php b/src/Socialbox/Objects/ClientRequest.php index 6e6bf7c..ecb942a 100644 --- a/src/Socialbox/Objects/ClientRequest.php +++ b/src/Socialbox/Objects/ClientRequest.php @@ -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 @@ public function getSignature(): ?string 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; + } } } \ No newline at end of file diff --git a/src/Socialbox/Objects/ResolvedServer.php b/src/Socialbox/Objects/ResolvedServer.php new file mode 100644 index 0000000..4ecd4e5 --- /dev/null +++ b/src/Socialbox/Objects/ResolvedServer.php @@ -0,0 +1,25 @@ +endpoint = $endpoint; + $this->publicKey = $publicKey; + } + + public function getEndpoint(): string + { + return $this->endpoint; + } + + public function getPublicKey(): string + { + return $this->publicKey; + } +} \ No newline at end of file diff --git a/src/Socialbox/Objects/RpcResponse.php b/src/Socialbox/Objects/RpcResponse.php index 01ac789..a109eab 100644 --- a/src/Socialbox/Objects/RpcResponse.php +++ b/src/Socialbox/Objects/RpcResponse.php @@ -55,13 +55,6 @@ private function convertToArray(mixed $data): mixed 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; } diff --git a/tests/Socialbox/Classes/CryptographyTest.php b/tests/Socialbox/Classes/CryptographyTest.php index a90815b..483ba13 100644 --- a/tests/Socialbox/Classes/CryptographyTest.php +++ b/tests/Socialbox/Classes/CryptographyTest.php @@ -20,6 +20,8 @@ public function testGenerateKeyPair() $this->assertObjectHasProperty('privateKey', $keyPair); $this->assertIsString($keyPair->getPublicKey()); $this->assertIsString($keyPair->getPrivateKey()); + + print_r($keyPair); } /** diff --git a/tests/Socialbox/Classes/ServerResolverTest.php b/tests/Socialbox/Classes/ServerResolverTest.php new file mode 100644 index 0000000..ef7e8a1 --- /dev/null +++ b/tests/Socialbox/Classes/ServerResolverTest.php @@ -0,0 +1,21 @@ +getEndpoint()); + self::assertNotEmpty($resolvedServer->getPublicKey()); + } +} \ No newline at end of file diff --git a/tests/Socialbox/Managers/SessionManagerTest.php b/tests/Socialbox/Managers/SessionManagerTest.php index 6b0f954..1da708c 100644 --- a/tests/Socialbox/Managers/SessionManagerTest.php +++ b/tests/Socialbox/Managers/SessionManagerTest.php @@ -6,7 +6,6 @@ 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 @@ public function testCreateSession(): void $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(); diff --git a/tests/Socialbox/Managers/VariableManagerTest.php b/tests/Socialbox/Managers/VariableManagerTest.php new file mode 100644 index 0000000..d1060f3 --- /dev/null +++ b/tests/Socialbox/Managers/VariableManagerTest.php @@ -0,0 +1,34 @@ +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'); + } + +} \ No newline at end of file