Page MenuHomeDevCentral

D3772.diff
No OneTemporary

D3772.diff

diff --git a/composer.json b/composer.json
--- a/composer.json
+++ b/composer.json
@@ -43,7 +43,7 @@
"keruald/commands": "0.0.1",
"keruald/database": "0.4.0",
"keruald/github": "0.2.1",
- "keruald/omnitools": "0.14.0",
+ "keruald/omnitools": "0.15.0",
"keruald/report": "0.1.0"
},
"autoload": {
diff --git a/database/composer.json b/database/composer.json
--- a/database/composer.json
+++ b/database/composer.json
@@ -8,6 +8,9 @@
"phpunit/phpunit": "^10.2",
"nasqueron/codestyle": "^0.1.2"
},
+ "suggest": {
+ "keruald/omnitools": "Allows to fire database events"
+ },
"scripts": {
"lint-src": "find src -type f -name '*.php' | xargs -n1 php -l",
"lint-tests": "find tests -type f -name '*.php' | xargs -n1 php -l",
diff --git a/database/src/Database.php b/database/src/Database.php
--- a/database/src/Database.php
+++ b/database/src/Database.php
@@ -16,7 +16,7 @@
*
* 2) through Database::load() if you want to use a singleton pattern.
*/
-class Database {
+abstract class Database {
///
/// Factory pattern
diff --git a/database/src/DatabaseEngine.php b/database/src/DatabaseEngine.php
--- a/database/src/DatabaseEngine.php
+++ b/database/src/DatabaseEngine.php
@@ -151,4 +151,37 @@
);
}
+
+ ///
+ /// Events
+ ///
+
+ public const string EVENTS_PROPAGATION_CLASS = "Keruald\\OmniTools\\Events\\Propagation";
+
+ /**
+ * @event CantConnectToHost Functions to call when it's not possible to connect to the database host
+ * @eventparam Database $db The current database instance
+ */
+ public array $cantConnectToHostEvents = [];
+
+ /**
+ * @event QueryError Functions to call when a query fails.
+ * @eventparam Database $db The current database instance
+ * @eventparam string $query The failed query
+ * @eventparam DatabaseException $ex The exception describing the query error
+ */
+ public array $queryErrorEvents = [];
+
+ /**
+ * Called on connect failure
+ */
+ protected abstract function onCantConnectToHost() : void;
+
+ /**
+ * Called on query error
+ *
+ * @param string $query The query executed when the error occurred
+ */
+ protected abstract function onQueryError (string $query) : void;
+
}
diff --git a/database/src/Engines/BlackholeEngine.php b/database/src/Engines/BlackholeEngine.php
--- a/database/src/Engines/BlackholeEngine.php
+++ b/database/src/Engines/BlackholeEngine.php
@@ -40,4 +40,15 @@
return true;
}
+ ///
+ /// Events
+ ///
+
+ protected function onCantConnectToHost () : void {
+ // We can always connect to a black hole engine.
+ }
+
+ protected function onQueryError (string $query) : void {
+ // Queries are always valid.
+ }
}
diff --git a/database/src/Engines/MySQLiEngine.php b/database/src/Engines/MySQLiEngine.php
--- a/database/src/Engines/MySQLiEngine.php
+++ b/database/src/Engines/MySQLiEngine.php
@@ -7,6 +7,7 @@
use Keruald\Database\Exceptions\SqlException;
use Keruald\Database\Result\MySQLiDatabaseResult;
+
use RuntimeException;
use mysqli;
@@ -53,6 +54,11 @@
// Connects to MySQL server
$this->driver = new mysqli_driver();
$this->db = new mysqli($host, $username, $password);
+
+ if ($this->db->connect_error !== null) {
+ $this->onCantConnectToHost();
+ }
+
$this->setCharset('utf8mb4');
// Selects database
@@ -78,7 +84,7 @@
return false;
}
- $this->throwException($ex, $query);
+ $this->onQueryError($query);
}
if (is_bool($result)) {
@@ -144,11 +150,6 @@
/// Engine mechanics methods
///
- private function throwException (mysqli_sql_exception $ex, string $query) {
- $context = $this->getExceptionContext();
- throw SqlException::fromException($ex, $query, $context);
- }
-
protected function getExceptionContext () : array {
return [
'error' => $this->db->error,
@@ -213,4 +214,42 @@
return $this->db;
}
+ ///
+ /// Events
+ ///
+
+ /**
+ * Called on connect failure
+ */
+ protected function onCantConnectToHost () : void {
+ $ex = new RuntimeException("Can't connect to SQL server: "
+ . $this->db->connect_error);
+
+ if (!class_exists(self::EVENTS_PROPAGATION_CLASS)) {
+ throw $ex;
+ }
+
+ $callable = [self::EVENTS_PROPAGATION_CLASS, "callOrThrow"];
+ $callable($this->cantConnectToHostEvents, $this, $ex);
+ }
+
+ /**
+ * Called on query error
+ *
+ * @param string $query The query executed when the error occurred
+ */
+ protected function onQueryError (string $query) : void {
+ $ex = SqlException::fromQuery(
+ $query,
+ $this->getExceptionContext(),
+ );
+
+ if (!class_exists(self::EVENTS_PROPAGATION_CLASS)) {
+ throw $ex;
+ }
+
+ $callable = [self::EVENTS_PROPAGATION_CLASS, "callOrThrow"];
+ $callable($this->queryErrorEvents, [$this, $query, $ex], $ex);
+ }
+
}
diff --git a/omnitools/src/Events/Propagation.php b/omnitools/src/Events/Propagation.php
new file mode 100644
--- /dev/null
+++ b/omnitools/src/Events/Propagation.php
@@ -0,0 +1,74 @@
+<?php
+declare(strict_types=1);
+
+namespace Keruald\OmniTools\Events;
+
+use BadFunctionCallException;
+use Keruald\OmniTools\DataTypes\Option\None;
+use Keruald\OmniTools\DataTypes\Option\Option;
+use Keruald\OmniTools\DataTypes\Option\Some;
+
+use RuntimeException;
+use Throwable;
+
+ /**
+ * Static class providing helper methods to propagate events.
+ */
+class Propagation {
+
+ /**
+ * Calls a set of functions with the specified parameters.
+ * This is intended for callback purpose.
+ *
+ * @param iterable $callables The functions to call, each item a callable
+ * @param array $parameters The parameters to pass to the functions [optional]
+ */
+ public static function call (iterable $callables, array $parameters = []) : void {
+ foreach ($callables as $callable) {
+ if (!is_callable($callable)) {
+ $previous = self::grabException($parameters);
+ throw new BadFunctionCallException("Callback for this method.", 0, $previous->orElse(null));
+ }
+
+ call_user_func_array($callable, $parameters);
+ }
+ }
+
+ /**
+ * Calls a set of functions with the specified parameters.
+ * If no function is present, throws an exception.
+ *
+ * @param iterable $callables The functions to call, each item a callable
+ * @param array $parameters The parameters to pass to the functions
+ * [optional]
+ * @param Throwable|null $exception The exception to throw if no callback is
+ * provided [optional]
+ *
+ * @throws Throwable
+ */
+ public static function callOrThrow (iterable $callables, array $parameters = [], Throwable $exception = null) : void {
+ if (!count($callables)) {
+ throw $exception ?? self::grabException($parameters)
+ ->orElse(new RuntimeException);
+ }
+
+ static::call($callables, $parameters);
+ }
+
+ /**
+ * Grabs the first exception among specified items.
+ *
+ * @param iterable $items The items to check
+ * @return Option<Throwable>
+ */
+ private static function grabException (iterable $items) : Option {
+ foreach ($items as $item) {
+ if ($item instanceof Throwable) {
+ return new Some($item);
+ }
+ }
+
+ return new None;
+ }
+
+}
diff --git a/omnitools/tests/Events/PropagationTest.php b/omnitools/tests/Events/PropagationTest.php
new file mode 100644
--- /dev/null
+++ b/omnitools/tests/Events/PropagationTest.php
@@ -0,0 +1,73 @@
+<?php
+
+namespace Keruald\OmniTools\Tests\Events;
+
+use Keruald\OmniTools\Events\Propagation;
+
+use PHPUnit\Framework\TestCase;
+
+use BadFunctionCallException;
+use Exception;
+use LogicException;
+use RuntimeException;
+
+class PropagationTest extends TestCase {
+
+ private int $counter;
+ private iterable $callbacks;
+
+ protected function setUp () : void {
+ $this->counter = 0;
+
+ $this->callbacks = [
+ function (int $a, int $b) {
+ $this->counter++;
+ },
+
+ function (int $a, int $b) {
+ $this->counter++;
+ },
+ ];
+ }
+
+ public function testCallWithoutAnyCallback () {
+ $this->expectNotToPerformAssertions();
+
+ Propagation::call([]);
+ }
+
+ public function testCall () {
+ Propagation::call($this->callbacks, [3, 4]);
+
+ $this->assertEquals(2, $this->counter);
+ }
+
+ public function testCallOrThrowWithCallbacks () {
+ Propagation::callOrThrow($this->callbacks, [3, 4]);
+
+ $this->assertEquals(2, $this->counter);
+ }
+
+ public function testCallOrThrowWithoutAnyCallback () {
+ $this->expectException(RuntimeException::class);
+ Propagation::callOrThrow([], [3, 4]);
+ }
+
+ public function testCallOrThrowWithCustomException () {
+ $this->expectException(LogicException::class);
+ Propagation::callOrThrow([], [3, 4], new LogicException);
+ }
+
+ public function testCallWhenArgumentIsAnException () {
+ // Dubious case with anonymous functions using strong types
+
+ $arguments = [
+ 3,
+ Exception::class,
+ ];
+
+ // Inner exception need to be
+ $this->expectException(BadFunctionCallException::class);
+ Propagation::call([null], $arguments);
+ }
+}

File Metadata

Mime Type
text/plain
Expires
Tue, Oct 21, 06:21 (20 h, 19 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3089280
Default Alt Text
D3772.diff (9 KB)

Event Timeline