Search by

xylo-is-coding / vanilla-cms

DavideFarmerbit

A minimal, framework-free, PHP library for defining content types, storing their data, routing requests to pages, and editing content through a small admin panel.

Package info

github.com/DavideFarmerbit/vanilla-cms

pkg:composer/xylo-is-coding/vanilla-cms

Statistics

Installs: 10

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

dev-main 2026-09-22 10:06 UTC

This package is auto-updated.

Last update: 2026-09-22 10:07:18 UTC


README

A minimal, framework-free, PHP library for defining content types, storing their data, routing requests to pages, and editing content through a small admin panel.

What's included

A Router class handles routing requests to pages. In your index.php file, you can use Router::dispatch, passing in an array of RouterDispatcher to associate url patterns with a callback function. The library comes with a set of default dispatchers (default_router_dispatchers()), which handles routing to pages and archetypes, using their slugs, and to the admin panel.

Site pages are defined by Page objects: extend Page, implement the Page::render_internal method, and provide the parameters requested by the parent's constructor. Finally, register the page through register_pages() which takes in an array of Page classes.

In Page classes you can define Field derived members, which must be initialized in the constructor. These fields will show up in the built-in admin panel to edit their values, and they can be freely accessed in the Page::render_internal method.

The library has an Admin panel, which allows editing and creating pages from the types that were registered. Pages can be either 'simple' or 'archetypes'. All non-archetype pages appear in the admin panel under the same tab. The system expects them to exist, and only one instance of those can be created. There is a tab for each page archetype, from there you can manage its instances (e.g., adding or modifying new posts in a blog).

To fully initialize the CMS, you must call Auth::set passing in a AuthDriver object, and a link to the login page. You must also call Storage::set passing in a StorageDriver object, to initialize the database connection. The library comes with a JsonStorage class, which can be created by passing in a root directory for the JSON files to be stored in. The final setup step is to call AdminAssets::set to register the admin panel .js and .css files (the library comes with a default theme you can use).

Minimal example

.htaccess

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [L]

index.php

// Register the page types
register_pages([
    HomePage::class,
    ArchivePage::class,
    ProductPage::class,
    LoginPage::class,
]);

// Set auth driver class for admin pannel
Auth::set(new class implements AuthDriver {
    public function isAdmin(): bool
    {
        return true;
    }
}, '/login');

// Set storage driver
Storage::set(new JsonStorage(__DIR__ . '/../storage'));
Storage::setUploadsRoot(__DIR__ . '/../public/uploads', '/uploads');
if (extension_loaded('gd')) {
    Storage::setImageSrcsetGenerator(new GdImageSrcsetGenerator());
}

// Set admin assets
AdminAssets::set('/path/to/admin.css', '/path/to/admin.css');
vcms_register_default_admin_tabs();

Router::dispatch(vcms_default_router_dispatchers());
namespace App\Pages;

use VanillaCms\Pages\Page;
use VanillaCms\Fields\TextField;

class ProductPage extends Page {
    
    public TextField $productName;

    public function __construct()
    {
        parent::__construct('product', 'Product', true);
        $this->productName = new TextField(['label' => 'Name']);
    }

    public function render_internal(): void
    {
        require_once __DIR__ . '/../includes/header.php';
        require_once __DIR__ . '/../includes/footer.php';

        generate_header($this->meta()->name());
        ?>
        <h1><?= htmlspecialchars($this->label()) ?>: <?= htmlspecialchars($this->meta()->slug()) ?></h1>
        <p>Name: <?= htmlspecialchars($this->productName->getText()) ?></p>
        <?php
        generate_footer();
    }
}
<?php

namespace App\Pages;

use VanillaCms\Admin\AdminController;
use VanillaCms\Auth\Auth;
use VanillaCms\Pages\Page;
use VanillaCms\Core\Router\Router;

class LoginPage extends Page {

    public function __construct()
    {
        parent::__construct('login', 'Login', false);
    }

    public function render_internal(): void
    {
        // If the user is already logged in, redirect to the page they were trying to access before logging in.
        if (Auth::isAdmin()) {
            Router::return(AdminController::getHomeUrl());
        }

        require_once __DIR__ . '/../includes/header.php';
        require_once __DIR__ . '/../includes/footer.php';

        generate_header($this->meta()->name());
        ?>
        <h1>Login</h1>
        <p>Login is required</p>
        <?php
        generate_footer();
    }
}

Requirements

  • PHP >= 8.4

Installation

composer require xylo-is-coding/vanilla-cms