From 44325cd74ce19f63f2587e2d9bc8cd900c474bcf Mon Sep 17 00:00:00 2001 From: Yisroel Baum Date: Sat, 25 Oct 2025 22:48:56 +0300 Subject: [PATCH 01/10] add DI Container to app instance --- public/index.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/public/index.php b/public/index.php index 9b2023a..de58be6 100644 --- a/public/index.php +++ b/public/index.php @@ -1,12 +1,17 @@ addErrorMiddleware(true, false, false); $app->get('/', function (Request $request, Response $response, $args) { $response->getBody()->write("Hello world!"); From 2383fd9d93a3e8937fa1b669bc6594951dbb7375 Mon Sep 17 00:00:00 2001 From: Yisroel Baum Date: Sat, 25 Oct 2025 22:51:34 +0300 Subject: [PATCH 02/10] add twig --- composer.json | 3 +- composer.lock | 461 ++++++++++++++++++++++++++++++++++++++++++++++- public/index.php | 5 + 3 files changed, 467 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index c7a2e76..1c09c25 100644 --- a/composer.json +++ b/composer.json @@ -24,6 +24,7 @@ "require": { "slim/slim": "4.*", "slim/psr7": "^1.7", - "php-di/php-di": "^7.1" + "php-di/php-di": "^7.1", + "slim/twig-view": "^3.4" } } diff --git a/composer.lock b/composer.lock index 8d98dea..93a9a35 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "23c8694ee036d4ff156175bf8fa3ad34", + "content-hash": "1d5fe2a473d3977745130ee120c50235", "packages": [ { "name": "fig/http-message-util", @@ -867,6 +867,306 @@ ], "time": "2025-08-20T18:16:16+00:00" }, + { + "name": "slim/twig-view", + "version": "3.4.1", + "source": { + "type": "git", + "url": "https://github.com/slimphp/Twig-View.git", + "reference": "b4268d87d0e327feba5f88d32031e9123655b909" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/slimphp/Twig-View/zipball/b4268d87d0e327feba5f88d32031e9123655b909", + "reference": "b4268d87d0e327feba5f88d32031e9123655b909", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0", + "psr/http-message": "^1.1 || ^2.0", + "slim/slim": "^4.12", + "symfony/polyfill-php81": "^1.29", + "twig/twig": "^3.11" + }, + "require-dev": { + "phpspec/prophecy-phpunit": "^2.0", + "phpstan/phpstan": "^1.10.59", + "phpunit/phpunit": "^9.6 || ^10", + "psr/http-factory": "^1.0", + "squizlabs/php_codesniffer": "^3.9" + }, + "type": "library", + "autoload": { + "psr-4": { + "Slim\\Views\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Josh Lockhart", + "email": "hello@joshlockhart.com", + "homepage": "http://joshlockhart.com" + }, + { + "name": "Pierre Berube", + "email": "pierre@lgse.com", + "homepage": "http://www.lgse.com" + } + ], + "description": "Slim Framework 4 view helper built on top of the Twig 3 templating component", + "homepage": "https://www.slimframework.com", + "keywords": [ + "framework", + "slim", + "template", + "twig", + "view" + ], + "support": { + "issues": "https://github.com/slimphp/Twig-View/issues", + "source": "https://github.com/slimphp/Twig-View/tree/3.4.1" + }, + "time": "2024-09-26T05:42:02+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:21:43+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", + "shasum": "" + }, + "require": { + "ext-iconv": "*", + "php": ">=7.2" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-12-23T08:48:59+00:00" + }, { "name": "symfony/polyfill-php80", "version": "v1.33.0", @@ -950,6 +1250,165 @@ } ], "time": "2025-01-02T08:10:11+00:00" + }, + { + "name": "symfony/polyfill-php81", + "version": "v1.33.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php81.git", + "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", + "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", + "shasum": "" + }, + "require": { + "php": ">=7.2" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php81\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php81/tree/v1.33.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-09T11:45:10+00:00" + }, + { + "name": "twig/twig", + "version": "v3.21.1", + "source": { + "type": "git", + "url": "https://github.com/twigphp/Twig.git", + "reference": "285123877d4dd97dd7c11842ac5fb7e86e60d81d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/285123877d4dd97dd7c11842ac5fb7e86e60d81d", + "reference": "285123877d4dd97dd7c11842ac5fb7e86e60d81d", + "shasum": "" + }, + "require": { + "php": ">=8.1.0", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-mbstring": "^1.3" + }, + "require-dev": { + "phpstan/phpstan": "^2.0", + "psr/container": "^1.0|^2.0", + "symfony/phpunit-bridge": "^5.4.9|^6.4|^7.0" + }, + "type": "library", + "autoload": { + "files": [ + "src/Resources/core.php", + "src/Resources/debug.php", + "src/Resources/escaper.php", + "src/Resources/string_loader.php" + ], + "psr-4": { + "Twig\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Twig Team", + "role": "Contributors" + }, + { + "name": "Armin Ronacher", + "email": "armin.ronacher@active-4.com", + "role": "Project Founder" + } + ], + "description": "Twig, the flexible, fast, and secure template language for PHP", + "homepage": "https://twig.symfony.com", + "keywords": [ + "templating" + ], + "support": { + "issues": "https://github.com/twigphp/Twig/issues", + "source": "https://github.com/twigphp/Twig/tree/v3.21.1" + }, + "funding": [ + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/twig/twig", + "type": "tidelift" + } + ], + "time": "2025-05-03T07:21:55+00:00" } ], "packages-dev": [ diff --git a/public/index.php b/public/index.php index de58be6..58b2f61 100644 --- a/public/index.php +++ b/public/index.php @@ -4,6 +4,8 @@ use DI\Container; use Psr\Http\Message\ResponseInterface as Response; use Psr\Http\Message\RequestInterface as Request; use Slim\Factory\AppFactory; +use Slim\Views\Twig; +use Slim\Views\TwigMiddleware; require __DIR__ . '/../vendor/autoload.php'; @@ -11,6 +13,9 @@ $container = new Container(); $app = AppFactory::createFromContainer($container); +$twig = Twig::create(__DIR__ . '/../templates', ['cache' => false]); + +$app->add(TwigMiddleware::create($app, $twig)); $app->addErrorMiddleware(true, false, false); $app->get('/', function (Request $request, Response $response, $args) { From 5775b4b3c3fedb0b6b4c151bc21e542275b06a43 Mon Sep 17 00:00:00 2001 From: Yisroel Baum Date: Sat, 25 Oct 2025 23:06:48 +0300 Subject: [PATCH 03/10] pass requests to controller, return twig template --- public/index.php | 8 ++------ src/Controllers/UserController.php | 19 +++++++++++++++++++ templates/home.html.twig | 9 +++++++++ 3 files changed, 30 insertions(+), 6 deletions(-) create mode 100644 src/Controllers/UserController.php create mode 100644 templates/home.html.twig diff --git a/public/index.php b/public/index.php index 58b2f61..ef7d773 100644 --- a/public/index.php +++ b/public/index.php @@ -1,8 +1,7 @@ false]); $app->add(TwigMiddleware::create($app, $twig)); $app->addErrorMiddleware(true, false, false); -$app->get('/', function (Request $request, Response $response, $args) { - $response->getBody()->write("Hello world!"); - return $response; -}); +$app->get('/', [UserController::class, 'home']); $app->run(); diff --git a/src/Controllers/UserController.php b/src/Controllers/UserController.php new file mode 100644 index 0000000..740776c --- /dev/null +++ b/src/Controllers/UserController.php @@ -0,0 +1,19 @@ +render($response, 'home.html.twig', [ + 'name' => '', + ]); + } +} diff --git a/templates/home.html.twig b/templates/home.html.twig new file mode 100644 index 0000000..a028e00 --- /dev/null +++ b/templates/home.html.twig @@ -0,0 +1,9 @@ + + + + Welcome to Slim! + + +

Hello {{ name }}

+ + From 4c8b07103b434f318f6613ac8b00355cd287ea9e Mon Sep 17 00:00:00 2001 From: Yisroel Baum Date: Wed, 29 Oct 2025 22:02:03 +0200 Subject: [PATCH 04/10] add auth middleware --- src/MiddleWare/AuthMiddleware.php | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 src/MiddleWare/AuthMiddleware.php diff --git a/src/MiddleWare/AuthMiddleware.php b/src/MiddleWare/AuthMiddleware.php new file mode 100644 index 0000000..6310e49 --- /dev/null +++ b/src/MiddleWare/AuthMiddleware.php @@ -0,0 +1,27 @@ +handle($request); + } + + $uri = $request->getUri()->getPath(); + if ($uri !== '/login' && $uri !== '/logout') { + $_SESSION['intended'] = $uri; + } + $resp = new SlimResponse(302); + + return $resp->withHeader('Location', '/login'); + } +} From aa89f77fcb8190f2f54d0c97d055518e9e6d6e1d Mon Sep 17 00:00:00 2001 From: Yisroel Baum Date: Wed, 29 Oct 2025 22:02:16 +0200 Subject: [PATCH 05/10] add dashboard and login templates --- templates/dashboard.html.twig | 10 ++++++++++ templates/login.html.twig | 13 +++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 templates/dashboard.html.twig create mode 100644 templates/login.html.twig diff --git a/templates/dashboard.html.twig b/templates/dashboard.html.twig new file mode 100644 index 0000000..94b27d6 --- /dev/null +++ b/templates/dashboard.html.twig @@ -0,0 +1,10 @@ + + + + Dashboard + + +

Dashboard

+

Only visible when logged in

+ + diff --git a/templates/login.html.twig b/templates/login.html.twig new file mode 100644 index 0000000..ea2cc8c --- /dev/null +++ b/templates/login.html.twig @@ -0,0 +1,13 @@ + + + + Login + + +
+
+
+ +
+ + From 175bd3734bea09bbd749018702d7335688dcaa22 Mon Sep 17 00:00:00 2001 From: Yisroel Baum Date: Wed, 29 Oct 2025 22:06:12 +0200 Subject: [PATCH 06/10] add session, container, and routes to app --- public/index.php | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/public/index.php b/public/index.php index ef7d773..0e49d46 100644 --- a/public/index.php +++ b/public/index.php @@ -1,22 +1,38 @@ true, + 'cookie_secure' => !empty($_SERVER['HTTPS']), + 'cookie_samesite' => 'Lax', +]); + +require __DIR__ . '/../vendor/autoload.php'; use DI\Container; use DigiWill\Controllers\UserController; use Slim\Factory\AppFactory; use Slim\Views\Twig; use Slim\Views\TwigMiddleware; +use Tests\Fakes\FakeUserRepository; +use DigiWill\Repositories\UserRepository; +use DigiWill\MiddleWare\AuthMiddleware; -require __DIR__ . '/../vendor/autoload.php'; - -$container = new Container(); +$container = new Container([ + UserRepository::class => DI\create(FakeUserRepository::class), +]); $app = AppFactory::createFromContainer($container); $twig = Twig::create(__DIR__ . '/../templates', ['cache' => false]); +$auth = new AuthMiddleware(); + $app->add(TwigMiddleware::create($app, $twig)); $app->addErrorMiddleware(true, false, false); $app->get('/', [UserController::class, 'home']); +$app->get('/login', [UserController::class, 'login']); +$app->get('/logout', [UserController::class, 'logout']); +$app->post('/login', [UserController::class, 'doLogin']); +$app->get('/dashboard', [UserController::class, 'dashboard'])->add($auth); $app->run(); From 20cc34ca1e7d552f80f32a12f1e99d791afbe903 Mon Sep 17 00:00:00 2001 From: Yisroel Baum Date: Wed, 29 Oct 2025 22:07:01 +0200 Subject: [PATCH 07/10] add login, logout, dashboard methods to user controller --- src/Controllers/UserController.php | 54 ++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/src/Controllers/UserController.php b/src/Controllers/UserController.php index 740776c..440dfa7 100644 --- a/src/Controllers/UserController.php +++ b/src/Controllers/UserController.php @@ -5,15 +5,69 @@ namespace DigiWill\Controllers; use Psr\Http\Message\ResponseInterface as Response; use Psr\Http\Message\RequestInterface as Request; use Slim\Views\Twig; +use DigiWill\Repositories\UserRepository; class UserController { + public function __construct( + private UserRepository $userRepo + ) {} + public function home(Request $request, Response $response): Response { + $this->userRepo->find(1); $view = Twig::fromRequest($request); return $view->render($response, 'home.html.twig', [ 'name' => '', ]); } + + public function login(Request $request, Response $response): Response + { + $view = Twig::fromRequest($request); + + return $view->render($response, 'login.html.twig', [ + 'name' => '', + ]); + } + + public function doLogin(Request $request, Response $response): Response + { + $_SESSION['user_id'] = 1; + $_SESSION['user'] = ['id' => 1, 'email' => 'email@email.com']; + + return $response->withHeader('Location', '/dashboard')->withStatus(302); + } + + public function dashboard(Request $request, Response $response): Response + { + $view = Twig::fromRequest($request); + + return $view->render($response, 'dashboard.html.twig', [ + 'name' => '', + ]); + } + + public function logout(Request $request, Response $response): Response + { + $_SESSION = []; + if (ini_get('session.use_cookies')) { + $params = session_get_cookie_params(); + setcookie( + session_name(), + '', + time() - 42000, + $params['path'], + $params['domain'], + $params['secure'], + $params['httponly'] + ); + } + session_destroy(); + + return $response + ->withHeader('Location', '/') + ->withStatus(302); + } } From 4ab2748498ddbe8e2f1f38e971cda29386f24087 Mon Sep 17 00:00:00 2001 From: Yisroel Baum Date: Wed, 29 Oct 2025 22:07:43 +0200 Subject: [PATCH 08/10] add id to user --- src/Domain/User.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Domain/User.php b/src/Domain/User.php index 6edda0b..2b70d3a 100644 --- a/src/Domain/User.php +++ b/src/Domain/User.php @@ -4,5 +4,12 @@ namespace DigiWill\Domain; class User { + public function __construct( + private ?int $id + ) {} + public function getId(): ?int + { + return $this->id; + } } From f20da46a3f5895338c969f7bd1fac5978d1242d9 Mon Sep 17 00:00:00 2001 From: Yisroel Baum Date: Wed, 29 Oct 2025 22:08:04 +0200 Subject: [PATCH 09/10] add a tag to login page from home --- templates/home.html.twig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/templates/home.html.twig b/templates/home.html.twig index a028e00..41a60c5 100644 --- a/templates/home.html.twig +++ b/templates/home.html.twig @@ -4,6 +4,7 @@ Welcome to Slim! -

Hello {{ name }}

+

Hello {{ name }}

+ login From 7ddb1a9cf22e1d0e019f6160693ea891b6422880 Mon Sep 17 00:00:00 2001 From: Yisroel Baum Date: Wed, 29 Oct 2025 22:08:22 +0200 Subject: [PATCH 10/10] unimplemented find methods in fake user repo --- tests/Fakes/FakeUserRepository.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/Fakes/FakeUserRepository.php b/tests/Fakes/FakeUserRepository.php index a6fd47e..58132c6 100644 --- a/tests/Fakes/FakeUserRepository.php +++ b/tests/Fakes/FakeUserRepository.php @@ -16,10 +16,12 @@ class FakeUserRepository implements UserRepository public function find(int $id): ?User { + return null; } public function findByEmail(string $email): ?User { + return null; } public function save(User $user): User