Changelog
All notable changes to phpbotgram are documented in this file. The format follows Keep a Changelog and the project adheres to Semantic Versioning.
0.1.0 — Initial release
First public release. Ports aiogram 3.29.0 / Telegram Bot API 10.1 to PHP 8.5 with full feature parity for the core framework. Behaviour divergences from upstream are documented inline at the call site (# Divergence:
comments) and in docs/superpowers/specs/
.
Added
Bot API 10.1 schema sync
- Generated Telegram Bot API surface synced from aiogram 3.29.0 / Bot API 10.1.
-
Rich-message support:
InputRichMessage,RichMessage,RichBlock*,RichText*,Link,InputMediaLink, and the generatedRichBlockUnion/RichTextUnionhelpers.Bot::sendRichMessage,Bot::sendRichMessageDraft,Message::answerRich, andMessage::replyRich.EditMessageText::$richMessagefor editing rich messages without breaking the existing positional$textshortcut.
- Chat-join request query methods:
Bot::answerChatJoinRequestQueryandBot::sendChatJoinRequestWebApp. - Serializer support for PHPDoc-backed generated list parameters such as
list<RichBlock>and nested rich-block table structures. - Serializer support for the recursive rich text wire union: a plain string, a rich-text object, or a list mixing strings and rich-text segments.
- README badges now track aiogram 3.29.0, Bot API 10.1, and the current passing test count.
- Narrative docs for sending, replying with, streaming, and reading rich messages.
Bot client and HTTP transport
Botfacade that builds typed API method DTOs for every Telegram Bot API method (codegen output, regenerate viamake regenerate).BaseSessionabstract withprepareValue,checkResponse,buildResponse,deserializeResult, and the polymorphicunion:ReturnsType discriminator.AmphpSessionproduction HTTP adapter built onamphp/http-client ^5(form-urlencoded bodies; multipart deferred).- Typed exception hierarchy:
TelegramApiException,TelegramBadRequestException,TelegramConflictException,TelegramForbiddenException,TelegramNetworkException,TelegramNotFoundException,TelegramRetryAfter,TelegramServerException,TelegramUnauthorizedException,TelegramEntityTooLarge,TelegramMigrateToChat,RestartingTelegram. DefaultBotProperties(parseMode, link-preview aggregation,ArrayAccessread-only surface).TelegramApiServerproduction / test / fromBase factories for the local-server mode.Serializer::dump()/Serializer::load()for snake_case wire conversion withWireNamesper-class overrides and recursive TelegramObject / union resolution.BotShortcutstrait providinggetId,me,context,downloadFile,download, plus the FiberLocal current-bot binding.
Dispatcher, routers, middleware
DispatcherextendsRouterand owns the polling loop (startPolling/runPolling), graceful shutdown via signals, and the 25-update-type observer map.Routercascade with parent-lockedincludeRouter/includeRoutersand post-include immutability.-
TelegramEventObserverfor every update type, with:- registration via Closure or any callable (Filter subclass, MagicFilterAsFilter, anonymous invokables — all wrapped via
Closure::fromCallable). - dual decorator surface (
$observer($cb, filters:)eager vs.$observer(filters:)factory). - global filter chain plus per-handler filter pipeline.
- outer / inner middleware stacks with proper composition order.
- registration via Closure or any callable (Filter subclass, MagicFilterAsFilter, anonymous invokables — all wrapped via
BaseMiddlewareabstract for both outer and inner roles.Flagssystem with#[Flag]attribute and imperativeFlagDecorator::attachfor closures, with merge semantics ("manual wins").
Filters and the F-DSL
Commandfilter with prefix/case-sensitivity/bot-username gating andCommandObjectparsed result.CommandStartfor/startdeep links.CallbackDataabstract withpack/unpack/filter()static builder,#[CallbackPrefix]attribute, anddecodeComplex()for nested scalars / enums (int- and string-backed).StateFilterandState::__invokefor FSM state predicates.ChatMemberUpdatedFilterforchat_member/my_chat_membermember transition filtering.ExceptionTypeFilter,ExceptionMessageFilterfor error handlers.MagicDatafilter bridging the F-DSL against the dispatch data dict.Filter::all(),Filter::any(),Filter::invertOf()combinators returningAndFilter/OrFilter/InvertFilter.Fconstant +MagicFilterchain DSL with comparator ops (equals,notEquals,in_,contains,startsWith,endsWith,regexp,gt/gte/lt/lte,between,as_).- Typed
F-DSLfield wrappers:IntField,StringField,BoolField,DateTimeField,NullableIntField,NullableStringField,NullableObjectField,RegexField,BaseField.
FSM
FsmContextwith state/value accessors (getState,setState,getValue,setData,updateData,getData,clear).FsmStrategy(UserInChat,Chat,GlobalUser,UserInTopic,ChatTopic) plusStorageKeyresolver covering chat / user / message thread isolation.-
Storage backends:
MemoryStorage(in-process default).RedisStorage(integration tests gated byPHPBOTGRAM_TEST_REDIS_DSN).MongoStorage(integration tests gated byPHPBOTGRAM_TEST_MONGO_DSN).
StatesGroupandState(explicit; no metaclass auto-discovery).-
Scenes:
Scenebase with reflection-drivensceneConfig()extraction.SceneWizard(enter / exit / leave / retake / goto / back).SceneRegistrywith eageradd([Scene::class])wiring.ScenesManagerinjected as a handler kwarg.#[SceneState],#[OnMessage],#[OnCallbackQuery],#[OnChatJoinRequest], … attribute markers.Afterlifecycle directives (Enter, Exit, Back, etc.).
Webhook
AmphpServer::run()wrappingamphp/http-server ^3with shutdown hooks tied to the dispatcher lifecycle.SimpleRequestHandler(single bot) andTokenBasedRequestHandler(multi-tenant routing on the URL path token).IpFiltermiddleware enforcing Telegram's CIDR ranges.Setup::register()to splice the bot lifecycle into an existing amphp/http-server instance.- Constant-time secret-token validation via
hash_equals.
Utils
TextDecorationwithHtmlDecorationandMarkdownDecoration(Markdown V2) strategies — entity-aware escaping, full V2 special-char coverage, expandable-blockquote support.DeepLinkingfor/startpayload encode/decode with WeakMap-cached bot binding.- Keyboard builders:
InlineKeyboardBuilder,ReplyKeyboardBuilder, sharedKeyboardBuilderbase. MediaGroupBuilderfor grouped media uploads.ChatActionSender+ChatActionMiddlewarewithDeferredCancellation-managedraceDelayticking.CallbackAnswerDTO +CallbackAnswerMiddleware(pre/post modes).WebAppsignature verification (WebAppSignature,WebAppInitData,WebAppUser,WebAppChat) usingsodium_crypto_sign_verify_detachedfor Ed25519 andhash_equalsfor HMAC compare.AuthWidgetfor the Telegram Login Widget data validation.Payload,Link,Tokenparsing utilities.Backoff+BackoffConfigfor exponential-with-jitter retry pacing used by the polling loop.DeepLinkTypeenum classifying/startpayload kinds.
Tooling
tools/generator/producessrc/Types/Generated/,src/Methods/Generated/,src/Enums/Generated/, plus theBot.phpfacade from the upstream Telegram API spec.scripts/coverage-gate.phpenforces per-module coverage floors (Bot ≥80%, Session ≥75%, Dispatcher/Router/Filters/FSM ≥90%).- Make targets and composer scripts:
test,stan,lint,fix,regenerate,coverage,coverage-gate,docs-api(script and target names match). - 12 runnable examples under
examples/mirroring upstream aiogram's example surface. - Deployment templates under
deploy/(nginx reverse proxy, systemd unit, Docker compose). - API documentation pipeline:
phpdocumentor/shimcomposer dev-dep drops the official phpDocumentor v3 phar intovendor/bin/phpdoconcomposer install;composer docs-api(ormake docs-api) renders the site intobuild/docs/api/. GitHub Actions (.github/workflows/docs.yml) publishes the site to GitHub Pages on every push tomaster.
Added — narrative documentation
- Diataxis-structured narrative site under
docs/guide/en/(47 committed pages: 1 top-level landing + 6 tutorial + 22 how-to + 17 concept + 1 reference stub) plus 2 build-time copies of CHANGELOG.md and CONTRIBUTING.md → 49 rendered pages. phpdoc.dist.xml.tpltemplate (envsubst →phpdoc.dist.xml) rendering narrative + API into a single phpDocumentor v3 site underbuild/docs/api/..phpdoc/template/components/header.html.twigoverride injecting a navbar language+version switcher driven byversions.jsonandlanguages.jsonserved from the gh-pages branch root.-
Seven post-build CI gates in
scripts/build-docs.sh:check-docs-build-log.phpgreps phpdoc stderr for unresolved refs / orphan docs / missing-title / missing-alt-text warnings.check-docs-links.phpverifies every sentinel-URL (https://api.phpbotgram.local/...) points at a real API page.rewrite-api-links.phpHTML-aware DOM rewrite of sentinel URLs toclasses/..., with HTML5-doctype preservation and a 50% size-shrink sanity guard.check-internal-links.phpwalks rendered HTML for non-sentinel internal links and validates them against<base href>, with macOS/var-vs-/private/varrealpath canonicalisation.lint-docs.phprunsphp -lon every fenced```phpblock and bans inline raw HTML in narrative prose.check-docs-examples.phpverifies everyexamples/X.phplink resolves to an existing file.markdownlint-cli2@0.22.1for prose style.
.github/workflows/docs.ymlmigrated from Pages "workflow mode" to "branch mode" viapeaceiris/actions-gh-pages@v4; publishes master pushes to/en/dev/.- New
.github/workflows/docs-release.ymlpublishes tag pushes to/en/<tag>/+/en/latest/+ updatesversions.jsonwith a semver-shape validation gate at job entry. update-versions-json.phpatomic CLI withstable=autosemver-aware backport handling.copy-root-docs.phpmirrors project-rootCHANGELOG.mdandCONTRIBUTING.mdintodocs/guide/en/pre-build (gitignored copies with AUTO-GENERATED banner; mtime preserved).CONTRIBUTING.mdat project root with "Fenced-block conventions" documenting the```php(lint-gated) vs```php-fragment(lint-skipped) distinction..markdownlint.jsoncconfig compatible with the copied CHANGELOG/CONTRIBUTING (MD024 siblings_only for Keep-a-Changelog shape).composer.jsonconstraint forphpdocumentor/shimtightened from^3to~3.10.0.composer docs-apiandmake docs-apiboth delegate tobash scripts/build-docs.sh.
Quality bars
- 2168 PHPUnit tests with 6929 assertions (9 env-gated skips). Real Redis / MongoDB integration tests gated on
PHPBOTGRAM_TEST_REDIS_DSN/PHPBOTGRAM_TEST_MONGO_DSNenv vars. - PHPStan level 9, clean.
php-cs-fixerenforced.- Coverage gate passes at the documented per-module floors.
Known divergences from aiogram 3.29.0
- No async/await keywords — fiber-based runtime via amphp v3 / Revolt makes the dispatch path synchronous from the caller's perspective.
- Scenes are explicit (no metaclass auto-discovery) — register via
SceneRegistry::add([Scene::class])because PHP has no metaclasses. - No
model_dump/model_validate—Serializer::dump/Serializer::loadcover the same surface with PHP reflection. Filter::__invokesignature is(object $event, mixed ...$kwargs)(variadic) to round-trip dispatcher kwargs without case translation.- Handler kwargs use literal name matching (no snake_case ↔ camelCase conversion); filter return keys,
workflowDatakeys, and handler parameter names must agree.