andrey900/slimcms
SlimCMS is a lightweight PHP CMS built on the Slim framework, offering a simple way to create and manage websites with routing, templates, and admin tools. It suits small projects that need a fast, minimal, easy-to-extend content management system.
SlimCMS is a pre-built Slim 3 application skeleton with a CMS bolted on: front-end routing, an admin panel, a module system, and theme templates. You are not installing a library — you are forking an application. Treat the first hour as code archaeology, not setup.
Minimal steps
composer.json and replaced abandoned transitive dependencies. composer install on PHP 8 will likely fail or install a broken graph.git clone … && cd slimcms && composer install.app/config/ or config/) plus an .env/config.php pair. You need database credentials, baseUrl/basePath, and debug flags. There is no interactive installer worth relying on — read the sample config and copy it.install/, database/, or sql/ in the repo root) rather than expecting migrations. If migrations exist, run them via whatever console entry point ships in bin/ or the repo root.cache/, logs/, and the upload directory (usually under public/uploads or public/files) must be writable by the web user.public/, with a front-controller rewrite to public/index.php (Slim 3 needs the standard Apache .htaccess or the Nginx try_files $uri $uri/ /index.php?$query_string; block)./admin. Credentials come from the seeded users table — check the SQL dump for a default row and change it immediately.Where to look first
public/index.php — the entire bootstrap: container creation, middleware registration, route loading. This one file tells you 80% of the architecture.app/ (or src/) — bootstrap/container definitions, global route files, middleware. This is where you wire new services.app/Modules/, modules/, or src/Modules/) — each module typically bundles its own controllers, models, views, and route file. This is the extension unit.public/themes/ (or templates/, views/) — Twig (or plain PHP) templates with a layout/inheritance hierarchy. Find the layout, then the page template, then the partials.First use case
The fastest useful thing to do is add a single front-end page backed by the CMS: create a controller, register a GET route in a module route file, render a template that extends the site layout, and confirm the view resolves. Once that loop works end-to-end you understand the container keys, the template namespace, and the route-loading convention — which is everything you need for real work.
Route + controller + template (the core loop)
Slim 3 controllers are plain classes resolved from the container. Register routes explicitly rather than relying on magic:
$app->group('/blog', function () {
$this->get('', 'App\Modules\Blog\Controller\IndexController:index');
$this->get('/{slug}', 'App\Modules\Blog\Controller\IndexController:show');
})->add($container->get('csrf'))->add($container->get('auth'));
Keep controllers thin: request in, service call, $this->view->render($response, 'blog/show.twig', $data) out. If a controller starts touching the DB directly, move that into a module service and register it in the container.
Module as the unit of work
A module is the natural boundary. When adding a feature — say "news" — copy the shape of an existing module rather than inventing one:
Modules/News/Controller/ — front and admin controllersModules/News/Model/ — entities/repositoriesModules/News/views/ — front templatesModules/News/views/admin/ — admin templatesModules/News/routes.php — front routesModules/News/routes.admin.php — admin routesModules/News/config.php — module-level config merged at bootThen register the module's route file in the bootstrap and its namespace in Composer's autoload (PSR-4). Modules that are auto-discovered by scanning a directory are convenient but fragile — prefer explicit registration.
Admin CRUD pattern
Every CMS content type follows the same five-endpoint shape: list, create form, store, edit form, update, delete. Build one generic admin base controller (or trait) that handles pagination, flash messages, and CSRF checks, then let each content type supply its model and form fields. This saves you from writing the same validation block eight times.
Middleware for cross-cutting concerns
Slim 3 middleware is where auth, CSRF, locale, and admin-guard logic belongs. Register global middleware in index.php, group-level middleware on route groups. Route-level is where you enforce "admin only" — do not rely on the admin controller to check permissions itself, because a forgotten check is a silent hole.
Service layer over the ORM
Whatever the shipped persistence layer is, wrap it. Expose PageRepository::findBySlug(), MenuRepository::tree() and similar. This does two things: it gives you a seam to swap the ORM out later, and it keeps template-facing data shapes stable when you eventually migrate to Eloquent/Doctrine.
Theming
Front templates should extend a single layout and use named blocks. Theme switching (if supported) usually means a config value that changes the template path or Twig namespace. If you plan more than one theme, make sure templates reference asset URLs through a helper rather than hardcoding /themes/default/... — otherwise every theme change becomes a find-and-replace.
Assets and uploads
Keep CSS/JS in public/ and reference them via a base-path-aware helper. Uploads should go through a single upload service that validates MIME type, renames files, and stores metadata in the DB — the admin panel likely has an ad-hoc version of this you'll want to consolidate.
Testing seams
There are almost certainly no tests. Add PHPUnit and build requests against the Slim app instance ($app->getContainer() plus a PSR-7 request) so you can test routes without a browser. Start with smoke tests for /, /admin, and your new module — they pay for themselves the first time you touch the bootstrap.
This is an abandoned 2017 codebase. That is the dominant fact of this assessment. Slim 3 itself is end-of-life, and any transitive dependency (Twig, PSR-7 implementations, the container) may have known CVEs. Before shipping anything public-facing:
composer audit and replace or patch flagged packages.License is GPL-3.0. This is copyleft. If you distribute a modified version, or run a modified version as a network service in a way your legal team interprets as covered, you inherit source-disclosure obligations. For an internal tool this is usually fine; for a commercial SaaS or a distributed product, get a lawyer's opinion before building on it. This genuinely kills the package for many use cases.
Base path and routing are the #1 source of "it 404s locally." Slim 3's basePath must match the URL prefix the app is served under. Behind a reverse proxy or in a subdirectory, you need the X-Forwarded-* headers set correctly and the base path configured — otherwise every generated URL and every route match is wrong. Symptom: works at /, breaks at /cms/.
.htaccess / Nginx rewrites. If static assets work but routes 404, your rewrite is wrong. If /admin redirects to a 500, it's usually a missing PHP module or an unwritable cache dir. Check both before debugging application code.
Cache is a trap. CMSs of this vintage often cache compiled templates and rendered fragments to disk. After editing a template or config, clear cache/. If you see stale output after a deploy, this is why. Add a cache-clear step to your deploy script rather than remembering it manually.
Container key collisions. Slim 3's default container also holds settings, view, router, request, response, logger. If you register a service under one of those names, you will break the framework in confusing ways. Namespace your own keys (service.mailer, repo.page).
Debug mode leaks secrets. The bundled error handler prints stack traces and config. Ensure debug is off in production and that the error handler is the production variant — check the bootstrap, not just the config file, since these projects often hardcode a handler.
Don't extend the model layer downward. The ORM the project ships with may be simplistic. Resist the urge to build complex query logic inside it. Isolate queries in repositories and, when you hit the ceiling, swap the implementation behind that interface. Migrating a repository layer is a week; migrating a controller layer is a quarter.
Extension points worth knowing. The three that matter: container definitions (add services without touching bootstrap logic), the middleware stack (add cross-cutting behavior in one place), and module route files (add features without editing core routing). Anything that requires editing files inside a vendor-ish core directory should be reconsidered — you'll lose it on the next pull.
Migration path, if this survives. Slim 3 → Slim 4 is not a drop-in: PSR-7/PSR-15 changed the middleware signature, the container is no longer built in, and $app->getContainer() access changed. Plan the migration module by module, keeping the repository and service layers untouched. The modules that render through Twig will port with the least friction; the ones that reach into $container from inside controllers will be the expensive ones.
Recommendation summary. Use this as a reference implementation for CMS structure, or as a base for a small internal site where you control the server and accept the maintenance burden. Do not use it for anything public-facing, commercially licensed, or long-lived without a funded migration plan — the stars and structure are real, but the 2017 last release and GPL-3.0 license are the constraints that decide the answer.
How can I help you explore Laravel packages today?