Added some boilerplate code to Hanyuu.
This commit is contained in:
parent
594c92179b
commit
bb9d7d4058
11 changed files with 299 additions and 6 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -2,3 +2,6 @@
|
|||
[Dd]esktop.ini
|
||||
.DS_Store
|
||||
/public/robots.txt
|
||||
/lib/index-dev
|
||||
/.debug
|
||||
/config/config.ini
|
||||
|
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
[submodule "lib/index"]
|
||||
path = lib/index
|
||||
url = https://git.flash.moe/flash/index.git
|
0
config/config.example.ini
Normal file
0
config/config.example.ini
Normal file
46
hanyuu.php
Normal file
46
hanyuu.php
Normal file
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
namespace Hanyuu;
|
||||
|
||||
use Index\Autoloader;
|
||||
use Index\Environment;
|
||||
use Hanyuu\Config\ArrayConfig;
|
||||
|
||||
define('HAU_STARTUP', microtime(true));
|
||||
define('HAU_ROOT', __DIR__);
|
||||
define('HAU_CLI', PHP_SAPI === 'cli');
|
||||
define('HAU_DEBUG', is_file(HAU_ROOT . '/.debug'));
|
||||
define('HAU_DIR_PUBLIC', HAU_ROOT . '/public');
|
||||
define('HAU_DIR_SOURCE', HAU_ROOT . '/src');
|
||||
define('HAU_DIR_LIBRARIES', HAU_ROOT . '/lib');
|
||||
define('HAU_DIR_CONFIG', HAU_ROOT . '/config');
|
||||
|
||||
define('HAU_NDX_PATH', HAU_DIR_LIBRARIES . '/index');
|
||||
define('HAU_NDX_PATH_DEV', HAU_DIR_LIBRARIES . '/index-dev');
|
||||
|
||||
require_once (HAU_DEBUG && is_dir(HAU_NDX_PATH_DEV) ? HAU_NDX_PATH_DEV : HAU_NDX_PATH) . '/index.php';
|
||||
|
||||
Autoloader::addNamespace(__NAMESPACE__, HAU_DIR_SOURCE);
|
||||
Environment::setDebug(HAU_DEBUG);
|
||||
|
||||
mb_internal_encoding('utf-8');
|
||||
date_default_timezone_set('utc');
|
||||
set_include_path(get_include_path() . PATH_SEPARATOR . HAU_ROOT);
|
||||
|
||||
set_exception_handler(function(\Throwable $ex) {
|
||||
if(HAU_CLI)
|
||||
die((string)$ex);
|
||||
|
||||
http_response_code(500);
|
||||
ob_clean();
|
||||
|
||||
if(HAU_DEBUG) {
|
||||
header('Content-Type: text/plain; charset=utf-8');
|
||||
die((string)$ex);
|
||||
}
|
||||
|
||||
header('Content-Type: text/html; charset-utf-8');
|
||||
die('<h2>Hanyuu is sad.</h2>');
|
||||
});
|
||||
|
||||
$hau = new HanyuuContext(ArrayConfig::open(HAU_DIR_CONFIG . '/config.ini'));
|
||||
$hau->connectDb();
|
1
lib/index
Submodule
1
lib/index
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit f8c6602ab953491a3e540d1ab9c77f2d885ee120
|
|
@ -1,6 +1,9 @@
|
|||
<!doctype html>
|
||||
<title><?=ucfirst(explode('.', $_SERVER['HTTP_HOST'])[1]);?> ID</title>
|
||||
<center>
|
||||
<h1>Under Construction</h1>
|
||||
<img src="//static.flash.moe/images/me-tan-2.png"/>
|
||||
</center>
|
||||
<?php
|
||||
namespace Hanyuu;
|
||||
|
||||
require_once __DIR__ . '/../hanyuu.php';
|
||||
|
||||
$request = \Index\Http\HttpRequest::fromRequest();
|
||||
|
||||
$hau->setUpHttp();
|
||||
$hau->dispatchHttp($request);
|
||||
|
|
78
src/Config/ArrayConfig.php
Normal file
78
src/Config/ArrayConfig.php
Normal file
|
@ -0,0 +1,78 @@
|
|||
<?php
|
||||
namespace Hanyuu\Config;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
class ArrayConfig implements IConfig {
|
||||
private const SCOPE_CHAR = ':';
|
||||
private const SCANNER_MODE = INI_SCANNER_TYPED;
|
||||
private const TEST_VALUE = 'X-Config-Test';
|
||||
|
||||
private array $cache = [];
|
||||
private array $exists = [];
|
||||
|
||||
public function __construct(
|
||||
private array $config
|
||||
) {}
|
||||
|
||||
public function scopeTo(string $prefix): IConfig {
|
||||
return new ScopedConfig($this, $prefix);
|
||||
}
|
||||
|
||||
private function getRaw(string $name): mixed {
|
||||
$parts = array_reverse(explode(self::SCOPE_CHAR, $name));
|
||||
$value = $this->config;
|
||||
|
||||
while(count($parts) > 1) {
|
||||
$part = array_pop($parts);
|
||||
if(!array_key_exists($part, $value))
|
||||
break;
|
||||
$value = $value[$part];
|
||||
}
|
||||
|
||||
if($parts[0] === '')
|
||||
return $value;
|
||||
|
||||
return $value[$parts[0]] ?? null;
|
||||
}
|
||||
|
||||
public function getValue(string $name, string $type = IConfig::T_ANY, $default = null): mixed {
|
||||
if(array_key_exists($name, $this->cache))
|
||||
$value = $this->cache[$name];
|
||||
else {
|
||||
$this->cache[$name] = $value = $this->getRaw($name);
|
||||
$this->exists[$name] = $value !== null;
|
||||
}
|
||||
|
||||
if($type !== IConfig::T_ANY && CfgTools::type($value) !== $type)
|
||||
$value = null;
|
||||
|
||||
return $value ?? $default ?? CfgTools::default($type);
|
||||
}
|
||||
|
||||
public function hasValue(string $name): bool {
|
||||
if(array_key_exists($name, $this->exists))
|
||||
return $this->exists[$name];
|
||||
|
||||
$exists = array_key_exists($name, $this->cache)
|
||||
|| $this->getRaw($name) !== null;
|
||||
|
||||
return $this->exists[$name] = $exists;
|
||||
}
|
||||
|
||||
public static function open(string $path): self {
|
||||
$parsed = parse_ini_file($path, true, self::SCANNER_MODE);
|
||||
if($parsed === false)
|
||||
throw new InvalidArgumentException('Unable to parse configuration file in $path.');
|
||||
|
||||
return new static($parsed);
|
||||
}
|
||||
|
||||
public static function from(string $string): self {
|
||||
$parsed = parse_ini_string($string, true, self::SCANNER_MODE);
|
||||
if($parsed === false)
|
||||
throw new InvalidArgumentException('Unable to parse configuration string in $string.');
|
||||
|
||||
return new static($parsed);
|
||||
}
|
||||
}
|
27
src/Config/CfgTools.php
Normal file
27
src/Config/CfgTools.php
Normal file
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
namespace Hanyuu\Config;
|
||||
|
||||
final class CfgTools {
|
||||
public static function type($value): string {
|
||||
return gettype($value);
|
||||
}
|
||||
|
||||
public static function isValidType(string $type): bool {
|
||||
return $type === IConfig::T_ARR
|
||||
|| $type === IConfig::T_BOOL
|
||||
|| $type === IConfig::T_INT
|
||||
|| $type === IConfig::T_STR;
|
||||
}
|
||||
|
||||
private const DEFAULTS = [
|
||||
IConfig::T_ANY => null,
|
||||
IConfig::T_STR => '',
|
||||
IConfig::T_INT => 0,
|
||||
IConfig::T_BOOL => false,
|
||||
IConfig::T_ARR => [],
|
||||
];
|
||||
|
||||
public static function default(string $type) {
|
||||
return self::DEFAULTS[$type] ?? null;
|
||||
}
|
||||
}
|
14
src/Config/IConfig.php
Normal file
14
src/Config/IConfig.php
Normal file
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
namespace Hanyuu\Config;
|
||||
|
||||
interface IConfig {
|
||||
public const T_ANY = '';
|
||||
public const T_STR = 'string';
|
||||
public const T_INT = 'integer';
|
||||
public const T_BOOL = 'boolean';
|
||||
public const T_ARR = 'array';
|
||||
|
||||
public function scopeTo(string $prefix): IConfig;
|
||||
public function getValue(string $name, string $type = IConfig::T_ANY, $default = null): mixed;
|
||||
public function hasValue(string $name): bool;
|
||||
}
|
37
src/Config/ScopedConfig.php
Normal file
37
src/Config/ScopedConfig.php
Normal file
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
namespace Hanyuu\Config;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
class ScopedConfig implements IConfig {
|
||||
private IConfig $config;
|
||||
private string $prefix;
|
||||
|
||||
private const SCOPE_CHAR = ':';
|
||||
|
||||
public function __construct(IConfig $config, string $prefix) {
|
||||
if($prefix === '')
|
||||
throw new InvalidArgumentException('$prefix may not be empty.');
|
||||
if(!str_ends_with($prefix, self::SCOPE_CHAR))
|
||||
$prefix .= self::SCOPE_CHAR;
|
||||
|
||||
$this->config = $config;
|
||||
$this->prefix = $prefix;
|
||||
}
|
||||
|
||||
private function getName(string $name): string {
|
||||
return $this->prefix . $name;
|
||||
}
|
||||
|
||||
public function scopeTo(string $prefix): IConfig {
|
||||
return $this->config->scopeTo($this->getName($prefix));
|
||||
}
|
||||
|
||||
public function getValue(string $name, string $type = IConfig::T_ANY, $default = null): mixed {
|
||||
return $this->config->getValue($this->getName($name), $type, $default);
|
||||
}
|
||||
|
||||
public function hasValue(string $name): bool {
|
||||
return $this->config->hasValue($this->getName($name));
|
||||
}
|
||||
}
|
81
src/HanyuuContext.php
Normal file
81
src/HanyuuContext.php
Normal file
|
@ -0,0 +1,81 @@
|
|||
<?php
|
||||
namespace Hanyuu;
|
||||
|
||||
use RuntimeException;
|
||||
use Index\Data\IDbConnection;
|
||||
use Index\Data\DbTools;
|
||||
use Index\Http\HttpFx;
|
||||
use Index\Http\HttpRequest;
|
||||
use Index\Routing\IRouter;
|
||||
use Hanyuu\Config\IConfig;
|
||||
|
||||
class HanyuuContext {
|
||||
private const DB_INIT = 'SET SESSION time_zone = \'+00:00\', sql_mode = \'STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION\';';
|
||||
|
||||
private IConfig $config;
|
||||
private IDbConnection $dbConn;
|
||||
|
||||
public function __construct(IConfig $config) {
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function connectDb(?IDbConnection $dbConn = null): void {
|
||||
$dbConn ??= DbTools::create($this->config->getValue('database:dsn', IConfig::T_STR, 'null'));
|
||||
$dbConn->execute(self::DB_INIT);
|
||||
$this->dbConn = $dbConn;
|
||||
}
|
||||
|
||||
public function getDb(): IDbConnection {
|
||||
return $this->dbConn;
|
||||
}
|
||||
|
||||
public function getDbQueryCount(): int {
|
||||
$result = $this->dbConn->query('SHOW SESSION STATUS LIKE "Questions"');
|
||||
return $result->next() ? $result->getInteger(0) : 0;
|
||||
}
|
||||
|
||||
public function setUpHttp(): void {
|
||||
$this->router = new HttpFx;
|
||||
$this->router->use('/', function($response) {
|
||||
$response->setPoweredBy('Hanyuu');
|
||||
});
|
||||
|
||||
$this->registerErrorPages();
|
||||
$this->registerHttpRoutes();
|
||||
}
|
||||
|
||||
public function dispatchHttp(?HttpRequest $request = null): void {
|
||||
$this->router->dispatch($request);
|
||||
}
|
||||
|
||||
private function registerErrorPages(): void {
|
||||
/*$this->router->addErrorHandler(400, function($response) {
|
||||
$response->setContent(Template::renderRaw('errors.400'));
|
||||
});
|
||||
$this->router->addErrorHandler(403, function($response) {
|
||||
$response->setContent(Template::renderRaw('errors.403'));
|
||||
});
|
||||
$this->router->addErrorHandler(404, function($response) {
|
||||
$response->setContent(Template::renderRaw('errors.404'));
|
||||
});
|
||||
$this->router->addErrorHandler(500, function($response) {
|
||||
$response->setContent(file_get_contents(MSZ_TEMPLATES . '/500.html'));
|
||||
});
|
||||
$this->router->addErrorHandler(503, function($response) {
|
||||
$response->setContent(file_get_contents(MSZ_TEMPLATES . '/503.html'));
|
||||
});*/
|
||||
}
|
||||
|
||||
private function registerHttpRoutes(): void {
|
||||
$this->router->get('/', function($response, $request) {
|
||||
$siteName = $this->config->getValue('site:name', IConfig::T_STR, 'Hanyuu');
|
||||
|
||||
return "<!doctype html>\r\n"
|
||||
. "<title>{$siteName} ID</title>\r\n"
|
||||
. "<center>\r\n"
|
||||
. " <h1>Under Construction</h1>\r\n"
|
||||
. " <img src=\"//static.flash.moe/images/me-tan-2.png\" alt=\"\"/>\r\n"
|
||||
. "</center>\r\n";
|
||||
});
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue