andrey900/flight-skeleton
andrey900/flight-skeleton provides a clean starter skeleton for the Flight PHP microframework, with a minimal project structure and sensible defaults for lightweight apps, APIs, and prototypes. It helps you bootstrap routing, views, and basic organization without extra setup.
The fastest path is composer create-project andrey900/flight-skeleton my-app, or clone the repo and run composer install. This is a skeleton — its value is the wiring, not the feature set — so treat it as a starting commit you will immediately edit rather than a library you depend on.
First things to open, in order:
public/index.php (or index.php at root) — this is the single entry point where Flight is bootstrapped, Twig/PDO/Monolog are registered into Flight's container, and route files are required. Everything hangs off this file.config/ directory — database credentials, Twig paths/cache, Monolog channel and log file location. Confirm .env or equivalent is being read here.Flight::route(...) calls. This is where you will spend most of your time.views/ for Twig templates and src/ (or app/) for any controllers/models the skeleton ships with.Point your web server's document root at public/. If you're using PHP's built-in server for a first smoke test, php -S localhost:8000 -t public is enough.
First useful exercise: add a route that pulls rows from an Eloquent model and renders them through Twig. That touches all three integrations in one pass and confirms DB, ORM, and templating are actually wired — which is really the only thing this package is promising.
Routing and controllers. Flight is procedural at its core: Flight::route('GET /users', function(){...}). For anything non-trivial, group routes by concern in separate files and require them from the bootstrapper. Controllers work through Flight's resolver — Flight::route('GET /users', [UserController::class, 'index']) — but the class must be resolvable via the container or autoloader. Registering a PSR-11 container (often already done in the bootstrapper) is what makes constructor injection of Twig/Monolog/DB possible.
Twig rendering. The skeleton registers Twig as a view engine, so Flight::render('users/index.twig', ['users' => $users]) is the idiomatic call. Keep the template directory pointed at the repo's views/ and pass only the data each template needs — avoid dumping the whole model into the template and iterating there; hydrate to arrays or DTOs first if the view logic gets heavy.
Eloquent without Laravel. Eloquent is bootstrapped via Illuminate\Database\Capsule\Manager. Once $capsule->bootEloquent() and $capsule->setAsGlobal() run in the entry point, your models are plain extends Illuminate\Database\Eloquent\Model classes in src/Models/. Relationships, accessors, and query builder all work. What you do not get: migrations runner, artisan, model factories, or the DB facade. Plan for raw SQL migration scripts or a standalone migration package.
Logging. Monolog is registered into Flight's container under a key (commonly log). Fetch it with Flight::get('log') or inject it. Use it in Flight::before('start', ...) for request logging and in a global error handler — Flight's Flight::map('error', ...) is the natural hook so uncaught exceptions land in Monolog instead of the default HTML page.
Middleware. Flight's before/after filters are your middleware. Register auth as Flight::before('start', ...) with a path check, or use route-level before filters. This is thinner than Slim/Laravel middleware — there is no $next chain, so keep middleware as guards and side effects rather than pipeline transforms.
Config and environment. Keep the config files returning arrays, load them once in the bootstrapper, and set values into Flight via Flight::set(). That gives routes and controllers a single place to read from (Flight::get('config.db')) without scattering getenv() calls.
Flight:: static methods share global state across the whole request lifecycle. Tests that spin up multiple "apps" in one process will bleed state. If you need testability, wrap route registration in functions and reset with Flight::set() between tests, or accept that Flight is a request-scoped framework and test via HTTP.setAsGlobal() and bootEloquent() are idempotent-ish but re-running the capsule setup (e.g., in a test bootstrap and the entry point) causes confusing "connection not set" errors. Bootstrap the capsule in exactly one place and require that place from CLI scripts too.bin/migrate.php) early — retrofitting one after data exists is painful.auto_reload) in dev, or you will edit a template and see stale output. Cache directory must be writable.logs/ (or storage/logs/). On shared hosting or after a git clone as a different user, the directory is often read-only. chmod -R 775 logs and add it to .gitignore.public/index.php. The skeleton may ship .htaccess for Apache but not nginx — add a try_files $uri $uri/ /index.php?$query_string; block or you'll get 404s on every non-root route.Flight::map('error', ...) to render a Twig error template and log via Monolog; the default behavior leaks stack traces in production.Flight::register() in a dedicated bootstrap/services.php and keep index.php thin. This is what makes the skeleton maintainable as it grows past "micro."composer.json versions and expect to own the upgrade path yourself; don't count on the skeleton tracking Flight 3.x or Eloquent 11.x changes for you.How can I help you explore Laravel packages today?