The Framework for
Content Applications
VelvetCMS Core is a lightweight, explicit PHP framework built for content-driven applications. Four runtime dependencies. Sub-millisecond boot. No magic, no facades, no surprises.
$ composer create-project velvetcms/core my-app
$ git clone https://github.com/VelvetCMS/core.git
Measured, Not Marketed
Real benchmarks against real frameworks. Same machine, same tests, same conditions.
Hello World
Framework overhead, no DB, no template
Database Query
Route → query → JSON response
Template Render
Route → data → compiled template → HTML
Full Page
Route → DB → template → middleware → response
Hello World
Framework overhead, OPcache warm
Database Query
Route → cached query → JSON
Template Render
Route → data → cached template → HTML
Full Page
Route → cached DB → cached template → middleware → response
Framework Laptop 16 · Fedora Linux 43 · PHP 8.4.18 · OPcache enabled · SQLite
Benchmarking tools on GitHub — run them yourself
Explicit by Design
Build with primitives you know, optimized for content.
🛤️ Expressive Routing Map URLs to closures or controllers with middleware, groups, and rate limiting.
- GET, POST, PUT, DELETE, PATCH methods
- Named routes with programmatic URL generation
- Required, optional, and wildcard parameters
- Per-route and global middleware pipelines
$router->get('/posts/{slug}', [PostController::class, 'show'], 'posts.show');
$router->post('/contact', [ContactController::class, 'submit']);
// Optional parameters
$router->get('/archive/{year?}', function (Request $request, ?string $year = null) {
return "Archive for " . ($year ?? date('Y'));
});
// Wildcard — captures everything including slashes
$router->get('/docs/{path*}', [DocsController::class, 'render']);
💉 Dependency Injection Explicit core wiring for speed. Autowiring available for your classes when you want it.
- Core services manually wired for maximum performance
- Autowiring available as a pragmatic fallback
- No static proxies or facades
- Standard injection patterns
class PageController
{
public function __construct(
private PageService $pages,
private CacheInterface $cache,
) {}
public function show(Request $req, string $slug): Response
{
$page = $this->cache->remember(
"page:{$slug}", 3600,
fn() => $this->pages->findBySlug($slug)
);
return view('pages.show', ['page' => $page]);
}
}
🔍 Fluent Query Builder Expressive SQL generation with joins, subqueries, and automatic query caching.
- Chainable query API with prepared statements
- Joins, subqueries, and aggregates
- find(), first(), pluck(), paginate() helpers
- Automatic query caching via cache driver
$posts = $db->table('posts')
->where('status', 'published')
->where('published_at', '<=', now())
->orderBy('published_at', 'desc')
->limit(10)
->get();
// Shorthand lookups
$post = $db->table('posts')->find(42);
$titles = $db->table('posts')->pluck('title');
📦 Module System PSR-4 autoloading, dependency resolution, manifest-based loading.
- Self-contained packages with routes, views, migrations
- Version constraints and dependency resolution
- Conflict detection and load-order management
- module.json manifest for declarative config
{
"name": "velvetcms/blog",
"version": "1.0.0",
"requires": {
"velvetcms/core": "^1.0"
},
"providers": ["VelvetCMS\\Blog\\BlogModule"]
}
🎨 View Engine Blade-like syntax with layouts, partials, inheritance, and module namespaces.
- Familiar template syntax with @directives
- Layout inheritance and section blocks
- Namespaced views for module isolation
- Global data sharing via share()
// Render a view with data
return view('posts.show', ['post' => $post]);
// Namespaced module views
return view('blog::posts.index');
// Share data with all views
$view->share('siteName', 'My Site');
⏰ Task Scheduler Fluent cron-style scheduling. Commands or closures, any frequency.
- Fluent API: hourly(), dailyAt(), everyMinute()
- Schedule commands or inline closures
- Overlap prevention and run-on-one-server
- Output logging and failure notifications
$schedule->command('sitemap:generate')->dailyAt('03:00');
$schedule->command('cache:prune')->hourly();
$schedule->call(function () {
$this->analytics->aggregate();
})->everyMinute()->withoutOverlapping();
⚡ Smart Caching APCu, File, or Redis. Cache queries, pages, routes, and templates.
- Three drivers: APCu, File, Redis
- Query result caching with automatic invalidation
- Route and compiled view caching
- remember() pattern for lazy population
$cache->set('key', $value, 3600);
$value = $cache->get('key');
// Lazy population — only computes if not cached
$stats = $cache->remember('dashboard:stats', 900, function () {
return $this->analytics->computeStats();
});
📝 Markdown Engine Pluggable drivers with frontmatter extraction, tables, and task lists.
- Frontmatter extraction (YAML metadata)
- GFM tables, task lists, strikethrough
- Pluggable driver architecture
- Syntax highlighting for code fences
---
title: "Getting Started"
author: "Anvyr"
tags: [guide, tutorial]
---
# Welcome to Core
Build something **great** with:
- [x] Routing
- [x] Query builder
- [ ] Your imagination
✅ Validation Service Standalone validator for controllers, CLI, API, and imports.
- Fluent rule definitions
- Works in any context: HTTP, CLI, API
- Custom rules and error messages
- Request-level validation shorthand
$v = Validator::make($data, [
'title' => 'required|string|max:200',
'email' => 'required|email',
'status' => 'in:draft,published,archived',
]);
if ($v->fails()) {
$errors = $v->errors();
}
🏗️ Schema Builder Database-agnostic migrations for SQLite, MySQL, PostgreSQL.
- Blueprint API for table definitions
- Up/down migrations with rollback
- Index, foreign key, and constraint support
- SQLite, MySQL, PostgreSQL drivers
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->string('title', 200);
$table->string('slug')->unique();
$table->text('content');
$table->enum('status', ['draft', 'published'])->default('draft');
$table->timestamps();
});
🌐 Multi-Tenancy Single config switch. Host, path, or custom resolvers with full isolation.
- Host-based, path-based, or custom resolvers
- Per-tenant databases, storage, and config
- Single config switch to enable
- Tenant-aware paths and URL generation
// config/tenancy.php
return [
'enabled' => true,
'resolver' => 'host', // 'host', 'path', or custom class
'isolate' => ['database', 'storage', 'cache'],
];
🔬 No Magic No facades, no global state, no hidden behavior.
Every dependency is injected. Every side-effect is explicit. When something breaks, Ctrl+Click takes you directly to the source — no facade resolution, no service container magic, no hidden boot sequence.
Scale with Your Content
Start with files. Evolve to a database. Never rewrite your frontend.
File Driver
Markdown files with frontmatter. Perfect for docs, blogs, small sites.
- Human-readable, Git-friendly
- Zero database setup
- Easy migration from static generators
Hybrid Driver
Files for editing, SQL for metadata. Best of both worlds.
- Edit as Markdown, metadata indexed in SQL
- Scales to thousands of pages
- Automatic sync between file and DB
Database Driver
Full SQL storage for high-volume content applications.
- Everything in DB with full query capabilities
- Ideal for CMS admin panels
- SQLite, MySQL, PostgreSQL
Auto Driver
Automatically selects the best driver based on content type and volume.
- Recommended default for new projects
- Switches strategy per content type
- Zero configuration required
Security by Default
Every layer is hardened. You have to opt OUT of protection.
CSRF Protection
Automatic token validation on all state-changing requests. No opt-in required.
XSS Auto-Escaping
{{ }} escapes by default. Raw output via {!! !!} — explicit, intentional, auditable.
SQL Injection Prevention
Prepared statements everywhere. The query builder never concatenates user input.
Secure Sessions
HTTP-only cookies, SameSite strict, automatic regeneration on authentication changes.
Start Building
One command to get started.
$ composer create-project velvetcms/core my-app
$ git clone https://github.com/VelvetCMS/core.git