Last Modified 2022-05-12 by Ralf Lang (B1 Systems GmbH)

Bootstrapping an environment

Horde's registry and bootstrapping predates composer, composer autoloader and most modern conventions. It is not helped by being super flexible in almost every aspect.
Bootstrapping is the order in which (a necessary part of) the Horde application environment is set up before actually running useful code. The current process is hard to understand and reason about.
With modern standards and concepts, a basic service or app need not carry around baggage from assumptions that may be entire irrelevant.

For example, the horde base app


Not necessarily in that order. Even getting the order right is non-trivial.

- Calling the core from the page, service endpoint or CLI
- setting up preliminary autoloading
- Setting up some fundamental constants, including guessing some paths.
- Lots of chicken/egg things
- setting up a preliminary (opinionated) selection of Dependency Injection keys for the Horde\Injector DI
- setting up all sorts of scary globals
- finding installed applications
- finding config for installed applications
- finding out if or if not snippets matching the current domain vhost should be applied
- Setting up even more autoloading for the current app
- Setting up filesystem locations and web-visible paths (possibly domains, too) for apps and the horde base app, themes, javascript, templates ...
- Session handling, based on config
- determine if the current session is administrative or has a certain permission
- Language handling
- Setting up fundamental services like auth, logging
- Everything happens live on request


Three stages:

Early stage

- Web server calls entrypoint.
- Composer autoloader first. One load to rule them all.
- Feed the DI with the bare minimum for routing purposes. (Less is better)
- Load cached list of relevant apps/services that expose routes. If it does not exist, assemble it.
- some primitive early error handling

Routing stage

- Load routes
- Assemble request object
- match request with routes
- call stack associated with route
- Perform PSR7/15 dance
- handle route-not-found / default route

Middleware/Controller stage

- authentication, if they want it
- permission handling, if they want it
- prefs handling, if they want it
- login to backing services, if they want it
- handle any errors



Random considerations

- Apps (and services) are basically groups of routes that share a codebase and a subdomain + webroot combination.
- Something standalone would not need registry, but would need (top level) routing.
- Is Registry is just an added value top layer router that houses per-app / per-service sub routers?
- Registry Config MAY depend on host of the request. To keep it lean, vhost is "always on". Parsing a config to find out if you should load a file (or ignore if missing) is always more expensive than just doing it.
- Registry Config determines webroots for apps, Routes depend on it. Setting up registry should be fast with as few side effects as possible. It should not imply heavy lifting, setting up DB/IMAP or other expensive connections.
- But we do not need heavy weight app init upfront.
- For routing setup, we do not care if the app in question is disabled, inaccessible for the current credentials or anything. This should be handled by some middleware.
- Each app/service has its endpoint, identifying a first routing level and at least some registry-defined routes for URI generation (js, themes, templates, appwebroot)
- We want to be able to generate routes towards other apps inside this installation.
- nested route groups can be resolved to one single set of combined routes.
- nested route groups can have default middleware stacks at each level. These can be merged to a single middleware stack, including the route's handler, middleware or stack
- nested route groups can have a catchall on each level.
- How to attach / autogenerate useful API docs (like openapi) from routing information?
- Are verbs part of the routing or should they be filtered by a middleware?
- URI parameter constraints.
- Collecting the routes into one consumable RoutesCollection should be separate from consuming the RoutesCollection. Collect at install/config time and save the result.
- Routes Matching should produce a MatchedRoute exposing useful information but should not call/dispatch the resulting stack.
- The router must allow to pass through some information it is not interested in by itself or does not recognize