Hello World

There are three ways to handle a request in TrueFramework, in order of how much code you have to write. Pick the smallest one that fits.

For a request to /hello this:

  1. Tries to include app/controllers/hello.php — silently skipped if it doesn't exist.
  2. Renders app/views/hello.phtml with whatever $vars the controller (if any) set.

That means most pages need no route registration at all. Pick the pattern that fits the request you're building.

1. View only — no controller, no route

For purely informational pages — home, about, terms, anything where the URL just needs to render a template — drop a .phtml file in app/views/ and you're done.

app/views/hello.phtml

title = "Hello"

description = "This is the hello description."
{endmeta}

<h1>Hello, world!</h1>

Visit http://localhost:8000/hello and the page renders inside your layout. No route, no controller, no boilerplate.

Folders work too. app/views/about/team.phtml is reachable at /about/team. The catch-all uses the URL path verbatim to find the template.

2. View + controller — when the page needs data

Once a page has logic — reading the query string, hitting the database, deciding what to show — promote it to a controller. The controller is a flat PHP file at app/controllers/<path>.php with the same name as the URL. Anything you put into $vars is unpacked into the template's local scope.

app/controllers/hello.php

$vars['who'] = $request->get->name('name') ?: 'world';

app/views/hello.phtml

title = "Hello"
{endmeta}

<h1>Hello, <?=esc($who)?>!</h1>

Now /hello?name=Daniel reads and sanitizes the query string, passes it to the view, and renders. Still no explicit route — the catch-all wires the controller and view together by URL.

3. Explicit route — when verbs and shapes matter

For anything that isn't "render an HTML page" — APIs, form posts, webhooks, anything that needs a specific HTTP verb or returns JSON — register a route in app/routes.php. Routes go above the catch-all so they match first.

app/routes.php

Add ABOVE the catch-all

$App->router->get('/api/hello/:name', function($request) use ($App) {
    $App->response([
        'message' => 'Hello, ' . $request->route->name . '!',
        'time'    => date('c'),
    ], 'json');
});

A GET to /api/hello/Daniel returns:

{
    "message": "Hello, Daniel!",
    "time": "2026-04-30T15:42:11+00:00"
}

Use the same approach for post(), put(), patch(), delete(), or multi-verb any() handlers. Route placeholders like :name land on $request->route. See Route Patterns for the full set.

Which one should I use?

If you're building…Use…
A static or near-static page (about, services, FAQ)View only
A page that reads input, hits a DB, or branches on stateView + controller
An API endpoint, webhook, JSON return, or dynamic urlsExplicit route
A form POST handler tied to a pageEither — controller for the page, explicit route for the POST, or both in the controller checking $request->method