magicsunday / webtrees-module-base
Shared PHP base classes (date, name, image, place processors plus module helpers) for the magicsunday/webtrees-* chart modules.
Package info
github.com/magicsunday/webtrees-module-base
pkg:composer/magicsunday/webtrees-module-base
Fund package maintenance!
Requires
- php: 8.3 - 8.5
- ext-dom: *
- ext-intl: *
- ext-mbstring: *
- fisharebest/webtrees: ~2.2.0
Requires (Dev)
- infection/infection: ^0.35.2
- magicsunday/coding-standard: ^3.0
- phpat/phpat: ^0.12.4
Suggests
- phpunit/phpunit: Needed to extend MagicSunday\Webtrees\ModuleBase\Testing\AbstractCatalogueStructureTestCase in the consuming module's own test suite
Provides
None
Conflicts
None
Replaces
None
README
webtrees-module-base
Shared PHP base classes for the magicsunday family of webtrees chart modules. Centralises the name, image and date processing logic, common models, and module helpers (GitHub release-version checking with file cache) so the chart modules do not have to reimplement the shared pieces.
This package ships no UI of its own — it is consumed as a Composer dependency by:
- webtrees-fan-chart — SVG ancestor fan chart
- webtrees-pedigree-chart — SVG pedigree chart
- webtrees-descendants-chart — SVG descendants chart
- webtrees-statistics — genealogy statistics dashboard (consumes only the shared core plus
IsoCountryMap)
Scope note: not every base component is consumed by every module yet. The place-name subsystem (
PlaceProcessor, itsPlaceFormat*/PlaceStylemodels) and the compact, generation-aware date API (DateProcessor'sgetFormatted*/get*Fullmethods,CompactDateFormatand theSymbolsenum) are currently used only by the fan chart; the pedigree and descendants charts consume just the shared core (name/image processing andDateProcessor's legacy locale-aware methods).IsoCountryMapis shared by the fan chart and the statistics module. These still-fan-only pieces live in the base as deliberate pre-investment, so a second consumer can adopt them without a namespace move — as the statistics module already did forIsoCountryMap.
Requirements
- PHP 8.3 - 8.5 with extensions
dom,intlandmbstring - webtrees
~2.2
Installation
This package is pulled in automatically when you install any of the chart modules above. To depend on it directly from your own webtrees module:
composer require magicsunday/webtrees-module-base
If your module uses ImageProcessor (silhouette URL handling), declare the marker interface so the constructor's intersection type is satisfied:
use Fisharebest\Webtrees\Module\AbstractModule; use Fisharebest\Webtrees\Module\ModuleCustomInterface; use Fisharebest\Webtrees\Module\ModuleCustomTrait; use MagicSunday\Webtrees\ModuleBase\Contract\ModuleAssetUrlInterface; class Module extends AbstractModule implements ModuleCustomInterface, ModuleAssetUrlInterface { use ModuleCustomTrait; // ... }
ModuleCustomTrait already provides the required assetUrl() method — only the interface declaration is new.
What's inside
src/Processor/
DateProcessor— generation-aware date formatting (compactgetFormatted*/get*FullAPI for tight chart labels; locale-aware legacygetBirth*/getDeath*/getLifetimeDescriptionAPI for everything else; marriage dates only via the compact API —getFormattedMarriageDate(),getFormattedMarriageDateOfParents(),getMarriageDateFull())NameProcessor— name extraction from webtrees name HTML (DOM/XPath based — splits first/last/preferred, handles starredname, alternative and married names)ImageProcessor— highlight image + silhouette URL resolutionPlaceProcessor— place name shortening (configurable parts) for chart labels
src/Model/
Symbols— backed enum for genealogical symbols (Birth ★, Death †, en-dash separator, MarriageDateUnknown sentinel)NameAbbreviation— backed enum +resolve()helper for the name-abbreviation strategy used in chart labels (auto / given-first / surname-first)PlaceStyle— enum for the way a place name is shortened (Full,Levels,CityCountry,CityIso2,CityIso3)PlaceFormatSpec—final readonlyvalue object holding a fully resolved place-formatting instruction (style, level count, from-which-end)PlaceFormatChoice— backed enum for the place-detail options a module offers in its configuration; label-free, so the consuming module supplies its own translations
src/Support/
CompactDateFormat— derives a locale-aware, compact (numeric) date format string from a locale's CLDR/ICU short-date pattern, forDateProcessor's compact APITextDirection— resolves script direction (LTR/RTL) for arbitrary stringsLocale/IsoCountryMap— maps free-text country names from GEDCOM PLAC lines to ISO-3166-1 codes, built onext-intl; used byPlaceProcessor's country-resolving styles
src/Module/
VersionInformation— checks GitHub releases for newer module versions, with file cache. No chart module references it directly; it is instantiated by this library's ownTraits\ModuleCustomTrait::customModuleLatestVersion()(which overrides webtrees' core method), and the webtrees control panel invokes that trait method
src/Traits/
ModuleCustomTrait— overrides webtrees'customModuleLatestVersion()to route throughVersionInformation, and provides theassetUrl()helper the marker interface declaresModuleChartTrait— shared chart-module helpers for the consuming modules
src/Facade/
ModuleAwareDataFacadeTrait/RouteAwareDataFacadeTrait— traits a chart module's DataFacade uses to receive the module instance and route access
src/Contract/
ModuleAssetUrlInterface— marker interface that declares webtrees'assetUrl()helper soImageProcessorcan be type-narrowed withoutmethod_existsruntime checks
src/Testing/
AbstractCatalogueStructureTestCase, a PHPUnit test case that checks the translation catalogues a module ships. Extend it in the module'stests/and return the catalogue directory fromlanguageDirectory():
use MagicSunday\Webtrees\ModuleBase\Testing\AbstractCatalogueStructureTestCase; use PHPUnit\Framework\Attributes\CoversNothing; #[CoversNothing] final class CatalogueStructureTest extends AbstractCatalogueStructureTestCase { protected static function languageDirectory(): string { return __DIR__ . '/../resources/lang'; } }
webtrees merges a module's catalogue over the catalogue of webtrees itself, so a plural entry with the wrong number of forms or an empty form damages the translation of the same text everywhere in the installation. The checks guard against that. PHPUnit is a dev dependency of the consuming module. The subclass carries #[CoversNothing] because it covers no class. A phpunit.xml with requireCoverageMetadata="true" demands the attribute. A module whose catalogues carry no plural string overrides shipsPluralEntries() and returns false. The case then checks that no plural entry exists, so one added later fails until the override is removed.
Development
See AGENTS.md for the full development workflow, including the
make link-base sibling-clone pattern and the tooling-parity policy with
consumer modules.
Quick reference — this library has no module-local container; PHP runs through the webtrees-docker buildbox:
cd /path/to/webtrees-docker && docker compose run --rm buildbox bash -c \ 'cd /var/webtrees/app/vendor/magicsunday/webtrees-module-base && composer ci:test'
Substitute composer install, composer update, composer ci:cgl or
composer ci:rector for the last command as needed.
License
GPL-3.0-or-later — see LICENSE.