OAOAUOEUAOEUAOUEOAOOOOGAABOOOGAOGABOGA

This commit is contained in:
William 2023-02-03 12:00:13 +01:00
parent e7c27c3770
commit 5491c98563
13 changed files with 255 additions and 209 deletions

49
app/App/Config.php Normal file
View File

@ -0,0 +1,49 @@
<?php
namespace App;
abstract class Config
{
private static string $default_file;
private static string $override_file;
private static array $config;
static function file_location(string $default, string $override)
{
self::$default_file = $default;
self::$override_file = $override;
}
static function get(string $key): string|int|float|bool
{
if (isset(self::$config)) {
return self::$config[$key];
}
self::$config = require self::$default_file;
if (!file_exists(self::$override_file)) {
return self::$config[$key];
}
foreach (require self::$override_file as $override_key => $override_value) {
if (!array_key_exists($override_key, self::$config)) {
trigger_error(
'Failed to validate config: ' .
"Undefined option '$override_key'"
, E_USER_ERROR);
}
if (gettype($override_value) !== gettype(self::$config[$override_key])) {
trigger_error(
'Failed to validate config: ' .
"Type mismatch in config file: '$override_key' should be of type "
. gettype(self::$config[$override_key]) .
' not '
. gettype($override_value)
, E_USER_ERROR);
}
self::$config[$override_key] = $override_value;
}
return self::$config[$key];
}
}

View File

@ -0,0 +1,94 @@
<?php
namespace App\Controller;
use App\Config;
ChatController::init();
abstract class ChatController
{
private static array $messages;
static function init()
{
if (!file_exists(Config::get('path_to_chat_json_file'))) {
file_put_contents(
Config::get('path_to_chat_json_file'),
json_encode([])
);
}
self::$messages = json_decode(
file_get_contents(Config::get('path_to_chat_json_file')),
true
);
}
static function index()
{
if (empty($_POST)) {
return view('pages/chat/index', [
'nick' => 'Willy',
'just_sent_message' => false,
'errmsg' => false
]);
}
$nick = filter_input(INPUT_POST, 'nick');
$text = filter_input(INPUT_POST, 'text');
$errmsg = (function() use (&$text, &$nick): string|false {
if (empty(trim($nick, ' '))) {
return 'You must choose a nickname.';
}
$nick_max_chars = 128;
if (strlen($nick) > $nick_max_chars) {
return 'Your nickname is TOO LONG! ' . strlen($nick) . ' out of ' . $nick_max_chars. ' characters.';
}
$text_max_chars = 4096;
if (strlen($text) > $text_max_chars) {
return 'Your message is TOO LONG!!!! ' . strlen($text) . ' out of ' . $text_max_chars . ' characters.';
}
if (empty(trim($text, ' '))) {
return 'Message body cannot be empty.';
}
return false;
})();
if (!$errmsg) {
if (count(self::$messages) > 100) {
array_pop(self::$messages);
}
array_unshift(self::$messages, [
'nick' => $nick,
'date' => time(),
'text' => $text
]);
file_put_contents(Config::get('path_to_chat_json_file'),
json_encode(
self::$messages
)
);
}
view('pages/chat/index', [
'nick' => $nick,
'just_sent_message' => true,
'errmsg' => $errmsg
]);
}
static function messages()
{
view('pages/chat/messages', ['messages' => self::$messages]);
}
static function sync()
{
json_response(hash('crc32', serialize(self::$messages)));
}
}

View File

@ -0,0 +1,21 @@
<?php
namespace App\Controller;
abstract class DefaultController
{
static function index()
{
view('pages/home');
}
static function echo($text = 'You sent nothing...')
{
json_response($text);
}
static function error()
{
1 / 0;
}
}

21
app/App/Database.php Normal file
View File

@ -0,0 +1,21 @@
<?php
namespace App;
use \PDO;
use App\Config;
abstract class Database
{
private static PDO $handle;
static function get_handle(): PDO
{
if (!isset(self::$handle)) {
self::$handle = new PDO(
'sqlite:' . Config::get('path_to_sqlite_file')
);
}
return self::$handle;
}
}

View File

@ -2,37 +2,37 @@
namespace WillySoft; namespace WillySoft;
abstract class ErrorHandler { abstract class ErrorHandler
private static array $error_messages = []; {
/** /**
* On error or exception erase the output buffer then hand the errors as an array of HTML formatted messages to the callback * On error or exception erase the output buffer and hand the
* error message as an HTML formatted string to the callback
* then die
*/ */
static function register(callable $callback) { static function register(callable $callback)
{
ob_start(); ob_start();
set_error_handler(function($errno, $errstr, $errfile, $errline) { set_error_handler(function($errno, $errstr, $errfile, $errline) use (&$callback) {
error_log("Error[$errno]: $errstr in $errfile at line $errline\n"); error_log("Error[$errno]: $errstr in $errfile at line $errline\n");
$errstr = htmlspecialchars($errstr); $errstr = htmlspecialchars($errstr);
self::$error_messages[] = "<b>Error[$errno]:</b> $errstr in <b>$errfile</b> at line <b>$errline</b>"; self::oof($callback, "<b>Error[$errno]:</b> $errstr in <b>$errfile</b> at line <b>$errline</b>");
}); });
set_exception_handler(function($exception) { set_exception_handler(function($exception) use (&$callback) {
error_log("Uncaught Exception: $exception\n"); error_log("Uncaught Exception: $exception\n");
self::$error_messages[] = "<b>Uncaught Exception:</b> {$exception}"; self::oof($callback, "<b>Uncaught Exception:</b> {$exception}");
}); });
register_shutdown_function(function() use(&$callback) {
if (!self::$error_messages) {
return;
} }
private static function oof(callable $callback, string $error_message)
{
ob_end_clean(); ob_end_clean();
http_response_code(500); http_response_code(500);
header_remove(); header_remove();
$callback(self::$error_messages); $callback($error_message);
}); die();
} }
} }

View File

@ -10,11 +10,6 @@ abstract class Route
static string $prefix = ''; static string $prefix = '';
/**
* Check to see if the methods and path satisfy the request.
* and do some magic to retrieve the parameters before calling
* the callback passing those as arguments
*/
static function match(string $methods, string $path, callable $callback) static function match(string $methods, string $path, callable $callback)
{ {
if (!in_array($_SERVER['REQUEST_METHOD'], if (!in_array($_SERVER['REQUEST_METHOD'],
@ -70,10 +65,6 @@ abstract class Route
static function form(string $path, callable $callback) { self::match('get|post', $path, $callback); } static function form(string $path, callable $callback) { self::match('get|post', $path, $callback); }
static function any(string $path, callable $callback) { self::match('get|post|put|patch|delete|options', $path, $callback); } static function any(string $path, callable $callback) { self::match('get|post|put|patch|delete|options', $path, $callback); }
/**
* Require that a callable or an array of callables have to run
* if a route is matched within the current or all child groups
*/
static function middleware(callable|array $middlewares) static function middleware(callable|array $middlewares)
{ {
if (!is_array($middlewares)) { if (!is_array($middlewares)) {
@ -86,7 +77,6 @@ abstract class Route
} }
} }
static function group(string $prefix = '', ?callable $callback = null) static function group(string $prefix = '', ?callable $callback = null)
{ {
if (!str_starts_with($_SERVER['REQUEST_URI'], self::$prefix . $prefix)) { if (!str_starts_with($_SERVER['REQUEST_URI'], self::$prefix . $prefix)) {

View File

@ -1,8 +1,8 @@
<?php <?php
/** /**
* Do not modify this file! * Do not edit this file!
* Copy and name it config.php to override these defaults. * Copy and name it config.php to override these defaults.
*/ */
return [ return [
'debug' => false 'path_to_chat_json_file' => '/dev/shm/database.json'
]; ];

View File

@ -1,15 +1,4 @@
<?php <?php
/**
* _____
* / \
* | () () |
* \ ^ /
* |||||
* |||||
*
* Tread carefully!
*/
// PSR-4 like autoloader // PSR-4 like autoloader
spl_autoload_register( spl_autoload_register(
function ($class_name) { function ($class_name) {
@ -18,31 +7,6 @@ spl_autoload_register(
} }
); );
/**
* Shitty config loader
*/
function config(string $key): bool|string|int|float {
static $config;
if (isset($config)) {
return $config[$key];
}
$config = require __DIR__ . '/../config/default.config.php';
$config_override_path = __DIR__ . '/../config/config.php';
if (!file_exists($config_override_path)) {
return $config[$key];
}
foreach (require $config_override_path as $override_key => $override_value) {
if (!array_key_exists($override_key, $config)) {
trigger_error('Undefined key in config file', E_USER_ERROR);
}
if (gettype($override_value) !== gettype($config[$override_key])) {
trigger_error('Type mismatch in config file', E_USER_ERROR);
}
$config[$override_key] = $override_value;
}
return $config[$key];
}
/** /**
* Helper for evaluating/including views * Helper for evaluating/including views
*/ */
@ -86,121 +50,4 @@ function url(string $url, bool $full = false): string
return isset($_SERVER['HTTPS']) ? 'https' : 'http' . '://' . $_SERVER['HTTP_HOST'] . $dir . $url; return isset($_SERVER['HTTPS']) ? 'https' : 'http' . '://' . $_SERVER['HTTP_HOST'] . $dir . $url;
} }
//----------------------------------------------------- require '../routes/start.php';
// Let's get routing!
//-----------------------------------------------------
use WillySoft\ErrorHandler;
use WillySoft\Route;
Route::get('/', function() {
view('pages/home');
});
ErrorHandler::register(function($error_messages) {
if (config('debug')) {
view('errors/500', ['error_messages' => $error_messages]);
} else {
view('errors/500', ['error_messages' => []]);
}
});
Route::group(function() {
Route::get('/', fn() => view('pages/home'));
Route::get('/echo/$text?', fn($text= 'You sent nothing...') => json_response($text));
Route::get('/error', fn() => 1 / 0);
});
Route::group(function() {
if (!config('debug')) {
return;
}
Route::get('/blog/', fn() => view('pages/blog/home'));
Route::get('/blog/test', fn() => view('pages/blog/test'));
});
Route::group(function() {
$data_path = '/dev/shm/database.json';
$messages = [];
Route::middleware(function() use (&$data_path, &$messages) {
if (!file_exists($data_path)) {
file_put_contents(
$data_path,
json_encode([])
);
}
$messages = json_decode(
file_get_contents($data_path),
true
);
});
Route::form('/willychat/', function() use (&$data_path, &$messages) {
if (empty($_POST)) {
return view('pages/willychat/index', [
'nick' => 'Willy',
'just_sent_message' => false,
'errmsg' => false
]);
}
$nick = filter_input(INPUT_POST, 'nick');
$text = filter_input(INPUT_POST, 'text');
$errmsg = (function() use (&$text, &$nick): string|false {
if (empty(trim($nick, ' '))) {
return 'You must choose a nickname.';
}
$nick_max_chars = 128;
if (strlen($nick) > $nick_max_chars) {
return 'Your nickname is TOO LONG! ' . strlen($nick) . ' out of ' . $nick_max_chars. ' characters.';
}
$text_max_chars = 4096;
if (strlen($text) > $text_max_chars) {
return 'Your message is TOO LONG!!!! ' . strlen($text) . ' out of ' . $text_max_chars . ' characters.';
}
if (empty(trim($text, ' '))) {
return 'Message body cannot be empty.';
}
return false;
})();
if (!$errmsg) {
if (count($messages) > 100) {
array_pop($messages);
}
array_unshift($messages, [
'nick' => $nick,
'date' => time(),
'text' => $text
]);
file_put_contents($data_path,
json_encode(
$messages
)
);
}
view('pages/willychat/index', [
'nick' => $nick,
'just_sent_message' => true,
'errmsg' => $errmsg
]);
});
Route::get('/willychat/messages', function() use (&$messages) {
view('pages/willychat/messages', ['messages' => $messages]);
});
Route::get('/willychat/sync', function() use (&$messages) {
json_response(hash('crc32', serialize($messages)));
});
});
http_response_code(404);
view('errors/404');

37
routes/start.php Normal file
View File

@ -0,0 +1,37 @@
<?php
use App\Config;
use WillySoft\ErrorHandler;
use WillySoft\Route as App;
Config::file_location(
__DIR__ . '/../config/default.config.php',
__DIR__ . '/../config/config.php'
);
ErrorHandler::register(function($error_message) {
view('errors/500', [
'error_message' => $error_message
]);
});
use App\Controller\DefaultController;
App::get('/',
DefaultController::index(...));
App::get('/echo/$text?',
DefaultController::echo(...));
App::get('/error',
DefaultController::error(...));
use App\Controller\ChatController;
App::group('/chat', function() {
App::form('/',
ChatController::index(...));
App::get('/messages',
ChatController::messages(...));
});
http_response_code(404);
view('errors/404');

View File

@ -2,27 +2,14 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>500 Internal Server Error</title> <title>Internal Server Error</title>
</head> </head>
<body> <body>
<style> <h1>Internal Server Error (××)</h1>
body { <?php if(!empty($error_message)): ?>
margin: 1rem; <div><?=$error_message?></div>
background: tomato; <?php endif; ?>
line-height: 1.75;
font-family: "Comic Sans MS", sans-serif;
}
.error {
padding: 1rem;
margin-bottom: 1rem;
background: white;
border: .1rem solid black;
}
</style>
<h1>Error!!1 (××)</h1>
<?php foreach($error_messages as $error_message): ?>
<div class="error"><?=$error_message?></div>
<?php endforeach; ?>
</body> </body>
</html> </html>

View File

@ -68,7 +68,7 @@
<?php endif; ?> <?php endif; ?>
<div class="messages"> <div class="messages">
<iframe src="<?=url('/willychat/messages')?>" marginwidth="0" marginheight="0" scrolling="yes" frameborder="0"></iframe> <iframe src="<?=url('./messages')?>" marginwidth="0" marginheight="0" scrolling="yes" frameborder="0"></iframe>
</div> </div>
<script> <script>

View File

@ -13,7 +13,7 @@
height: 100%; height: 100%;
} }
</style> </style>
<iframe class="willychat" src="<?=url('/willychat/')?>" frameborder="0" scrolling="no"></iframe> <iframe class="willychat" src="<?=url('/chat/')?>" frameborder="0" scrolling="no"></iframe>
<hr> <hr>
<br> <br>