Skip to content

v0.9.0

@Dillonsmart Dillonsmart tagged this 09 Sep 06:49
Middleware was declared in public/index.php, which the introspection
commands must not load — it boots and serves the application. So nothing
could report what runs around a request, and `tether explain` claimed to
show "the path a URI takes" while hiding the first thing that happens to
it. A Principle 6 failure by the standard the previous release set.

Moving the declaration was only half the fix. The console has to *build*
the list to read the class names off it, and there is no way around that
without a container to resolve class names, which is the magic this
framework rejects — middleware takes constructor arguments, so a list of
strings cannot be turned back into objects without one.

Which left the real defect: Session::__construct() called
session_start(). Merely naming a Session issued a cookie and wrote a
file, whether or not anything went on to read or write a value. That was
invisible while the Kernel built the Session itself, and it is the rule
the framework already states — a returned value beats a side effect, and
a constructor is the least visible place to put work.

Sessions start on first use now. hasStarted() says whether one has, so a
caller can hold a Session without becoming responsible for one.

That buys the contract the whole approach rests on, and it is documented
as a rule for anyone writing middleware:

  Building a middleware must have no side effects. Do the work in
  __invoke().

VerifyCsrfToken holds a Session and is in the test fixture's list exactly
to prove it: a test asserts session_status() is unchanged after `tether
routes` runs.

Applications declare the list in routes/middleware.php, beside
routes/web.php — web.php says where a request goes, middleware.php says
what it passes through. The file returns a callable taking an Env and a
Log, the way web.php returns one taking a Router.

- explain lists the middleware before the route, and says it wraps
  everything below; the stage column widened by one to fit the label
- routes reports what every route in the table goes through first
- context carries it as an ordered list, so an agent can see that a write
  will be refused without a CSRF token — which the route table never said
- a file that cannot be built is reported rather than swallowed, and a
  missing file means no middleware rather than an error

The rule that these commands never construct application objects still
holds for Actions, Domains and Responders. Middleware is the exception,
it is declared in a file written to be loaded, and it is paid for with a
stated contract rather than a hope.

143 tests and PHPStan level 8 pass. Not tagged.
Assets 2
Loading