Skip to content

feat(core): allow packages to declare global middleware in module.php#80

Merged
markshust merged 4 commits into
marko-php:developfrom
michalbiarda:feature/module-global-middleware
May 26, 2026
Merged

feat(core): allow packages to declare global middleware in module.php#80
markshust merged 4 commits into
marko-php:developfrom
michalbiarda:feature/module-global-middleware

Conversation

@michalbiarda
Copy link
Copy Markdown
Contributor

Summary

  • Adds globalMiddleware key to module.php — accepts class strings or ['class' => ..., 'priority' => N] entries
  • GlobalMiddlewareResolver now collects, deduplicates (app > modules > vendor), and sorts by priority from module declarations only
  • Removes DEFAULT_BUILT_INS constant and builtIns parameter — built-in middleware moved to their packages
  • PageCacheMiddleware (priority 10), SessionMiddleware (priority 20), LayoutMiddleware (priority 30) now self-register in their module.php
  • Package-level PackageStructureTest files verify each middleware declaration and priority

Test plan

  • composer test passes (excluding pre-existing DevDownCommandTest flake)
  • packages/core/tests/Unit/Module/GlobalMiddlewareResolverTest.php — all 14 resolver requirements covered
  • packages/session/tests/PackageStructureTest.php — SessionMiddleware at priority 20
  • packages/page-cache/tests/PackageStructureTest.php — PageCacheMiddleware at priority 10
  • packages/layout/tests/PackageStructureTest.php — LayoutMiddleware at priority 30

Closes #79

🤖 Generated with Claude Code

@github-actions github-actions Bot added the enhancement New feature or request label May 20, 2026
michalbiarda and others added 3 commits May 26, 2026 12:34
Packages can now declare global HTTP middleware in module.php via the
`globalMiddleware` key (flat class-strings or array with class+priority).
GlobalMiddlewareResolver merges module entries with built-in defaults,
sorts by priority (ascending), and deduplicates using source priority
(app > modules > vendor). Built-in GLOBAL_MIDDLEWARE entries retain
silent-skip behavior for backwards compatibility.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
PageCacheMiddleware (priority 10), SessionMiddleware (priority 20), and
LayoutMiddleware (priority 30) are now declared via globalMiddleware in
their respective module.php files instead of being hardcoded in
GlobalMiddlewareResolver::DEFAULT_BUILT_INS.

The DEFAULT_BUILT_INS constant and builtIns parameter are removed from
GlobalMiddlewareResolver — the resolver now only works with module
declarations. Package-level tests verify each middleware's priority.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
…iscovery

withPathAndSource() was silently dropping the globalMiddleware field,
causing all module-declared global middleware to never be registered.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
@markshust markshust self-assigned this May 26, 2026
Replaces the priority-based scheme with module load order. Modules are
already topologically sorted by DependencyResolver (composer require +
sequence: { after, before }), so the resolver just walks them in order
and dedupes by class, with app > modules > vendor winning on position
when the same class is declared in multiple sources.

- Drops the priority field and array-form entries — globalMiddleware
  is now a flat list of class-strings only.
- session declares sequence: { after: ['marko/page-cache'] } (soft).
- layout declares sequence: { after: ['marko/session'] } (soft).
- Tightens validation: non-string entries throw loudly instead of being
  silently accepted.

Rationale: the project already had three ordering systems (plugin
sortOrder, observer priority, module sequence). Adding a fourth
convention with magic numbers (10/20/30) was unnecessary — module
sequence already expresses ordering declaratively and is self-
documenting ("session runs after page-cache" reads better than
"priority 20 vs 10").

Co-Authored-By: Michał Biarda <[email protected]>
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@markshust markshust force-pushed the feature/module-global-middleware branch from 9ec897c to adcea79 Compare May 26, 2026 16:44
@markshust
Copy link
Copy Markdown
Collaborator

Thanks for the PR, @michalbiarda — solid premise and clean implementation. The hardcoded GLOBAL_MIDDLEWARE constant in Application.php had no business naming downstream packages, and module-declared middleware aligns perfectly with Marko's true-modularity principle.

I pushed a refactor commit on top (force-with-lease, your three commits are preserved). The main change is dropping the priority field entirely:

Why: the project already has three ordering systems — plugins use sortOrder (lower first, default 0), observers use priority (higher first, default 0), and modules use sequence: { after, before }. The PR introduced a fourth: priority (lower first, default 100). That's the magic-numbers smell — 10/20/30 only make sense if you've memorized which slot each built-in occupies.

Instead: middleware order is just module load order. DependencyResolver already topologically sorts modules from composer require + sequence: { after, before }, so the resolver just walks them in order and dedupes by class. Source priority (app > modules > vendor) still applies to position when the same class is declared in multiple modules.

Built-in ordering is now self-documenting: marko/session declares sequence: { after: ['marko/page-cache'] }, marko/layout declares sequence: { after: ['marko/session'] }. Both are soft — only enforced when the named module is also installed.

Other small changes:

  • globalMiddleware entries are now class-strings only (no array form). Non-string entries throw loudly.
  • Tests reshaped to the new contract; 13 cases cover order preservation, dedup, source override, and validation.

Full suite green (5276 passing). Merging shortly — thanks again for the contribution!

@markshust markshust merged commit b3e7d52 into marko-php:develop May 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allow packages to declare global middleware in module.php

2 participants