From 00c33b2a3dc7b4657821e47e4f448886e8eff7a2 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Thu, 8 Jan 2026 18:21:23 +0100 Subject: [PATCH 1/5] added custom latte-lint --- .gitattributes | 1 + latte-lint | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) create mode 100755 latte-lint diff --git a/.gitattributes b/.gitattributes index e1bccc4b..808b90ff 100644 --- a/.gitattributes +++ b/.gitattributes @@ -7,3 +7,4 @@ tests/ export-ignore *.php* diff=php *.sh text eol=lf +latte-lint text eol=lf diff --git a/latte-lint b/latte-lint new file mode 100755 index 00000000..622a84d3 --- /dev/null +++ b/latte-lint @@ -0,0 +1,18 @@ +#!/usr/bin/env php +bootConsoleApplication(); +$latte = $container->getByType(Nette\Bridges\ApplicationLatte\TemplateFactory::class) + ->createTemplate() + ->getLatte(); + +$latte->addExtension(new Latte\Tools\LinterExtension); + +$linter = new Latte\Tools\Linter($latte, strict: true); +$ok = $linter->scanDirectory($path); +exit($ok ? 0 : 1); \ No newline at end of file From 96747d6fd24e4c8d05f07fdced4e6e193f9be922 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Sun, 28 Dec 2025 03:02:32 +0100 Subject: [PATCH 2/5] added CLAUDE.md --- .gitattributes | 1 + CLAUDE.md | 249 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 250 insertions(+) create mode 100644 CLAUDE.md diff --git a/.gitattributes b/.gitattributes index 808b90ff..64eb7294 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,6 +1,7 @@ .gitattributes export-ignore .github/ export-ignore .gitignore export-ignore +CLAUDE.md export-ignore ncs.* export-ignore phpstan*.neon export-ignore tests/ export-ignore diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..4dfe3b5d --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,249 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +This is a **Nette 3.2 Web Project** - a PHP web application skeleton using: +- **Backend:** PHP 8.2+, Nette 3.2 framework, Latte 3.0 templating +- **Database:** Nette Database Explorer (default: SQLite in-memory) +- **Frontend:** Vite 6.3+ with TypeScript support, nette-forms for client-side validation +- **Testing:** Nette Tester for unit tests +- **Static Analysis:** PHPStan level 5 + +## Essential Commands + +### Frontend Build +```bash +# Install dependencies (first time) +npm install + +# Start Vite dev server with hot reload +npm run dev + +# Build production assets +npm run build +``` + +**Note:** Vite is optional. To activate: +1. Uncomment `type: vite` in `config/common.neon` under `assets.mapping.default` +2. Run `npm install && npm run build` + +### Backend Testing +```bash +# Run all tests +composer run tester + +# Run specific test file +vendor/bin/tester tests/path/to/file.phpt -s + +# Run tests in specific directory +vendor/bin/tester tests/directory/ -s +``` + +### Code Quality +```bash +# Run PHPStan static analysis +composer run phpstan + +# Analyze specific files +vendor/bin/phpstan analyse app/Presentation/Home/ +``` + +## Architecture + +### Application Entry Point Flow + +1. **HTTP Request** → `/www/index.php` +2. **Bootstrap** (`App\Bootstrap::bootWebApplication()`) + - Initializes environment and Tracy debugger + - Loads configuration from `config/common.neon` and `config/services.neon` + - Creates DI container +3. **Router** (`App\Core\RouterFactory::createRouter()`) + - Default route: `/[/]` → `Home:default` +4. **Presenter** processes request and renders Latte template +5. **Response** sent to client + +### Directory Structure + +``` +app/ +├── Bootstrap.php # Application initialization +├── Core/ +│ └── RouterFactory.php # URL routing configuration +└── Presentation/ # UI layer (MVC Controllers & Views) + ├── @layout.latte # Master layout template + ├── Accessory/ + │ └── LatteExtension.php # Application-wide template filters & functions + ├── Home/ + │ ├── HomePresenter.php # Presenter (Controller) + │ └── default.latte # Template (View) + └── Error/ # Error handling presenters + ├── Error4xx/ # Client errors (403, 404, 410) + └── Error5xx/ # Server errors (500, 503) +``` + +### Configuration Files + +**`config/common.neon`** - Framework configuration: +- Application settings (error presenters, presenter mapping) +- Database connection (currently SQLite in-memory) +- Latte configuration (strict types & parsing) +- Assets configuration (Vite integration) + +**`config/services.neon`** - Dependency injection: +- Manual service registration +- Auto-discovery for classes ending with: `*Facade`, `*Factory`, `*Repository`, `*Service` + +### Presenter-Template Mapping + +Convention: `App\Presentation\\Presenter` + +Examples: +- `Home:default` → `App\Presentation\Home\HomePresenter::renderDefault()` + `Home/default.latte` +- `Error:Error4xx` → `App\Presentation\Error\Error4xx\Error4xxPresenter` + +### Error Handling Architecture + +**Client Errors (4xx):** `Error:Error4xx` presenter +- Renders appropriate template based on HTTP code (403.latte, 404.latte, 410.latte, or 4xx.latte fallback) + +**Server Errors (5xx):** `Error:Error5xx` presenter +- Logs exception via Tracy +- Renders minimal error page (500.phtml, 503.phtml) + +Configuration in `config/common.neon`: +```neon +application: + errorPresenter: + 4xx: Error:Error4xx + 5xx: Error:Error5xx +``` + +### Asset Pipeline + +**Development mode:** +- Source files in `assets/` directory +- Vite dev server provides hot module replacement +- Entry point: `assets/main.js` (initializes Nette Forms) + +**Production mode:** +- Built assets in `www/assets/` with versioned filenames +- Manifest file for asset resolution +- Load in templates: `{asset 'main.js'}` + +**Vite configuration** (`vite.config.ts`): +- Entry point: `main.js` +- CSS source maps enabled for development +- `emptyOutDir: true` clears output before build + +### Service Auto-Discovery + +The DI container automatically registers classes matching these patterns: +- `*Facade` - Application facades +- `*Factory` - Factory classes +- `*Repository` - Data repositories +- `*Service` - Service classes + +Located anywhere under `app/` directory (configured in `config/services.neon`). + +### Latte Template System + +**Master Layout:** `app/Presentation/@layout.latte` +- Shared structure for all pages +- Includes Tracy debugger bar in debug mode + +**Template Extension:** `App\Presentation\Accessory\LatteExtension` +- Application-wide custom filters and functions +- Registered in `config/common.neon` + +**Strict Mode:** Enabled via `latte.strictParsing` +- All variables must be explicitly defined +- Type safety in templates + +## Development Notes + +### Adding New Presenters + +1. Create presenter class: `app/Presentation//Presenter.php` +2. Create template: `app/Presentation//.latte` +3. Services are auto-registered if following naming conventions + +### Database Configuration + +Default is SQLite in-memory for quick prototyping. To use persistent database: + +```neon +# config/common.neon +database: + dsn: 'mysql:host=127.0.0.1;dbname=mydb' + user: username + password: password +``` + +### Testing Conventions + +- Test files use `.phpt` extension +- Bootstrap: `tests/bootstrap.php` +- Nette Tester documentation: https://tester.nette.org/ + +### Security + +- Directories `app/`, `config/`, `log/`, `temp/` must NOT be web-accessible +- `.htaccess` blocks access to hidden files and sensitive paths +- Tracy debugger should be disabled in production + +### PHPStan Configuration + +**Current level:** 5 (strict analysis) +**Analyzed paths:** `src`, `bin`, `tests` + +Note: `phpstan.neon` references `src/` directory but project uses `app/`. This may need correction if adding actual source code to analyze. + +### Adding Nette Packages + +To add additional Nette packages to the project: + +```bash +# Find the package you need at https://nette.org/packages +# Install using Composer +composer require nette/package-name + +# Example: Adding Nette Utils +composer require nette/utils +``` + +## NEON Configuration Reference + +Configuration files use NEON format. Available configuration sections in `config/common.neon`: + +- **`application`** - Application settings (error presenters, mapping, routing) +- **`assets`** - Asset management and Vite integration +- **`constants`** - Definition of PHP constants +- **`database`** - Database connection configuration +- **`decorator`** - Service decoration rules +- **`di`** - DI Container settings +- **`extensions`** - Installation of additional DI extensions +- **`forms`** - Forms configuration +- **`http`** - HTTP headers configuration +- **`includes`** - Including additional configuration files +- **`latte`** - Latte template engine settings +- **`mail`** - Mailing configuration +- **`parameters`** - Custom parameters accessible via `%paramName%` +- **`php`** - PHP configuration options (date.timezone, etc.) +- **`routing`** - Alternative routing configuration +- **`search`** - Automatic service registration patterns +- **`security`** - Access control and authentication +- **`services`** - Manual service definitions +- **`session`** - Session configuration +- **`tracy`** - Tracy debugger settings + +### NEON Syntax Notes + +**Escaping percent sign:** To write a string containing `%`, escape it by doubling: + +```neon +parameters: + discount: '15%%' # Results in "15%" + url: 'https://example.com?discount=50%%' +``` From 296493630c12c1484183bc00af5a0a8596175a45 Mon Sep 17 00:00:00 2001 From: David Grudl Date: Fri, 20 Feb 2026 05:54:50 +0100 Subject: [PATCH 3/5] uses nette/phpstan-rules --- composer.json | 7 +++++-- phpstan.neon | 8 ++------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/composer.json b/composer.json index 031e0caa..bf278fa0 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,9 @@ }, "require-dev": { "nette/tester": "^2.5", - "phpstan/phpstan-nette": "^2", + "phpstan/phpstan": "^2.1", + "phpstan/extension-installer": "^1.4", + "nette/phpstan-rules": "^1.0", "symfony/thanks": "^1" }, "autoload": { @@ -38,7 +40,8 @@ "minimum-stability": "stable", "config": { "allow-plugins": { - "symfony/thanks": true + "symfony/thanks": true, + "phpstan/extension-installer": true } } } diff --git a/phpstan.neon b/phpstan.neon index 6a3b774e..10d4731f 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,10 +1,6 @@ parameters: - level: 5 + level: 8 paths: - - src + - app - bin - - tests - -includes: - - vendor/phpstan/phpstan-nette/extension.neon From 9341da725668b8f4a60723d28d0942d1772e27ad Mon Sep 17 00:00:00 2001 From: David Grudl Date: Mon, 23 Feb 2026 02:22:38 +0100 Subject: [PATCH 4/5] singleline declare statements --- app/Bootstrap.php | 4 +--- app/Core/RouterFactory.php | 4 +--- app/Presentation/Accessory/LatteExtension.php | 4 +--- app/Presentation/Error/Error4xx/Error4xxPresenter.php | 4 +--- app/Presentation/Error/Error5xx/503.phtml | 4 +--- app/Presentation/Error/Error5xx/Error5xxPresenter.php | 4 +--- app/Presentation/Home/HomePresenter.php | 4 +--- latte-lint | 2 +- tests/bootstrap.php | 4 +--- www/index.php | 4 +--- 10 files changed, 10 insertions(+), 28 deletions(-) diff --git a/app/Bootstrap.php b/app/Bootstrap.php index fbf1498e..1af33e08 100644 --- a/app/Bootstrap.php +++ b/app/Bootstrap.php @@ -1,6 +1,4 @@ - Date: Thu, 2 Apr 2026 16:51:43 +0700 Subject: [PATCH 5/5] Layout: added css file --- app/Presentation/@layout.latte | 1 + www/assets/style.css | 88 ++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 www/assets/style.css diff --git a/app/Presentation/@layout.latte b/app/Presentation/@layout.latte index 659cd69e..35065895 100644 --- a/app/Presentation/@layout.latte +++ b/app/Presentation/@layout.latte @@ -7,6 +7,7 @@ {ifset title}{include title|stripHtml} | {/ifset}Nette Web {asset? 'main.js'} + {asset? 'style.css'} diff --git a/www/assets/style.css b/www/assets/style.css new file mode 100644 index 00000000..fc4d08e3 --- /dev/null +++ b/www/assets/style.css @@ -0,0 +1,88 @@ +body { + font: 16px/1.5 Georgia, Verdana, Arial; + margin: 0 auto; + width: 600px; + color: #333; + background-color: #fff; +} + +div.flash { + color: black; + background: #FFF9D7; + border: 1px solid #E2C822; + padding: 1em; + margin: 1em 0; +} + +a[href^="error:"] { + background: red; + color: white; +} + +form tr, form td { + vertical-align: top; + font-weight: normal; +} + +form th { + text-align: right; +} + +form .required label { + font-weight: bold; +} + +form .error { + color: #D00; + font-weight: bold; +} + +h1, h2 { + color: #3484D2; + font-size: 1.9em; + text-align: center; + font-weight: normal; +} + +h1 a { + color: #3484D2; +} + +h2 { + font-size: 1.4em; +} + +.date { + color: #999; + text-align: center; + font-variant: small-caps; + font-size: .8em; + letter-spacing: 3px; + margin: 0 0 -1em; +} + +.post { + background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAPEAAAAUCAMAAABrnCsKAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQ5QTFRFr66tsK+usLCusbCvtLOytbSzubi2ubm3urm3urq4vLu6vb27v769wL+9wL++w8LAxMPBxcTCxcXDxsbEx8bEx8fFyMfFyMjGysnIzMvLzMzLzMzMzc3Lzs7Mz87Mz8/N0M/N0tHQ09PS1NPR1NTS1NTT1dTS1dXT1tXU1tbU19bU19bV19fV2NfV2NfW2djW2dnW2dnX2dnY2tnX2trX29rY29vY29vZ3NzZ3dza3t3b3t7c39/d4N/d4ODd4ODf4eDe4eHh4uLg4+Lg4+Lh4+Pg4+Ph5OPh5OTi5eXj5eXk5ubj5+bm6Ofl6enm6unn6+ro8vHx9fT09fX19vX19/f3+Pj3+vr6/f39////uLBi1QAAAM5JREFUWMNjiBxpgGHUx6M+HvXxqI/JAGKRIi6RgiPHx5qRIgpmGoKikSFhI8THwkIy/v5O3iq8PCMjjsO9IgUCnK1t3Fy5R0qqFuA38LS0MDd3V+NkjxgoH0vTFKDbKOhvY2FuYmBlI4sqLk0XQP84VuTR8rMyMzbQtXBSYA0fGSWXkbmdob6uvpO8o33wiPCxD4eyr62psYM6B/MIKbmUIvX49N08dNhCJaVGSpuLJYiLU06VQWLEtLkCwyMZI5m0I8VHexKjPh71MZUAAOANSHaOv55SAAAAAElFTkSuQmCC) no-repeat center bottom; + padding-bottom:2em; + margin-bottom:2em; +} + +.comment { + padding-bottom: 2em; +} + +.comments { + font-size: .9em; +} + +.navig { + margin: 0 0 1em; + padding: 0; + text-align: right; +} + +.navig li { + display: inline; + margin: 0 1em 0 0; + padding: 0; +} \ No newline at end of file