Compare commits

..

No commits in common. "2a8fa3f397c9933549339d59c6cca9887faf80ac" and "fdd24d90da8093d687c4e10e65193307af13a613" have entirely different histories.

12 changed files with 28 additions and 461 deletions

View File

@ -1,2 +0,0 @@
config.json

View File

@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2022 Micha Espey <tracer@24unix.net> Copyright (c) <year> <copyright holders>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

View File

@ -1,7 +0,0 @@
{
"dbHost": "localhost",
"dbPort": 3306,
"dbDatabase": "tracer_addressbook",
"dbUser": "tracer_addressbook",
"dbPassword": "secret",
}

View File

@ -1,13 +0,0 @@
DirectoryIndex index.php
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_URI}::$0 ^(/.+)/(.*)::\2$
RewriteRule .* - [E=BASE:%1]
RewriteCond %{HTTP:Authorization} .+
RewriteRule ^ - [E=HTTP_AUTHORIZATION:%0]
RewriteCond %{ENV:REDIRECT_STATUS} =""
RewriteRule ^index\.php(?:/(.*)|$) %{ENV:BASE}/$1 [R=301,L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ %{ENV:BASE}/index.php [L]
</IfModule>

View File

@ -3,38 +3,12 @@
namespace App\Controller; namespace App\Controller;
use App\Service\Template; use App\Service\Template;
use stdClass;
class AddressBook class AddressBook extends stdClass
{ {
public function __construct(private readonly Template $template) public function __construct(Template $template)
{ {
$template->render(templateName: 'index.tpl');
} }
public function main(): void
{
try {
$this->template->render(templateName: 'index.tpl');
} catch (\Exception $e) {
die($e->getMessage());
}
}
public function admin(string $command = '')
{
try {
$this->template->render(templateName: 'admin/index.tpl');
} catch (\Exception $e) {
die($e->getMessage());
}
}
public function login()
{
try {
$this->template->render(templateName: 'admin/index.tpl');
} catch (\Exception $e) {
die($e->getMessage());
}
}
} }

View File

@ -9,10 +9,9 @@ class User
private string $password, private string $password,
private string $first = '', private string $first = '',
private string $last = '', private string $last = '',
private int $id = 0, private int $id = 0
private bool $isAdmin = false
) )
{ {
// empty body // empty body
} }
} }

View File

@ -1,255 +0,0 @@
<?php
namespace App\Repository;
use App\Service\Config;
use App\Service\DatabaseConnection;
use App\Entity\User;
use PDO;
use PDOException;
/**
*
*/
class DomainRepository
{
public function __construct(
private readonly DatabaseConnection $databaseConnection,
private readonly Config $configController)
{
// empty body
}
/**
* @return array
*/
public function findAll(): array
{
$users = [];
$sql = "
SELECT id, nick, first, last, is_admin
FROM " . DatabaseConnection::TABLE_USERS . "
ORDER BY name";
try {
$statement = $this->databaseConnection->getConnection()->prepare(query: $sql);
$statement->execute();
while ($result = $statement->fetch(mode: PDO::FETCH_ASSOC)) {
$domain = new Domain(name: $result['name'], panel: $result['panel'], id: $result['id']);
$domains[] = $domain;
}
return $domains;
} catch (PDOException $e) {
exit($e->getMessage());
}
}
/**
* @param int $id
*
* @return bool|\App\Entity\Domain
*/
public function findByID(int $id): bool|Domain
{
$this->logger->debug(message: "findById($id)");
$sql = "
SELECT id, name, panel
FROM . " . DatabaseConnection::TABLE_DOMAINS . "
WHERE id = :id";
try {
$statement = $this->databaseConnection->getConnection()->prepare(query: $sql);
$statement->bindParam(param: ':id', var: $id);
$statement->execute();
if ($result = $statement->fetch(mode: PDO::FETCH_ASSOC)) {
return new Domain(name: $result['name'], panel: $result['panel'], id: $result['id']);
} else {
return false;
}
} catch (PDOException $e) {
exit($e->getMessage());
}
}
/**
* @param String $name
*
* @return \App\Entity\Domain|bool
*/
public function findByName(string $name): Domain|bool
{
$this->logger->debug(message: "findByName($name)");
$sql = "
SELECT id, name, panel
FROM " . DatabaseConnection::TABLE_DOMAINS . "
WHERE name = :name";
try {
$statement = $this->databaseConnection->getConnection()->prepare(query: $sql);
$statement->bindParam(param: ':name', var: $name);
$statement->execute();
if ($result = $statement->fetch(mode: PDO::FETCH_ASSOC)) {
return new Domain(name: $result['name'], panel: $result['panel'], id: $result['id']);
} else {
return false;
}
} catch (PDOException $e) {
exit($e->getMessage());
}
}
/**
* @param string $host
*
* @return \App\Entity\Domain|bool
*/
public function findByHost(string $host): Domain|bool
{
$this->logger->debug(message: "findByHost($host)");
$host = strtolower(string: trim(string: $host));
$count = substr_count(haystack: $host, needle: '.');
if ($count == 2) {
if (strlen(string: explode(separator: '.', string: $host)[1]) > 3) {
$host = explode(separator: '.', string: $host, limit: 2)[1];
}
} elseif ($count > 2) {
$host = $this->findByHost(host: explode(separator: '.', string: $host, limit: 2)[1]);
}
if ($domain = $this->findByName(name: $host)) {
return $domain;
} else {
return false;
}
}
/**
* @param \App\Entity\Domain $domain
*
* @return string|false
*/
public function insert(Domain $domain): bool|string
{
$domainName = $domain->getName();
$this->logger->info(message: "insert($domainName)");
$sql = "
INSERT INTO " . DatabaseConnection::TABLE_DOMAINS . " (name, panel)
VALUES (:name, :panel)";
try {
$name = $domain->getName();
$panel = $domain->getPanel();
$statement = $this->databaseConnection->getConnection()->prepare(query: $sql);
$statement->bindParam(param: ':name', var: $name);
$statement->bindParam(param: ':panel', var: $panel);
$statement->execute();
return $this->databaseConnection->getConnection()->lastInsertId();
} catch (PDOException $e) {
exit($e->getMessage());
}
}
/**
* @param \App\Entity\Domain $domain
*
* @return false|int
*/
public function update(Domain $domain): bool|int
{
$domainName = $domain->getName();
$this->logger->debug(message: "update($domainName)");
$id = $domain->getId();
$current = $this->findByID(id: $id);
if (empty($domain->getName())) {
$name = $current->getName();
} else {
$name = $domain->getName();
}
if (empty($domain->getPanel())) {
$panel = $current->getPanel();
} else {
$panel = $domain->getPanel();
}
$sql = "
UPDATE " . DatabaseConnection::TABLE_DOMAINS . " SET
name = :name,
panel = :panel
WHERE id = :id";
try {
$statement = $this->databaseConnection->getConnection()->prepare(query: $sql);
$statement->bindParam(param: 'id', var: $id);
$statement->bindParam(param: 'name', var: $name);
$statement->bindParam(param: 'panel', var: $panel);
$statement->execute();
return $statement->rowCount();
} catch (PDOException $e) {
echo $e->getMessage();
return false;
}
}
/**
* @param \App\Entity\Domain $domain
*
* @return int
*/
public function delete(Domain $domain): int
{
$domainName = $domain->getName();
$this->logger->debug(message: "delete($domainName)");
$sql = "
DELETE FROM " . DatabaseConnection::TABLE_DOMAINS . "
WHERE id = :id";
try {
$statement = $this->databaseConnection->getConnection()->prepare(query: $sql);
$id = $domain->getId();
$statement->bindParam(param: 'id', var: $id);
$statement->execute();
return $statement->rowCount();
} catch (PDOException $e) {
exit($e->getMessage());
}
}
/**
* @param String $field
*
* @return int
*/
public function getLongestEntry(string $field): int
{
$sql = "
SELECT MAX(LENGTH(" . $field . ")) as length FROM " . DatabaseConnection::TABLE_DOMAINS;
try {
$statement = $this->databaseConnection->getConnection()->prepare(query: $sql);
$statement->execute();
$result = $statement->fetch();
return $result['length'];
} catch (PDOException $e) {
exit($e->getMessage());
}
}
}

View File

@ -1,40 +0,0 @@
<?php
namespace App\Service;
use Exception;
/**
*
*/
class Config
{
private array $config;
/**
* @throws Exception
*/
public function __construct()
{
$configFile = dirname(path: __DIR__, levels: 2) . "/config.json.local";
if (!file_exists(filename: $configFile)) {
$configFile = dirname(path: __DIR__, levels: 2) . "/config.json";
}
if (!file_exists(filename: $configFile)) {
throw new Exception(message: 'Missing config file');
}
$configJSON = file_get_contents(filename: $configFile);
if (json_decode(json: $configJSON) === null) {
throw new Exception(message: 'Config file is not valid JSON.');
}
$this->config = json_decode(json: $configJSON, associative: true);
}
public function getConfig(string $configKey): string
{
return $this->config[$configKey];
}
}

View File

@ -3,30 +3,31 @@
namespace App\Service; namespace App\Service;
use App\Controller\AddressBook; use App\Controller\AddressBook;
use Exception;
use stdClass;
class Container class Container
{ {
// caveat: Classes are always instantiated // no autowiring yet, maybe later, but it might fit for a demo
// No autowiring (yet, maybe later, but it might fit for a demo)
private Template $template; private Template $template;
private AddressBook $addressBook; private AddressBook $addressBook;
private Router $router;
public function __construct() public function __construct()
{ {
$this->template = new Template(templateDir: dirname(path: __DIR__, levels: 2) . '/templates/'); $this->template = new Template(templateDir: dirname(path: __DIR__, levels: 2) . '/templates/');
$this->addressBook = new AddressBook(template: $this->template); $this->addressBook = new AddressBook(template: $this->template);
$this->router = new Router();
} }
public function get(string $className): object
/**
* @throws Exception
*/
public function get(string $class): stdClass
{ {
return match ($className) { return match($class) {
'App\Controller\AddressBook' => $this->addressBook, 'App\Controller\AddressBook' => $this->addressBook,
'App\Service\Router' => $this->router, default => throw new Exception(message: "Missing class definition: $class")
//default => throw new Exception(message: "Missing class definition: $class")
default => die("Missing class definition: $className")
}; };
} }
} }

View File

@ -1,37 +0,0 @@
<?php
namespace App\Service;
use PDO;
/**
*
*/
class DatabaseConnection
{
private PDO $dbConnection;
const TABLE_PREFIX = '';
const TABLE_USERS = self::TABLE_PREFIX . "users";
const TABLE_ADDRESSES = self::TABLE_PREFIX . "addresses";
public function __construct(private readonly Config $configController)
{
$dbHost = $this->configController->getConfig(configKey: 'dbHost');
$dbPort = $this->configController->getConfig(configKey: 'dbPort');
$dbDatabase = $this->configController->getConfig(configKey: 'dbDatabase');
$dbUser = $this->configController->getConfig(configKey: 'dbUser');
$dbPassword = $this->configController->getConfig(configKey: 'dbPassword');
$this->dbConnection = new PDO(
dsn: "mysql:host=$dbHost;port=$dbPort;charset=utf8mb4;dbname=$dbDatabase",
username: $dbUser,
password: $dbPassword
);
}
public function getConnection(): PDO
{
return $this->dbConnection;
}
}

View File

@ -1,53 +0,0 @@
<?php
namespace App\Service;
class Router
{
private array $uri;
private array $routes;
public function __construct()
{
$uri = parse_url(url: $_SERVER['REQUEST_URI'], component: PHP_URL_PATH);
$this->uri = explode(separator: '/', string: $uri);
}
public function registerRoute(string $route, \Closure $callback): void
{
$this->routes[$route] = $callback;
}
private function matchRoute($url = '/users/tracer/posts/tracer', $method = 'GET')
{
$reqUrl = $url;
$reqUrl = rtrim(string: $reqUrl, characters: "/");
foreach ($this->routes as $route => $closure) {
// convert urls like '/users/:uid/posts/:pid' to regular expression
// $pattern = "@^" . preg_replace('/\\\:[a-zA-Z0-9\_\-]+/', '([a-zA-Z0-9\-\_]+)', preg_quote($route['url'])) . "$@D";
$pattern = "@^" . preg_replace('\\/users/:[a-zA-Z0-9\_\-]+/', '([a-zA-Z0-9\-\_]+)', $route) . "$@D";
// echo $pattern."\n";
$params = [];
// check if the current request params the expression
$match = preg_match($pattern, $reqUrl, $params);
if ($match) {
// remove the first match
array_shift($params);
// call the callback with the matched positions as params
// return call_user_func_array($route['callback'], $params);
return [$route, $params];
}
}
return [];
}
public function handleRouting(): void
{
$foo = $this->matchRoute();
var_dump($foo);
}
}

View File

@ -1,15 +1,15 @@
<?php <?php
spl_autoload_register(callback: function ($className) { spl_autoload_register(callback: function($className) {
$prefix = 'App'; $prefix = 'App';
$baseDir = __DIR__; $baseDir = __DIR__;
$prefixLen = strlen(string: $prefix); $len = strlen(string: $prefix);
if (strncmp(string1: $prefix, string2: $className, length: $prefixLen) !== 0) { if (strncmp(string1: $prefix, string2: $className, length: $len) !== 0) {
die("Invalid class: $className"); return;
} }
$realClassNamePSRpath = substr(string: $className, offset: $prefixLen); $realClassName = substr(string: $className, offset: $len);
$classLocation = $baseDir . str_replace(search: '\\', replace: '/', subject: $realClassNamePSRpath) . '.php'; $classLocation = $baseDir . str_replace(search: '\\', replace: '/', subject: $realClassName) . '.php';
if (file_exists(filename: $classLocation)) { if (file_exists(filename: $classLocation)) {
require $classLocation; require $classLocation;
} else { } else {