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