MVCLM PHP
A small MVC framework in plain PHP, shipped with the application most projects rebuild from scratch. A new project starts from something that already runs.
What Is Included
All of it working on the day you clone it, not scaffolded for you to finish.
Authentication
Users and Access Levels
Admin
Security
Operations
Getting Started
Requirements
PHP 8.1 is a floor rather than a preference. The database wrapper relies on mysqli raising an exception when a call fails, which is the default from 8.1 onward. Both the routing and the deny over the source folder are .htaccess rules, so neither works without AllowOverride.
Installation
# Clone and generate the autoloader
git clone https://github.com/jaanus-saarnak/mvclm.git
cd mvclm
composer install --working-dir=www/src
# Fill in url and database credentials
cp env.example.php env.php
# Create the schema and seed the accounts
php migration.php
Point the web server's document root at www/ and sign in. The migration script is destructive and asks nothing first: it drops each of its five tables before creating them, and it rebuilds whichever database the settings file names.
composer install pulls no packages. It is there to generate the autoloader that the front controller requires on every request.
Worth Knowing Before You Build On It
The handful of things that will cost you an afternoon if nobody tells you.
A new form needs a CSRF field
The front controller refuses any POST without one, before routing, so the request never reaches a controller. A form that silently bounces with "That form has expired" is a missing hidden field, not an expired session.
A route matches the path, never the method
A state-changing route has to test for POST itself. The router will happily hand a GET to an action that deletes something.
Routes are matched in order
The profile route is registered last and accepts any single word as a username, so a route added after it is unreachable.
Demo mode is not a read-only switch
It guards five features by name. It cannot block writes in general, because signing in writes to the database. The reset and contact pages stay visible so the features can still be seen; only their submissions are refused.
Small On Purpose
Seven classes in the core. No service container, no configuration discovery, no code generation. Every request enters through one front controller and the routing table is a single readable file. Every front-end library is served from the repository at a pinned version, so no script can change under the application without a commit. The stylesheet ships built, which is why running it needs no Node.
License
MIT. The bundled third-party front-end code keeps its own terms, and everything bundled is MIT as well: jQuery, Chart.js and DataTables, each with its licence file beside it. The Inter webfont is deliberately not bundled, since it is under the SIL Open Font License rather than MIT, and is loaded from Google Fonts instead. Every page still renders without it.