DB Entity: Inheriting/Extending
This commit is contained in:
		
							parent
							
								
									3b2b5984d6
								
							
						
					
					
						commit
						26a22f5299
					
				| @ -69,8 +69,11 @@ namespace Core\API\Routes { | |||||||
|   use Core\Driver\SQL\Condition\Compare; |   use Core\Driver\SQL\Condition\Compare; | ||||||
|   use Core\Driver\SQL\Condition\CondBool; |   use Core\Driver\SQL\Condition\CondBool; | ||||||
|   use Core\Objects\Context; |   use Core\Objects\Context; | ||||||
|  |   use Core\Objects\DatabaseEntity\Route; | ||||||
|   use Core\Objects\Router\DocumentRoute; |   use Core\Objects\Router\DocumentRoute; | ||||||
|  |   use Core\Objects\Router\RedirectPermanentlyRoute; | ||||||
|   use Core\Objects\Router\RedirectRoute; |   use Core\Objects\Router\RedirectRoute; | ||||||
|  |   use Core\Objects\Router\RedirectTemporaryRoute; | ||||||
|   use Core\Objects\Router\Router; |   use Core\Objects\Router\Router; | ||||||
|   use Core\Objects\Router\StaticFileRoute; |   use Core\Objects\Router\StaticFileRoute; | ||||||
| 
 | 
 | ||||||
| @ -350,41 +353,20 @@ namespace Core\API\Routes { | |||||||
| 
 | 
 | ||||||
|     protected function _execute(): bool { |     protected function _execute(): bool { | ||||||
|       $sql = $this->context->getSQL(); |       $sql = $this->context->getSQL(); | ||||||
|       $res = $sql |       $routes = Route::findBy(Route::createBuilder($sql, false) | ||||||
|         ->select("id", "request", "action", "target", "extra", "exact") |         ->whereTrue("active") | ||||||
|         ->from("Route") |         ->orderBy("id") | ||||||
|         ->where(new CondBool("active")) |         ->ascending()); | ||||||
|         ->orderBy("id")->ascending() |  | ||||||
|         ->execute(); |  | ||||||
| 
 | 
 | ||||||
|       $this->success = $res !== false; |       $this->success = $routes !== false; | ||||||
|       $this->lastError = $sql->getLastError(); |       $this->lastError = $sql->getLastError(); | ||||||
|       if (!$this->success) { |       if (!$this->success) { | ||||||
|         return false; |         return false; | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       $this->router = new Router($this->context); |       $this->router = new Router($this->context); | ||||||
|       foreach ($res as $row) { |       foreach ($routes as $route) { | ||||||
|         $request = $row["request"]; |         $this->router->addRoute($route); | ||||||
|         $target = $row["target"]; |  | ||||||
|         $exact = $sql->parseBool($row["exact"]); |  | ||||||
|         switch ($row["action"]) { |  | ||||||
|           case "redirect_temporary": |  | ||||||
|             $this->router->addRoute(new RedirectRoute($request, $exact, $target, 307)); |  | ||||||
|             break; |  | ||||||
|           case "redirect_permanently": |  | ||||||
|             $this->router->addRoute(new RedirectRoute($request, $exact, $target, 308)); |  | ||||||
|             break; |  | ||||||
|           case "static": |  | ||||||
|             $this->router->addRoute(new StaticFileRoute($request, $exact, $target)); |  | ||||||
|             break; |  | ||||||
|           case "dynamic": |  | ||||||
|             $extra = json_decode($row["extra"]) ?? []; |  | ||||||
|             $this->router->addRoute(new DocumentRoute($request, $exact, $target, ...$extra)); |  | ||||||
|             break; |  | ||||||
|           default: |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       $this->success = $this->router->writeCache($this->routerCachePath); |       $this->success = $this->router->writeCache($this->routerCachePath); | ||||||
|  | |||||||
| @ -6,6 +6,10 @@ use Core\Driver\SQL\SQL; | |||||||
| use Core\Objects\DatabaseEntity\Controller\DatabaseEntity; | use Core\Objects\DatabaseEntity\Controller\DatabaseEntity; | ||||||
| use Core\Objects\DatabaseEntity\Group; | use Core\Objects\DatabaseEntity\Group; | ||||||
| use Core\Objects\DatabaseEntity\Language; | use Core\Objects\DatabaseEntity\Language; | ||||||
|  | use Core\Objects\DatabaseEntity\Route; | ||||||
|  | use Core\Objects\Router\DocumentRoute; | ||||||
|  | use Core\Objects\Router\StaticFileRoute; | ||||||
|  | use Core\Objects\Router\StaticRoute; | ||||||
| use PHPUnit\Util\Exception; | use PHPUnit\Util\Exception; | ||||||
| 
 | 
 | ||||||
| class CreateDatabase extends DatabaseScript { | class CreateDatabase extends DatabaseScript { | ||||||
| @ -32,27 +36,17 @@ class CreateDatabase extends DatabaseScript { | |||||||
|       ->addString("cookie", 26) |       ->addString("cookie", 26) | ||||||
|       ->unique("day", "cookie"); |       ->unique("day", "cookie"); | ||||||
| 
 | 
 | ||||||
|     $queries[] = $sql->createTable("Route") |     $queries[] = Route::getHandler($sql)->getInsertQuery([ | ||||||
|       ->addSerial("id") |       new DocumentRoute("/admin", false, \Core\Documents\Admin::class), | ||||||
|       ->addString("request", 128) |       new DocumentRoute("/register", true, \Core\Documents\Account::class, "account/register.twig"), | ||||||
|       ->addEnum("action", array("redirect_temporary", "redirect_permanently", "static", "dynamic")) |       new DocumentRoute("/confirmEmail", true, \Core\Documents\Account::class, "account/confirm_email.twig"), | ||||||
|       ->addString("target", 128) |       new DocumentRoute("/acceptInvite", true, \Core\Documents\Account::class, "account/accept_invite.twig"), | ||||||
|       ->addString("extra", 64, true) |       new DocumentRoute("/resetPassword", true, \Core\Documents\Account::class, "account/reset_password.twig"), | ||||||
|       ->addBool("active", true) |       new DocumentRoute("/login", true, \Core\Documents\Account::class, "account/login.twig"), | ||||||
|       ->addBool("exact", true) |       new DocumentRoute("/resendConfirmEmail", true, \Core\Documents\Account::class, "account/resend_confirm_email.twig"), | ||||||
|       ->primaryKey("id") |       new DocumentRoute("/debug", true, \Core\Documents\Info::class), | ||||||
|       ->unique("request"); |       new StaticFileRoute("/static", true, "/static/welcome.html"), | ||||||
| 
 |     ]); | ||||||
|     $queries[] = $sql->insert("Route", ["request", "action", "target", "extra", "exact"]) |  | ||||||
|       ->addRow("/admin", "dynamic", "\\Core\\Documents\\Admin", NULL, false) |  | ||||||
|       ->addRow("/register", "dynamic", "\\Core\\Documents\\Account", json_encode(["account/register.twig"]), true) |  | ||||||
|       ->addRow("/confirmEmail", "dynamic", "\\Core\\Documents\\Account", json_encode(["account/confirm_email.twig"]), true) |  | ||||||
|       ->addRow("/acceptInvite", "dynamic", "\\Core\\Documents\\Account", json_encode(["account/accept_invite.twig"]), true) |  | ||||||
|       ->addRow("/resetPassword", "dynamic", "\\Core\\Documents\\Account", json_encode(["account/reset_password.twig"]), true) |  | ||||||
|       ->addRow("/login", "dynamic", "\\Core\\Documents\\Account", json_encode(["account/login.twig"]), true) |  | ||||||
|       ->addRow("/resendConfirmEmail", "dynamic", "\\Core\\Documents\\Account", json_encode(["account/resend_confirm_email.twig"]), true) |  | ||||||
|       ->addRow("/debug", "dynamic", "\\Core\\Documents\\Info", NULL, true) |  | ||||||
|       ->addRow("/", "static", "/static/welcome.html", NULL, true); |  | ||||||
| 
 | 
 | ||||||
|     $queries[] = $sql->createTable("Settings") |     $queries[] = $sql->createTable("Settings") | ||||||
|       ->addString("name", 32) |       ->addString("name", 32) | ||||||
| @ -140,7 +134,7 @@ class CreateDatabase extends DatabaseScript { | |||||||
|             $className = substr($file, 0, strlen($file) - strlen($suffix)); |             $className = substr($file, 0, strlen($file) - strlen($suffix)); | ||||||
|             $className = "\\$baseDir\\Objects\\DatabaseEntity\\$className"; |             $className = "\\$baseDir\\Objects\\DatabaseEntity\\$className"; | ||||||
|             $reflectionClass = new \ReflectionClass($className); |             $reflectionClass = new \ReflectionClass($className); | ||||||
|             if ($reflectionClass->isSubclassOf(DatabaseEntity::class)) { |             if ($reflectionClass->getParentClass()?->getName() === DatabaseEntity::class) { | ||||||
|               $method = "$className::getHandler"; |               $method = "$className::getHandler"; | ||||||
|               $handler = call_user_func($method, $sql); |               $handler = call_user_func($method, $sql); | ||||||
|               $persistables[$handler->getTableName()] = $handler; |               $persistables[$handler->getTableName()] = $handler; | ||||||
|  | |||||||
| @ -7,12 +7,18 @@ use Core\Driver\SQL\SQL; | |||||||
| class Logger { | class Logger { | ||||||
| 
 | 
 | ||||||
|   public const LOG_FILE_DATE_FORMAT = "Y-m-d_H-i-s_v"; |   public const LOG_FILE_DATE_FORMAT = "Y-m-d_H-i-s_v"; | ||||||
|  |   public const LOG_LEVEL_DEBUG = 0; | ||||||
|  |   public const LOG_LEVEL_INFO = 1; | ||||||
|  |   public const LOG_LEVEL_WARNING = 2; | ||||||
|  |   public const LOG_LEVEL_ERROR = 3; | ||||||
|  |   public const LOG_LEVEL_SEVERE = 4; | ||||||
|  | 
 | ||||||
|   public const LOG_LEVELS = [ |   public const LOG_LEVELS = [ | ||||||
|     0 => "debug", |     self::LOG_LEVEL_DEBUG => "debug", | ||||||
|     1 => "info", |     self::LOG_LEVEL_INFO => "info", | ||||||
|     2 => "warning", |     self::LOG_LEVEL_WARNING => "warning", | ||||||
|     3 => "error", |     self::LOG_LEVEL_ERROR => "error", | ||||||
|     4 => "severe" |     self::LOG_LEVEL_SEVERE => "severe" | ||||||
|   ]; |   ]; | ||||||
| 
 | 
 | ||||||
|   public static Logger $INSTANCE; |   public static Logger $INSTANCE; | ||||||
| @ -59,6 +65,10 @@ class Logger { | |||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     if ($severity >= self::LOG_LEVEL_WARNING) { | ||||||
|  |       error_log($message); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     if ($this->sql !== null && $this->sql->isConnected()) { |     if ($this->sql !== null && $this->sql->isConnected()) { | ||||||
|       $success = $this->sql->insert("SystemLog", ["module", "message", "severity"]) |       $success = $this->sql->insert("SystemLog", ["module", "message", "severity"]) | ||||||
|         ->addRow($this->module, $message, $severity) |         ->addRow($this->module, $message, $severity) | ||||||
|  | |||||||
| @ -0,0 +1,18 @@ | |||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | namespace Core\Objects\DatabaseEntity\Attribute; | ||||||
|  | 
 | ||||||
|  | #[\Attribute(\Attribute::TARGET_PROPERTY)]
 | ||||||
|  | class ExtendingEnum extends EnumArr { | ||||||
|  | 
 | ||||||
|  |   private array $mappings; | ||||||
|  | 
 | ||||||
|  |   public function __construct(array $values) { | ||||||
|  |     parent::__construct(array_keys($values)); | ||||||
|  |     $this->mappings = $values; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public function getMappings(): array { | ||||||
|  |     return $this->mappings; | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -32,7 +32,7 @@ abstract class DatabaseEntity { | |||||||
|     return $handler->entityFromRow($row); |     return $handler->entityFromRow($row); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   public static function newInstance(\ReflectionClass $reflectionClass, array $row) { |   public static function newInstance(\ReflectionClass $reflectionClass) { | ||||||
|     return $reflectionClass->newInstanceWithoutConstructor(); |     return $reflectionClass->newInstanceWithoutConstructor(); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @ -127,6 +127,12 @@ abstract class DatabaseEntity { | |||||||
|       $class = $obj_or_class; |       $class = $obj_or_class; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     // if we are in an extending context, get the database handler for the root entity,
 | ||||||
|  |     // as we do not persist attributes of the inheriting class
 | ||||||
|  |     while ($class->getParentClass()->getName() !== DatabaseEntity::class) { | ||||||
|  |       $class = $class->getParentClass(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     $handler = self::$handlers[$class->getShortName()] ?? null; |     $handler = self::$handlers[$class->getShortName()] ?? null; | ||||||
|     if (!$handler) { |     if (!$handler) { | ||||||
|       $handler = new DatabaseEntityHandler($sql, $class); |       $handler = new DatabaseEntityHandler($sql, $class); | ||||||
|  | |||||||
| @ -10,7 +10,6 @@ use Core\Driver\SQL\Column\EnumColumn; | |||||||
| use Core\Driver\SQL\Column\IntColumn; | use Core\Driver\SQL\Column\IntColumn; | ||||||
| use Core\Driver\SQL\Column\JsonColumn; | use Core\Driver\SQL\Column\JsonColumn; | ||||||
| use Core\Driver\SQL\Column\StringColumn; | use Core\Driver\SQL\Column\StringColumn; | ||||||
| use Core\Driver\SQL\Condition\Compare; |  | ||||||
| use Core\Driver\SQL\Condition\CondAnd; | use Core\Driver\SQL\Condition\CondAnd; | ||||||
| use Core\Driver\SQL\Condition\CondBool; | use Core\Driver\SQL\Condition\CondBool; | ||||||
| use Core\Driver\SQL\Condition\CondIn; | use Core\Driver\SQL\Condition\CondIn; | ||||||
| @ -33,12 +32,12 @@ use Core\Driver\SQL\Type\CurrentColumn; | |||||||
| use Core\Driver\SQL\Type\CurrentTable; | use Core\Driver\SQL\Type\CurrentTable; | ||||||
| use Core\Objects\DatabaseEntity\Attribute\Enum; | use Core\Objects\DatabaseEntity\Attribute\Enum; | ||||||
| use Core\Objects\DatabaseEntity\Attribute\DefaultValue; | use Core\Objects\DatabaseEntity\Attribute\DefaultValue; | ||||||
|  | use Core\Objects\DatabaseEntity\Attribute\ExtendingEnum; | ||||||
| use Core\Objects\DatabaseEntity\Attribute\Json; | use Core\Objects\DatabaseEntity\Attribute\Json; | ||||||
| use Core\Objects\DatabaseEntity\Attribute\MaxLength; | use Core\Objects\DatabaseEntity\Attribute\MaxLength; | ||||||
| use Core\Objects\DatabaseEntity\Attribute\Multiple; | use Core\Objects\DatabaseEntity\Attribute\Multiple; | ||||||
| use Core\Objects\DatabaseEntity\Attribute\Transient; | use Core\Objects\DatabaseEntity\Attribute\Transient; | ||||||
| use Core\Objects\DatabaseEntity\Attribute\Unique; | use Core\Objects\DatabaseEntity\Attribute\Unique; | ||||||
| use PHPUnit\Util\Exception; |  | ||||||
| 
 | 
 | ||||||
| class DatabaseEntityHandler implements Persistable { | class DatabaseEntityHandler implements Persistable { | ||||||
| 
 | 
 | ||||||
| @ -49,6 +48,8 @@ class DatabaseEntityHandler implements Persistable { | |||||||
|   private array $relations; |   private array $relations; | ||||||
|   private array $constraints; |   private array $constraints; | ||||||
|   private array $nmRelations; |   private array $nmRelations; | ||||||
|  |   private array $extendingClasses; | ||||||
|  |   private ?\ReflectionProperty $extendingProperty; | ||||||
|   private SQL $sql; |   private SQL $sql; | ||||||
|   private Logger $logger; |   private Logger $logger; | ||||||
| 
 | 
 | ||||||
| @ -66,7 +67,9 @@ class DatabaseEntityHandler implements Persistable { | |||||||
|     $this->properties = [];  // property name => \ReflectionProperty
 |     $this->properties = [];  // property name => \ReflectionProperty
 | ||||||
|     $this->relations = [];   // property name => DatabaseEntityHandler
 |     $this->relations = [];   // property name => DatabaseEntityHandler
 | ||||||
|     $this->constraints = []; // \Driver\SQL\Constraint\Constraint
 |     $this->constraints = []; // \Driver\SQL\Constraint\Constraint
 | ||||||
|     $this->nmRelations = [];    // table name => NMRelation
 |     $this->nmRelations = []; // table name => NMRelation
 | ||||||
|  |     $this->extendingClasses = [];  // enum value => \ReflectionClass
 | ||||||
|  |     $this->extendingProperty = null;  // only one attribute can hold the type of the extending class
 | ||||||
| 
 | 
 | ||||||
|     foreach ($this->entityClass->getProperties() as $property) { |     foreach ($this->entityClass->getProperties() as $property) { | ||||||
|       $propertyName = $property->getName(); |       $propertyName = $property->getName(); | ||||||
| @ -91,6 +94,36 @@ class DatabaseEntityHandler implements Persistable { | |||||||
|         continue; |         continue; | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|  |       $ext = self::getAttribute($property, ExtendingEnum::class); | ||||||
|  |       if ($ext !== null) { | ||||||
|  |         if ($this->extendingProperty !== null) { | ||||||
|  |           $this->raiseError("Cannot have more than one extending property"); | ||||||
|  |         } else { | ||||||
|  |           $this->extendingProperty = $property; | ||||||
|  |           $enumMappings = $ext->getMappings(); | ||||||
|  |           foreach ($enumMappings as $key => $extendingClass) { | ||||||
|  |             if (!is_string($key)) { | ||||||
|  |               $type = gettype($key); | ||||||
|  |               $this->raiseError("Extending enum must be an array of string => class, got type '$type' for key: " . print_r($key, true)); | ||||||
|  |             } else if (!is_string($extendingClass)) { | ||||||
|  |               $type = gettype($extendingClass); | ||||||
|  |               $this->raiseError("Extending enum must be an array of string => class, got type '$type' for value: " . print_r($extendingClass, true)); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             try { | ||||||
|  |               $requestedClass = new \ReflectionClass($extendingClass); | ||||||
|  |               if (!$requestedClass->isSubclassOf($this->entityClass)) { | ||||||
|  |                 $this->raiseError("Class '$extendingClass' must be an inheriting from '" . $this->entityClass->getName() . "' for an extending enum"); | ||||||
|  |               } else { | ||||||
|  |                 $this->extendingClasses[$key] = $requestedClass; | ||||||
|  |               } | ||||||
|  |             } catch (\ReflectionException $ex) { | ||||||
|  |               $this->raiseError("Cannot persist extending enum for class $extendingClass: " . $ex->getMessage()); | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|       $defaultValue = (self::getAttribute($property, DefaultValue::class))?->getValue(); |       $defaultValue = (self::getAttribute($property, DefaultValue::class))?->getValue(); | ||||||
|       $isUnique = !empty($property->getAttributes(Unique::class)); |       $isUnique = !empty($property->getAttributes(Unique::class)); | ||||||
| 
 | 
 | ||||||
| @ -112,18 +145,6 @@ class DatabaseEntityHandler implements Persistable { | |||||||
|         $this->columns[$propertyName] = new BoolColumn($columnName, $defaultValue ?? false); |         $this->columns[$propertyName] = new BoolColumn($columnName, $defaultValue ?? false); | ||||||
|       } else if ($propertyTypeName === 'DateTime') { |       } else if ($propertyTypeName === 'DateTime') { | ||||||
|         $this->columns[$propertyName] = new DateTimeColumn($columnName, $nullable, $defaultValue); |         $this->columns[$propertyName] = new DateTimeColumn($columnName, $nullable, $defaultValue); | ||||||
|         /*} else if ($propertyName === 'array') { |  | ||||||
|           $many = self::getAttribute($property, Many::class); |  | ||||||
|           if ($many) { |  | ||||||
|             $requestedType = $many->getValue(); |  | ||||||
|             if (isClass($requestedType)) { |  | ||||||
|               $requestedClass = new \ReflectionClass($requestedType); |  | ||||||
|             } else { |  | ||||||
|               $this->raiseError("Cannot persist class '$className': Property '$propertyName' has non persist-able type: $requestedType"); |  | ||||||
|             } |  | ||||||
|           } else { |  | ||||||
|             $this->raiseError("Cannot persist class '$className': Property '$propertyName' has non persist-able type: $propertyTypeName"); |  | ||||||
|           }*/ |  | ||||||
|       } else if ($propertyTypeName === "array") { |       } else if ($propertyTypeName === "array") { | ||||||
|         $multiple = self::getAttribute($property, Multiple::class); |         $multiple = self::getAttribute($property, Multiple::class); | ||||||
|         if (!$multiple) { |         if (!$multiple) { | ||||||
| @ -248,42 +269,62 @@ class DatabaseEntityHandler implements Persistable { | |||||||
|     return $rel_row; |     return $rel_row; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   private function getValueFromRow(array $row, string $propertyName, mixed &$value): bool { | ||||||
|  |     $column = $this->columns[$propertyName] ?? null; | ||||||
|  |     if (!$column) { | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     $columnName = $column->getName(); | ||||||
|  |     if (!array_key_exists($columnName, $row)) { | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     $value = $row[$columnName]; | ||||||
|  |     if ($column instanceof DateTimeColumn) { | ||||||
|  |       $value = new \DateTime($value); | ||||||
|  |     } else if ($column instanceof JsonColumn) { | ||||||
|  |       $value = json_decode($value); | ||||||
|  |     } else if (isset($this->relations[$propertyName])) { | ||||||
|  |       $relColumnPrefix = self::getColumnName($propertyName) . "_"; | ||||||
|  |       if (array_key_exists($relColumnPrefix . "id", $row)) { | ||||||
|  |         $relId = $row[$relColumnPrefix . "id"]; | ||||||
|  |         if ($relId !== null) { | ||||||
|  |           $relationHandler = $this->relations[$propertyName]; | ||||||
|  |           $value = $relationHandler->entityFromRow(self::getPrefixedRow($row, $relColumnPrefix)); | ||||||
|  |         } else if (!$column->notNull()) { | ||||||
|  |           $value = null; | ||||||
|  |         } else { | ||||||
|  |           return false; | ||||||
|  |         } | ||||||
|  |       } else { | ||||||
|  |         return false; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   public function entityFromRow(array $row): ?DatabaseEntity { |   public function entityFromRow(array $row): ?DatabaseEntity { | ||||||
|     try { |     try { | ||||||
| 
 | 
 | ||||||
|       $entity = call_user_func($this->entityClass->getName() . "::newInstance", $this->entityClass, $row); |       $constructorClass = $this->entityClass; | ||||||
|  |       if ($this->extendingProperty !== null) { | ||||||
|  |         if ($this->getValueFromRow($row, $this->extendingProperty->getName(), $enumValue)) { | ||||||
|  |           if ($enumValue && isset($this->extendingClasses[$enumValue])) { | ||||||
|  |             $constructorClass = $this->extendingClasses[$enumValue]; | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|  |       $entity = call_user_func($constructorClass->getName() . "::newInstance", $constructorClass); | ||||||
|       if (!($entity instanceof DatabaseEntity)) { |       if (!($entity instanceof DatabaseEntity)) { | ||||||
|         $this->logger->error("Created Object is not of type DatabaseEntity"); |         $this->logger->error("Created Object is not of type DatabaseEntity"); | ||||||
|         return null; |         return null; | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       foreach ($this->columns as $propertyName => $column) { |       foreach ($this->properties as $property) { | ||||||
|         $columnName = $column->getName(); |         if ($this->getValueFromRow($row, $property->getName(), $value)) { | ||||||
|         if (array_key_exists($columnName, $row)) { |  | ||||||
|           $value = $row[$columnName]; |  | ||||||
|           $property = $this->properties[$propertyName]; |  | ||||||
| 
 |  | ||||||
|           if ($column instanceof DateTimeColumn) { |  | ||||||
|             $value = new \DateTime($value); |  | ||||||
|           } else if ($column instanceof JsonColumn) { |  | ||||||
|             $value = json_decode($value); |  | ||||||
|           } else if (isset($this->relations[$propertyName])) { |  | ||||||
|             $relColumnPrefix = self::getColumnName($propertyName) . "_"; |  | ||||||
|             if (array_key_exists($relColumnPrefix . "id", $row)) { |  | ||||||
|               $relId = $row[$relColumnPrefix . "id"]; |  | ||||||
|               if ($relId !== null) { |  | ||||||
|                 $relationHandler = $this->relations[$propertyName]; |  | ||||||
|                 $value = $relationHandler->entityFromRow(self::getPrefixedRow($row, $relColumnPrefix)); |  | ||||||
|               } else if (!$column->notNull()) { |  | ||||||
|                 $value = null; |  | ||||||
|               } else { |  | ||||||
|                 continue; |  | ||||||
|               } |  | ||||||
|             } else { |  | ||||||
|               continue; |  | ||||||
|             } |  | ||||||
|           } |  | ||||||
| 
 |  | ||||||
|           $property->setAccessible(true); |           $property->setAccessible(true); | ||||||
|           $property->setValue($entity, $value); |           $property->setValue($entity, $value); | ||||||
|         } |         } | ||||||
| @ -449,7 +490,7 @@ class DatabaseEntityHandler implements Persistable { | |||||||
|         } |         } | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       $rows = $relEntityQuery->execute(); |       $rows = $relEntityQuery->executeSQL(); | ||||||
|       if (!is_array($rows)) { |       if (!is_array($rows)) { | ||||||
|         $this->logger->error("Error fetching n:m relations from table: '$nmTable': " . $this->sql->getLastError()); |         $this->logger->error("Error fetching n:m relations from table: '$nmTable': " . $this->sql->getLastError()); | ||||||
|         return; |         return; | ||||||
| @ -698,7 +739,7 @@ class DatabaseEntityHandler implements Persistable { | |||||||
| 
 | 
 | ||||||
|   private function raiseError(string $message) { |   private function raiseError(string $message) { | ||||||
|     $this->logger->error($message); |     $this->logger->error($message); | ||||||
|     throw new Exception($message); |     throw new \Exception($message); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   public function getSQL(): SQL { |   public function getSQL(): SQL { | ||||||
| @ -713,6 +754,9 @@ class DatabaseEntityHandler implements Persistable { | |||||||
| 
 | 
 | ||||||
|     $firstEntity = (is_array($entities) ? current($entities) : $entities); |     $firstEntity = (is_array($entities) ? current($entities) : $entities); | ||||||
|     $firstRow = $this->prepareRow($firstEntity, "insert"); |     $firstRow = $this->prepareRow($firstEntity, "insert"); | ||||||
|  |     if ($firstRow === false) { | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     $statement = $this->sql->insert($this->tableName, array_keys($firstRow)) |     $statement = $this->sql->insert($this->tableName, array_keys($firstRow)) | ||||||
|       ->addRow(...array_values($firstRow)); |       ->addRow(...array_values($firstRow)); | ||||||
|  | |||||||
| @ -137,4 +137,8 @@ class DatabaseEntityQuery extends Select { | |||||||
|       return null; |       return null; | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|  |   public function executeSQL() { | ||||||
|  |     return parent::execute(); | ||||||
|  |   } | ||||||
| } | } | ||||||
| @ -1,19 +1,53 @@ | |||||||
| <?php | <?php | ||||||
| 
 | 
 | ||||||
| namespace Core\Objects\Router; | namespace Core\Objects\DatabaseEntity; | ||||||
| 
 | 
 | ||||||
| use Core\API\Parameter\Parameter; | use Core\API\Parameter\Parameter; | ||||||
|  | use Core\Objects\DatabaseEntity\Attribute\DefaultValue; | ||||||
|  | use Core\Objects\DatabaseEntity\Attribute\ExtendingEnum; | ||||||
|  | use Core\Objects\DatabaseEntity\Attribute\MaxLength; | ||||||
|  | use Core\Objects\DatabaseEntity\Attribute\Unique; | ||||||
|  | use Core\Objects\DatabaseEntity\Controller\DatabaseEntity; | ||||||
|  | use Core\Objects\Router\DocumentRoute; | ||||||
|  | use Core\Objects\Router\RedirectRoute; | ||||||
|  | use Core\Objects\Router\Router; | ||||||
|  | use Core\Objects\Router\StaticFileRoute; | ||||||
| 
 | 
 | ||||||
| abstract class AbstractRoute { | abstract class Route extends DatabaseEntity { | ||||||
| 
 | 
 | ||||||
|   const PARAMETER_PATTERN = "/^{([^:]+)(:(.*?)(\?)?)?}$/"; |   const PARAMETER_PATTERN = "/^{([^:]+)(:(.*?)(\?)?)?}$/"; | ||||||
|  |   const ROUTE_TYPES = [ | ||||||
|  |     "redirect_temporary" => RedirectRoute::class, | ||||||
|  |     "redirect_permanently" => RedirectRoute::class, | ||||||
|  |     "static" => StaticFileRoute::class, | ||||||
|  |     "dynamic" => DocumentRoute::class | ||||||
|  |   ]; | ||||||
| 
 | 
 | ||||||
|  |   #[MaxLength(128)]
 | ||||||
|  |   #[Unique]
 | ||||||
|   private string $pattern; |   private string $pattern; | ||||||
|  | 
 | ||||||
|  |   #[ExtendingEnum(self::ROUTE_TYPES)]
 | ||||||
|  |   private string $type; | ||||||
|  | 
 | ||||||
|  |   #[MaxLength(128)]
 | ||||||
|  |   private string $target; | ||||||
|  | 
 | ||||||
|  |   #[MaxLength(64)]
 | ||||||
|  |   protected ?string $extra; | ||||||
|  | 
 | ||||||
|  |   #[DefaultValue(true)]
 | ||||||
|  |   private bool $active; | ||||||
|  | 
 | ||||||
|   private bool $exact; |   private bool $exact; | ||||||
| 
 | 
 | ||||||
|   public function __construct(string $pattern, bool $exact = true) { |   public function __construct(string $type, string $pattern, string $target, bool $exact = true) { | ||||||
|  |     parent::__construct(); | ||||||
|  |     $this->target = $target; | ||||||
|     $this->pattern = $pattern; |     $this->pattern = $pattern; | ||||||
|     $this->exact = $exact; |     $this->exact = $exact; | ||||||
|  |     $this->type = $type; | ||||||
|  |     $this->active = true; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   private static function parseParamType(?string $type): ?int { |   private static function parseParamType(?string $type): ?int { | ||||||
| @ -37,6 +71,10 @@ abstract class AbstractRoute { | |||||||
|     return $this->pattern; |     return $this->pattern; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   public function getTarget(): string { | ||||||
|  |     return $this->target; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   public abstract function call(Router $router, array $params): string; |   public abstract function call(Router $router, array $params): string; | ||||||
| 
 | 
 | ||||||
|   protected function getArgs(): array { |   protected function getArgs(): array { | ||||||
| @ -154,4 +192,16 @@ abstract class AbstractRoute { | |||||||
| 
 | 
 | ||||||
|     return $parameterNames; |     return $parameterNames; | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|  |   public function jsonSerialize(): array { | ||||||
|  |     return [ | ||||||
|  |       "id" => $this->getId(), | ||||||
|  |       "pattern" => $this->pattern, | ||||||
|  |       "type" => $this->type, | ||||||
|  |       "target" => $this->target, | ||||||
|  |       "extra" => $this->extra, | ||||||
|  |       "exact" => $this->exact, | ||||||
|  |       "active" => $this->active, | ||||||
|  |     ]; | ||||||
|  |   } | ||||||
| } | } | ||||||
| @ -3,7 +3,7 @@ | |||||||
| namespace Core\Objects\DatabaseEntity; | namespace Core\Objects\DatabaseEntity; | ||||||
| 
 | 
 | ||||||
| use Core\Driver\SQL\SQL; | use Core\Driver\SQL\SQL; | ||||||
| use Core\Objects\DatabaseEntity\Attribute\Enum; | use Core\Objects\DatabaseEntity\Attribute\ExtendingEnum; | ||||||
| use Core\Objects\DatabaseEntity\Attribute\MaxLength; | use Core\Objects\DatabaseEntity\Attribute\MaxLength; | ||||||
| use Core\Objects\TwoFactor\KeyBasedTwoFactorToken; | use Core\Objects\TwoFactor\KeyBasedTwoFactorToken; | ||||||
| use Core\Objects\TwoFactor\TimeBasedTwoFactorToken; | use Core\Objects\TwoFactor\TimeBasedTwoFactorToken; | ||||||
| @ -11,7 +11,12 @@ use Core\Objects\DatabaseEntity\Controller\DatabaseEntity; | |||||||
| 
 | 
 | ||||||
| abstract class TwoFactorToken extends DatabaseEntity { | abstract class TwoFactorToken extends DatabaseEntity { | ||||||
| 
 | 
 | ||||||
|   #[Enum('totp','fido')] private string $type;
 |   const TWO_FACTOR_TOKEN_TYPES = [ | ||||||
|  |     "totp" => TimeBasedTwoFactorToken::class, | ||||||
|  |     "fido" => KeyBasedTwoFactorToken::class, | ||||||
|  |   ]; | ||||||
|  | 
 | ||||||
|  |   #[ExtendingEnum(self::TWO_FACTOR_TOKEN_TYPES)] private string $type;
 | ||||||
|   private bool $confirmed; |   private bool $confirmed; | ||||||
|   private bool $authenticated; |   private bool $authenticated; | ||||||
|   #[MaxLength(512)] private string $data;
 |   #[MaxLength(512)] private string $data;
 | ||||||
| @ -62,17 +67,6 @@ abstract class TwoFactorToken extends DatabaseEntity { | |||||||
|     return $this->id; |     return $this->id; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   public static function newInstance(\ReflectionClass $reflectionClass, array $row) { |  | ||||||
|     if ($row["type"] === TimeBasedTwoFactorToken::TYPE) { |  | ||||||
|       return (new \ReflectionClass(TimeBasedTwoFactorToken::class))->newInstanceWithoutConstructor(); |  | ||||||
|     } else if ($row["type"] === KeyBasedTwoFactorToken::TYPE) { |  | ||||||
|       return (new \ReflectionClass(KeyBasedTwoFactorToken::class))->newInstanceWithoutConstructor(); |  | ||||||
|     } else { |  | ||||||
|       // TODO: error message
 |  | ||||||
|       return null; |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   public function isAuthenticated(): bool { |   public function isAuthenticated(): bool { | ||||||
|     return $this->authenticated; |     return $this->authenticated; | ||||||
|   } |   } | ||||||
|  | |||||||
| @ -4,13 +4,14 @@ namespace Core\Objects\Router; | |||||||
| 
 | 
 | ||||||
| use Core\API\Request; | use Core\API\Request; | ||||||
| use Core\Elements\TemplateDocument; | use Core\Elements\TemplateDocument; | ||||||
|  | use Core\Objects\DatabaseEntity\Route; | ||||||
| use ReflectionClass; | use ReflectionClass; | ||||||
| use ReflectionException; | use ReflectionException; | ||||||
| 
 | 
 | ||||||
| class ApiRoute extends AbstractRoute { | class ApiRoute extends Route { | ||||||
| 
 | 
 | ||||||
|   public function __construct() { |   public function __construct() { | ||||||
|     parent::__construct("/api/{endpoint:?}/{method:?}", false); |     parent::__construct("API", "/api/{endpoint:?}/{method:?}", false); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   private static function checkClass(string $className): bool { |   private static function checkClass(string $className): bool { | ||||||
|  | |||||||
| @ -2,34 +2,44 @@ | |||||||
| 
 | 
 | ||||||
| namespace Core\Objects\Router; | namespace Core\Objects\Router; | ||||||
| 
 | 
 | ||||||
|  | use Core\Driver\SQL\SQL; | ||||||
| use Core\Elements\Document; | use Core\Elements\Document; | ||||||
| use Core\Objects\Context; | use Core\Objects\Context; | ||||||
|  | use Core\Objects\DatabaseEntity\Route; | ||||||
| use Core\Objects\Search\Searchable; | use Core\Objects\Search\Searchable; | ||||||
| use Core\Objects\Search\SearchQuery; | use Core\Objects\Search\SearchQuery; | ||||||
|  | use JetBrains\PhpStorm\Pure; | ||||||
| use ReflectionException; | use ReflectionException; | ||||||
| 
 | 
 | ||||||
| class DocumentRoute extends AbstractRoute { | class DocumentRoute extends Route { | ||||||
| 
 | 
 | ||||||
|   use Searchable; |   use Searchable; | ||||||
| 
 | 
 | ||||||
|   private string $className; |  | ||||||
|   private array $args; |   private array $args; | ||||||
|   private ?\ReflectionClass $reflectionClass; |   private ?\ReflectionClass $reflectionClass = null; | ||||||
| 
 | 
 | ||||||
|   public function __construct(string $pattern, bool $exact, string $className, ...$args) { |   public function __construct(string $pattern, bool $exact, string $className, ...$args) { | ||||||
|     parent::__construct($pattern, $exact); |     parent::__construct("dynamic", $pattern, $className, $exact); | ||||||
|     $this->className = $className; |  | ||||||
|     $this->args = $args; |     $this->args = $args; | ||||||
|     $this->reflectionClass = null; |     $this->extra = json_encode($args); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public function postFetch(SQL $sql, array $row) { | ||||||
|  |     parent::postFetch($sql, $row); | ||||||
|  |     $this->args = json_decode($this->extra); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   #[Pure] private function getClassName(): string {
 | ||||||
|  |     return $this->getTarget(); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   private function loadClass(): bool { |   private function loadClass(): bool { | ||||||
| 
 | 
 | ||||||
|     if ($this->reflectionClass === null) { |     if ($this->reflectionClass === null) { | ||||||
|       try { |       try { | ||||||
|         $file = getClassPath($this->className); |         $file = getClassPath($this->getClassName()); | ||||||
|         if (file_exists($file)) { |         if (file_exists($file)) { | ||||||
|           $this->reflectionClass = new \ReflectionClass($this->className); |           $this->reflectionClass = new \ReflectionClass($this->getClassName()); | ||||||
|           if ($this->reflectionClass->isSubclassOf(Document::class)) { |           if ($this->reflectionClass->isSubclassOf(Document::class)) { | ||||||
|             return true; |             return true; | ||||||
|           } |           } | ||||||
| @ -56,20 +66,22 @@ class DocumentRoute extends AbstractRoute { | |||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   protected function getArgs(): array { |   protected function getArgs(): array { | ||||||
|     return array_merge(parent::getArgs(), [$this->className], $this->args); |     return array_merge(parent::getArgs(), [$this->getClassName()], $this->args); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   public function call(Router $router, array $params): string { |   public function call(Router $router, array $params): string { | ||||||
|  |     $className = $this->getClassName(); | ||||||
|  | 
 | ||||||
|     try { |     try { | ||||||
|       if (!$this->loadClass()) { |       if (!$this->loadClass()) { | ||||||
|         return $router->returnStatusCode(500, [ "message" =>  "Error loading class: $this->className"]); |         return $router->returnStatusCode(500, [ "message" =>  "Error loading class: $className"]); | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       $args = array_merge([$router], $this->args, $params); |       $args = array_merge([$router], $this->args, $params); | ||||||
|       $document = $this->reflectionClass->newInstanceArgs($args); |       $document = $this->reflectionClass->newInstanceArgs($args); | ||||||
|       return $document->load($params); |       return $document->load($params); | ||||||
|     } catch (\ReflectionException $e) { |     } catch (\ReflectionException $e) { | ||||||
|       return $router->returnStatusCode(500, [ "message" =>  "Error loading class $this->className: " . $e->getMessage()]); |       return $router->returnStatusCode(500, [ "message" =>  "Error loading class $className: " . $e->getMessage()]); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -2,10 +2,12 @@ | |||||||
| 
 | 
 | ||||||
| namespace Core\Objects\Router; | namespace Core\Objects\Router; | ||||||
| 
 | 
 | ||||||
| class EmptyRoute extends AbstractRoute { | use Core\Objects\DatabaseEntity\Route; | ||||||
|  | 
 | ||||||
|  | class EmptyRoute extends Route { | ||||||
| 
 | 
 | ||||||
|   public function __construct(string $pattern, bool $exact = true) { |   public function __construct(string $pattern, bool $exact = true) { | ||||||
|     parent::__construct($pattern, $exact); |     parent::__construct("empty", $pattern, $exact); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   public function call(Router $router, array $params): string { |   public function call(Router $router, array $params): string { | ||||||
|  | |||||||
							
								
								
									
										9
									
								
								Core/Objects/Router/RedirectPermanentlyRoute.class.php
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										9
									
								
								Core/Objects/Router/RedirectPermanentlyRoute.class.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | |||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | namespace Core\Objects\Router; | ||||||
|  | 
 | ||||||
|  | class RedirectPermanentlyRoute extends RedirectRoute { | ||||||
|  |   public function __construct(string $pattern, bool $exact, string $destination) { | ||||||
|  |     parent::__construct("redirect_permanently", $pattern, $exact, $destination, 308); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -2,24 +2,29 @@ | |||||||
| 
 | 
 | ||||||
| namespace Core\Objects\Router; | namespace Core\Objects\Router; | ||||||
| 
 | 
 | ||||||
| class RedirectRoute extends AbstractRoute { | use Core\Objects\DatabaseEntity\Route; | ||||||
|  | use JetBrains\PhpStorm\Pure; | ||||||
|  | 
 | ||||||
|  | class RedirectRoute extends Route { | ||||||
| 
 | 
 | ||||||
|   private string $destination; |  | ||||||
|   private int $code; |   private int $code; | ||||||
| 
 | 
 | ||||||
|   public function __construct(string $pattern, bool $exact, string $destination, int $code = 307) { |   public function __construct(string $type, string $pattern, bool $exact, string $destination, int $code = 307) { | ||||||
|     parent::__construct($pattern, $exact); |     parent::__construct($type, $pattern, $destination, $exact); | ||||||
|     $this->destination = $destination; |  | ||||||
|     $this->code = $code; |     $this->code = $code; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   #[Pure] private function getDestination(): string {
 | ||||||
|  |     return $this->getTarget(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   public function call(Router $router, array $params): string { |   public function call(Router $router, array $params): string { | ||||||
|     header("Location: $this->destination"); |     header("Location: " . $this->getDestination()); | ||||||
|     http_response_code($this->code); |     http_response_code($this->code); | ||||||
|     return ""; |     return ""; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   protected function getArgs(): array { |   protected function getArgs(): array { | ||||||
|     return array_merge(parent::getArgs(), [$this->destination, $this->code]); |     return array_merge(parent::getArgs(), [$this->getDestination(), $this->code]); | ||||||
|   } |   } | ||||||
| } | } | ||||||
							
								
								
									
										9
									
								
								Core/Objects/Router/RedirectTemporaryRoute.class.php
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										9
									
								
								Core/Objects/Router/RedirectTemporaryRoute.class.php
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | |||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | namespace Core\Objects\Router; | ||||||
|  | 
 | ||||||
|  | class RedirectTemporaryRoute extends RedirectRoute { | ||||||
|  |   public function __construct(string $pattern, bool $exact, string $destination) { | ||||||
|  |     parent::__construct("redirect_temporary", $pattern, $exact, $destination, 307); | ||||||
|  |   } | ||||||
|  | } | ||||||
| @ -4,12 +4,13 @@ namespace Core\Objects\Router; | |||||||
| 
 | 
 | ||||||
| use Core\Driver\Logger\Logger; | use Core\Driver\Logger\Logger; | ||||||
| use Core\Objects\Context; | use Core\Objects\Context; | ||||||
|  | use Core\Objects\DatabaseEntity\Route; | ||||||
| 
 | 
 | ||||||
| class Router { | class Router { | ||||||
| 
 | 
 | ||||||
|   private Context $context; |   private Context $context; | ||||||
|   private Logger $logger; |   private Logger $logger; | ||||||
|   private ?AbstractRoute $activeRoute; |   private ?Route $activeRoute; | ||||||
|   private ?string $requestedUri; |   private ?string $requestedUri; | ||||||
|   protected array $routes; |   protected array $routes; | ||||||
|   protected array $statusCodeRoutes; |   protected array $statusCodeRoutes; | ||||||
| @ -31,7 +32,7 @@ class Router { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   public function getActiveRoute(): ?AbstractRoute { |   public function getActiveRoute(): ?Route { | ||||||
|     return $this->activeRoute; |     return $this->activeRoute; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| @ -75,7 +76,7 @@ class Router { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   public function addRoute(AbstractRoute $route) { |   public function addRoute(Route $route) { | ||||||
|     if (preg_match("/^\/(\d+)$/", $route->getPattern(), $re)) { |     if (preg_match("/^\/(\d+)$/", $route->getPattern(), $re)) { | ||||||
|       $this->statusCodeRoutes[$re[1]] = $route; |       $this->statusCodeRoutes[$re[1]] = $route; | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -2,22 +2,29 @@ | |||||||
| 
 | 
 | ||||||
| namespace Core\Objects\Router; | namespace Core\Objects\Router; | ||||||
| 
 | 
 | ||||||
|  | use Core\Driver\SQL\SQL; | ||||||
| use Core\Objects\Context; | use Core\Objects\Context; | ||||||
|  | use Core\Objects\DatabaseEntity\Route; | ||||||
| use Core\Objects\Search\Searchable; | use Core\Objects\Search\Searchable; | ||||||
| use Core\Objects\Search\SearchQuery; | use Core\Objects\Search\SearchQuery; | ||||||
| use Core\Objects\Search\SearchResult; | use Core\Objects\Search\SearchResult; | ||||||
|  | use JetBrains\PhpStorm\Pure; | ||||||
| 
 | 
 | ||||||
| class StaticFileRoute extends AbstractRoute { | class StaticFileRoute extends Route { | ||||||
| 
 | 
 | ||||||
|   use Searchable; |   use Searchable; | ||||||
| 
 | 
 | ||||||
|   private string $path; |  | ||||||
|   private int $code; |   private int $code; | ||||||
| 
 | 
 | ||||||
|   public function __construct(string $pattern, bool $exact, string $path, int $code = 200) { |   public function __construct(string $pattern, bool $exact, string $path, int $code = 200) { | ||||||
|     parent::__construct($pattern, $exact); |     parent::__construct("static", $pattern, $path, $exact); | ||||||
|     $this->path = $path; |  | ||||||
|     $this->code = $code; |     $this->code = $code; | ||||||
|  |     $this->extra = json_encode($this->code); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   public function postFetch(SQL $sql, array $row) { | ||||||
|  |     parent::postFetch($sql, $row); | ||||||
|  |     $this->code = json_decode($this->extra); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   public function call(Router $router, array $params): string { |   public function call(Router $router, array $params): string { | ||||||
| @ -26,12 +33,16 @@ class StaticFileRoute extends AbstractRoute { | |||||||
|     return ""; |     return ""; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   #[Pure] private function getPath(): string {
 | ||||||
|  |     return $this->getTarget(); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   protected function getArgs(): array { |   protected function getArgs(): array { | ||||||
|     return array_merge(parent::getArgs(), [$this->path, $this->code]); |     return array_merge(parent::getArgs(), [$this->getPath(), $this->code]); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   public function getAbsolutePath(): string { |   public function getAbsolutePath(): string { | ||||||
|     return WEBROOT . DIRECTORY_SEPARATOR . $this->path; |     return WEBROOT . DIRECTORY_SEPARATOR . $this->getPath(); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   public static function serveStatic(string $path, ?Router $router = null) { |   public static function serveStatic(string $path, ?Router $router = null) { | ||||||
|  | |||||||
| @ -2,13 +2,15 @@ | |||||||
| 
 | 
 | ||||||
| namespace Core\Objects\Router; | namespace Core\Objects\Router; | ||||||
| 
 | 
 | ||||||
| class StaticRoute extends AbstractRoute { | use Core\Objects\DatabaseEntity\Route; | ||||||
|  | 
 | ||||||
|  | class StaticRoute extends Route { | ||||||
| 
 | 
 | ||||||
|   private string $data; |   private string $data; | ||||||
|   private int $code; |   private int $code; | ||||||
| 
 | 
 | ||||||
|   public function __construct(string $pattern, bool $exact, string $data, int $code = 200) { |   public function __construct(string $pattern, bool $exact, string $data, int $code = 200) { | ||||||
|     parent::__construct($pattern, $exact); |     parent::__construct("static", $pattern, $exact); | ||||||
|     $this->data = $data; |     $this->data = $data; | ||||||
|     $this->code = $code; |     $this->code = $code; | ||||||
|   } |   } | ||||||
|  | |||||||
| @ -3,16 +3,15 @@ | |||||||
| namespace Core\Objects\TwoFactor; | namespace Core\Objects\TwoFactor; | ||||||
| 
 | 
 | ||||||
| use Cose\Algorithm\Signature\ECDSA\ECSignature; | use Cose\Algorithm\Signature\ECDSA\ECSignature; | ||||||
| use Core\Objects\DatabaseEntity\Attribute\Transient; |  | ||||||
| use Core\Objects\DatabaseEntity\TwoFactorToken; | use Core\Objects\DatabaseEntity\TwoFactorToken; | ||||||
| 
 | 
 | ||||||
| class KeyBasedTwoFactorToken extends TwoFactorToken { | class KeyBasedTwoFactorToken extends TwoFactorToken { | ||||||
| 
 | 
 | ||||||
|   const TYPE = "fido"; |   const TYPE = "fido"; | ||||||
| 
 | 
 | ||||||
|   #[Transient] private ?string $challenge;
 |   private ?string $challenge; | ||||||
|   #[Transient] private ?string $credentialId;
 |   private ?string $credentialId; | ||||||
|   #[Transient] private ?PublicKey $publicKey;
 |   private ?PublicKey $publicKey; | ||||||
| 
 | 
 | ||||||
|   protected function readData(string $data) { |   protected function readData(string $data) { | ||||||
|     if ($this->isConfirmed()) { |     if ($this->isConfirmed()) { | ||||||
|  | |||||||
| @ -6,14 +6,12 @@ use Base32\Base32; | |||||||
| use chillerlan\QRCode\QRCode; | use chillerlan\QRCode\QRCode; | ||||||
| use chillerlan\QRCode\QROptions; | use chillerlan\QRCode\QROptions; | ||||||
| use Core\Objects\Context; | use Core\Objects\Context; | ||||||
| use Core\Objects\DatabaseEntity\Attribute\Transient; |  | ||||||
| use Core\Objects\DatabaseEntity\TwoFactorToken; | use Core\Objects\DatabaseEntity\TwoFactorToken; | ||||||
| use Core\Objects\DatabaseEntity\User; |  | ||||||
| 
 | 
 | ||||||
| class TimeBasedTwoFactorToken extends TwoFactorToken { | class TimeBasedTwoFactorToken extends TwoFactorToken { | ||||||
| 
 | 
 | ||||||
|   const TYPE = "totp"; |   const TYPE = "totp"; | ||||||
|   #[Transient] private string $secret;
 |   private string $secret; | ||||||
| 
 | 
 | ||||||
|   public function __construct(string $secret) { |   public function __construct(string $secret) { | ||||||
|     parent::__construct(self::TYPE); |     parent::__construct(self::TYPE); | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Roman Hergenreder
						Roman Hergenreder