Flight PHP Skeleton

August 7, 2026 · View on GitHub

Official starter for Flight PHP — a fast, simple, extensible micro-framework.

This repository is what you get from:

composer create-project flightphp/skeleton cool-project-name

It is built so you can write every line yourself, following one clear application pattern, and so AI coding tools succeed when you choose to use them. Same codebase either way.


Who this is for

You…Start here
Want to code the app yourselfQuick startHow you work day to dayFlight docs ↔ this skeleton
Use any AI coding agentSame as above, then AI-assisted development, root AGENTS.md, and SECURITY.md
Are comparing to older Flight demosFlight docs ↔ this skeleton

Flight’s framework APIs live in the docs and in vendor/flightphp/core. This skeleton’s job is a default application layout (folders, DI, config, views, models) so you are not inventing structure on day one.


Requirements

  • PHP 8.1+ recommended (app code stays careful about syntax; some deps such as Runway 1.x need 8.2+)
  • Composer
  • ext-json, ext-pdo (pdo_sqlite for the default database)

Create a project

composer create-project flightphp/skeleton cool-project-name
cd cool-project-name

That step copies config_sample.phpconfig.php, .env.example.env (when present), creates cache/log dirs, and writes .runway-config.json if needed.


Quick start

# Optional: edit .env or app/config/config.php
composer start
# → http://localhost:8000

# Sample data (posts table + ActiveRecord example)
php runway migrate
# → http://localhost:8000/posts
# → http://localhost:8000/api/posts

Docker

docker compose up -d
# → http://localhost:8080

Vagrant

vagrant up
# → http://localhost:8000

Project structure

project-root/
├── README.md             # You are here (humans first)
├── AGENTS.md             # Root AI instructions (source of truth)
├── SECURITY.md           # Security policy (secrets, headers, reporting)
├── .env.example          # Documented env overlays (secrets / deploy)
├── public/index.php      # Web entry only
├── app/
│   ├── config/           # bootstrap, routes, services + AGENTS.md
│   ├── Utils/            # Config, Env, DatabaseFactory + AGENTS.md
│   ├── Controller/       # App\Controller\* + AGENTS.md
│   ├── Middleware/       # App\Middleware\* + AGENTS.md
│   ├── Model/            # App\Model\* + AGENTS.md
│   ├── commands/         # Runway CLI + AGENTS.md
│   ├── views/            # Twig + AGENTS.md
│   ├── cache/
│   └── log/
├── migrations/           # SQL + AGENTS.md
└── tests/                # PHPUnit + AGENTS.md

Namespaces are App\… (PascalCase folders: Controller, not controllers). Framework code stays flight\….


How you work day to day

You do not need an AI tool. The loop is:

  1. Route — add a line in app/config/routes.php
  2. Controller — class under app/Controller/ with constructor injection
  3. View or JSON — Twig under app/views/, or $this->app->json(...)
  4. Database (optional) — migration in migrations/, model under app/Model/, inject SimplePdo

Minimal controller

namespace App\Controller;

use flight\Engine;

class HelloController
{
    private $app;

    public function __construct(Engine $app)
    {
        $this->app = $app;
    }

    public function index(): void
    {
        $this->app->render('welcome', [
            'message' => 'Hello from a controller',
        ]);
    }
}
// app/config/routes.php
$router->get('/hello', [HelloController::class, 'index']);

Dice builds the controller and injects the same Engine instance used at boot (see app/config/services.php). That matches Flight’s dependency-injection and unit-testing guidance: prefer $app / injected services over the static Flight:: facade in application classes.

Configuration

Three layers:

  1. .env — secrets and deploy overrides (DB_PASSWORD, APP_ENV, Docker)
  2. app/config/config.php — structured literal defaults (safe for runway config:set)
  3. Bootstrap merge — mapped env keys win when set (App\Utils\Config::mergeEnv)
TaskWhere
Local defaults / non-secret flagsconfig.php or php runway config:set …
Secrets / production.env (gitignored)
Read file configphp runway config:get

Do not put $_ENV[...] expressions inside config.php. Runway rewrites that file as static PHP and would bake resolved values (including secrets) into the file.

Full env→config map: AGENTS.md.

Useful commands

CommandPurpose
composer startPHP built-in server on port 8000
composer testPHPUnit
composer analysePHPStan level 8
composer checkPHPUnit + PHPStan
php runway migrateApply migrations for active driver (.sql / .mysql.sql)
php runway --helpList CLI commands
php runway config:get / config:setFile config helpers

Only rely on commands that actually appear in php runway --help for your install.


Stack (this skeleton’s defaults)

ConcernChoiceWhy this default
Frameworkflightphp/core (Engine, SimplePdo)Long-term Flight APIs
DIDice + Engine substitutionsTestable controllers; official DI pattern
ViewsTwigWide ecosystem; $app->render() is mapped to Twig
ModelsActiveRecordOne model story
DB connectionSimplePdoPreferred over deprecated PdoWrapper
Sessionsflightphp/sessionInjectable; avoid raw $_SESSION
CLIRunwayMigrations + scaffolding host
DebuggerTracy (+ tracy-extensions in dev)Error UX in development

These are deliberate product defaults for the official starter, not the only way to use Flight. A micro app can still be a single file and Flight::route() — that path is documented in core docs / zip installs, not duplicated here.


Flight docs ↔ this skeleton

Docs teach the framework. The skeleton fixes an application shape so copy-paste from tutorials does not fight the tree. When they differ, prefer this repository’s layout for code you add under app/, and use docs for method names, options, and plugins.

TopicDocs often showThis skeleton expects
Entry / demo styleFlight::route(...), sometimes one-filepublic/index.php → bootstrap → routes.php + controllers
App handleFlight::… static facadeInject flight\Engine $app in controllers/middleware; bootstrap may still call Flight::app()
ControllersVarious namespaces / ad hoc classesApp\Controller\…app/Controller/
Routing fileInline in index or mixedAll HTTP routes in app/config/routes.php
ViewsBuilt-in PHP views, Latte examples, etc.Twig only under app/views/; $app->render('name', $data)
Database helperOlder PdoWrapper examples still aroundSimplePdo (PdoWrapper is deprecated as of core 3.18)
ModelsRaw SQL, or ActiveRecord in plugin docsActiveRecord under App\Model\; connection is SimplePdo
ConfigArrays, env snippets, register()Literal config.php + .env overlay; inject App\Utils\Config
DIOptional / several containersDice wired in services.php with Engine substitutions
TestingConstruct controller with new Engine() + mocksSame idea; see tests/Unit/ and the unit testing guide

Reading docs without fighting the skeleton

  1. Learn the API from docs (request(), json(), route patterns, middleware before, ActiveRecord methods, SimplePdo helpers).
  2. Place new code in this tree (Controller, Middleware, Model, views, routes.php, services.php).
  3. Prefer constructor injection over new static Flight:: calls inside app classes.
  4. If a doc example uses Flight::db() or Flight::render(), the equivalent here is usually injected SimplePdo / $this->app->render() (render is already mapped to Twig).

Where docs and skeleton already agree

Flight’s own unit testing guide steers away from Flight:: globals toward Engine injection and DI — the same stance this skeleton takes for app/ code. Short facade examples in learn pages remain valid for quick experiments; they are not the house style for this starter.

Docs site updates

Install / structure pages on docs.flightphp.com should stay in sync with this README (especially App\ namespaces and Twig/SimplePdo defaults) whenever the skeleton ships a breaking layout change. Until then, this README is the source of truth for create-project layout.


AI-assisted development (optional)

Nothing in the runtime requires an AI tool. There is no create-project question about which assistant you use.

This repo standardizes on the open AGENTS.md convention only (no separate Copilot / Cursor / Gemini / Windsurf rule files):

FileRole
AGENTS.mdRoot rules + routing table to scoped files
app/**/AGENTS.md, migrations/AGENTS.md, tests/AGENTS.mdLight, area-specific tips (controllers, Twig, Runway, …) loaded when working in that tree
SECURITY.mdSecrets, headers, XSS/SQL, reporting — keep security deliberate and separate

If you use an AI assistant:

  1. Point it at root AGENTS.md (and let it follow links to scoped files when editing those folders).
  2. Prefer docs.flightphp.com and MCP https://mcp.flightphp.com/mcp.
  3. Verify APIs under vendor/flightphp/core — do not invent Flight methods.
  4. Project AGENTS / SECURITY win over generic training data.
  5. After application-code changes: add/update unit tests and run composer check (PHPUnit + PHPStan level 8).

Hand-written and AI-generated code should look the same: one controller style, one config path, one view layer.


First customization checklist

  1. Add a route in app/config/routes.php
  2. Add app/Controller/YourController.php (constructor injection)
  3. Add a Twig template under app/views/ or return JSON from the controller
  4. For DB: SQL file in migrations/ (.sql for SQLite, .mysql.sql for MySQL), php runway migrate, model in app/Model/, inject SimplePdo
  5. After code changes: add/update tests, then run composer check (PHPUnit + PHPStan level 8)

License

MIT — see LICENSE.