Developer’s guide to upgrading from webtrees 2.2 to 2.3
This document describes the breaking changes and notable API differences between webtrees 2.2.x and 2.3.x that affect authors of third-party modules.
jQuery has been removed
None of our dependencies require jQuery, and our own code has been rewritten to use “vanillaJS”. So jQuery has been removed from our vendor.js bundle.
Routing and request handlers
To respond to HTTP requests, a module needs to registar itself as a handler for the URL, and provide methods to respond to requests.
To register a URL, the syntax has changed slightly.
- Old:
Registry::routeFactory()->routeMap()->get($name, $url, $handler) - New:
Registry::routeFactory()->routeMap()->add($url, $handler, $optional_middleware)
To handle the request, we now have separate methods for each HTTP verb (GET, POST, PUT, DELETE, etc.)
- Old:
public function handle(ServerRequestInterface $request): ResponseInterface - New:
public function get(...$params): ResponseInterface
The old ServerRequestInterface method will continue to work.
However, you can instead create methods ->get(), ->post(), ->delete(), etc. which
accept requests for that HTTP method. These methods can now typehint their parameters,
and have them injected directly.
e.g. public function get(Tree $tree, Individual $xref, int $generations, string $layout, Sex $sex)
Time and Timestamps
Our Timestamp class is replaced by CarbonImmutable.
Our TimeFactory class is replaced by PSR-20 ClockInterface.
// Create a timestamp
$timestamp = Registry::timestampFactory()->fromEpoch(time());
$timestamp = Registry::timestampFactory()->now();
$timestamp = Registry::timestampFactory()->fromString($date_string);
// Use CarbonImmutable methods directly
$timestamp->diffForHumans();
$timestamp->format('Y-m-d');
$timestamp->addDays(7);
// Julian day is now available from the timestamp factory, rather than a timestamp
$julian_day = Registry::timestampFactory()->todayJulianDay();
HTTP requests
Guzzle is replaced by PSR-18 ClientInterface, with the symfony/http-client implementation.
$http_client = Registry::container()->get(ClientInterface::class);
$request_factory = Registry::container()->get(RequestFactoryInterface::class);
$request = $request_factory->createRequest('GET', 'https://example.com/api');
try {
$response = $http_client->sendRequest($request);
} catch (ClientExceptionInterface) {
// Handle connection failure
}
Languages and localisation
Our language modules have been replaced with language classes, in /app/I18N.
Logic from the package fisharebest/localization has been imported.
Previously, we used translation to generate dates and relationship names. These are now generated by the language classes, using custom logic for each language.
The main updates to I18N are;
I18N::language(): LanguageInterfacethe current language - replaces->locale()I18N::languageTag()e.g.en-USI18N::formatDate($date)I18N::formatList($array)-['red','green','blue'] => 'red, green, blue'I18N::formatListAnd($array)-['red','green','blue'] => 'red, green and blue'I18N::formatListOr($array)-['red','green','blue'] => 'red, green or blue'
The translator/translation functions are rewritten. To create an array of translations from a .PO or .MO file, use the following.
$stream = fopen($po_file, 'rb');
$translations = Translation::fromPoStream($stream)->toArray();
fclose($stream);
Generating thumbnail images
We no longer use intervention/image. Instead, we call GD functions directly.
ImageMagick is no longer needed or supported, as GD is faster and requires less memory.
ImageFactoryInterface has been simplified. Generating watermarks is (will soon be) a
separate concern.
HTML editors
We replaced CKEditor4 with TinyMCE. TinyMCE is simpler and more lightweight.
For server-side filtering, ezyang/htmlpurifier was abandoned, and is
replaced by symfony/html-sanitizer.
PSR-11 Container
Our container now supports lazy instantiation.
$container->bind($class_or_interface_name, $concrete_class_name);
New Enums
We now make extensive use of Enums. See /app/Enums/...
The major ones are:
Restriction - replaces the strings 'PRIVACY', 'CONFIDENTIAL', 'LOCKED', etc.
UserRole - replaces the constant strings 'none', 'access', 'edit', 'accept', 'admin'
AccessLevel - replaces the constant integers -1, 0, 1, 2
To check whether a user has permission to do something, change
$user_access_level = Auth::accessLevel($user);
$required_access_level = (int) $tree->getPreference('CAN_DO_SOMETHING');
if ($required_access_level >= $user_access_level) {...}
to
$user_access_level = Auth::accessLevel($user);
$required_access_level = AccessLevel::fromTreePreference($tree, 'CAN_DO_SOMETHING');
if ($required_access_level->allows($user_access_level) {}
Sorting records
To sort records by one of its properties or methods, we have new helpers
usort($family_array, FamilyComparator::byMarriageDate());usort($individual_array, IndividualComparator::byBirthDate());etc.
CLI tools and build scripts
Composer scripts have been updated. Run composer list to see them.
webtrees CLI scripts have been updated. Run php index.php list to see them.