diff --git a/core/Configuration/CreateDatabase.class.php b/core/Configuration/CreateDatabase.class.php index 73869b7..b3d317b 100644 --- a/core/Configuration/CreateDatabase.class.php +++ b/core/Configuration/CreateDatabase.class.php @@ -265,6 +265,7 @@ class CreateDatabase extends DatabaseScript { ->addRow("Contact/get", array(USER_GROUP_ADMIN, USER_GROUP_SUPPORT), "Allows users to see messages within a contact request"); self::loadPatches($queries, $sql); + self::loadEntities($queries, $sql); return $queries; } @@ -286,4 +287,24 @@ class CreateDatabase extends DatabaseScript { } } } + + private static function loadEntities(&$queries, $sql) { + $entityDirectory = './core/Objects/DatabaseEntity/'; + if (file_exists($entityDirectory) && is_dir($entityDirectory)) { + $scan_arr = scandir($entityDirectory); + $files_arr = array_diff($scan_arr, array('.', '..')); + foreach ($files_arr as $file) { + $suffix = ".class.php"; + if (endsWith($file, $suffix)) { + $className = substr($file, 0, strlen($file) - strlen($suffix)); + if (!in_array($className, ["DatabaseEntity", "DatabaseEntityHandler"])) { + $className = "\\Objects\\DatabaseEntity\\$className"; + $method = "$className::getHandler"; + $handler = call_user_func($method, $sql); + $queries[] = $handler->getTableQuery(); + } + } + } + } + } } diff --git a/core/External/composer.json b/core/External/composer.json index 8aa9920..fdbb8b0 100644 --- a/core/External/composer.json +++ b/core/External/composer.json @@ -4,8 +4,8 @@ "twig/twig": "^3.0", "chillerlan/php-qrcode": "^4.3", "christian-riesen/base32": "^1.6", - "spomky-labs/cbor-php": "2.0.1", - "web-auth/cose-lib": "^3.3", + "spomky-labs/cbor-php": "2.1.0", + "web-auth/cose-lib": "3.3.12", "firebase/php-jwt": "^6.2" }, "require-dev": { diff --git a/core/External/composer.lock b/core/External/composer.lock index 68a6ac3..e5f68ed 100644 --- a/core/External/composer.lock +++ b/core/External/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "edfe59e1489b0594cfe6ff0276be3419", + "content-hash": "af894b476b8bab945acf32399608ac5f", "packages": [ { "name": "beberlei/assert", @@ -533,16 +533,16 @@ }, { "name": "php-mqtt/client", - "version": "v1.3.0", + "version": "v1.4.0", "source": { "type": "git", "url": "https://github.com/php-mqtt/client.git", - "reference": "5813fd257f27e1e9537cd29c0152102e129437d0" + "reference": "d54381306e68baf7c2c089392c0c6d1d06e278c6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-mqtt/client/zipball/5813fd257f27e1e9537cd29c0152102e129437d0", - "reference": "5813fd257f27e1e9537cd29c0152102e129437d0", + "url": "https://api.github.com/repos/php-mqtt/client/zipball/d54381306e68baf7c2c089392c0c6d1d06e278c6", + "reference": "d54381306e68baf7c2c089392c0c6d1d06e278c6", "shasum": "" }, "require": { @@ -584,36 +584,36 @@ ], "support": { "issues": "https://github.com/php-mqtt/client/issues", - "source": "https://github.com/php-mqtt/client/tree/v1.3.0" + "source": "https://github.com/php-mqtt/client/tree/v1.4.0" }, - "time": "2022-05-26T17:31:01+00:00" + "time": "2022-06-15T19:19:03+00:00" }, { "name": "psr/log", - "version": "3.0.0", + "version": "1.1.4", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" + "reference": "d49695b909c3b7628b6289db5479a1c204601f11" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", - "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", + "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11", + "reference": "d49695b909c3b7628b6289db5479a1c204601f11", "shasum": "" }, "require": { - "php": ">=8.0.0" + "php": ">=5.3.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.x-dev" + "dev-master": "1.1.x-dev" } }, "autoload": { "psr-4": { - "Psr\\Log\\": "src" + "Psr\\Log\\": "Psr/Log/" } }, "notification-url": "https://packagist.org/downloads/", @@ -634,34 +634,43 @@ "psr-3" ], "support": { - "source": "https://github.com/php-fig/log/tree/3.0.0" + "source": "https://github.com/php-fig/log/tree/1.1.4" }, - "time": "2021-07-14T16:46:02+00:00" + "time": "2021-05-03T11:20:27+00:00" }, { "name": "spomky-labs/cbor-php", - "version": "v2.0.1", + "version": "v2.1.0", "source": { "type": "git", "url": "https://github.com/Spomky-Labs/cbor-php.git", - "reference": "9776578000be884cd7864eeb7c37a4ac92d8c995" + "reference": "28e2712cfc0b48fae661a48ffc6896d7abe83684" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Spomky-Labs/cbor-php/zipball/9776578000be884cd7864eeb7c37a4ac92d8c995", - "reference": "9776578000be884cd7864eeb7c37a4ac92d8c995", + "url": "https://api.github.com/repos/Spomky-Labs/cbor-php/zipball/28e2712cfc0b48fae661a48ffc6896d7abe83684", + "reference": "28e2712cfc0b48fae661a48ffc6896d7abe83684", "shasum": "" }, "require": { "brick/math": "^0.8.15|^0.9.0", + "ext-mbstring": "*", "php": ">=7.3" }, "require-dev": { - "phpstan/phpstan": "^0.12", - "phpstan/phpstan-beberlei-assert": "^0.12", - "phpstan/phpstan-deprecation-rules": "^0.12", - "phpstan/phpstan-phpunit": "^0.12", - "phpstan/phpstan-strict-rules": "^0.12" + "ekino/phpstan-banned-code": "^1.0", + "ext-json": "*", + "infection/infection": "^0.18|^0.25", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-beberlei-assert": "^1.0", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-phpunit": "^1.0", + "phpstan/phpstan-strict-rules": "^1.0", + "phpunit/phpunit": "^9.5", + "rector/rector": "^0.12", + "roave/security-advisories": "dev-latest", + "symplify/easy-coding-standard": "^10.0" }, "suggest": { "ext-bcmath": "GMP or BCMath extensions will drastically improve the library performance. BCMath extension needed to handle the Big Float and Decimal Fraction Tags", @@ -695,15 +704,19 @@ ], "support": { "issues": "https://github.com/Spomky-Labs/cbor-php/issues", - "source": "https://github.com/Spomky-Labs/cbor-php/tree/v2.0.1" + "source": "https://github.com/Spomky-Labs/cbor-php/tree/v2.1.0" }, "funding": [ + { + "url": "https://github.com/Spomky", + "type": "github" + }, { "url": "https://www.patreon.com/FlorentMorselli", "type": "patreon" } ], - "time": "2020-08-31T20:08:03+00:00" + "time": "2021-12-13T12:46:26+00:00" }, { "name": "symfony/polyfill-ctype", @@ -3035,5 +3048,5 @@ "prefer-lowest": false, "platform": [], "platform-dev": [], - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.2.0" } diff --git a/core/Objects/DatabaseEntity/DatabaseEntity.class.php b/core/Objects/DatabaseEntity/DatabaseEntity.class.php index 25fccb8..5508642 100644 --- a/core/Objects/DatabaseEntity/DatabaseEntity.class.php +++ b/core/Objects/DatabaseEntity/DatabaseEntity.class.php @@ -2,7 +2,6 @@ namespace Objects\DatabaseEntity; -use Driver\Logger\Logger; use Driver\SQL\Condition\Condition; use Driver\SQL\SQL; @@ -16,18 +15,18 @@ abstract class DatabaseEntity { } public static function find(SQL $sql, int $id): ?DatabaseEntity { - $handler = self::getHandler(); - return $handler->fetchOne($sql, $id); + $handler = self::getHandler($sql); + return $handler->fetchOne($id); } public static function findAll(SQL $sql, ?Condition $condition): ?array { - $handler = self::getHandler(); - return $handler->fetchMultiple($sql, $condition); + $handler = self::getHandler($sql); + return $handler->fetchMultiple($condition); } public function save(SQL $sql): bool { - $handler = self::getHandler(); - $res = $handler->insertOrUpdate($sql, $this); + $handler = self::getHandler($sql); + $res = $handler->insertOrUpdate($this); if ($res === false) { return false; } else if ($this->id === null) { @@ -38,17 +37,16 @@ abstract class DatabaseEntity { } public function delete(SQL $sql): bool { - $handler = self::getHandler(); + $handler = self::getHandler($sql); if ($this->id === null) { - $className = $handler->getReflection()->getName(); - (new Logger("DatabaseEntity", $sql))->error("Cannot delete entity of class '$className' without id"); + $handler->getLogger()->error("Cannot delete entity without id"); return false; } - return $handler->delete($sql, $this->id); + return $handler->delete($this->id); } - public static function getHandler($obj_or_class = null): DatabaseEntityHandler { + public static function getHandler(SQL $sql, $obj_or_class = null): DatabaseEntityHandler { if (!$obj_or_class) { $obj_or_class = get_called_class(); @@ -62,7 +60,7 @@ abstract class DatabaseEntity { $handler = self::$handlers[$class->getShortName()] ?? null; if (!$handler) { - $handler = new DatabaseEntityHandler($class); + $handler = new DatabaseEntityHandler($sql, $class); self::$handlers[$class->getShortName()] = $handler; } diff --git a/core/Objects/DatabaseEntity/DatabaseEntityHandler.php b/core/Objects/DatabaseEntity/DatabaseEntityHandler.php index 3138601..8fd7aba 100644 --- a/core/Objects/DatabaseEntity/DatabaseEntityHandler.php +++ b/core/Objects/DatabaseEntity/DatabaseEntityHandler.php @@ -2,6 +2,7 @@ namespace Objects\DatabaseEntity; +use Driver\Logger\Logger; use Driver\SQL\Column\BoolColumn; use Driver\SQL\Column\DateTimeColumn; use Driver\SQL\Column\IntColumn; @@ -11,6 +12,7 @@ use Driver\SQL\Condition\Condition; use Driver\SQL\Column\DoubleColumn; use Driver\SQL\Column\FloatColumn; use Driver\SQL\Constraint\ForeignKey; +use Driver\SQL\Query\CreateTable; use Driver\SQL\SQL; use Driver\SQL\Strategy\CascadeStrategy; use Driver\SQL\Strategy\SetNullStrategy; @@ -22,13 +24,17 @@ class DatabaseEntityHandler { private string $tableName; private array $columns; private array $properties; + private SQL $sql; + private Logger $logger; - public function __construct(\ReflectionClass $entityClass) { + public function __construct(SQL $sql, \ReflectionClass $entityClass) { + $this->sql = $sql; $className = $entityClass->getName(); + $this->logger = new Logger($entityClass->getShortName(), $sql); $this->entityClass = $entityClass; if (!$this->entityClass->isSubclassOf(DatabaseEntity::class) || !$this->entityClass->isInstantiable()) { - throw new Exception("Cannot persist class '$className': Not an instance of DatabaseEntity or not instantiable."); + $this->raiseError("Cannot persist class '$className': Not an instance of DatabaseEntity or not instantiable."); } $this->tableName = $this->entityClass->getShortName(); @@ -41,7 +47,7 @@ class DatabaseEntityHandler { $propertyType = $property->getType(); $columnName = self::getColumnName($propertyName); if (!($propertyType instanceof \ReflectionNamedType)) { - throw new Exception("Cannot persist class '$className': Property '$propertyName' has no valid type"); + $this->raiseError("Cannot persist class '$className': Property '$propertyName' has no valid type"); } $nullable = $propertyType->allowsNull(); @@ -63,13 +69,13 @@ class DatabaseEntityHandler { $requestedClass = new \ReflectionClass($propertyTypeName); if ($requestedClass->isSubclassOf(DatabaseEntity::class)) { $requestedHandler = ($requestedClass->getName() === $this->entityClass->getName()) ? - $this : DatabaseEntity::getHandler($requestedClass); + $this : DatabaseEntity::getHandler($this->sql, $requestedClass); $strategy = $nullable ? new SetNullStrategy() : new CascadeStrategy(); $this->columns[$propertyName] = new IntColumn($columnName, $nullable); $this->relations[$propertyName] = new ForeignKey($columnName, $requestedHandler->tableName, "id", $strategy); } } catch (\Exception $ex) { - throw new Exception("Cannot persist class '$className': Property '$propertyName' has non persist-able type: $propertyTypeName"); + $this->raiseError("Cannot persist class '$className': Property '$propertyName' has non persist-able type: $propertyTypeName"); } } @@ -88,16 +94,25 @@ class DatabaseEntityHandler { return $this->entityClass; } - private function entityFromRow(array $row): DatabaseEntity { - $entity = $this->entityClass->newInstanceWithoutConstructor(); - foreach ($this->columns as $propertyName => $column) { - $this->properties[$propertyName]->setValue($entity, $row[$column]); - } - return $entity; + public function getLogger(): Logger { + return $this->logger; } - public function fetchOne(SQL $sql, int $id): ?DatabaseEntity { - $res = $sql->select(...array_keys($this->columns)) + private function entityFromRow(array $row): DatabaseEntity { + try { + $entity = $this->entityClass->newInstanceWithoutConstructor(); + foreach ($this->columns as $propertyName => $column) { + $this->properties[$propertyName]->setValue($entity, $row[$column]); + } + return $entity; + } catch (\Exception $exception) { + $this->logger->error("Error creating entity from database row: " . $exception->getMessage()); + throw $exception; + } + } + + public function fetchOne(int $id): ?DatabaseEntity { + $res = $this->sql->select(...array_keys($this->columns)) ->from($this->tableName) ->where(new Compare("id", $id)) ->first() @@ -110,8 +125,8 @@ class DatabaseEntityHandler { } } - public function fetchMultiple(SQL $sql, ?Condition $condition = null): ?array { - $query = $sql->select(...array_keys($this->columns)) + public function fetchMultiple(?Condition $condition = null): ?array { + $query = $this->sql->select(...array_keys($this->columns)) ->from($this->tableName); if ($condition) { @@ -130,8 +145,8 @@ class DatabaseEntityHandler { } } - public function createTable(SQL $sql): bool { - $query = $sql->createTable($this->tableName) + public function getTableQuery(): CreateTable { + $query = $this->sql->createTable($this->tableName) ->onlyIfNotExists() ->addSerial("id") ->primaryKey("id"); @@ -144,10 +159,15 @@ class DatabaseEntityHandler { $query->addConstraint($constraint); } + return $query; + } + + public function createTable(): bool { + $query = $this->getTableQuery(); return $query->execute(); } - public function insertOrUpdate(SQL $sql, DatabaseEntity $entity) { + public function insertOrUpdate(DatabaseEntity $entity) { $id = $entity->getId(); if ($id === null) { $columns = []; @@ -158,18 +178,18 @@ class DatabaseEntityHandler { $row[] = $this->properties[$propertyName]->getValue($entity); } - $res = $sql->insert($this->tableName, $columns) + $res = $this->sql->insert($this->tableName, $columns) ->addRow(...$row) ->returning("id") ->execute(); if ($res !== false) { - return $sql->getLastInsertId(); + return $this->sql->getLastInsertId(); } else { return false; } } else { - $query = $sql->update($this->tableName) + $query = $this->sql->update($this->tableName) ->where(new Compare("id", $id)); foreach ($this->columns as $propertyName => $column) { @@ -182,7 +202,12 @@ class DatabaseEntityHandler { } } - public function delete(SQL $sql, int $id) { - return $sql->delete($this->tableName)->where(new Compare("id", $id))->execute(); + public function delete(int $id) { + return $this->sql->delete($this->tableName)->where(new Compare("id", $id))->execute(); + } + + private function raiseError(string $message) { + $this->logger->error($message); + throw new Exception($message); } } \ No newline at end of file