AI blog · DotApp PHP Framework 2.0
What's new in DotApp PHP Framework
Dated kernel notes since 2.0. Old modules keep working: trigger() still ignores listener returns,
a v1 modulesAutoLoader.php still loads, and a public catch-all that already used a tight prefix does not have to change.
This article is the digest. Each item links to a complete how-to.
Official intro: What's new.
Common mistakes
| Wrong | Right |
|---|---|
Wake on /{path*} and skip /admin only in initializeCondition |
Put {not:/admin*} on the wake string itself |
Treat return false on trigger() as a stop |
Events::triggerWithVeto() and a Veto object |
Call Extender::extend() directly in register() |
Subscribe to dotapp.module.shop.loading, then extend() inside that callback |
Persist or email only on dotapp.catchall |
Named event. Catchall is the debug tap |
URL {not:} — exclude prefixes before the match (NEW – 2026-08-26)
Router::match_url() understands {not:mask|mask} on every URL selector:
Router::get, before, onPath, Module::initializeRoutes(), and
Listeners::initializeRoutes().
Exclusions run before the positive pattern (strpos / substr on a trailing-* prefix).
A public catch-all stays one string and still stays out of /admin, /api/v1, and /assets.
Router::get('/{path*}{not:/admin*|/api/v1*|/assets*}', 'Shop:Public@page!');
- Order matters:
/admin/loginagainst/{path*}{not:/admin*}dies on the exclude, not on{path*}. /admin*vs/admin/*:/admin/*does not match exact/admin. Use{not:/admin*}when the admin index must stay out too.- Same syntax on wake lists. A public
/{path*}that only skips/adminininitializeConditionstill wakes the module — put{not:}on the wake string. - Do not mark that catch-all
Router::STATIC_ROUTE.
Full match table and CMS wake map: How URL {not:} selectors work.
AIRULES: AIRULES/03-MODULES-AND-ROUTING.md (path parameters). Sample: AIRULES/examples/EX-18-url-not.md.
Triggers, Extender, and independent listener routes (NEW – 2026-08-22)
The event bus and module loader grew. Matching listeners always register before matching modules initialize.
-
Events::triggerWithVeto()plusDotsystems\App\Parts\Veto— an explicit stop before a reversible action. Ordinarytrigger()still ignores listener returns, including aVeto. How trigger with veto works. -
Dotsystems\App\Parts\Extender— judged, request-local replacement of a meaningful output. The owner usesexists()/call(). An ordinary result replaces it.isOriginal()continues owner logic only for the uniqueoriginal()marker. InListeners::register()subscribe todotapp.module.shop.loading, thenextend()with a string such as'Loyalty:Pricing@quote!'. How Extender works. -
Listeners::initializeRoutes()— a listener can wake on its own URL masks without running the module’sinitialize(). Omit the method and it inherits the module map. How independent listener routes work. -
php dotapper.php --optimize-moduleswrites optimizer format v2 ($modules,$listeners,$modulesAutoLoaderVersion = 2). A v1 file that only exports$modulesremains compatible. After a wake-map change, re-run the optimizer. How module initialization works.
dotapp.catchall debug tap (2026-08-21)
Every trigger() except dotapp.catchall itself first fires that name with
($result, $eventname, ...$data). triggerWithVeto() does the same.
Triggering catchall does not re-enter catchall. A throw there skips the named event.
Keep persist and email on a named event. If you log with Logger::use(), skip
dotapp.log or you recurse.
Events and listeners — catchall.
Version 2.0 (2026-08-18)
The 2.0 kernel is the production rewrite: modules, fo-rm + crcCheck() once,
encrypted ids, DSM, $dotapp(), AIRULES for agents. That is the baseline these later items sit on.
Start at the official documentation and
how to create a module.
FAQ
Do I have to rewrite old modules?
No. trigger() behaviour is unchanged. A listener without initializeRoutes()
still inherits the module map. {not:} is opt-in — add it when a public catch-all must stay out of admin.
Why not {not:/admin/*}?
That mask is prefix /admin/. Exact /admin still matches the positive route.
Use {not:/admin*} when the admin index must stay out too.
Where should an AI agent read first?
This page for the list. Then the linked how-to for the API you are about to use.
AIRULES 03 for {not:}, 12 for Events / Extender / veto.
See also
- Official documentation — What's new
- How URL {not:} selectors work in DotApp PHP Framework
- How trigger with veto works in DotApp PHP Framework
- How Extender works in DotApp PHP Framework
- How independent listener routes work in DotApp PHP Framework
- Events and listeners in DotApp PHP Framework
- How routing works in DotApp PHP Framework