Skip to content

Instantly share code, notes, and snippets.

@thekid
Last active August 30, 2020 14:38
Show Gist options
  • Select an option

  • Save thekid/3bb2b6cd099f58bce85fa8447103a833 to your computer and use it in GitHub Desktop.

Select an option

Save thekid/3bb2b6cd099f58bce85fa8447103a833 to your computer and use it in GitHub Desktop.
<?php
use web\frontend\{Frontend, Templates, View};
use web\session\{InFileSystem, Cookies};
use web\{Application, Filter, Filters};
class Uploads extends Application {
/** @return [:var] */
public function routes() {
// Upload handler
$upload= new class() {
#[@get]
public function show() {
return View::named('form');
}
#[@post, @$request: request()]
public function handle($request) {
$uploaded= [];
foreach ($request->multipart()->files() as $file) {
$uploaded[]= ['name' => $file->name(), 'size' => strlen($file->bytes())];
}
return empty($uploaded)
? View::named('form')
: View::named('uploaded')->with(['files' => $uploaded])
;
}
};
// Simple template engine with inline templates
$templates= new class() implements Templates {
private static $templates= [
'login' => '
<h1>Login for {{request.uri.path}}</h1>
<form method="POST">
<input type="hidden" name="return" value="{{request.uri.path}}"/>
<label for="user">Username</label><input type="text" name="user"><br/>
<label for="pass">Password</label><input type="password" name="pass"><br/>
<input type="submit" value="Login">
</form>
',
'form' => '
<h1>Welcome @{{request.values.user}}</h1
<p>Please upload a file</p>
<form method="POST" enctype="multipart/form-data">
<input type="hidden" name="token" value="{{request.values.token}}"/>
<label for="file">Select file</label><input type="file" name="upload"/><br/>
<input type="submit" value="Upload"/>
</form>
',
'uploaded' => '
<h1>Thanks, @{{request.values.user}}!</h1
<p>You have uploaded the file {{files.0.name}}, which is {{files.0.size}} bytes in size</p>
<a href="/">Upload another file</a>
'
];
public function write($name, $context, $out) {
$out->write(preg_replace_callback(
'/\{\{([^}]+)\}\}/',
function($match) use($name, $context) {
$lookup= $context + ['this' => $name];
foreach (explode('.', $match[1]) as $segment) {
$lookup= is_object($lookup) ? $lookup->{$segment}() : $lookup[$segment] ?? null;
}
return $lookup;
},
'<!DOCTYPE html><html><head><title>{{this}}</title></head><body>'.
(self::$templates[$name] ?? '(Undefined template "'.$name.'")').
'</body></html>'
));
}
};
// Simple form-based authentication
$auth= new class($templates) implements Filter {
private $sessions, $templates;
public function __construct($templates) {
$this->sessions= (new InFileSystem('.'))->via((new Cookies())->insecure(true));
$this->templates= $templates;
}
public function filter($request, $response, $invocation) {
if ($session= $this->sessions->locate($request)) {
$request->pass('user', $session->value('user'));
$request->pass('token', $session->value('token'));
return $invocation->proceed($request, $response);
}
// Authenticate, then redirect to get rid of POST method
if ('POST' === $request->method() && 'test:test' === $request->param('user').':'.$request->param('pass')) {
$session= $this->sessions->create();
$session->register('user', 'test');
$session->register('token', uniqid());
$response->answer(303);
$response->header('Location', $request->param('return', '/'));
$session->transmit($response);
return;
}
// Show login form
$response->answer(200);
$response->header('Content-Type', 'text/html; charset=utf-8');
$out= $response->stream();
try {
$this->templates->write('login', ['request' => $request], $out);
} finally {
$out->close();
}
}
};
return [
'/favicon.ico' => fn($request, $response) => $response->answer(404),
'/' => new Filters([$auth], new Frontend($upload, $templates)),
];
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment