Override System

Everything in phlat/ is a default. Place a matching file in site/ and it takes precedence — no configuration required.

How it works

PhlatPage resolves files by checking site/ first, then falling back to phlat/:

site/views/default.latte        ← used if it exists
phlat/views/default.latte       ← fallback

site/routers/shop.php           ← used if it exists
phlat/routers/shop.php          ← fallback

site/controllers/app.php        ← used if it exists
phlat/controllers/app.php       ← fallback

What can be overridden

Type Location
Views site/views/{view}.latte
View field definitions site/views/{view}.json
Routers site/routers/{view}.php
Controllers site/controllers/{view}.php
Field types site/fields/{type}/
Users site/users/{slug}/

Pages are different

Pages are the exception — they live only in site/pages/ and are entirely user-controlled. The framework never ships built-in pages.

Replacing vs extending

An override fully replaces the framework default — there is no merge at the file level. To extend a router rather than replace it, invoke the framework version from inside your override:

<?php
// site/routers/admin.php

use Phlat\App;
use Phlat\Page;

return function (App $app, Page $page): void {
    // include all framework admin routes
    (include "{$app->system}/routers/admin.php")($app, $page);

    // then add site-specific routes
    $page->router->get('reports', fn() => null);
};

Overriding a field type

Replace the built-in markdown field with one that supports GitHub Flavored Markdown:

<?php
// site/fields/markdown/markdown.php

namespace Phlat;

use League\CommonMark\GithubFlavoredMarkdownConverter;

class FieldMarkdown extends Field
{
    public function decode(): mixed
    {
        if (!$this->value) return '';
        return (new GithubFlavoredMarkdownConverter())
            ->convert((string) $this->value)
            ->getContent();
    }
}

Addons

Addons extend framework behaviour without replacing files. Each addon is a folder containing index.php, executed during App::execute(). Addons can register dependencies or attach tooling — but they cannot register their own page URLs. Routes must be wired by pages.

site/addons/
  analytics/
    index.php   ← receives $app and $path as locals

Addons in site/addons/ override matching addons in phlat/addons/.

<?php
// site/routers/admin.php — extends phlat/routers/admin.php

use Phlat\App;
use Phlat\Page;

return function (App $app, Page $page): void {
    // pull in all framework admin routes first
    (include "{$app->system}/routers/admin.php")($app, $page);

    // add site-specific admin routes
    $page->router->get('reports', fn() => null);
    $page->router->get('export',  fn() => null);
};