From 45fc84a33a28eccef4ab67efeee770289703f571 Mon Sep 17 00:00:00 2001 From: Ibrahim BinAlshikh Date: Tue, 6 Jan 2026 10:53:30 +0300 Subject: [PATCH 1/3] chore: Exclude Examples from Scan --- sonar-project.properties | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/sonar-project.properties b/sonar-project.properties index 281a0445..ec19bba5 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -1,12 +1,12 @@ -sonar.projectKey=WebFiori_database -sonar.organization=webfiori - -sonar.projectName=database -sonar.projectVersion=1.0 - -sonar.exclusions=tests/** -sonar.coverage.exclusions=tests/** - -sonar.php.coverage.reportPaths=clover.xml -# Encoding of the source code. Default is default system encoding -sonar.sourceEncoding=UTF-8 +sonar.projectKey=WebFiori_database +sonar.organization=webfiori + +sonar.projectName=database +sonar.projectVersion=1.0 + +sonar.exclusions=tests/**,examples/** +sonar.coverage.exclusions=tests/**,examples/** + +sonar.php.coverage.reportPaths=clover.xml +# Encoding of the source code. Default is default system encoding +sonar.sourceEncoding=UTF-8 From a417c6bd977d4185e5e1e336879b8915cd5c6545 Mon Sep 17 00:00:00 2001 From: Ibrahim BinAlshikh Date: Tue, 6 Jan 2026 11:12:55 +0300 Subject: [PATCH 2/3] refactor: Fix Quality Issues --- .../Attributes/AttributeTableBuilder.php | 4 +- WebFiori/Database/Attributes/ForeignKey.php | 3 +- .../Attributes/InvalidAttributeException.php | 8 +++ WebFiori/Database/Entity/EntityGenerator.php | 54 ++++++------------- WebFiori/Database/Entity/RecordMapper.php | 7 +-- WebFiori/Database/Query/InsertBuilder.php | 15 +++--- .../Repository/AbstractRepository.php | 6 +-- .../Repository/RepositoryException.php | 8 +++ .../Schema/DatabaseChangeGenerator.php | 4 +- WebFiori/Database/Schema/SchemaException.php | 8 +++ WebFiori/Database/Schema/SchemaRunner.php | 4 +- .../Attributes/ForeignKeyAttributeTest.php | 4 +- .../Repository/AbstractRepositoryTest.php | 9 ++-- .../Schema/DatabaseChangeGeneratorTest.php | 2 +- 14 files changed, 68 insertions(+), 68 deletions(-) create mode 100644 WebFiori/Database/Attributes/InvalidAttributeException.php create mode 100644 WebFiori/Database/Repository/RepositoryException.php create mode 100644 WebFiori/Database/Schema/SchemaException.php diff --git a/WebFiori/Database/Attributes/AttributeTableBuilder.php b/WebFiori/Database/Attributes/AttributeTableBuilder.php index b80b5b13..fc8920b4 100644 --- a/WebFiori/Database/Attributes/AttributeTableBuilder.php +++ b/WebFiori/Database/Attributes/AttributeTableBuilder.php @@ -13,7 +13,7 @@ public static function build(string $entityClass, string $dbType = 'mysql'): Tab $tableAttr = $reflection->getAttributes(Table::class)[0] ?? null; if (!$tableAttr) { - throw new \RuntimeException("Class $entityClass must have #[Table] attribute"); + throw new InvalidAttributeException("Class $entityClass must have #[Table] attribute"); } $tableConfig = $tableAttr->newInstance(); @@ -25,7 +25,7 @@ public static function build(string $entityClass, string $dbType = 'mysql'): Tab if (!empty($classColumnAttrs)) { foreach ($classColumnAttrs as $columnAttr) { $columnConfig = $columnAttr->newInstance(); - $columnKey = $columnConfig->name ?? throw new \RuntimeException("Column name is required for class-level attributes"); + $columnKey = $columnConfig->name ?? throw new InvalidAttributeException("Column name is required for class-level attributes"); $columns[$columnKey] = self::columnConfigToArray($columnConfig); } diff --git a/WebFiori/Database/Attributes/ForeignKey.php b/WebFiori/Database/Attributes/ForeignKey.php index aaafa406..4a83fbfa 100644 --- a/WebFiori/Database/Attributes/ForeignKey.php +++ b/WebFiori/Database/Attributes/ForeignKey.php @@ -2,7 +2,6 @@ namespace WebFiori\Database\Attributes; use Attribute; -use InvalidArgumentException; #[Attribute(Attribute::TARGET_PROPERTY | Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)] class ForeignKey { @@ -15,7 +14,7 @@ public function __construct( public string $onDelete = 'set null' ) { if ($column !== null && !empty($columns)) { - throw new InvalidArgumentException( + throw new InvalidAttributeException( "ForeignKey: Use either 'column' or 'columns', not both" ); } diff --git a/WebFiori/Database/Attributes/InvalidAttributeException.php b/WebFiori/Database/Attributes/InvalidAttributeException.php new file mode 100644 index 00000000..f93052b6 --- /dev/null +++ b/WebFiori/Database/Attributes/InvalidAttributeException.php @@ -0,0 +1,8 @@ +isAutoInc()) { + if ($col->isAutoInc() || $col->isNull()) { return ' = null'; } - if ($col->isNull()) { - return ' = null'; - } - - $default = $col->getDefault(); - - if ($default !== null) { - $phpType = $col->getPHPType(); - - if ($phpType === 'string') { - return " = '".addslashes($default)."'"; - } - - if ($phpType === 'int' || $phpType === 'float') { - return " = {$default}"; - } - - if ($phpType === 'bool') { - return $default ? ' = true' : ' = false'; - } - } - - // Required field with no default $phpType = $col->getPHPType(); + $default = $col->getDefault(); - if ($phpType === 'string') { - return " = ''"; - } - - if ($phpType === 'int') { - return ' = 0'; - } - - if ($phpType === 'float') { - return ' = 0.0'; - } + $typeDefaults = [ + 'string' => " = ''", + 'int' => ' = 0', + 'float' => ' = 0.0', + 'bool' => ' = false' + ]; - if ($phpType === 'bool') { - return ' = false'; + if ($default !== null) { + return match ($phpType) { + 'string' => " = '" . addslashes($default) . "'", + 'int', 'float' => " = {$default}", + 'bool' => $default ? ' = true' : ' = false', + default => '' + }; } - return ''; + return $typeDefaults[$phpType] ?? ''; } /** diff --git a/WebFiori/Database/Entity/RecordMapper.php b/WebFiori/Database/Entity/RecordMapper.php index ad61e6a7..c435455a 100644 --- a/WebFiori/Database/Entity/RecordMapper.php +++ b/WebFiori/Database/Entity/RecordMapper.php @@ -128,11 +128,8 @@ public function map(array $record) { foreach ($this->getSettersMap() as $method => $colsNames) { if (is_callable([$instance, $method])) { foreach ($colsNames as $colName) { - try { - if (isset($record[$colName])) { - $instance->$method($record[$colName]); - } - } catch (\Throwable $ex) { + if (isset($record[$colName])) { + $instance->$method($record[$colName]); } } } diff --git a/WebFiori/Database/Query/InsertBuilder.php b/WebFiori/Database/Query/InsertBuilder.php index 6fb4db66..c18f6016 100644 --- a/WebFiori/Database/Query/InsertBuilder.php +++ b/WebFiori/Database/Query/InsertBuilder.php @@ -206,17 +206,16 @@ private function build() { } private function buildColsArr() { $colsArr = []; - $colsStr = ''; - $table = $this->getTable(); + $tableObj = $this->getTable(); foreach ($this->cols as $colKey) { - $colObj = $table->getColByKey($colKey); + $colObj = $tableObj->getColByKey($colKey); if ($colObj === null) { - $table->addColumns([ + $tableObj->addColumns([ $colKey => [] ]); - $colObj = $table->getColByKey($colKey); + $colObj = $tableObj->getColByKey($colKey); } $colObj->setWithTablePrefix(false); $colsArr[] = $colObj->getName(); @@ -266,7 +265,7 @@ private function initValsArr() { $colsAndVals = $this->data; if (isset($colsAndVals['cols']) && isset($colsAndVals['values'])) { - $cols = $colsAndVals['cols']; + $colsArr = $colsAndVals['cols']; $tempVals = $colsAndVals['values']; $temp = []; $topIndex = 0; @@ -275,14 +274,14 @@ private function initValsArr() { $index = 0; $temp[] = []; - foreach ($cols as $colKey) { + foreach ($colsArr as $colKey) { $temp[$topIndex][$colKey] = $valsArr[$index]; $index++; } $topIndex++; } $this->vals = $temp; - $this->cols = $cols; + $this->cols = $colsArr; } else { $this->cols = array_keys($colsAndVals); $this->vals = [$colsAndVals]; diff --git a/WebFiori/Database/Repository/AbstractRepository.php b/WebFiori/Database/Repository/AbstractRepository.php index eb9cbfc1..c49cd248 100644 --- a/WebFiori/Database/Repository/AbstractRepository.php +++ b/WebFiori/Database/Repository/AbstractRepository.php @@ -61,7 +61,7 @@ public function deleteAll(): void { public function deleteById(mixed $id = null): void { $id = $id ?? $this->getEntityId(); if ($id === null) { - throw new \InvalidArgumentException('Cannot delete: no ID provided'); + throw new RepositoryException('Cannot delete: no ID provided'); } $this->db->table($this->getTableName()) ->delete() @@ -94,7 +94,7 @@ public function findAll(): array { public function findById(mixed $id = null): ?object { $id = $id ?? $this->getEntityId(); if ($id === null) { - throw new \InvalidArgumentException('Cannot find: no ID provided'); + throw new RepositoryException('Cannot find: no ID provided'); } $result = $this->db->table($this->getTableName()) ->select() @@ -225,7 +225,7 @@ public function paginateByCursor( */ public function save(?object $entity = null): void { if ($entity === null && !property_exists($this, $this->getIdField())) { - throw new \InvalidArgumentException('Cannot save: no entity provided'); + throw new RepositoryException('Cannot save: no entity provided'); } $entity = $entity ?? $this; $data = $this->toArray($entity); diff --git a/WebFiori/Database/Repository/RepositoryException.php b/WebFiori/Database/Repository/RepositoryException.php new file mode 100644 index 00000000..8a5d1d5b --- /dev/null +++ b/WebFiori/Database/Repository/RepositoryException.php @@ -0,0 +1,8 @@ +path)) { - throw new \RuntimeException('Path not set. Call setPath() first.'); + throw new DatabaseException('Path not set. Call setPath() first.'); } if (!is_dir($this->path)) { diff --git a/WebFiori/Database/Schema/SchemaException.php b/WebFiori/Database/Schema/SchemaException.php new file mode 100644 index 00000000..c8740d28 --- /dev/null +++ b/WebFiori/Database/Schema/SchemaException.php @@ -0,0 +1,8 @@ +expectException(InvalidArgumentException::class); + $this->expectException(InvalidAttributeException::class); $this->expectExceptionMessage("ForeignKey: Use either 'column' or 'columns', not both"); new ForeignKey(table: 'users', column: 'id', columns: ['local_id' => 'id']); diff --git a/tests/WebFiori/Tests/Database/Repository/AbstractRepositoryTest.php b/tests/WebFiori/Tests/Database/Repository/AbstractRepositoryTest.php index 91dd92bf..e9eeabea 100644 --- a/tests/WebFiori/Tests/Database/Repository/AbstractRepositoryTest.php +++ b/tests/WebFiori/Tests/Database/Repository/AbstractRepositoryTest.php @@ -8,6 +8,7 @@ use WebFiori\Database\ColOption; use WebFiori\Database\DataType; use WebFiori\Database\Repository\AbstractRepository; +use WebFiori\Database\Repository\RepositoryException; class TestEntity { public ?int $id = null; @@ -176,14 +177,14 @@ public function testSaveAllMixed() { } public function testFindByIdWithNullThrowsException() { - $this->expectException(\InvalidArgumentException::class); + $this->expectException(RepositoryException::class); $this->expectExceptionMessage('Cannot find: no ID provided'); self::$repo->findById(null); } public function testDeleteByIdWithNullThrowsException() { - $this->expectException(\InvalidArgumentException::class); + $this->expectException(RepositoryException::class); $this->expectExceptionMessage('Cannot delete: no ID provided'); self::$repo->deleteById(null); @@ -211,7 +212,7 @@ public function testDeleteByIdWithValidId() { } public function testSaveWithNullOnPureRepoThrowsException() { - $this->expectException(\InvalidArgumentException::class); + $this->expectException(RepositoryException::class); $this->expectExceptionMessage('Cannot save: no entity provided'); self::$repo->save(); @@ -234,7 +235,7 @@ public function testReloadWithEntity() { } public function testReloadWithNullOnPureRepoThrowsException() { - $this->expectException(\InvalidArgumentException::class); + $this->expectException(RepositoryException::class); $this->expectExceptionMessage('Cannot find: no ID provided'); self::$repo->reload(); diff --git a/tests/WebFiori/Tests/Database/Schema/DatabaseChangeGeneratorTest.php b/tests/WebFiori/Tests/Database/Schema/DatabaseChangeGeneratorTest.php index 38d25762..a50d9fa4 100644 --- a/tests/WebFiori/Tests/Database/Schema/DatabaseChangeGeneratorTest.php +++ b/tests/WebFiori/Tests/Database/Schema/DatabaseChangeGeneratorTest.php @@ -174,7 +174,7 @@ public function testCreateWithoutNamespace() { public function testCreateWithoutPathThrows() { $generator = new DatabaseChangeGenerator(); - $this->expectException(\RuntimeException::class); + $this->expectException(\WebFiori\Database\DatabaseException::class); $this->expectExceptionMessage('Path not set'); $generator->createMigration('CreateUsersTable'); From ed99655cf0542d7be8d2bcfb92147e708cf7015e Mon Sep 17 00:00:00 2001 From: Ibrahim BinAlshikh Date: Tue, 6 Jan 2026 11:14:41 +0300 Subject: [PATCH 3/3] chore: Updated License --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index f18f60fb..a86e8ad0 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2020 WebFiori Framework +Copyright (c) 2020-present WebFiori Framework Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal