bare-site/README.md

6.7 KiB
Raw Blame History

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:

# 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):

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

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

# 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 (400504)
└── 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.