web-base/Core/Configuration/CreateDatabase.class.php

183 lines
6.0 KiB
PHP
Raw Permalink Normal View History

2020-04-02 00:02:51 +02:00
<?php
2022-11-18 18:06:46 +01:00
namespace Core\Configuration;
2020-04-02 00:02:51 +02:00
2023-01-16 21:47:23 +01:00
use Core\API\Request;
use Core\Driver\Logger\Logger;
use Core\Driver\SQL\Query\CreateTable;
2022-11-18 18:06:46 +01:00
use Core\Driver\SQL\SQL;
2022-11-20 17:13:53 +01:00
use Core\Objects\DatabaseEntity\Controller\DatabaseEntity;
2022-06-20 19:52:31 +02:00
use PHPUnit\Util\Exception;
2020-04-02 00:02:51 +02:00
2024-05-04 15:07:24 +02:00
class CreateDatabase {
2020-04-02 00:02:51 +02:00
private static ?Logger $logger = null;
public static function getLogger(SQL $sql): Logger {
if (self::$logger === null) {
self::$logger = new Logger("CreateDatabase", $sql);
}
return self::$logger;
}
2021-04-02 21:58:06 +02:00
public static function createQueries(SQL $sql): array {
2020-04-02 00:02:51 +02:00
$queries = array();
2024-05-04 15:07:24 +02:00
self::loadEntities($sql, $queries);
2020-04-02 00:02:51 +02:00
2020-06-25 16:54:58 +02:00
$queries[] = $sql->createTable("Settings")
->addString("name", 32)
2024-04-23 12:14:28 +02:00
->addJson("value", true)
2020-06-26 23:32:45 +02:00
->addBool("private", false) // these values are not returned from '/api/settings/get', but can be changed
->addBool("readonly", false) // these values are neither returned, nor can be changed from outside
2020-06-25 16:54:58 +02:00
->primaryKey("name");
2024-05-04 12:23:14 +02:00
$defaultSettings = Settings::loadDefaults(loadEnv());
$settingsQuery = $sql->insert("Settings", ["name", "value", "private", "readonly"]);
$defaultSettings->addRows($settingsQuery);
2020-06-25 16:54:58 +02:00
$queries[] = $settingsQuery;
2020-06-27 01:18:10 +02:00
$queries[] = $sql->createTable("ApiPermission")
->addString("method", 32)
->addJson("groups", true, '[]')
2020-06-27 22:47:12 +02:00
->addString("description", 128, false, "")
2024-03-27 14:12:01 +01:00
->primaryKey("method")
2024-03-29 16:37:42 +01:00
->addBool("is_core", false);
2020-06-27 01:18:10 +02:00
2024-05-04 15:07:24 +02:00
self::loadDefaultACL($sql, $queries);
self::loadPatches($sql, $queries);
2020-06-27 01:18:10 +02:00
2020-04-02 00:02:51 +02:00
return $queries;
}
2020-06-26 14:58:17 +02:00
2024-05-04 15:07:24 +02:00
private static function loadPatches(SQL $sql, array &$queries): void {
$patchFiles = array_merge(
glob('Core/Configuration/Patch/*.php'),
glob('Site/Configuration/Patch/*.php')
);
sort($patchFiles);
foreach ($patchFiles as $file) {
@include_once $file;
2021-01-07 15:54:19 +01:00
}
}
private static function getCreatedTables(SQL $sql, array $queries): ?array {
$createdTables = $sql->listTables();
if ($createdTables !== null) {
foreach ($queries as $query) {
if ($query instanceof CreateTable) {
$tableName = $query->getTableName();
if (!in_array($tableName, $createdTables)) {
$createdTables[] = $tableName;
2022-06-20 19:52:31 +02:00
}
}
}
} else {
self::getLogger($sql)->warning("Error querying existing tables: " . $sql->getLastError());
}
return $createdTables;
}
public static function createEntityQueries(SQL $sql, array $entityClasses, array &$queries, bool $skipExisting = false): void {
if (empty($entityClasses)) {
return;
}
// first, check what tables are already created
$createdTables = self::getCreatedTables($sql, $queries);
if ($createdTables === null) {
throw new \Exception("Error querying existing tables");
2023-01-09 17:10:47 +01:00
}
2022-06-20 19:52:31 +02:00
// then collect all persistable entities (tables, relations, etc.)
$persistables = [];
foreach ($entityClasses as $className) {
$reflectionClass = new \ReflectionClass($className);
if ($reflectionClass->isSubclassOf(DatabaseEntity::class)) {
$handler = ("$className::getHandler")($sql, null, true);
$persistables[$handler->getTableName()] = $handler;
foreach ($handler->getNMRelations() as $nmTableName => $nmRelation) {
$persistables[$nmTableName] = $nmRelation;
}
} else {
throw new \Exception("Class '$className' is not a subclass of DatabaseEntity");
}
}
// now order the persistable entities so all dependencies are met.
2023-01-09 17:10:47 +01:00
$tableCount = count($persistables);
while (!empty($persistables)) {
$prevCount = $tableCount;
$unmetDependenciesTotal = [];
foreach ($persistables as $tableName => $persistable) {
$dependsOn = $persistable->dependsOn();
$unmetDependencies = array_diff($dependsOn, $createdTables);
if (empty($unmetDependencies)) {
$queries = array_merge($queries, $persistable->getCreateQueries($sql, $skipExisting));
2023-01-09 17:10:47 +01:00
$createdTables[] = $tableName;
unset($persistables[$tableName]);
} else {
$unmetDependenciesTotal = array_merge($unmetDependenciesTotal, $unmetDependencies);
}
2023-01-09 17:10:47 +01:00
}
2022-06-20 19:52:31 +02:00
2023-01-09 17:10:47 +01:00
$tableCount = count($persistables);
if ($tableCount === $prevCount) {
throw new Exception("Circular or unmet table dependency detected. Unmet dependencies: "
. implode(", ", $unmetDependenciesTotal));
}
}
}
2023-01-16 21:47:23 +01:00
private static function loadEntities(SQL $sql, array &$queries): void {
$classes = [];
$baseDirs = ["Core", "Site"];
foreach ($baseDirs as $baseDir) {
$entityDirectory = "./$baseDir/Objects/DatabaseEntity/";
if (file_exists($entityDirectory) && is_dir($entityDirectory)) {
$scan_arr = scandir($entityDirectory);
$files_arr = array_diff($scan_arr, [".", ".."]);
foreach ($files_arr as $file) {
$suffix = ".class.php";
if (endsWith($file, $suffix)) {
$className = substr($file, 0, strlen($file) - strlen($suffix));
$className = "\\$baseDir\\Objects\\DatabaseEntity\\$className";
$reflectionClass = new \ReflectionClass($className);
if ($reflectionClass->isSubclassOf(DatabaseEntity::class)) {
$classes[] = $className;
}
}
}
}
}
self::createEntityQueries($sql, $classes, $queries);
}
2024-05-04 15:07:24 +02:00
public static function loadDefaultACL(SQL $sql, array &$queries): void {
2024-03-29 16:37:42 +01:00
$query = $sql->insert("ApiPermission", ["method", "groups", "description", "is_core"]);
2023-01-16 21:47:23 +01:00
foreach (Request::getApiEndpoints() as $reflectionClass) {
2024-04-23 12:14:28 +02:00
$className = $reflectionClass->getName();
if (("$className::hasConfigurablePermissions")()) {
$method = ("$className::getEndpoint")();
$groups = ("$className::getDefaultPermittedGroups")();
$description = ("$className::getDescription")();
$isCore = startsWith($className, "Core\\API\\");
$query->addRow($method, $groups, $description, $isCore);
}
2023-01-16 21:47:23 +01:00
}
2024-04-23 12:14:28 +02:00
2023-01-16 21:47:23 +01:00
if ($query->hasRows()) {
$queries[] = $query;
}
}
2020-04-02 00:02:51 +02:00
}