diff --git a/src/Driver/Cassandra/Cassandra.php b/src/Driver/Cassandra/Cassandra.php index 039b08d..d6dabc5 100644 --- a/src/Driver/Cassandra/Cassandra.php +++ b/src/Driver/Cassandra/Cassandra.php @@ -121,14 +121,18 @@ protected function connect(): void * * @param string $query * @return Rows - * @throws Exception + * @throws CassandraModelException if an error occurs while executing the query */ protected function rawQuery(string $query): Rows { $this->connect(); $statement = new SimpleStatement($query); - return $this->connection->execute($statement, null); + try { + return $this->connection->execute($statement, null); + } catch (Exception $e) { + throw CassandraModelException::wrapping($e); + } } /** @@ -136,7 +140,7 @@ protected function rawQuery(string $query): Rows * * @param ModelInterface $model * @return bool - * @throws Exception + * @throws CassandraModelException if an error occurs while executing the query */ public function save(ModelInterface $model): bool { @@ -158,7 +162,7 @@ public function save(ModelInterface $model): bool * @param mixed $id * @param ModelInterface|null $model * @return ModelInterface|null - * @throws Exception + * @throws CassandraModelException if an error occurs while executing the query */ public function get(string $modelClass, mixed $id, ?ModelInterface $model = null): ?ModelInterface { @@ -183,7 +187,7 @@ public function get(string $modelClass, mixed $id, ?ModelInterface $model = null * * @param ModelInterface $model * @return bool - * @throws Exception + * @throws CassandraModelException if an error occurs while executing the query */ public function delete(ModelInterface $model): bool { @@ -200,7 +204,7 @@ public function delete(ModelInterface $model): bool * * @param Query $query * @return QueryResult - * @throws Exception + * @throws CassandraModelException if an error occurs while executing the query */ public function query(Query $query): QueryResult { @@ -278,4 +282,4 @@ public function setKeyspace(string $keyspace): Cassandra $this->keyspace = $keyspace; return $this; } -} \ No newline at end of file +} diff --git a/src/Driver/Cassandra/CassandraModelException.php b/src/Driver/Cassandra/CassandraModelException.php new file mode 100644 index 0000000..174b697 --- /dev/null +++ b/src/Driver/Cassandra/CassandraModelException.php @@ -0,0 +1,20 @@ +getMessage(), $exception->getCode(), $exception); + } +} diff --git a/src/Driver/Mysqli/MysqlConnectionFailedException.php b/src/Driver/Mysqli/MysqlConnectionFailedException.php new file mode 100644 index 0000000..7f25df5 --- /dev/null +++ b/src/Driver/Mysqli/MysqlConnectionFailedException.php @@ -0,0 +1,13 @@ +getCode(), $previous); + } +} diff --git a/src/Driver/Mysqli/MysqlException.php b/src/Driver/Mysqli/MysqlException.php new file mode 100644 index 0000000..3a9d9c1 --- /dev/null +++ b/src/Driver/Mysqli/MysqlException.php @@ -0,0 +1,38 @@ +connection || !@mysqli_ping($this->connection)) { $this->connection = mysqli_connect($this->host, $this->username, $this->password, $this->database, $this->port, $this->socket); if (!$this->connection) { - throw new Exception("Could not connect to Mysqli database. Error: " . mysqli_error($this->connection)); + throw new MysqlConnectionFailedException(new MysqlException($this->connection)); } } } @@ -117,17 +114,14 @@ protected function connect(): void * * @param string $query * @return bool|mysqli_result - * @throws Exception + * @throws MysqlConnectionFailedException if connecting to the mysql database fails + * @throws MysqlException if a mysql error occurs while executing the query */ protected function rawQuery(string $query): mysqli_result|bool { $this->connect(); $result = mysqli_query($this->connection, $query); - - if (mysqli_error($this->connection)) { - throw new Exception("MySQLi Error #" . mysqli_errno($this->connection) . ": " . mysqli_error($this->connection)); - } - + MysqlException::checkConnection($this->connection); return $result; } @@ -136,7 +130,8 @@ protected function rawQuery(string $query): mysqli_result|bool * * @param ModelInterface $model * @return bool - * @throws Exception + * @throws MysqlConnectionFailedException if connecting to the mysql database fails + * @throws MysqlException if a mysql error occurs while executing the query */ public function save(ModelInterface $model): bool { @@ -182,7 +177,8 @@ public function save(ModelInterface $model): bool * @param mixed $id * @param ModelInterface|null $model * @return ModelInterface|null - * @throws Exception + * @throws MysqlConnectionFailedException if connecting to the mysql database fails + * @throws MysqlException if a mysql error occurs while executing the query */ public function get(string $modelClass, mixed $id, ?ModelInterface $model = null): ?ModelInterface { @@ -208,7 +204,8 @@ public function get(string $modelClass, mixed $id, ?ModelInterface $model = null * * @param ModelInterface $model * @return bool - * @throws Exception + * @throws MysqlConnectionFailedException if connecting to the mysql database fails + * @throws MysqlException if a mysql error occurs while executing the query */ public function delete(ModelInterface $model): bool { @@ -227,7 +224,8 @@ public function delete(ModelInterface $model): bool * * @param Query $query * @return QueryResult - * @throws Exception + * @throws MysqlConnectionFailedException if connecting to the mysql database fails + * @throws MysqlException if a mysql error occurs while executing the query */ public function query(Query $query): QueryResult { @@ -323,4 +321,4 @@ public function setDatabase(string $database): Mysqli $this->database = $database; return $this; } -} \ No newline at end of file +} diff --git a/src/Driver/OpenSearch/Exception/OpenSearchException.php b/src/Driver/OpenSearch/Exception/OpenSearchException.php index 947cf09..1d9bdb3 100644 --- a/src/Driver/OpenSearch/Exception/OpenSearchException.php +++ b/src/Driver/OpenSearch/Exception/OpenSearchException.php @@ -2,9 +2,9 @@ namespace Aternos\Model\Driver\OpenSearch\Exception; -use Exception; +use Aternos\Model\ModelException; -class OpenSearchException extends Exception +class OpenSearchException extends ModelException { } diff --git a/src/Driver/OpenSearch/OpenSearch.php b/src/Driver/OpenSearch/OpenSearch.php index 4ca5672..30bebef 100644 --- a/src/Driver/OpenSearch/OpenSearch.php +++ b/src/Driver/OpenSearch/OpenSearch.php @@ -72,10 +72,9 @@ public function __construct( * @param string $uri * @param mixed|null $body * @return stdClass - * @throws HttpErrorResponseException - * @throws HttpTransportException - * @throws SerializeException - * @throws OpenSearchException + * @throws HttpErrorResponseException If the response status code is not 2xx + * @throws HttpTransportException If an error happens while the http client processes the request + * @throws SerializeException If an error happens during (de-)serialization */ protected function request(string $method, string $uri, mixed $body = null): stdClass { @@ -96,6 +95,7 @@ protected function request(string $method, string $uri, mixed $body = null): std throw $e; } } + /** @var OpenSearchException $lastError */ throw $lastError; } @@ -113,7 +113,9 @@ protected function buildUrl(string ...$path): string * * @param ModelInterface $model * @return bool - * @throws OpenSearchException + * @throws HttpErrorResponseException If the response status code is not 2xx + * @throws HttpTransportException If an error happens while the http client processes the request + * @throws SerializeException If an error happens during (de-)serialization */ public function save(ModelInterface $model): bool { @@ -132,10 +134,9 @@ public function save(ModelInterface $model): bool * @param mixed $id * @param ModelInterface|null $model * @return ModelInterface|null - * @throws HttpErrorResponseException - * @throws HttpTransportException - * @throws OpenSearchException - * @throws SerializeException + * @throws HttpErrorResponseException If the response status code is not 2xx + * @throws HttpTransportException If an error happens while the http client processes the request + * @throws SerializeException If an error happens during (de-)serialization */ public function get(string $modelClass, mixed $id, ?ModelInterface $model = null): ?ModelInterface { @@ -151,17 +152,7 @@ public function get(string $modelClass, mixed $id, ?ModelInterface $model = null throw $e; } - if (!isset($response->_id) || !is_string($response->_id)) { - throw new SerializeException("Received invalid document _id from OpenSearch"); - } - - if (isset($response->_source) && is_object($response->_source)) { - $data = get_object_vars($response->_source); - } else { - $data = []; - } - - $data[$modelClass::getIdField()] = $response->_id; + $data = $this->getModelData($response, $modelClass); if ($model) { return $model->applyData($data); @@ -174,7 +165,9 @@ public function get(string $modelClass, mixed $id, ?ModelInterface $model = null * * @param ModelInterface $model * @return bool - * @throws OpenSearchException + * @throws HttpErrorResponseException If the response status code is not 2xx + * @throws HttpTransportException If an error happens while the http client processes the request + * @throws SerializeException If an error happens during (de-)serialization */ public function delete(ModelInterface $model): bool { @@ -208,10 +201,9 @@ protected function getHitCountRelation(string $name): ?CountRelation /** * @param Search $search * @return SearchResult - * @throws HttpErrorResponseException - * @throws HttpTransportException - * @throws OpenSearchException - * @throws SerializeException + * @throws HttpErrorResponseException If the response status code is not 2xx + * @throws HttpTransportException If an error happens while the http client processes the request + * @throws SerializeException If an error happens during (de-)serialization */ public function search(Search $search): SearchResult { @@ -242,23 +234,32 @@ public function search(Search $search): SearchResult } foreach ($response->hits->hits as $resultDocument) { - if (!isset($resultDocument->_id) || !is_string($resultDocument->_id)) { - throw new SerializeException("Received invalid document _id from OpenSearch"); - } - - if (isset($resultDocument->_source) && is_object($resultDocument->_source)) { - $data = get_object_vars($resultDocument->_source); - } else { - $data = []; - } - - $data[$modelClassName::getIdField()] = $resultDocument->_id; - /** @var ModelInterface $model */ $model = new $modelClassName(); - $model->applyData($data); + $model->applyData($this->getModelData($resultDocument, $modelClassName)); $result->add($model); } return $result; } + + /** + * @param stdClass $response + * @param class-string $modelClass $modelClass + * @return array + */ + public function getModelData(stdClass $response, string $modelClass): array + { + if (!isset($response->_id) || !is_string($response->_id)) { + throw new SerializeException("Received invalid document _id from OpenSearch"); + } + + if (isset($response->_source) && is_object($response->_source)) { + $data = get_object_vars($response->_source); + } else { + $data = []; + } + + $data[$modelClass::getIdField()] = $response->_id; + return $data; + } } diff --git a/src/Driver/OpenSearch/OpenSearchHost.php b/src/Driver/OpenSearch/OpenSearchHost.php index e5b33c1..7392e0d 100644 --- a/src/Driver/OpenSearch/OpenSearchHost.php +++ b/src/Driver/OpenSearch/OpenSearchHost.php @@ -40,9 +40,9 @@ public function __construct( * @param string $uri * @param mixed|null $body * @return stdClass - * @throws HttpErrorResponseException - * @throws HttpTransportException - * @throws SerializeException + * @throws HttpErrorResponseException If the response status code is not 2xx + * @throws HttpTransportException If an error happens while the http client processes the request + * @throws SerializeException If an error happens during (de-)serialization */ public function request(string $method, string $uri, mixed $body = null): stdClass { @@ -78,8 +78,9 @@ public function request(string $method, string $uri, mixed $body = null): stdCla /** * @param ResponseInterface $response * @return stdClass - * @throws HttpTransportException - * @throws SerializeException + * @throws HttpTransportException If the response body could not be read + * @throws HttpTransportException If the response is not JSON + * @throws SerializeException If the response body could not be deserialized */ protected function parseResponse(ResponseInterface $response): stdClass { @@ -100,7 +101,7 @@ protected function parseResponse(ResponseInterface $response): stdClass /** * @param mixed $data * @return string - * @throws SerializeException + * @throws SerializeException If the data could not be serialized */ protected function serialize(mixed $data): string { @@ -114,7 +115,8 @@ protected function serialize(mixed $data): string /** * @param string $data * @return stdClass - * @throws SerializeException + * @throws SerializeException If the data could not be deserialized + * @throws SerializeException If the data is not an object */ protected function deserialize(string $data): stdClass { diff --git a/src/Driver/Redis/Redis.php b/src/Driver/Redis/Redis.php index 85cca3f..97748ac 100644 --- a/src/Driver/Redis/Redis.php +++ b/src/Driver/Redis/Redis.php @@ -67,16 +67,20 @@ public function __construct(?string $host = null, ?int $port = null, ?string $so /** * Connect to redis * - * @throws RedisException + * @throws RedisModelException */ protected function connect(): void { if (!$this->connection) { $this->connection = new \Redis(); - if (!$this->socket) { - $this->connection->connect($this->host, $this->port); - } else { - $this->connection->connect($this->socket); + try { + if (!$this->socket) { + $this->connection->connect($this->host, $this->port); + } else { + $this->connection->connect($this->socket); + } + } catch (RedisException $e) { + throw RedisModelException::wrapping($e); } } } @@ -98,7 +102,7 @@ protected function generateCacheKey(string $modelClass, mixed $id): string * * @param ModelInterface $model * @return bool - * @throws RedisException + * @throws RedisModelException */ public function save(ModelInterface $model): bool { @@ -107,7 +111,12 @@ public function save(ModelInterface $model): bool } $this->connect(); - return $this->connection->set($this->generateCacheKey($model::class, $model->getId()), json_encode($model), $model->getCacheTime()); + $key = $this->generateCacheKey($model::class, $model->getId()); + try { + return $this->connection->set($key, json_encode($model), $model->getCacheTime()); + } catch (RedisException $e) { + throw RedisModelException::wrapping($e); + } } /** @@ -117,7 +126,7 @@ public function save(ModelInterface $model): bool * @param mixed $id * @param ModelInterface|null $model * @return ModelInterface|null - * @throws RedisException + * @throws RedisModelException */ public function get(string $modelClass, mixed $id, ?ModelInterface $model = null): ?ModelInterface { @@ -126,7 +135,11 @@ public function get(string $modelClass, mixed $id, ?ModelInterface $model = null } $this->connect(); - $rawData = $this->connection->get($this->generateCacheKey($modelClass, $id)); + try { + $rawData = $this->connection->get($this->generateCacheKey($modelClass, $id)); + } catch (RedisException $e) { + throw RedisModelException::wrapping($e); + } if (!$rawData) { return null; @@ -148,7 +161,7 @@ public function get(string $modelClass, mixed $id, ?ModelInterface $model = null * * @param ModelInterface $model * @return bool - * @throws RedisException + * @throws RedisModelException */ public function delete(ModelInterface $model): bool { @@ -157,7 +170,12 @@ public function delete(ModelInterface $model): bool } $this->connect(); - return $this->connection->del($this->generateCacheKey($model::class, $model->getId())); + $key = $this->generateCacheKey($model::class, $model->getId()); + try { + return $this->connection->del($key); + } catch (RedisException $e) { + throw RedisModelException::wrapping($e); + } } /** @@ -189,4 +207,4 @@ public function setSocket(?string $socket): Redis $this->socket = $socket; return $this; } -} \ No newline at end of file +} diff --git a/src/Driver/Redis/RedisModelException.php b/src/Driver/Redis/RedisModelException.php new file mode 100644 index 0000000..17b05e0 --- /dev/null +++ b/src/Driver/Redis/RedisModelException.php @@ -0,0 +1,10 @@ +getTable($query->modelClassName::getName()); return $table->query($query); } -} \ No newline at end of file +} diff --git a/src/GenericModel.php b/src/GenericModel.php index 30c3b11..9f5671f 100644 --- a/src/GenericModel.php +++ b/src/GenericModel.php @@ -372,7 +372,7 @@ public static function clearTestEntries(): void * @param string $id * @param bool $update * @return static|null - * @throws Exception + * @throws ModelException */ public static function get(string $id, bool $update = false): ?static { @@ -560,7 +560,7 @@ public static function update(null|array $fields = nul } /** - * @throws Exception + * @throws ModelException */ public function reload(): static { diff --git a/src/ModelException.php b/src/ModelException.php new file mode 100644 index 0000000..14e8b20 --- /dev/null +++ b/src/ModelException.php @@ -0,0 +1,14 @@ +getMessage(), $exception->getCode(), $exception); + } + + final protected function __construct(string $message, int $code = null, ?Throwable $previous = null) + { + parent::__construct($message, $code, $previous); + } +} diff --git a/test/tests/TestDriverTest.php b/test/tests/TestDriverTest.php index a09cd5c..1e9c235 100644 --- a/test/tests/TestDriverTest.php +++ b/test/tests/TestDriverTest.php @@ -14,14 +14,10 @@ use Aternos\Model\Query\WhereCondition; use Aternos\Model\Query\WhereGroup; use Aternos\Model\Test\Src\TestModel; -use Exception; use PHPUnit\Framework\TestCase; class TestDriverTest extends TestCase { - /** - * @return void - */ protected function setUp(): void { $testData = "ABCDEFGHIJ"; @@ -34,10 +30,6 @@ protected function setUp(): void } } - /** - * @return void - * @throws Exception - */ public function testGet(): void { $model = TestModel::get("1B"); @@ -56,20 +48,12 @@ public function testGetOnNonExistingTable(): void $this->assertNull($model); } - /** - * @return void - * @throws Exception - */ public function testGetNull(): void { $model = TestModel::get("DOES_NOT_EXIST"); $this->assertNull($model); } - /** - * @return void - * @throws Exception - */ public function testDelete(): void { $model = TestModel::get("1B"); @@ -77,19 +61,12 @@ public function testDelete(): void $this->assertNull(TestModel::get("1B")); } - /** - * @return void - */ public function testDeleteNull(): void { $model = new TestModel(); $this->assertFalse($model->delete()); } - /** - * @return void - * @throws Exception - */ public function testSaveNew(): void { $model = new TestModel(); @@ -103,10 +80,6 @@ public function testSaveNew(): void $this->assertEquals(10, $getModel->number); } - /** - * @return void - * @throws Exception - */ public function testSaveExisting(): void { $model = TestModel::get("1B"); @@ -548,10 +521,6 @@ public function testSelectGroupMax(): void } } - /** - * @return void - * @throws Exception - */ public function testUpdate(): void { TestModel::disableRegistry(); @@ -574,10 +543,6 @@ public function testUpdate(): void $this->assertEquals("C", $model->text); } - /** - * @return void - * @throws Exception - */ public function testDeleteQuery(): void { TestModel::disableRegistry(); @@ -599,9 +564,6 @@ public function testDeleteQuery(): void $this->assertNull($model); } - /** - * @return void - */ protected function tearDown(): void { TestModel::clearTestEntries();