From 2929d3b9bc40f035a7d8e9ca5ee266952746594b Mon Sep 17 00:00:00 2001 From: cclohmar Date: Thu, 25 Jun 2026 14:14:52 +0000 Subject: [PATCH] docs: add README with origin story, philosophy, and full setup guide --- README.md | 183 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 183 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..65a7552 --- /dev/null +++ b/README.md @@ -0,0 +1,183 @@ +# bare-site + +**A zero-dependency PHP micro-framework for content websites. No database. No Composer. No build step. Just files on disk.** + +--- + +## The Story + +This framework wasn't built by a developer. It was built by a solutions architect who spent 25 years watching the industry overcomplicate simple things. + +In the early 2000s, I was a webmaster. PHP files with inline HTML. It worked. Then the industry decided that every website needed a CMS, a database, an ORM, a build pipeline, and 847 npm dependencies. I watched startups spend $500/month on cloud infrastructure for a 5-page brochure site. I watched enterprise sales demos fail because "composer install" hung during the client meeting. + +So I kept doing what worked: a single `index.php` that routes URLs, language files that *are* the content store, and an nginx config that blocks everything except the entry point. For 8 years, this pattern has powered: + +- **lohmar.co.uk** — a multi-language company website (7 languages, served from Forgejo) +- **A guesthouse booking site** — with e-commerce via Beds24 iframe integration +- **Enterprise IDV demos** — for Jumio, Keyless Go, and Jaguar Land Rover +- **Multi-brand landing pages** — where the language routing system was repurposed to switch CSS per brand + +When JLR's legal team discovered this little framework predated our engagement by years, they were not happy. Previous art. It wasn't theirs. + +The philosophy is simple: **you don't need a hammer drill to punch a hole in cardboard. Most websites are cardboard.** Use the right tool. + +--- + +## What It Is + +| Feature | Implementation | +|---------|---------------| +| **Router** | 80-line array-driven URL mapper in `start.php` | +| **Templates** | Raw `.phtml` files — HTML with optional inline PHP | +| **Languages** | File-based: `lng/{lang}/menu.php` + `.phtml` per page | +| **Security** | nginx isolates PHP — only `index.php` executes | +| **Bot detection** | 30+ UA patterns + browser heuristic bypass | +| **Rate limiting** | CSV-based velocity tracking, blocks at 5 req/s | +| **Logging** | Structured JSON, weekly rotation (`errors_2026-W26.log`) | +| **Sessions** | httponly, SameSite=Lax, strict_mode, UUIDv4 correlation IDs | +| **Optional** | Geo-location (ipgeolocation.io), Telegram visitor alerts | + +## What It's NOT + +- A WordPress alternative (no admin panel, no users, no plugins) +- A headless CMS (no content API, no editing interface) +- A Laravel competitor (no ORM, no migrations, no queues, no Artisan) +- A general-purpose framework (no database abstraction, no routing DSL, no middleware) + +If you need any of those things, this is the wrong tool. If you have a 5-20 page website that needs to exist in multiple languages and never break, this is exactly the right tool. + +--- + +## Quick Start + +### ⚠️ Step 1: Configure your web server (NOT optional) + +bare-site's entire security model depends on the web server blocking direct access to PHP files. The framework *will not work* without these rules. + +**nginx (recommended)** — copy `nginx/default.conf`: +```nginx +# Only index.php can execute PHP +location = /index.php { + include fastcgi_params; + fastcgi_pass unix:/run/php/php8.4-fpm.sock; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; +} +location ~ \.php$ { return 404; } # ← blocks config.php, functions.php, etc. + +# Everything routes through index.php +location / { try_files $uri $uri/ /index.php?$query_string; } +``` + +**Apache (.htaccess)**: +```apache +RewriteEngine On +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d +RewriteRule ^(.*)$ index.php [QSA,L] +RewriteCond %{REQUEST_URI} !^/index\.php$ +RewriteRule \.php$ - [R=404,L] +``` + +**Caddy**: +``` +your-domain.com { + root * /var/www/html + php_fastcgi unix//run/php/php8.4-fpm.sock + file_server + @blockedPhp path_regexp /(?!index\.php$).*\.php$ + respond @blockedPhp 404 +} +``` + +### Step 2: Drop the files + +```bash +git clone https://git.lohmar.co.uk/cclohmar/bare-site.git /var/www/html/ +``` + +### Step 3: Edit your config + +Open `html/class/config.php` and set: +- `SITE_NAME` — your site title +- `SITE_DESCRIPTION` — meta description +- Page slugs (`PAGE_00_SLUG` through `PAGE_05_SLUG`) +- Supported languages in `$languages` array +- Optional: `GEO_KEY` (ipgeolocation.io), `TELEGRAM_BOT_TOKEN` + `TELEGRAM_CHAT_ID` + +### Step 4: Create your pages + +``` +lng/en/ +├── menu.php # navigation labels + error messages +├── home.phtml # your content — raw HTML, no template engine +├── about.phtml # another page +└── ... +``` + +Content files are plain HTML inside a `
`. Use inline PHP if needed: ``. + +### Step 5: Add a language + +```bash +# 1. Add to config.php: +$languages = ['en' => 'English', 'de' => 'Deutsch']; + +# 2. Create the directory and copy files: +mkdir lng/de/ && cp lng/en/* lng/de/ + +# 3. Translate menu.php + .phtml files + +# Done — appears in the nav dropdown automatically. +``` + +--- + +## Architecture + +``` +html/ +├── index.php # Entry point: session → route +├── class/ +│ ├── config.php # Site constants, env detection, API keys, security +│ ├── functions.php # Logging, bot detection, geo-location, rate limiting +│ └── site.php # Session bootstrap, language detection +├── app/ +│ ├── controler/ +│ │ ├── start.php # URL router (80 lines, 6 slugs, sub-page support) +│ │ └── request.php # POST/GET form handler +│ ├── model/menu.php # Dynamic nav builder from language constants +│ └── view/ +│ ├── _header.phtml # HTML head, navbar, Bootstrap CDN +│ ├── _footer.phtml # Footer, JS, debug block +│ └── _error.phtml # HTTP error page renderer (400–504) +└── lng/{en,de,fr,...}/ + ├── menu.php # Language-specific text constants + └── {slug}.phtml # Page content templates +``` + +--- + +## Optional Features + +| Feature | How to enable | +|---------|--------------| +| **Geo-location** | Set `GEO_KEY` in config.php (free at ipgeolocation.io) | +| **Telegram alerts** | Set `TELEGRAM_BOT_TOKEN` + `TELEGRAM_CHAT_ID` | +| **Weekly logs** | Auto-rotated: `logs/errors_2026-W26.log` | +| **Article sub-pages** | Create `lng/en/{slug}/{article}.phtml` — routed at `/{slug}/{article}` | +| **Multi-language** | Add to `$languages` array, create directory, translate | + +--- + +## Requirements + +- PHP 8.1+ (uses `FILTER_SANITIZE_FULL_SPECIAL_CHARS`, named arguments not required) +- nginx, Apache, or Caddy with the isolation rules above +- No database +- No Composer +- No npm +- No build step + +## License + +MIT — use it, fork it, ship it. If this saves you from deploying WordPress for a 5-page site, we've already won.