12 Commits
Author SHA1 Message Date
bence 948b36c80d Merge pull request 'add generic HttpResponse' (#6) from feature/create-generic-class-for-http-response into master
soko-web/pipeline/head This commit looks good
Reviewed-on: #6
2023-04-16 20:52:06 +02:00
bence 635b68ab46 add generic HttpResponse
soko-web/pipeline/pr-master This commit looks good
2023-04-16 20:46:16 +02:00
bence 8df2e64872 Merge pull request 'unify url handling for routes and redirect' (#5) from feature/unify-url-handling into master
soko-web/pipeline/head This commit looks good
Reviewed-on: #5
2023-04-16 20:45:23 +02:00
bence 427426bebe unify url handling for routes and redirect
soko-web/pipeline/pr-master This commit looks good
2023-04-16 20:33:22 +02:00
bence 014a548096 Merge pull request 'add interface IAuthenticationRequired' (#4) from feature/interface-for-authentication-required into master
soko-web/pipeline/head This commit looks good
Reviewed-on: #4
2023-04-16 16:54:22 +02:00
bence 298a1e34ac add interface IAuthenticationRequired
soko-web/pipeline/pr-master This commit looks good
2023-04-16 16:43:53 +02:00
bence 4af7ae3521 Merge pull request 'fix check in PersistentDataManager' (#3) from bugfix/fix-check-in-persistentdatamanager into master
soko-web/pipeline/head This commit looks good
Reviewed-on: #3
2023-04-16 16:00:29 +02:00
bence 9d4cb86d41 fix check in PersistentDataManager
soko-web/pipeline/pr-master This commit looks good
2023-04-16 15:59:47 +02:00
bence bf4520a7dd Merge pull request 'get and serve headers in Request' (#2) from feature/implement-header-parsing into master
soko-web/pipeline/head This commit looks good
Reviewed-on: #2
2023-04-16 15:54:55 +02:00
bence fda796ab18 get and serve headers in Request
soko-web/pipeline/pr-master This commit looks good
2023-04-16 15:52:50 +02:00
bence ba33a9bb37 Merge pull request 'add README with build badge' (#1) from add-readme into master
soko-web/pipeline/head This commit looks good
Reviewed-on: #1
2023-04-07 21:22:45 +02:00
bence 8a20bb76ef add README with build badge
soko-web/pipeline/pr-master This commit looks good
2023-04-07 21:20:25 +02:00
9 changed files with 152 additions and 7 deletions

No files matched your search

+9
View File
@@ -0,0 +1,9 @@
# SokoWeb
[![Build Status](https://ci.esoko.eu/job/soko-web/job/master/badge/icon)](https://ci.esoko.eu/job/soko-web/job/master/)
This is the SokoWeb framework.
---
*License: **GNU GPL 3.0**. Full license text can be found in file `LICENSE`.*
@@ -0,0 +1,6 @@
<?php namespace SokoWeb\Interfaces\Authentication;
interface IAuthenticationRequired
{
public function isAuthenticationRequired(): bool;
}
+2
View File
@@ -12,6 +12,8 @@ interface IRequest
public function post(string $key); public function post(string $key);
public function header(string $key);
public function session(): ISession; public function session(): ISession;
public function setUser(?IUser $user): void; public function setUser(?IUser $user): void;
+1 -1
View File
@@ -57,7 +57,7 @@ class PersistentDataManager
$value = current($data); $value = current($data);
$relation = key($relations); $relation = key($relations);
if (strpos($key, '__') == false) { if (strpos($key, '__') === false) {
$method = 'set' . str_replace('_', '', ucwords($key, '_')); $method = 'set' . str_replace('_', '', ucwords($key, '_'));
if (method_exists($model, $method) && isset($value)) { if (method_exists($model, $method) && isset($value)) {
+19 -1
View File
@@ -15,15 +15,24 @@ class Request implements IRequest
private array $post; private array $post;
private array $headers;
private Session $session; private Session $session;
private ?IUser $user = null; private ?IUser $user = null;
public function __construct(string $base, array &$get, array &$post, array &$session, IUserRepository $userRepository) public function __construct(
string $base,
array &$get,
array &$post,
array $headers,
array &$session,
IUserRepository $userRepository)
{ {
$this->base = $base; $this->base = $base;
$this->get = &$get; $this->get = &$get;
$this->post = &$post; $this->post = &$post;
$this->headers = $headers;
$this->session = new Session($session); $this->session = new Session($session);
$userId = $this->session->get('userId'); $userId = $this->session->get('userId');
@@ -64,6 +73,15 @@ class Request implements IRequest
return null; return null;
} }
public function header($key)
{
if (isset($this->headers[$key])) {
return $this->headers[$key];
}
return null;
}
public function session(): ISession public function session(): ISession
{ {
return $this->session; return $this->session;
+109
View File
@@ -0,0 +1,109 @@
<?php namespace SokoWeb\Response;
use SokoWeb\Interfaces\Response\IRedirect;
use SokoWeb\Interfaces\Response\IContent;
use SokoWeb\Interfaces\Authentication\IAuthenticationRequired;
use SokoWeb\Interfaces\Authorization\ISecured;
use SokoWeb\Interfaces\Request\IRequest;
use SokoWeb\Response\Redirect;
use SokoWeb\Response\HtmlContent;
use SokoWeb\Response\JsonContent;
use SokoWeb\Routing\RouteCollection;
class HttpResponse
{
private IRequest $request;
private RouteCollection $routeCollection;
private array $appConfig;
private string $method;
private string $rawUrl;
private array $parsedUrl;
public function __construct(
IRequest $request,
RouteCollection $routeCollection,
array $appConfig,
string $requestMethod,
string $requestUrl
) {
$this->request = $request;
$this->routeCollection = $routeCollection;
$this->appConfig = $appConfig;
$this->method = strtolower($requestMethod);
$this->parsedUrl = parse_url($requestUrl);
$this->rawUrl = $requestUrl;
}
public function render(): void
{
$match = $this->routeCollection->match($this->method, $this->parsedUrl['path']);
if ($match === null) {
$this->render404();
return;
}
list($route, $params) = $match;
$this->request->setParsedRouteParams($params);
$handler = $route->getHandler();
$controller = new $handler[0]($this->request);
if (
$controller instanceof IAuthenticationRequired &&
$controller->isAuthenticationRequired() &&
$this->request->user() === null
) {
$this->redirectToLogin();
return;
}
if (
$this->method === 'post' &&
!in_array($this->parsedUrl['path'], $this->appConfig['antiCsrfTokenExceptions']) &&
$this->request->post($this->appConfig['antiCsrfTokenName']) !== $this->request->session()->get($this->appConfig['antiCsrfTokenName'])
) {
$this->renderAntiCsrfError();
return;
}
if ($controller instanceof ISecured && !$controller->authorize()) {
$this->render404();
return;
}
$response = call_user_func([$controller, $handler[1]]);
if ($response instanceof IContent) {
header('Content-Type: ' . $response->getContentType() . '; charset=UTF-8');
$response->render();
} elseif ($response instanceof IRedirect) {
header('Location: ' . $response->getUrl(), true, $response->getHttpCode());
} else {
$this->render404();
}
}
private function redirectToLogin(): void
{
$this->request->session()->set('redirect_after_login', $this->rawUrl);
$response = new Redirect($this->routeCollection->getRoute($this->appConfig['loginRouteId'])->generateLink(), IRedirect::TEMPORARY);
header('Location: ' . $response->getUrl(), true, $response->getHttpCode());
}
private function renderAntiCsrfError(): void
{
$content = new JsonContent($this->appConfig['antiCsrfTokenErrorResponse']);
header('Content-Type: text/html; charset=UTF-8', true, 403);
$content->render();
}
private function render404(): void
{
$content = new HtmlContent($this->appConfig['error404View']);
header('Content-Type: text/html; charset=UTF-8', true, 404);
$content->render();
}
}
+1 -1
View File
@@ -19,7 +19,7 @@ class Redirect implements IRedirect
if (preg_match('/^http(s)?/', $this->target) === 1) { if (preg_match('/^http(s)?/', $this->target) === 1) {
$link = $this->target; $link = $this->target;
} else { } else {
$link = \Container::$request->getBase() . '/' . $this->target; $link = \Container::$request->getBase() . $this->target;
} }
return $link; return $link;
+1 -1
View File
@@ -53,7 +53,7 @@ class Route
$query = count($queryParams) > 0 ? '?' . http_build_query($queryParams) : ''; $query = count($queryParams) > 0 ? '?' . http_build_query($queryParams) : '';
return implode('/', $link) . $query; return '/' . implode('/', $link) . $query;
} }
public function testAgainst(array $path): ?array public function testAgainst(array $path): ?array
+4 -3
View File
@@ -41,9 +41,10 @@ class RouteCollection
return $this->routes[$id]; return $this->routes[$id];
} }
public function match(string $method, array $uri): ?array public function match(string $method, string $uri): ?array
{ {
$groupNumber = count($uri); $path = $uri === '/' ? [] : explode('/', ltrim($uri, '/'));
$groupNumber = count($path);
// response to HEAD request with the GET content // response to HEAD request with the GET content
if ($method === 'head') { if ($method === 'head') {
@@ -55,7 +56,7 @@ class RouteCollection
} }
foreach ($this->searchTable[$method][$groupNumber] as $route) { foreach ($this->searchTable[$method][$groupNumber] as $route) {
if (($parameters = $route->testAgainst($uri)) !== null) { if (($parameters = $route->testAgainst($path)) !== null) {
return [$route, $parameters]; return [$route, $parameters];
} }
} }