M composer.json => composer.json +1 -0
@@ 30,6 30,7 @@
"./src/common.php",
"./src/Clock.php",
"./src/Renderer.php",
+ "./src/Session.php",
"./src/http/Response.php",
"./src/Url.php"
]
M src/Application.php => src/Application.php +1 -0
@@ 69,6 69,7 @@ final class Application
fn(Request $request) => $handler($request, $args),
];
+ // todo: replace this with the last middeware
$action = fn(Request $input) => $input;
foreach (array_reverse($middlewares) as $middleware) {
M src/Clock.php => src/Clock.php +7 -2
@@ 12,9 12,14 @@ function now(): DateTimeImmutable
return new DateTimeImmutable;
}
-function tz(): DateTimeZone
+function tz(string $tz = null): DateTimeZone
{
- return new DateTimeZone(date_default_timezone_get());
+ return new DateTimeZone($tz ?? date_default_timezone_get());
+}
+
+function timezone_is_valid(string $tz): bool
+{
+ return in_array($tz, \timezone_identifiers_list());
}
interface Clock
M src/Session.php => src/Session.php +29 -1
@@ 3,6 3,34 @@ declare(strict_types=1);
namespace Thirdplace;
+function flash(string $message)
+{
+ $messages = $_SESSION['messages'] ?? [];
+ $messages[] = $message;
+ $_SESSION['messages'] = $messages;
+}
+
+class SessionMiddleware
+{
+ private array $options;
+
+ public function __construct(array $options = [])
+ {
+ $defaultOptions = [
+ 'name' => 'sid',
+ 'cookie_lifetime' => 60 * 60 * 24 * 7, // 7 days
+ // Avoid unnecessary locking if desired
+ //'read_and_close' => true,
+ ];
+ $this->options = array_merge($defaultOptions, $options);
+ }
+
+ function __invoke(Request $request, $next) {
+ session_start($this->options);
+ return $next($request);
+ }
+}
+
/**
* Manipulates php's built-in $_SESSION
*/
@@ 51,4 79,4 @@ final class Session
{
$_SESSION = [];
}
-}>
\ No newline at end of file
+}
M src/common.php => src/common.php +28 -0
@@ 23,3 23,31 @@ function create_sane_stacktrace(\Throwable $e)
}
return array_reverse($stackTrace);
}
+
+/**
+ * Beware: the entropy cannot be deduced from the length
+ */
+function create_random_hex_string(int $length = 8)
+{
+ $bytes = (int) ceil($length / 2);
+ // todo: remove annoying chars such as 0, 0, 1, l
+ return substr(bin2hex(openssl_random_pseudo_bytes($bytes)), 0, $length);
+}
+
+class ExceptionMiddleware
+{
+ private $fn;
+
+ public function __construct(callable $fn)
+ {
+ $this->fn = $fn;
+ }
+
+ function __invoke(Request $request, $next) {
+ try {
+ return $next($request);
+ } catch (\Throwable $e) {
+ return ($this->fn)($e);
+ }
+ }
+}
M src/http/Response.php => src/http/Response.php +4 -1
@@ 19,8 19,11 @@ function json(array $body, int $code = 200): Response
return response($json, $code)->withHeader('content-type', 'application/json');
}
-function redirect(string $url): Response
+function redirect(string $url, string $flash = '')
{
+ if ($flash !== '') {
+ flash($flash);
+ }
return response('', 302)->withHeader('location', $url);
}