The Kernel and the Boot Lifecycle
Kernel\Kernel is the equivalent of Android's Zygote + System
Server combined: the single place that builds every framework
subsystem, wires it together, and prepares it to process Updates.
Two Lines to Boot
use Aicrion\Tandroid\Kernel\Kernel; $kernel = Kernel::fromConfigFile(__DIR__ . '/config/aicrion.yaml')->boot();
fromConfigFile() reads the YAML file via
Config\FrameworkConfig::fromFile(). boot() performs every step
below, in this exact order:
What Happens Inside boot()
- Build the DI Container. A
Symfony\Component\DependencyInjection\ContainerBuilderis created. - Register
FrameworkConfig. Registered as a synthetic service so any class (including plugin Activities and Repositories) can type-hint it in their constructor and receive it automatically. - Build the Cache Pool.
Cache\CachePoolFactory::create()builds either a Redis+filesystem chain or filesystem-only pool based onredis_dsn(serviceaicrion.cache). - Configure the HTTP Client and Telegram calls. A
Symfony\Component\HttpClient\HttpClientis built and connected to the staticTelegramfacade viaApi\Telegram::configure()— from this point on,Telegram::message(),Telegram::photo(), and every other facade method work. - Discover plugins.
Package\PackageManager::discover()reads everyplugins/*/manifest.phpand adds it to the manifest list. - Run pending migrations automatically.
PackageManager::runPendingMigrations()invokesPackage\MigrationRunnerfor each plugin — no CLI command required. Full details in Database and Doctrine. - Build the EntityManager.
Database\EntityManagerFactory::create()uses the list of entities from every plugin (Manifest::$entities) to build a singleDoctrine\ORM\EntityManagerInterface, registered in the container as a synthetic service. - Register Activities.
FallbackActivityMarker(the built-in "404 Activity") is always registered; then every Activity declared inManifest::$activitiesis registered withautowire(). - Wire the navigation core.
IntentResolver(with the IntentFilter registry collected from every manifest),BackStackStore,ViewModel\StateStore, and finallyActivityManagerare built. - Wire Broadcasts.
Broadcast\BroadcastDispatcheris built, and every Receiver declared inManifest::$receiversis registered withautowire()and wired into the dispatcher. compile(). The container is frozen; immediately after, synthetic services (FrameworkConfigandEntityManagerInterface) are set with their real instances (this can't be done beforecompile()).
After boot(), the Kernel instance is ready to process Updates via
handle().
Processing an Update
$view = $kernel->handle($update);
handle() performs these steps:
- If this is the first time this
chat_idhas been seen, theBroadcast\Event\UserJoinedEventevent is published (see Broadcasts for details). ActivityManager::dispatch($update)is called — this method resolves the appropriate Intent, instantiates the target Activity, runs its lifecycle, and if aNavigationRequestis returned, follows the navigation chain right then and there (see Activities and Intents).- If a
Viewwas produced, it is sent immediately throughApi\Telegram::message()to the same chat — the host code (webhook.php/bin/poll.php) doesn't need to send the reply itself.
Important: Kernel::handle() both runs the Activity and delivers
the reply. If you want to control the reply yourself (e.g. for
logging or testing), you can work directly with
ActivityManager::dispatch(), which only returns the View without
sending it.
ActivityManager — the Heart of the Kernel
Kernel\ActivityManager is the equivalent of Android's
ActivityManagerService. Its responsibilities:
- Resolving the Intent. Via
IntentResolver— either explicit (fromcallback_data) or implicit (based on registered#[IntentFilter]s). - Managing the lifecycle. Calling
onCreate/onNewIntent/onResumein the correct order, andonPauseon the previous Activity. - Following the navigation chain. If a lifecycle hook returns a
NavigationRequest(whether fromstartActivity()orfinishWithResult()),ActivityManagerimmediately follows it — within the same incoming Update, with no extra round-trip to Telegram. The chain depth is limited toMAX_CHAIN_DEPTH = 8to prevent infinite loops. - Injecting the StateStore. If an Activity uses the
Activity\HasViewModeltrait,ActivityManagerautomatically injectsStateStore, and persists the ViewModel after every lifecycle call. - Managing the Back Stack. Via
BackStackStore(backed by cache), keeping each user's stack so the virtual Back button and returning to a previous Activity work correctly.
Overall Flow of an Update
Update (Webhook/Polling)
│
▼
Kernel::handle()
│
▼
ActivityManager::dispatch()
│
▼
IntentResolver::resolve() ──▶ explicit or implicit Intent
│
▼
instantiate(Activity) from the DI Container
│
▼
onCreate/onNewIntent → onResume
│
├── non-null NavigationRequest? ──▶ continue the chain (recursively)
│
▼
BackStackStore::push()
│
▼
View is returned
│
▼
Kernel sends a real message to Telegram