Prejsť na obsah

Framework DotApp Aktualizované: 2026-08-27


O autorovi

Volám sa Štefan Miščík a som senior fullstack web vývojár v Dotsystems s.r.o. (WEB)

Ciele návrhu

DotApp nie je ďalší bežný PHP framework, akých sú stovky. Je napísaný od nuly, bez extra Composer závislostí a bez Laravel či Symfony stacku pod kapotou. Jadro je ultralahké a vysoko škálovateľné: smerovanie, vykresľovanie, bezpečnosť a databázová vrstva zdieľajú jedno runtime prostredie a jednu sadu konvencií.

Aplikačná logika žije v moduloch. Každý modul vlastní svoje trasy, kontroléry, middleware, views a assets — tím si tak vie rozdeliť prácu bez kolízií a AIRULES povedia AI agentovi tú istú zmluvu. Bezpečnosť je v runtime (chránené vstupy, fo-rm, šifrované identifikátory), nie ako pridaný balík.

Práce na DotApp sa začali v roku 2014 ako kompletná aplikačná architektúra, nie ako obal okolo cudzieho stromu závislostí.


Novinky

Aktualizácie jadra od DotApp 2.0. Staré moduly ostávajú funkčné. Prehľad podľa dátumu: What's new in DotApp PHP Framework.

URL {not:} — vylúčenie prefixov pred matchom (NEW – 26. 8. 2026)

Router má operátor {not:mask|mask}. Vylúčenia bežia pred pozitívnym vzorom (strpos / substr na prefixe s koncovým *). Verejný catch-all ostane jeden reťazec a predsa sa vyhne /admin, /api/v1 a /assets.


Router::get('/{path*}{not:/admin*|/api/v1*|/assets*}', 'Shop:Public@page!');
            
  • Záleží na poradí: /admin/login proti /{path*}{not:/admin*} skončí na exclude, nie na {path*}.
  • /admin* vs /admin/*: /admin/* nesedí na presné /admin. Keď musí vypadnúť aj index adminu, použite {not:/admin*}.
  • Rovnaká syntax na wake zoznamoch Module::initializeRoutes() / Listeners::initializeRoutes(). Verejné /{path*}, ktoré /admin odreže až v initializeCondition, modul aj tak zobudí — {not:} dajte na wake reťazec.

Docs: AIRULES/03-MODULES-AND-ROUTING.md (parametre cesty). Návod: Ako fungujú URL selektory {not:}.

Triggery, Extender a samostatné routy listenerov (NEW – 22. 8. 2026)

Zbernica udalostí a načítavanie modulov sa rozšírili bez narušenia starých modulov. Zodpovedajúce listenere sa vždy zaregistrujú skôr, ako sa inicializujú zodpovedajúce moduly.

  • Events::triggerWithVeto() a nová trieda Dotsystems\App\Parts\Veto — explicitné zastavenie pred vratnou akciou. Bežný trigger() naďalej ignoruje návraty listenerov, vrátane objektu Veto.
  • Dotsystems\App\Parts\Extender — uvážená opt-in náhrada významného výstupu pre tento request. Vlastník použije exists() / call(); bežný výsledok ho nahradí a isOriginal() pokračuje v pôvodnej logike iba pre jedinečný marker original(). V Listeners::register() sa iba prihláste na dotapp.module.shop.loading; extend() volajte v tom callbacku reťazcom ako 'Loyalty:Pricing@quote!'.
  • Listeners::initializeRoutes() — listener sa môže zobudiť na vlastných maskách URL bez spustenia initialize() modulu. Ak metódu vynecháte, zdedí mapu modulu.
  • php dotapper.php --optimize-modules zapisuje formát optimalizátora v2 ($modules, $listeners, $modulesAutoLoaderVersion = 2). Súbor v1, ktorý exportuje len $modules, ostáva kompatibilný.
  • dotapp.catchall stále beží ako prvý na každom inom trigger() (aj na triggerWithVeto()), aby ste všetky udalosti videli na jednom mieste. Len debug — z catchall nepersistujte.

Návody: trigger with veto · samostatné routy listenerov · Extender · dotapp.catchall.


Konvencie

Verejné API používa fasády, modulové kontroléry a malú sadu konvencií:

Fasády

Fasády (Router, Route, DB, Request, Renderer::new()) sú verejné API pre kľúčové služby. Aplikačné trasy sa registrujú v module.init.php každého modulu.

Fasády Router a Route

Použite fasády Router a Route (sú to vzájomné aliasy) v module.init.php. Príklad: Router::get('/helloworld', 'HelloWorld:Home@index!', Router::STATIC_ROUTE).


// app/modules/HelloWorld/module.init.php → initialize($dotApp)
Router::get('/helloworld', fn() => 'Hello World', Router::STATIC_ROUTE);
Route::get('/helloworld', fn() => 'Hello World', Router::STATIC_ROUTE);
Router::get('/helloworld', 'HelloWorld:Home@index!', Router::STATIC_ROUTE);
Route::get('/helloworld', 'HelloWorld:Home@index!', Router::STATIC_ROUTE);
                    
Fasáda DB

Použite fasádu DB. Dopyty vykonávajte cez DB::module('RAW').


DB::module('RAW')->q(function ($qb) {
    $qb->select(['id', 'title'])->from('helloworld_notes')->where('id', '=', 1);
})->all();
                    
Fasáda Request

V kontroléroch čítajte aktuálnu požiadavku z argumentu $request. Fasáda Request je k dispozícii, keď ste mimo callbacku kontroléra. Prichádzajúce hodnoty sú automaticky chránené. data() je escapovaná kópia (bezpečná na výpis). data(true) sú pôvodné hodnoty — heslá, dešifrovanie, persistencia. Polia kanála: $request->data(true)['data'].


Request::getPath(); // Get the current request path
Request::getMethod(); // Get the HTTP method (e.g., GET, POST)
Request::data();        // protected/escaped copy — OK to print
Request::data(true);    // original values — passwords, decrypt, compare
$request->form(['POST'], 'myForm', function ($request) {
    return 'Form submitted!';
}, function () {
    return 'Invalid form';
});
                    


Inštancia DotApp

Uprednostnite fasády: Router::, DB::, Renderer::new(), Config::, Events::, Bridge::listen, DSM::use(). Keď potrebujete samotné jadro (DI bind/resolve, unprotect, ajaxReply, reaktívny polling), získajte ho takto:


use \Dotsystems\App\DotApp;
$dotApp = DotApp::DotApp();
            

Trasy registrujte pomocou Router::get (a ostatných metód HTTP slovies) v metóde initialize($dotApp) modulu.



Kľúčové vlastnosti DotApp

Jednoduchosť bez kompromisov

DotApp spája intuitívny návrh s vysokým výkonom. Nepotrebujete zložité nastavenia ani nadmerné konfigurácie – stačí definovať trasy a moduly a všetko ostatné sa spravuje samo. Trasy sa spracúvajú len tam, kde sú potrebné, a na zachovanie výkonu nie sú potrebné žiadne ďalšie kroky – všetko je automatické a efektívne.


// Example of simplicity when working with the DotApp framework
namespace Dotsystems\App\Modules\HelloWorld\Controllers;

class TestController1 extends \Dotsystems\App\Parts\Controller {
    public static function testMiddlewareFn($request) {
        return "Hello " . $request->body(); // Adds text at the beginning
    }
    
    public static function mainFn($request) {
        return $request->body() . "World"; // Adds text at the end
    }
}

// Simple controller call
// app/modules/HelloWorld/module.init.php → initialize($dotApp)
Router::get("/home", "HelloWorld:TestController1@mainFn!", Router::STATIC_ROUTE)
    ->before("HelloWorld:TestController1@testMiddlewareFn!");

// Result for /home: "Hello World"
            
Dôraz na nízku spotrebu zdrojov

DotApp drží požiadavky na pamäť na minime – namiesto načítavania obrovských štruktúr trás a konfigurácií spracúva len to, čo je práve potrebné. To znamená rýchlejší štart a výborný výkon aj na slabších serveroch.

Rýchle spracovanie trás

DotApp inteligentne filtruje iba relevantné moduly a ich trasy, čím odstraňuje zbytočné vyhľadávanie. Výsledkom je rýchle načítanie aj pri tisícoch trás.

Príklad

Ukážka rýchlosti smerovania: Pred zobrazením tejto stránky sa do routera automaticky a náhodne pridalo 1000 jedinečných statických a 1000 jedinečných dynamických, zámerne neusporiadaných trás. Cieľom bolo ukázať rýchle načítanie napriek 2000 extra zbytočným trasám. Žiadna z nich nezodpovedá aktuálnej URL, takže všetky musia prejsť procesom párovania v routeri.


$p = rtrim((string) Config::module('Docs', 'prefix'), '/') ?: '/documentation';

for ($i = 0; $i < 1000; $i++) {
    $path = $p . '/_routa' . $i;
    Router::any($path, function () use ($path) {
        return "This is route: " . $path;
    }, Router::STATIC_ROUTE);
}

for ($i = 0; $i < 1000; $i++) {
    $path = $p . '/_routa' . $i . '(?:/{language})?';
    Router::any($path, function () use ($path) {
        return "This is route: " . $path;
    });
}

// Try it out: /documentation/_routa7
                            


Zobrazenie stránky vrátane vytvorenia trás, smerovania a generovania kódu šablónovým systémom trvalo:

PHP version: 8.3.15 fpm-fcgi
Script execution time: 0.054862 s
Memory used by the script: 1.05 MB
Peak memory usage: 1.66 MB
Loaded modules: 6

Modulárna efektivita s obojsmerným prepojením

DotApp spracúva iba trasy aktívneho modulu, čím šetrí zdroje. Moduly sa môžu navzájom načítavať: listener jedného modulu môže načítať iný a nadradený modul môže aktivovať podmoduly. Kombinácie zostávajú explicitné v module.listeners.php a initializeRoutes(). Listener môže mať vlastné masky, takže sa zobudí bez spustenia initialize() modulu.

Kaskádové načítanie modulov

Ak modul závisí od iného (napr. BBB potrebuje XXX), DotApp automaticky načíta XXX pred dokončením BBB. To zaisťuje spoľahlivosť – žiadne chyby v dôsledku chýbajúcich závislostí – a systém zostáva ľahký, pretože sa načítava len to, čo je potrebné.

Dynamická správa závislostí cez triggery a listenere

Každý modul má triggery ako init.start, loading, loaded a ďalšie, na ktoré listenere reagujú. Napríklad listener dotapp.module.Module1.loading môže spustiť načítanie modulu 2, ak je modul 1 aktívny. Funkcia load() zaisťuje, že sa modul načíta iba raz, či už kaskádovo (zhora nadol) alebo obojsmerne (zdola nahor).

Poznámka: Názvy triggerov nerozlišujú veľkosť písmen, takže dotapp.module.Module1.loading a Dotapp.Module.Module1.Loading sú ekvivalentné, ale pre konzistenciu odporúčame formát dotapp.module.ClassName.eventName.

Každý Events::trigger() aj Events::triggerWithVeto() okrem samotného dotapp.catchall najprv spustí túto debug udalosť, aby ste všetky triggery videli na jednom mieste. Použite ju len na ladenie. Výnimka v catchall listeneri preskočí pomenovanú udalosť. Kontrakt a ukážka: Events and listeners — dotapp.catchall. Zastavenie pred akciou: trigger with veto (trieda Veto). Listenere môžu mať vlastné routy: samostatné routy listenerov.


use Dotsystems\App\DotApp;
use Dotsystems\App\Parts\Events;

Events::on("dotapp.module.shop.loading", function () {
    DotApp::DotApp()->module("Cart")->load();
});
            
Automatické riešenie závislostí a DI

Moduly a ich závislosti sa načítavajú automaticky – stačí definovať logiku v initializeCondition() alebo v listeneroch. Dependency Injection (DI) je jednoduchá a efektívna – služby sa registrujú (napr. singleton) a DotApp ich dodá tam, kde sú potrebné, bez zbytočnej réžie.

Služby registrujte v initialize($dotApp) modulu pomocou singleton / bind. Kontroléry sú public static a prijímajú $request.


public function initialize($dotApp) {
    \Dotsystems\App\DotApp::DotApp()->singleton('cache', function () {
        return new CacheService();
    });
}

namespace Dotsystems\App\Modules\HelloWorld\Controllers;

class Home extends \Dotsystems\App\Parts\Controller {
    public static function index($request) {
        $cache = \Dotsystems\App\DotApp::DotApp()->resolve('cache');
        return "Hello World";
    }
}
            
Prvý callback vyhráva

Pre každú URL sa zachová iba prvý zodpovedajúci callback – následné pokusy o registráciu sa ignorujú, čo zvyšuje výkon a predchádza konfliktom.


Router::get('/documentation/test1', "HelloWorld:Home@index!");
Router::get('/documentation/test1', function () { return "Ignored"; });
// Only the first definition is used
            
Škálovateľnosť pre malé aj veľké projekty

DotApp je ideálny pre malé weby aj komplexné aplikácie – nízke nároky a vysokú rýchlosť si zachováva bez ohľadu na rozsah projektu. Veľké moduly môžete rozdeliť na menšie časti, ktoré sa podľa potreby načítavajú rekurzívne.

Žiadna zbytočná réžia

DotApp sa sústreďuje na podstatu – rýchle smerovanie, minimálne využitie zdrojov a jednoduchosť použitia. Nezaťažuje vás funkciami, ktoré nepotrebujete.

Šablónový systém

Views a layouts žijú v module. Hodnoty vypisujte pomocou {{ var: $title }}, fragmenty vkladajte cez {{ layout:partials/header }} a z kontroléra vykresľujte cez Renderer::new()->module('HelloWorld')->setView('hello'). Úplná referencia: Šablónový systém.

DotApp Bridge

Živé prepojenie – most medzi frontendom a backendom. Stačí použiť jednoduchý kód:

<button {{ dotbridge:on(click)="newsletter.subscribe(newsletter.email)" }}>Subscribe</button>

a na strane PHP v initialize() modulu:


use Dotsystems\App\Parts\Bridge;
use Dotsystems\App\Parts\Router;

$urls = ['/newsletter', '/newsletter/'];
Bridge::listen($urls, "newsletter.subscribe", function ($request) {
    $email = $request->data(true)['data']['newsletter.email'] ?? '';
    return ['ok' => true, 'email' => $email];
}, Router::STATIC_ROUTE);
            

Tlačidlo sa automaticky prepojí s PHP funkciou; bohaté možnosti budú predstavené v dokumentácii.

Príklad vygenerovaného kódu:


<button  dotbridge-key="ka1IOyMZu@s0JMm0CkrGRLttWzOBN00" dotbridge-id="oCNFKj6Q9rbtPcSHgQaaWRSlRiKnfKp8tysgr4aRbTbKLRq2026100304020402040209090204f921631df44b3899f1e1252731ee8d98" dotbridge-event="click" dotbridge-data="oFZLMSbBaH1hFbdGfwialXRjQjlLSzM4dDkrZTBnTXl5VFhBeUVkRG5NSjhCYVVXQkd2MEN0c0paTlk9" dotbridge-data-id="KeTF+Yto11i24EaxOLClWlUxRVNlRDFKVVJhSmRhWTlaZDlpS09oVUh1dlYveUlCSk8xVWNoVmdLUVRnL3BvVzZzaHBIOFgxUTNWa3JnbENzQUxNQTJNRmpBeDQ3YkJVTUxNa29tSjJkMTBlcDg4NkVaSnBJMktIRVlBPQ==" dotbridge-function="newsletter.subscribe" dotbridge-inputs="newsletter.email">Subscribe</button>
            
HTML

DotApp je šitý na mieru vývojárom, ktorí chcú efektívny nástroj bez zbytočností. Ponúka rýchlosť, nízke nároky a jednoduchosť, ktorá uľahčuje prácu. Je to framework, ktorý dokazuje, že menej môže byť viac – s výsledkami, ktoré hovoria samy za seba.

Vyskúšajte DotApp a presvedčte sa sami!