First Commit
This commit is contained in:
commit
923d6ca242
35 changed files with 4933 additions and 0 deletions
38
src/php/ContainerHandler.php
Normal file
38
src/php/ContainerHandler.php
Normal file
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GamesShop;
|
||||
|
||||
use League\Container\Container;
|
||||
use League\Container\ReflectionContainer;
|
||||
|
||||
final class ContainerHandler
|
||||
{
|
||||
private static Container|null $instance = null;
|
||||
|
||||
public static function getInstance(): Container
|
||||
{
|
||||
if (self::$instance === null) {
|
||||
self::createInstance();
|
||||
}
|
||||
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @template RequestedType
|
||||
*
|
||||
* @param class-string<RequestedType>|string $id
|
||||
*
|
||||
* @return RequestedType|mixed
|
||||
*/
|
||||
public static function get(string $id) {
|
||||
return self::getInstance()->get($id);
|
||||
}
|
||||
|
||||
private static function createInstance()
|
||||
{
|
||||
self::$instance = new Container();
|
||||
self::$instance->delegate(new ReflectionContainer(true));
|
||||
}
|
||||
}
|
13
src/php/Environment/DiscordEnvironment.php
Normal file
13
src/php/Environment/DiscordEnvironment.php
Normal file
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GamesShop\Environment;
|
||||
|
||||
final readonly class DiscordEnvironment
|
||||
{
|
||||
public function __construct(
|
||||
public string $clientId,
|
||||
public string $clientSecret,
|
||||
public string $loginUrl,
|
||||
) {}
|
||||
}
|
28
src/php/Environment/EnvironmentHandler.php
Normal file
28
src/php/Environment/EnvironmentHandler.php
Normal file
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GamesShop\Environment;
|
||||
|
||||
use DotenvVault\DotenvVault;
|
||||
use GamesShop\Paths;
|
||||
|
||||
final class EnvironmentHandler
|
||||
{
|
||||
private const string ENVIRONMENT_PATH = Paths::ROOT_PATH . '/config';
|
||||
|
||||
public function load() {
|
||||
$dotEnv = DotenvVault::createImmutable(
|
||||
self::ENVIRONMENT_PATH
|
||||
);
|
||||
|
||||
$dotEnv->safeLoad();
|
||||
}
|
||||
|
||||
public function getDiscordEnvironment(): DiscordEnvironment {
|
||||
return new DiscordEnvironment(
|
||||
$_SERVER['DISCORD_CLIENT_ID'],
|
||||
$_SERVER['DISCORD_CLIENT_SECRET'],
|
||||
$_SERVER['DISCORD_CLIENT_LOGIN_URI'],
|
||||
);
|
||||
}
|
||||
}
|
17
src/php/Login/LoginHandler.php
Normal file
17
src/php/Login/LoginHandler.php
Normal file
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GamesShop\Login;
|
||||
|
||||
final class LoginHandler
|
||||
{
|
||||
public function isLoggedIn(): bool {
|
||||
if (session_status() !== PHP_SESSION_ACTIVE) {
|
||||
session_start();
|
||||
}
|
||||
|
||||
return isset($_SESSION['accountid']);
|
||||
}
|
||||
|
||||
|
||||
}
|
11
src/php/Paths.php
Normal file
11
src/php/Paths.php
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GamesShop;
|
||||
|
||||
final class Paths
|
||||
{
|
||||
public const string ROOT_PATH = __DIR__ . '/../..';
|
||||
public const string PUBLIC_PATH = self::ROOT_PATH . '/public';
|
||||
public const string SOURCE_PATH = self::ROOT_PATH . '/src';
|
||||
}
|
20
src/php/Routing/ErrorRoute.php
Normal file
20
src/php/Routing/ErrorRoute.php
Normal file
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GamesShop\Routing;
|
||||
|
||||
use GamesShop\ContainerHandler;
|
||||
use GamesShop\Templates\TemplateEngine;
|
||||
use Laminas\Diactoros\Response;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
final class ErrorRoute
|
||||
{
|
||||
public function renderErrorPage(int $errorCode): ResponseInterface {
|
||||
$pageContent = ContainerHandler::get(TemplateEngine::class)->renderPage('error', [ 'errorCode' => $errorCode ]);
|
||||
|
||||
$response = new Response;
|
||||
$response->getBody()->write($pageContent);
|
||||
return $response;
|
||||
}
|
||||
}
|
33
src/php/Routing/IndexRoute.php
Normal file
33
src/php/Routing/IndexRoute.php
Normal file
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GamesShop\Routing;
|
||||
|
||||
use GamesShop\ContainerHandler;
|
||||
use GamesShop\Login\LoginHandler;
|
||||
use GamesShop\Templates\TemplateEngine;
|
||||
use Laminas\Diactoros\Response;
|
||||
use Laminas\Diactoros\Response\RedirectResponse;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
final class IndexRoute
|
||||
{
|
||||
public function __invoke(ServerRequestInterface $request): ResponseInterface {
|
||||
$loginHandler = ContainerHandler::get(LoginHandler::class);
|
||||
|
||||
if (!$loginHandler->isLoggedIn()) {
|
||||
return new RedirectResponse('/login');
|
||||
}
|
||||
|
||||
$pageContent = ContainerHandler::get(TemplateEngine::class)->renderPage('index');
|
||||
|
||||
$response = new Response;
|
||||
$response->getBody()->write($pageContent);
|
||||
return $response;
|
||||
}
|
||||
|
||||
public static function applyRoutes(\League\Route\Router $router): void {
|
||||
$router->get('/', self::class);
|
||||
}
|
||||
}
|
33
src/php/Routing/LoginRoutes.php
Normal file
33
src/php/Routing/LoginRoutes.php
Normal file
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GamesShop\Routing;
|
||||
|
||||
use GamesShop\ContainerHandler;
|
||||
use GamesShop\Environment\EnvironmentHandler;
|
||||
use GamesShop\Templates\TemplateEngine;
|
||||
use Laminas\Diactoros\Response;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
final class LoginRoutes
|
||||
{
|
||||
public function login(ServerRequestInterface $request) {
|
||||
$discordEnv = ContainerHandler::get(EnvironmentHandler::class)->getDiscordEnvironment();
|
||||
$pageContent = ContainerHandler::get(TemplateEngine::class)->renderPage(
|
||||
'login',
|
||||
[
|
||||
'discordUrl' => $discordEnv->loginUrl
|
||||
]
|
||||
);
|
||||
|
||||
$response = new Response;
|
||||
$response->getBody()->write($pageContent);
|
||||
return $response;
|
||||
}
|
||||
|
||||
public static function addRoutes(\League\Route\Router $router): void {
|
||||
$routes = ContainerHandler::get(LoginRoutes::class);
|
||||
|
||||
$router->get('/login', $routes->login(...));
|
||||
}
|
||||
}
|
51
src/php/Routing/ResourceRoute.php
Normal file
51
src/php/Routing/ResourceRoute.php
Normal file
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GamesShop\Routing;
|
||||
|
||||
use GamesShop\ContainerHandler;
|
||||
use GamesShop\Paths;
|
||||
use Laminas\Diactoros\Response;
|
||||
use Mimey\MimeTypes;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
final class ResourceRoute
|
||||
{
|
||||
private const array RESOURCE_EXTENSIONS = [
|
||||
'js',
|
||||
'css',
|
||||
'ttf', 'woff', 'woff2',
|
||||
'gif', 'svg', 'png', 'jpg'
|
||||
];
|
||||
|
||||
public function __invoke(ServerRequestInterface $request, array $args): ResponseInterface {
|
||||
|
||||
$filePath = Paths::PUBLIC_PATH . $request->getUri()->getPath();
|
||||
|
||||
if (!file_exists($filePath)) {
|
||||
$response = new Response(status: 404);
|
||||
$response->getBody()->write('File not found');
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
$mimey = ContainerHandler::get(MimeTypes::class);
|
||||
$response = new Response(
|
||||
headers: [
|
||||
'Content-Type' => $mimey->getMimeType(pathinfo($filePath, PATHINFO_EXTENSION)),
|
||||
'Cache-Control' => 'public, max-age=3600, must-revalidate',
|
||||
]
|
||||
);
|
||||
$response->getBody()->write(file_get_contents($filePath));
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
public static function addRouteEntry(\League\Route\Router $router): void {
|
||||
$joinedResourceExtensions = implode('|', self::RESOURCE_EXTENSIONS);
|
||||
$router->addPatternMatcher('resource', ".+[{$joinedResourceExtensions}]");
|
||||
|
||||
$router->get('/{resource:resource}', self::class);
|
||||
}
|
||||
}
|
37
src/php/Routing/Router.php
Normal file
37
src/php/Routing/Router.php
Normal file
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GamesShop\Routing;
|
||||
|
||||
use GamesShop\ContainerHandler;
|
||||
use GamesShop\Login\LoginHandler;
|
||||
use GamesShop\Templates\TemplateEngine;
|
||||
use Laminas\Diactoros\Response;
|
||||
use Laminas\Diactoros\ServerRequestFactory;
|
||||
use League\Container\Container;
|
||||
use League\Route\Strategy\ApplicationStrategy;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
|
||||
final class Router
|
||||
{
|
||||
public function route()
|
||||
{
|
||||
$request = ServerRequestFactory::fromGlobals(
|
||||
$_SERVER, $_GET, $_POST, $_COOKIE, $_FILES
|
||||
);
|
||||
$router = new \League\Route\Router;
|
||||
$strategy = (new ApplicationStrategy)->setContainer(ContainerHandler::getInstance());
|
||||
$router->setStrategy($strategy);
|
||||
|
||||
IndexRoute::applyRoutes($router);
|
||||
LoginRoutes::addRoutes($router);
|
||||
ResourceRoute::addRouteEntry($router);
|
||||
|
||||
try {
|
||||
return $router->dispatch($request);
|
||||
} catch (\League\Route\Http\Exception\NotFoundException $e) {
|
||||
return (new ErrorRoute())->renderErrorPage(404);
|
||||
}
|
||||
}
|
||||
}
|
16
src/php/Templates/ResourceEntry.php
Normal file
16
src/php/Templates/ResourceEntry.php
Normal file
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GamesShop\Templates;
|
||||
|
||||
final class ResourceEntry
|
||||
{
|
||||
/**
|
||||
* @param string[] $js
|
||||
* @param string[] $css
|
||||
*/
|
||||
public function __construct(
|
||||
public readonly array $js,
|
||||
public readonly array $css,
|
||||
) { }
|
||||
}
|
51
src/php/Templates/ResourceIndex.php
Normal file
51
src/php/Templates/ResourceIndex.php
Normal file
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GamesShop\Templates;
|
||||
|
||||
use Exception;
|
||||
use GamesShop\Paths;
|
||||
|
||||
final class ResourceIndex
|
||||
{
|
||||
private const string PATH = Paths::SOURCE_PATH . '/file-index.json';
|
||||
|
||||
/**
|
||||
* @var ResourceEntry[]
|
||||
*/
|
||||
private array $resources = [];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$fileContents = file_get_contents(self::PATH);
|
||||
$index = json_decode($fileContents, true);
|
||||
|
||||
foreach ($index as $entryKey => $resource) {
|
||||
$js = $resource['js'];
|
||||
if (is_string($js)) {
|
||||
$js = [$js];
|
||||
}
|
||||
|
||||
$css = $resource['css'];
|
||||
if (is_string($css)) {
|
||||
$css = [$css];
|
||||
}
|
||||
|
||||
$this->resources[$entryKey] = new ResourceEntry(
|
||||
$js, $css
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public function getResource(string $entry): ResourceEntry
|
||||
{
|
||||
if (!array_key_exists($entry, $this->resources)) {
|
||||
throw new Exception("Entry '$entry' not found");
|
||||
}
|
||||
|
||||
return $this->resources[$entry];
|
||||
}
|
||||
}
|
27
src/php/Templates/TemplateEngine.php
Normal file
27
src/php/Templates/TemplateEngine.php
Normal file
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace GamesShop\Templates;
|
||||
|
||||
use GamesShop\Paths;
|
||||
use League\Plates\Engine;
|
||||
|
||||
final class TemplateEngine extends Engine
|
||||
{
|
||||
private const string TEMPLATES_PATH = Paths::SOURCE_PATH . '/templates';
|
||||
|
||||
public function __construct(
|
||||
private ResourceIndex $resourceIndex,
|
||||
)
|
||||
{
|
||||
parent::__construct(self::TEMPLATES_PATH, 'php');
|
||||
$this->addData([
|
||||
'resources' => $this->resourceIndex,
|
||||
]);
|
||||
}
|
||||
|
||||
public function renderPage(string $page, array $data = array())
|
||||
{
|
||||
return parent::render("pages/$page", $data);
|
||||
}
|
||||
}
|
22
src/php/index.dev.php
Normal file
22
src/php/index.dev.php
Normal file
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
use GamesShop\ContainerHandler;
|
||||
use GamesShop\Environment\EnvironmentHandler;
|
||||
use GamesShop\Routing\Router;
|
||||
use Laminas\HttpHandlerRunner\Emitter\SapiEmitter;
|
||||
use Whoops\Handler\PrettyPageHandler;
|
||||
use Whoops\Run;
|
||||
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
ContainerHandler::get(EnvironmentHandler::class)->load();
|
||||
|
||||
$whoops = new Run();
|
||||
$whoops->pushHandler(new PrettyPageHandler);
|
||||
$whoops->register();
|
||||
|
||||
$router = ContainerHandler::getInstance()->get(Router::class);
|
||||
$result = $router->route();
|
||||
|
||||
(new SapiEmitter)->emit($result);
|
2
src/php/index.prod.php
Normal file
2
src/php/index.prod.php
Normal file
|
@ -0,0 +1,2 @@
|
|||
<?php
|
||||
declare(strict_types=1);
|
Loading…
Add table
Add a link
Reference in a new issue