summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/application/mystic/forum/Database.php26
-rw-r--r--src/application/mystic/forum/Messaging.php101
-rw-r--r--src/application/mystic/forum/attributes/References.php5
-rw-r--r--src/application/mystic/forum/exceptions/DatabaseConnectionException.php6
-rw-r--r--src/application/mystic/forum/orm/Post.php2
-rw-r--r--src/application/mystic/forum/utils/RequestUtils.php19
-rw-r--r--src/index.php61
7 files changed, 216 insertions, 4 deletions
diff --git a/src/application/mystic/forum/Database.php b/src/application/mystic/forum/Database.php
index f574386..9b9cf55 100644
--- a/src/application/mystic/forum/Database.php
+++ b/src/application/mystic/forum/Database.php
@@ -10,6 +10,7 @@ use mystic\forum\attributes\NotNull;
use mystic\forum\attributes\PrimaryKey;
use mystic\forum\attributes\References;
use mystic\forum\attributes\Table;
+use mystic\forum\exceptions\DatabaseConnectionException;
use mystic\forum\orm\Entity;
use mystic\forum\utils\ArrayUtils;
use mystic\forum\utils\StringUtils;
@@ -26,7 +27,13 @@ class Database {
private const REFERENCES = 0b0000_0100;
public function __construct(string $connectionString) {
- $this->connection = \pg_connect($connectionString);
+ try {
+ $conn = \pg_connect($connectionString);
+ if ($conn !== false)
+ $this->connection = $conn;
+ } catch (\ErrorException $ex) {
+ throw new DatabaseConnectionException($ex->getMessage(), $ex->getCode(), $ex);
+ }
}
public static function getConnectionString(string $host, string $user, string $password, string $dbname, int $port = 5432): string {
@@ -281,6 +288,23 @@ class Database {
return true;
}
+ public function delete(Entity &$entity): bool {
+ $entityClassName = get_class($entity);
+ $tableName = self::getTableName($entityClassName);
+ $reflClass = new ReflectionClass($entityClassName);
+ $cols = self::getColumns($reflClass);
+ $primaryCol = self::getPrimaryKeyColumn($cols);
+ if ($primaryCol === null)
+ throw new \RuntimeException("Deleting an entity requires a primary key column to be specified");
+ $query = "DELETE FROM $tableName WHERE $primaryCol = \$1;";
+ $result = \pg_query_params($this->connection, $query, [ $entity->{$cols[$primaryCol]["propertyName"]} ]);
+ if ($result === false)
+ throw new \RuntimeException("Deletion failed: " . \pg_last_error($this->connection));
+ $num_affected_rows = \pg_affected_rows($result);
+ \pg_free_result($result);
+ return $num_affected_rows >= 1;
+ }
+
public function update(Entity &$entity): bool {
$tableName = self::getTableName(get_class($entity));
$reflClass = new ReflectionClass($entity);
diff --git a/src/application/mystic/forum/Messaging.php b/src/application/mystic/forum/Messaging.php
new file mode 100644
index 0000000..2f5c80d
--- /dev/null
+++ b/src/application/mystic/forum/Messaging.php
@@ -0,0 +1,101 @@
+<?php
+declare(strict_types=1);
+
+namespace mystic\forum;
+
+use mystic\forum\utils\StaticClass;
+
+final class Messaging {
+ use StaticClass;
+
+ const ENT_FLAGS = \ENT_COMPAT | \ENT_HTML401 | \ENT_SUBSTITUTE;
+
+ protected static function message(array $items, string $headerText, string $headerColor, string $headerTextColor): void {
+ echo "<HTML><BODY>\n";
+ echo "<TABLE cellspacing=2 cellpadding=0 bgcolor=gray><TR><TD>\n";
+
+ echo "<TABLE width=\"100%\" cellspacing=0 cellpadding=4 bgcolor=\"".htmlentities($headerColor, self::ENT_FLAGS)."\"><TR><TD align=center>\n";
+ echo "<FONT face=Arial size=3 color=\"".htmlentities($headerTextColor, self::ENT_FLAGS)."\"><B>".htmlentities($headerText, self::ENT_FLAGS)."</B></FONT>\n";
+ echo "</TD></TR></TABLE>\n";
+
+ echo "</TD></TR><TR><TD>\n";
+
+ echo "<TABLE cellspacing=0 cellpadding=4 bgcolor=WHITE><TR><TD align=left><FONT size=2 face=Arial color=BLACK>\n";
+
+ foreach ($items as $item) {
+ if (is_scalar($item)) {
+ echo "<P>" . htmlentities(strval($item), self::ENT_FLAGS) . "</P>\n";
+ } elseif (is_array($item)) {
+ if (count(array_keys($item)) === 2 && isset($item["___!type"]) && isset($item["___!content"])) {
+ // special item
+ switch ($item["___!type"]) {
+ case "HTML":
+ echo $item["___!content"];
+ break;
+ default:
+ echo "invalid";
+ break;
+ }
+ } elseif (array_is_list($item)) {
+ echo "<UL>\n";
+ foreach ($item as $i)
+ echo "<LI>" . htmlentities($i, self::ENT_FLAGS) . "</LI>\n";
+ echo "</UL>\n";
+ } else {
+ echo "<TABLE cellspacing=0 cellpadding=2 border=1>\n";
+ foreach ($item as $k => $i) {
+ echo "<TR>\n";
+ echo "<TH align=left>" . htmlentities($k, self::ENT_FLAGS) . "</TH>\n";
+ echo "<TD>";
+ if (is_scalar($i)) {
+ echo htmlentities($i, self::ENT_FLAGS);
+ } else {
+ echo gettype($i);
+ }
+ echo "</TD>";
+ echo "</TR>\n";
+ }
+ echo "</TABLE>\n";
+ }
+ } else {
+ echo gettype($item);
+ }
+ }
+
+ echo "</FONT></TD></TR></TABLE>\n";
+
+ echo "</TD></TR></TABLE>\n";
+ echo "</BODY></HTML>\n";
+ }
+
+ public static function bold(string $contents): array {
+ return self::html("<P><B>" . htmlentities($contents, self::ENT_FLAGS) . "</B></P>\n");
+ }
+
+ public static function italic(string $contents): array {
+ return self::html("<P><I>" . htmlentities($contents, self::ENT_FLAGS) . "</I></P>\n");
+ }
+
+ public static function bold_italic(string $contents): array {
+ return self::html("<P><B><I>" . htmlentities($contents, self::ENT_FLAGS) . "</I></B></P>\n");
+ }
+
+ public static function html(string $contents): array {
+ return [
+ "___!type" => "HTML",
+ "___!content" => $contents,
+ ];
+ }
+
+ public static function info(string|array $contents): void {
+ if (is_string($contents))
+ $contents = [$contents];
+ self::message($contents, "INFORMATION", "SKYBLUE", "BLACK");
+ }
+
+ public static function error(string|array $contents): void {
+ if (is_string($contents))
+ $contents = [$contents];
+ self::message($contents, "ERROR", "RED", "WHITE");
+ }
+}
diff --git a/src/application/mystic/forum/attributes/References.php b/src/application/mystic/forum/attributes/References.php
index ac431ff..9e33927 100644
--- a/src/application/mystic/forum/attributes/References.php
+++ b/src/application/mystic/forum/attributes/References.php
@@ -7,9 +7,12 @@ class References {
public function __construct(
public readonly string $foreignTableName,
public readonly ?string $foreignColumnName = null,
+ public readonly bool $cascadeOnDelete = false,
) {}
public function __toString(): string {
- return $this->foreignTableName . ($this->foreignColumnName !== null ? " ({$this->foreignColumnName})" : "");
+ return $this->foreignTableName
+ . ($this->foreignColumnName !== null ? " ({$this->foreignColumnName})" : "")
+ . ($this->cascadeOnDelete ? " ON DELETE CASCADE" : "");
}
}
diff --git a/src/application/mystic/forum/exceptions/DatabaseConnectionException.php b/src/application/mystic/forum/exceptions/DatabaseConnectionException.php
new file mode 100644
index 0000000..f79d800
--- /dev/null
+++ b/src/application/mystic/forum/exceptions/DatabaseConnectionException.php
@@ -0,0 +1,6 @@
+<?php
+declare(strict_types=1);
+
+namespace mystic\forum\exceptions;
+
+class DatabaseConnectionException extends \Exception {}
diff --git a/src/application/mystic/forum/orm/Post.php b/src/application/mystic/forum/orm/Post.php
index db297e5..2c9a38c 100644
--- a/src/application/mystic/forum/orm/Post.php
+++ b/src/application/mystic/forum/orm/Post.php
@@ -13,5 +13,5 @@ class Post extends Entity {
public string $content;
#[References("public.users")] public string $authorId;
public \DateTimeImmutable $postDate;
- #[References("public.topics")] public string $topicId;
+ #[References("public.topics", cascadeOnDelete: true)] public string $topicId;
}
diff --git a/src/application/mystic/forum/utils/RequestUtils.php b/src/application/mystic/forum/utils/RequestUtils.php
new file mode 100644
index 0000000..2f40013
--- /dev/null
+++ b/src/application/mystic/forum/utils/RequestUtils.php
@@ -0,0 +1,19 @@
+<?php
+declare(strict_types=1);
+
+namespace mystic\forum\utils;
+
+use mystic\forum\Messaging;
+
+final class RequestUtils {
+ use StaticClass;
+
+ public static function ensureRequestMethod(string $method): void {
+ $rMethod = $_SERVER["REQUEST_METHOD"];
+ if (strcasecmp($rMethod, $method) !== 0) {
+ http_response_code(500);
+ Messaging::error("Invalid request method $rMethod");
+ exit;
+ }
+ }
+}
diff --git a/src/index.php b/src/index.php
index 62a300a..8e2ce4e 100644
--- a/src/index.php
+++ b/src/index.php
@@ -1,13 +1,72 @@
<?php
use mystic\forum\Database;
+use mystic\forum\exceptions\DatabaseConnectionException;
+use mystic\forum\Messaging;
use mystic\forum\orm\Post;
use mystic\forum\orm\Topic;
use mystic\forum\orm\User;
+use mystic\forum\utils\RequestUtils;
+
+function exception_error_handler($errno, $errstr, $errfile, $errline ) {
+ throw new ErrorException(html_entity_decode($errstr), $errno, 0, $errfile, $errline);
+}
+set_error_handler("exception_error_handler");
+
+session_name("fsid");
+session_start();
+
+$_rq_method = $_SERVER["REQUEST_METHOD"] ?? "GET";
+$_action = $_GET["_action"] ?? null;
require_once __DIR__ . "/vendor/autoload.php";
-$db = new Database(Database::getConnectionString("db", "postgres", "postgres", "postgres"));
+$db = null;
+try {
+ $db = new Database(Database::getConnectionString("db", "postgres", "postgres", "postgres"));
+} catch (DatabaseConnectionException $ex) {
+ Messaging::error([
+ Messaging::bold("Failed to connect to database!"),
+ Messaging::italic($ex->getMessage()),
+ ]);
+ exit;
+}
+
$db->ensureTable(User::class);
$db->ensureTable(Topic::class);
$db->ensureTable(Post::class);
+
+$superuser = new User();
+$superuser->id = "SUPERUSER";
+if (!$db->fetch($superuser)) {
+ $superUserPassword = base64_encode(random_bytes(12));
+
+ $superuser->name = "superuser";
+ $superuser->passwordHash = password_hash($superUserPassword, PASSWORD_DEFAULT);
+ $superuser->displayName = "SuperUser";
+ $superuser->created = new \DateTimeImmutable();
+
+ $db->insert($superuser);
+
+ Messaging::info([
+ Messaging::bold("Superuser account created"),
+ [
+ "Username" => $superuser->name,
+ "Password" => $superUserPassword,
+ ],
+ "Please note that the password can only be shown this time, so please note it down!",
+ ]);
+ exit;
+}
+
+// initialization finished
+
+if ($_action === "auth") {
+ RequestUtils::ensureRequestMethod("POST");
+ // TODO Login logic
+} elseif ($_action === null) {
+ echo "Hello";
+} else {
+ http_response_code(404);
+ Messaging::error("Invalid or unknown action $_action");
+}