Page Menu
Home
DevCentral
Search
Configure Global Search
Log In
Files
F12295695
D3772.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
9 KB
Referenced Files
None
Subscribers
None
D3772.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D3772: Propagate database events
Attached
Detach File
Event Timeline
Log In to Comment