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-nameIt 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.
| You… | Start here |
|---|---|
| Want to code the app yourself | Quick start → How you work day to day → Flight docs ↔ this skeleton |
| Use any AI coding agent | Same as above, then AI-assisted development, root AGENTS.md, and SECURITY.md |
| Are comparing to older Flight demos | Flight 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.
- 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_sqlitefor the default database)
composer create-project flightphp/skeleton cool-project-name
cd cool-project-nameThat step copies config_sample.php → config.php, .env.example → .env (when present), creates cache/log dirs, and writes .runway-config.json if needed.
# 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/postsdocker compose up -d
# → http://localhost:8080vagrant up
# → http://localhost:8000project-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\….
You do not need an AI tool. The loop is:
- Route — add a line in
app/config/routes.php - Controller — class under
app/Controller/with constructor injection - View or JSON — Twig under
app/views/, or$this->app->json(...) - Database (optional) — migration in
migrations/, model underapp/Model/, injectSimplePdo
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.
Three layers:
.env— secrets and deploy overrides (DB_PASSWORD,APP_ENV, Docker)app/config/config.php— structured literal defaults (safe forrunway config:set)- Bootstrap merge — mapped env keys win when set (
App\Utils\Config::mergeEnv)
| Task | Where |
|---|---|
| Local defaults / non-secret flags | config.php or php runway config:set … |
| Secrets / production | .env (gitignored) |
| Read file config | php 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.
| Command | Purpose |
|---|---|
composer start |
PHP built-in server on port 8000 |
composer test |
PHPUnit |
composer analyse |
PHPStan level 8 |
composer check |
PHPUnit + PHPStan |
php runway migrate |
Apply migrations for active driver (.sql / .mysql.sql) |
php runway --help |
List CLI commands |
php runway config:get / config:set |
File config helpers |
Only rely on commands that actually appear in php runway --help for your install.
| Concern | Choice | Why this default |
|---|---|---|
| Framework | flightphp/core (Engine, SimplePdo) |
Long-term Flight APIs |
| DI | Dice + Engine substitutions | Testable controllers; official DI pattern |
| Views | Twig | Wide ecosystem; $app->render() is mapped to Twig |
| Models | ActiveRecord | One model story |
| DB connection | SimplePdo |
Preferred over deprecated PdoWrapper |
| Sessions | flightphp/session | Injectable; avoid raw $_SESSION |
| CLI | Runway | Migrations + scaffolding host |
| Debugger | Tracy (+ 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.
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.
| Topic | Docs often show | This skeleton expects |
|---|---|---|
| Entry / demo style | Flight::route(...), sometimes one-file |
public/index.php → bootstrap → routes.php + controllers |
| App handle | Flight::… static facade |
Inject flight\Engine $app in controllers/middleware; bootstrap may still call Flight::app() |
| Controllers | Various namespaces / ad hoc classes | App\Controller\… → app/Controller/ |
| Routing file | Inline in index or mixed | All HTTP routes in app/config/routes.php |
| Views | Built-in PHP views, Latte examples, etc. | Twig only under app/views/; $app->render('name', $data) |
| Database helper | Older PdoWrapper examples still around | SimplePdo (PdoWrapper is deprecated as of core 3.18) |
| Models | Raw SQL, or ActiveRecord in plugin docs | ActiveRecord under App\Model\; connection is SimplePdo |
| Config | Arrays, env snippets, register() | Literal config.php + .env overlay; inject App\Utils\Config |
| DI | Optional / several containers | Dice wired in services.php with Engine substitutions |
| Testing | Construct controller with new Engine() + mocks |
Same idea; see tests/Unit/ and the unit testing guide |
Reading docs without fighting the skeleton
- Learn the API from docs (
request(),json(),routepatterns, middlewarebefore, ActiveRecord methods, SimplePdo helpers). - Place new code in this tree (
Controller,Middleware,Model,views,routes.php,services.php). - Prefer constructor injection over new static
Flight::calls inside app classes. - If a doc example uses
Flight::db()orFlight::render(), the equivalent here is usually injectedSimplePdo/$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.
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):
| File | Role |
|---|---|
| AGENTS.md | Root rules + routing table to scoped files |
app/**/AGENTS.md, migrations/AGENTS.md, tests/AGENTS.md |
Light, area-specific tips (controllers, Twig, Runway, …) loaded when working in that tree |
| SECURITY.md | Secrets, headers, XSS/SQL, reporting — keep security deliberate and separate |
If you use an AI assistant:
- Point it at root
AGENTS.md(and let it follow links to scoped files when editing those folders). - Prefer docs.flightphp.com and MCP
https://mcp.flightphp.com/mcp. - Verify APIs under
vendor/flightphp/core— do not invent Flight methods. - Project AGENTS / SECURITY win over generic training data.
- 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.
- Add a route in
app/config/routes.php - Add
app/Controller/YourController.php(constructor injection) - Add a Twig template under
app/views/or return JSON from the controller - For DB: SQL file in
migrations/(.sqlfor SQLite,.mysql.sqlfor MySQL),php runway migrate, model inapp/Model/, injectSimplePdo - After code changes: add/update tests, then run
composer check(PHPUnit + PHPStan level 8)
MIT — see LICENSE.