docs: add README with origin story, philosophy, and full setup guide
This commit is contained in:
parent
b93de83519
commit
2929d3b9bc
1 changed files with 183 additions and 0 deletions
183
README.md
Normal file
183
README.md
Normal file
|
|
@ -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 `<section>`. Use inline PHP if needed: `<?php echo date('Y'); ?>`.
|
||||||
|
|
||||||
|
### 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.
|
||||||
Loading…
Reference in a new issue