doveCMS

A blog CMS
that weighs
almost nothing.

One admin panel for many sites. Plain PHP, MySQL and GD. No framework, no Composer, no build step.

  • PHP 8.0+
  • MySQL / MariaDB
  • 0 dependencies
  • DDEV ready

Small on purpose

Everything a blog needs, nothing it doesn't. The whole thing is readable in an afternoon.

One panel, many sites

The admin lives in dovecore. Each website is a small dovesite folder with its own pages and config.

Nested categories

Categories go as deep as you like and URLs follow the tree: /travel/italy/my-post.

Custom fields

Any number of extra fields per article, plus generated or hand-written meta description and keywords.

Images, handled

Uploads are validated, re-encoded by GD, resized to large / medium / thumb and stripped of metadata.

Friendly errors

Debug off shows a clean error page and logs the details. Debug on shows the real error, file and stack trace.

Move it easily

DDEV exports the database on stop and imports it on start, so a project travels between servers as files.

How it works

The URL is split on /. The longest leading part that matches a controller wins, and the rest become parameters. The site folder is checked first, then the core.

Folders

/home/you/
  dovecore/      # shared CMS + admin panel
    core.php
    libraries/
    control/  view/  assets/
  dovesite/      # this website
    control/  view/
    storage/  database/
  public_html/    # web root
    index.php     # settings live here

Settings in public_html/index.php

const DEBUG = 'off';   // 'on' shows real errors

const DOVE_VARS = [
    'analytics_id' => 'G-XXXX',
];
// anywhere:
dove_var('analytics_id');

const DOVE_CONFIG = [
    'db' => [ 'host' => 'localhost', ... ],
    'site_name' => 'My Blog',
];

Routing

URLControllerWhere
/control/home.phpsite
/travel/italy/my-postcontrol/_content.php (category / article tree)site
/logincontrol/login.phpcore
/admin/article/12control/admin/article.php, params ['12']core

A site can override any core file (a view, a controller, a library) by using the same relative path in dovesite.

Install

Works on shared hosting. Upload three folders side by side.

  1. Upload dovecore, dovesite and public_html next to each other. Only public_html is web accessible.
  2. Create an empty MySQL database and put the credentials in DOVE_CONFIG in public_html/index.php.
  3. Make dovesite/storage and public_html/media writable by PHP.
  4. Open /install, choose a username and password. Then use /admin.

Careful by default

Logins

Five wrong passwords block the IP for three hours. Passwords use password_hash; sessions are HttpOnly and SameSite.

Requests

Every POST is CSRF-checked by the router. All SQL is prepared; output is escaped.

Content

Article HTML goes through a whitelist sanitizer. Uploads are re-encoded and scripts are blocked in media/.

doveCMS — a very small blog CMS
doveCMS

A blog CMS that weighs almost nothing.

One admin panel for many sites. Plain PHP, MySQL and GD. No framework, no Composer, no build step.

  • PHP 8.0+
  • MySQL / MariaDB
  • 0 dependencies
  • DDEV ready

Small on purpose

Everything a blog needs, nothing it doesn't. The whole thing is readable in an afternoon.

One panel, many sites

The admin lives in dovecore. Each website is a small dovesite folder with its own pages and config.

Nested categories

Categories go as deep as you like and URLs follow the tree: /travel/italy/my-post.

Custom fields

Any number of extra fields per article, plus generated or hand-written meta description and keywords.

Images, handled

Uploads are validated, re-encoded by GD, resized to large / medium / thumb and stripped of metadata.

Friendly errors

Debug off shows a clean error page and logs the details. Debug on shows the real error, file and stack trace.

Move it easily

DDEV exports the database on stop and imports it on start, so a project travels between servers as files.

How it works

The URL is split on /. The longest leading part that matches a controller wins, and the rest become parameters. The site folder is checked first, then the core.

Folders

/home/you/
  dovecore/      # shared CMS + admin panel
    core.php
    libraries/
    control/  view/  assets/
  dovesite/      # this website
    control/  view/
    storage/  database/
  public_html/    # web root
    index.php     # settings live here

Settings in public_html/index.php

const DEBUG = 'off';   // 'on' shows real errors

const DOVE_VARS = [
    'analytics_id' => 'G-XXXX',
];
// anywhere:
dove_var('analytics_id');

const DOVE_CONFIG = [
    'db' => [ 'host' => 'localhost', ... ],
    'site_name' => 'My Blog',
];

Routing

URLControllerWhere
/control/home.phpsite
/travel/italy/my-postcontrol/_content.php (category / article tree)site
/logincontrol/login.phpcore
/admin/article/12control/admin/article.php, params ['12']core

A site can override any core file (a view, a controller, a library) by using the same relative path in dovesite.

Install

Works on shared hosting. Upload three folders side by side.

  1. Upload dovecore, dovesite and public_html next to each other. Only public_html is web accessible.
  2. Create an empty MySQL database and put the credentials in DOVE_CONFIG in public_html/index.php.
  3. Make dovesite/storage and public_html/media writable by PHP.
  4. Open /install, choose a username and password. Then use /admin.

Careful by default

Logins

Five wrong passwords block the IP for three hours. Passwords use password_hash; sessions are HttpOnly and SameSite.

Requests

Every POST is CSRF-checked by the router. All SQL is prepared; output is escaped.

Content

Article HTML goes through a whitelist sanitizer. Uploads are re-encoded and scripts are blocked in media/.