183 lines
6.7 KiB
Markdown
183 lines
6.7 KiB
Markdown
# 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.
|