bookmark_borderSunsetting the Maintaina Horde Fork

A few years back I started a downstream fork of Horde to develop features I needed for foss and customer deployments without upstream dependencies. It went successful, was a great learning opportunity and a good exercise in critiquing our old tool chain and approaches. We had some well-known downstream users and contributors but I’d say it has run its course. It’s time to sunset Maintaina in a controlled way that’s fair towards our user base. As we are nearing a beta and prod release of horde 6 proper mostly built from Maintaina, we want to provide a smooth transition.

Horde 6 (upstream) is focusing on supporting PHP 8.4 without spamming warning&notices while Maintaina was originally targeted at PHP 7.4 through 8.1 – Still supporting anything before 8.2 is not a priority with upstream anymore. I will have to discuss with other maintainers of the fork.

Problems to solve:

  • Archive libraries which haven’t been touched for long
  • Coordinate upstreaming libraries with recent changes and archive them
  • Provide a feasible approach to consume only select maintaina packages and mostly upstream packagist
  • Clarify the future of changes downstream users want to keep but which compete with Horde upstream solutions
  • Invite maintainers of downstream code to maintain some upstream libraries to prevent stalling their own needs

I’ll keep you posted.

bookmark_borderPHP: The case for standalone null parameters

PHP 8.0 introduced null and false as members of union types but disallowed them as standalone parameter types. PHP 8.2 changed that and allowed null as standalone parameter types. What is this good for? Should they extend this to “never” one day? Why do I call standalone null parameters poor man’s generics?

What’s a null parameter? What’s a nullable parameter?

A null parameter is a parameter of type null which can only ever have one value null. Its core meaning is “not a value” as opposed to “empty string” or “false” or “zero”.

A union type which has other values but can also contain null is called nullable. For example, boolean is not a nullable type. It has the possible values true and false. If we want to allow a third state, we can create a new nullable type bool|null which has the possible values true, false and null.
In modern php, bool is a union type of true, which can only have the value true, and false, which can only have the value false. So bool|null is equivalent to true|false|null. Union types including null can also be written with a preceding question mark: bool|null is equivalent to ?bool

Isn’t this a bit pointless?

A null parameter by itself is not very interesting. After all, we know its only possible value. It is valuable as a type which can be extended. According to Liskov substitution principle parameters of subtypes should be contravariant to parent types. If the parent type accepts null as a parameter, the child type must accept null as a parameter but may accept anything else. The opposite is true for return types. The child class may have a more specific return type than the parent class but must not return anything the parent would not allow. This is called covariance. In PHP, the top type is called mixed and allows basically everything, even null values. The null type is at the other end of the scale. If the parent returns null, the child must not return anything else. There is one more restricted return type, never. A function of type never must not return at all. It is either an infinite loop or may only exit the whole program. But never is not an allowed parameter type. There is also a return type void which is in between the two. Void may return from functions, but it returns semantically nothing. Not even null.

What is it good for?

Defining an interface parameter as null is allows to define an almost generic interface which might be substantiated in derived classes. Let’s look at an example.

<?php

interface A {

        public function get(null $value): mixed;
}

class B implements A
{
        public function get(null $value): ?object;
        {
                return $value
        }
}

class C extends B
{
        public function get(A|null $value): XmlElement;
        {
            if (is_null($value)
            {
                $value = new SimpleXmlElement("Not Null");
            }

                return $value;
        }
}

class D implements A
{
    public function get(B $value)
    {

    }
}

If you don’t see the point, let me explain. interface A defines that any implementing class must have a method get(null $value) which has only one mandatory parameter. This parameter must accept null as a value. Any additional parameters must be optional. Any derived class may make the first parameter accept more types and even make it optional. The only drawback: Class D cannot be implemented because function get does not accept null as its first parameter $value.

Generics: I wish I had a little more nothing

This is as close to actual generics as one gets with PHP. Generics are code templates which do not have specific parameter and return types until an actual class is created from them. Some other languages do have them but PHP doesn’t. Larry Garfield and others have made the case for them over and over again.

There are some challenges in the engine which make them hard to implement. It would matter less if we had some tbd or changeme type which can only exist in interfaces and can be overridden by any other type. But we don’t. At least we have standalone null.

bookmark_borderStolperfrei: Wir können noch so viel lernen

Ria Weyprecht befasst sich seit über zwei Jahrzehnten mit Web-Gestaltung und Technik – ob nun mit WordPress, anderen Plattformen, Inhouse-Frameworks oder ganz frei. Mindestens seit der Halbzeit spielt auch Barrierefreiheit zunehmend eine Rolle. Mit großem Interesse lese ich seit einiger Zeit auch ihren deutschsprachigen Blog “stolperfrei.digital”. Dort stellt sie immer wieder Techniken vor, mit denen man die Barrierefreiheit von Webseiten verbessern und unnötige Schwierigkeiten vermeiden kann. Ob Zugänglichkeit für Sehbehinderte und Benutzer von Screen Readern, ob Leichte Sprache oder die Vermeidung von Reizüberflutung, immer nah am Stand der aktuellen Forschung und mit Blick auf die Praxis.

Mein beruflicher Schwerpunkt hat sich von Frontend-Belangen immer weiter weg entwickelt. Arbeite ich dann in der Freizeit an Projekten wie Horde Groupware oder JMRI, dann bekommen Profis und Betroffene wahrscheinlich das Grauen. Die technische Basis ist trotz vieler Neuerungen nicht ganz taufrisch und auch das Wissen um den richtigen Einsatz von Gestaltungsmitteln braucht immer mal wieder eine kleine Auffrischung. Es fehlt mir im Frontend auch die handwerkliche Routine und Praxis, die ich tiefer in der Applikation mühelos vorweisen kann. Wie gut, dass im Stolperfrei-Blog immer wieder kleine und große Themen aufgerissen werden. Ich kann direkt überprüfen: Gibt es hier für mich etwas zu tun? Kann ich das Gezeigte anwenden? Oft finde ich auch Hinweise auf weiterführende Themen.

Ich kann nur empfehlen, auch gleich den Newsletter zu abonnieren.

Dies ist keine bezahlte Werbung. Ich bin einfach begeistert.

bookmark_borderA wicked problem from the past

In the last few evenings there was great despair. Trying to solve one problem would reveal yet another one. As a result, I hesitated to release some of the changes immediately. I don’t want to make people suffer, having to deal with new improved problems of the day whenever they run an update on their horde installations. I’m glad feedback on the mailing list has improved quite a lot, sometimes coming with patches and suggestions and even people telling me things start to just work. That’s great. But whenever you see the light, there’s a new challenge on the horizon. Or rather something very old lurking in the shadows.

Break it till you make it

So recently we felt confident enough to switch our local installation from a frozen state to the latest version of the wicked wiki and the whups bug tracker. We also updated the PHP container, the database version and the likes. This turned into a great opportunity to test the rollback plan. 🙄 Issues were cascading.

Bullets you might have dodged

Generally having both the composer autoloader and the horde autoloader work together creates some friction. This is complicated when the exact order in which they are initialized is not the same in all scenarios. As reported previously, Horde’s apps are not strictly conforming the PSR-0 autoloading standard while the libraries mostly do. Newer releases of the apps autoload using a class map instead of PSR-0 logic. But that class map was not restricted enough in the first iterations. Thus you might have the canonical locations of classes in the autoloader, but also the version in the /web/ dir or, in case of custom hook classes, the version from the /var/config/ directory. Torben Dannhauer suggested to restrict the rules and we will do that with the next iteration. The first attempt to fix this unfortunately broke overriding the mime.local.php config file. An update is in the git tree and will be released as a version later this week. But I’m glad we ran into this as it revealed another dark secret

We part, each to wander the abyss alone

Turned out the wicked wiki software carried a little library Text_Wiki in its belly. Hailing from PHP 4 days, it’s archaic in its structure and its way of implementing features. Parts of the test suite predate phpunit and the way it’s packaged is clearly not from our times. This library also exists as a separate package horde/text_wiki. Which also exists as pear/text_wiki both in the classic PEAR archive and the modern github repository. While the base package does not exist in packagist, the extension driver for mediawiki format does. Odd enough. It’s really a shame the software from the old PEAR ecosystem is slowly fading away because of its ossification. They all share ancestry and they were all largely written and maintained by the same set of people but they are all different in subtle ways.

Text_Wiki works great when it works. But it’s a real treasure trove of incompatibilities with modern PHP versions. Over the years, unicode support has evolved along with the strictness of PHP’s core. While I am very much willing to contribute back any changes to the official pear version, I have my doubts if they will be accepted or handled at all.

Rising again like the phoenix

I really, really did not want to deal with any of this. Creating a fourth version out of three splinters feels like madness. But what can you do?
The result is an upcoming version of horde/text_wiki and horde/wicked which is a radical break. The new text/wiki has no PSR-0 lib/ content. It’s all PSR-4, it’s namespaced, every single class name and file name has changed. A new test suite has been started but only a tiny fraction of unit tests have been ported so far. The different extension drivers for mediawiki and tiki dialects have been incorporated into the base package as I did not want to maintain a hand full of packages.

The whole thing couples as little to horde as possible. It has its own exceptions based on the PHP builtin exception tree, so no dependency on horde/exception. I even stripped the dependency on Pear_Exception as there is no value in keeping that nowadays. Anything which ties this revamped library into the horde ecosystem now lives in horde/wicked. Extra markup rules which render horde portal blocks into a wiki page. Adapters querying the database if a linked page already exists or must be created. Dynamic links created from the registry and application states. None of this glue belongs into the text_wiki library.

Many incompatibilities with modern PHP have been solved. Often this was a matter of mixing and matching bits from the three different splinter versions out in the wild. Some of it is just a chore to do. Of course, the pear XML files are gone and the composer file is fully compliant with packagist and most recent composer. At least this part has been contributed back already.

Dawn, finally

It will be another few evenings until the new horde/wicked and the new horde/text_wiki are ready for a tagged release, probably along with some of the changes to other libraries I explained above. There’s probably something that will break and need fixing. But that shouldn’t block progress for too long.

bookmark_borderRunaway Trains Back on Track – Plans are just plans

Making plans is great but it’s just words. How are things going? Clearly moving towards goals but in unexpected ways.

Trains don’t only run on railroad tracks but supposedly on schedule. Commuters shall know every stop, arrival and departure, the order of cars and their position on the platform. If trains regularly arrive delayed, at another platform, composed of different or differently ordered cars it is a source of frustration. If they skip a station or get relayed to another course, it’s even worse. Software projects, on the other hand, are expected to detour regularly in very similar ways but hopefully arrive at certain way points more or less on time. Sometimes they get unplanned additional cars along the way. Sometimes you have to replace the engine. It never gets boring.

What is the goal?

In January, I anticipated there were some unknowns ahead but I did not detail how to tackle this.

Improve JMRI‘s support for the Märklin MCAN protocol and particularly the Mobile Station 2, Central Station 3 and Can-Digital-Bahn products. This might become a bit controversial. 

Back to the source code? The 2025 agenda – ralf-lang.de

The Märklin Hardware Throttles (Mobile Station 2) implement hot plugging and negotiate which one acts as the primary. They send a regular PING command announcing their type and serial and expect a PONG response from other devices on the bus. The most senior serial number is agreed to be the primary throttle even if the other one was the primary or single throttle until now. The primary offers its roster to the other throttles. If higher end Central Station systems are on the bus, one of the central station will always be the primary device and the throttles all behave a little different. This allows JMRI to passively listen to traffic and detect bus devices or actively send a PING and collect the PONGs.

The Can-Digital-Bahn devices have a similar PING/PONG mechanism used by their configuration utilities. Unfortunately there are different configuration utilities for different generations of their sensors, relays, turnout controllers, light controllers, switchboard components, mixed devices. These utilities are closed source Windows programs and you need different versions of the program for different versions of the components. Unfortunately, not all of the programs properly run on recent Windows systems.

What if I just had to push a button in the web browser and see all my MCAN throttles and devices in a table, loading the right configuration screen for each of them? Sounds much better, but how to achieve that?

Moving through the stack

To send the PING messages and receive the PONG messages a program needs to connect to the MCAN bus. This can be achieved by connecting the CC-Schnitte interface to the USB port of the computer or connecting to a Central Station device over TCP/IP. Both methods expose the raw MCAN bus to the software. Both options are already implemented in JMRI.

Next the necessary bus messages and response need to be added to the software. The MCAN PING command is already implemented but JMRI doesn’t implement the desired reactions to the answers. For can-digitial-bahn messages, support is still missing.

To use the found devices, a program needs to memorize some representation of them. JMRI currently does not have a generic way of handling external bus devices as such. Throttles and sensors are tracked in their specific roles, covering their common aspects as throttles or as sensors. For CBUS type connections, there is a node manager which looks very similar to what is needed for MCAN but the UI code and the table format probably deviates a bit. This is where it becomes a bit complicated.

Users need some kind of GUI to interact with found devices. There are many reasons to start out with a browser based UI instead of building it in JMRI’s native Java GUI. The browser based ui just works on any tablet or laptop connected to the network. It’s well decoupled from JMRI’s core. It can be styled or completely recomposed as needed without touching JMRI itself. JMRI does not need to bundle variants for different use cases but they can be installed separately, developed on their own schedule. JMRI only needs to expose the necessary interface protocol which also drives other parts of the browser based UI.

JMRI’s interface protocol is composed from JSON messages between the browser client and the JMRI server. These can be sent over the HTTP server port or through a separate WebSocket implementation. The latter provides better performance and less overhead.

JMRI’s WebSockets and HTTPS API

This is what I ended up doing. To familiarize myself with the API I built a simple demo use case to manipulate the displayed railroad company name. Previously this was only possible through the preferences screen in the native Java GUI. I ended up not integrating it in the regular browser based GUI but implement a separate demo page. JMRI core developers pointed out that special care is needed and unauthenticated calls to the API must not actually persist the changed configuration into JMRI.

Obviously this would not work for actually handling hardware. While JMRI already has a permissions and user authentication system, it is fairly new and does not cover the actual API messages. So my next step would be to implement the necessary messages in the JSON WebSocket API for retrieving an authentication token. This token would then be used to authorize further WebSocket calls which actually change something in JMRI. These new calls need to carry the authorization token as part of the data.

I plan to detail this development in a separate article later this month as I move through the process.

Seems like I should be getting somewhere

This free time project is a great adventure into the unknown between where I am and where I want to be next. In some ways this is similar to professional work. I commit to goals and target dates and I have a very clear understanding of next week and some ideas about the week after. Beyond that the way points and schedule dates become rarer.

It’s sometimes very challenging to tell project managers that there won’t be many super detailed milestones beyond the horizon of immediate next steps. Gladly, at work I have very smart project managers who know when to trust me and when to challenge me. Sadly, in free time I have a very harsh and unforgiving project manager who often won’t take no for an answer.

But that’s just me.

bookmark_borderMaintaina’s Horde 6 goes upstream

It’s been a while. It’s been much too long actually. Let’s look forward though.
I recently started to onboard into FOSS development again. People have been asking for PHP 8.2 support with Horde but capacity to deliver that was very limited. But we’re getting there.

I used the “Maintaina” fork to deliver fixed and upgraded customized versions of Horde beyond what was allowed in Horde’s master branch. It’s time to move forward though. Maintaina used to target PHP 7.4 to PHP 8.1, composer2 based install from a custom satis repo and some quite invasive changes to CalDAV/CardDAV support. Most prominently, Maintaina introduced library level compatibility with several PSRs (container, http middleware, logging) and the all new horde/http_server component.

At the moment I am importing the FRAMEWORK_6_0 branches and the alpha releases based on this branch to horde’s upstream repository. So far I have imported enough libraries to make the Horde Base application barely install from packagist.

Get the current state of affairs

Your magic carpet is:

composer create-project horde/bundle .

Originally I wanted to mimic maintaina’s setup with a separate satis server as a QA stage before release to packagist. I re-considered and dropped the separate satis server. Every update of the development branches and every tagged release is consumed via packagist. I will have to fix some of my tools and workflows to reflect that. The tagged alpha releases still use maintaina’s satis server. The upcoming releases won’t.

Onboarding Procedure


I leave the FRAMEWORK_5_2 and master branches mostly untouched. FRAMEWORK_6_0 is the new default branch on github for anything I am handling. I only edit other branches if they block packagist’s import. Usually I also rebase FRAMEWORK_6_0 on any latest commits of master, but in some cases I only cherry-picked from master branch. In some cases, some entries of the changelog between 2022 late and now (october 2023) might be missing. Pull Requests, bug reports and patches welcome.

What’s next

Before I move forward with the actual applications, I want to make sure the necessary infrastructure is in place. I need to fix some aspects of the FRAMEWORK_6 version of composer. The workflow files in each repo need some review, too. Does PHPUnit and PHPStan still pass? Can we improve management of 130+ repo’s workflows?

Finally https://dev.horde.org should index from packagist, not from the satis server we originally planned to use.

What will become of maintaina repos?

The maintaina repos have had direct contributions from some trusted maintainers from the company I used to work for. They service some customers out of these repos and the related SATIS server so I won’t actively break it. However, with the move to Horde upstream, maintaina has served its purpose for me and I will not actively support the fork anymore. I suggest once everything is ported to upstream, maintaina should be archived. I will need to consult other stakeholders of this fork and the satis server.

Maintenance cost

Over the lifetime of the fork I have explored and applied various strategies for keeping the effort in check. Still, a fork of 100+ repos and the accompanying infrastructure for testing and deploying is a major burden which detracts from actually developing and maintaining code. I am glad I can save on this now and actually contribute to Horde directly in a way that doesn’t slow down activity too much. We now have the chance to speed up the cycle of feedback and releases. I hope this attracts some occassional and regular contributors.

bookmark_borderSatis is now a Composer Plugin.

Satis is the lightweight, static repository generator for the composer package manager. It enables admins to provide PHP software packages inside airgapped CIs, OS packaging environments and restricted data centers.

Back in August I added a plugin mode to satis to make it work as a regular composer plugin. While working on it, I also fixed some preexisting issues found by static analysis and made it compatible with the recent composer versions 2.3 and 2.4.

This week, the upstream maintainers merged my contribution. I feel a bit satis-fied 😉

Why make it a plugin?

When looking under the hood, it is easy to see that satis always has been some awkward kind of plugin or extension. It literally sits on top of an internal copy of composer. It hooks into its internals in not quite transparent ways, it uses its class and interface organs for its own vital functions. You might call it a parasite that attaches to composer’s body for its own needs. There are downsides to this approach. The first is that you need a private copy of composer. The second is that any refactoring of composer internals likely breaks satis compatibility. That happened some time ago when composer 2.3 and 2.4 were released and not for the first time. Composer has a maturing plugin API with nice, well-defined integration points. It provides some means to overload or amend core functionality but it also provides messaging between core and plugins. I only did the bare minimum work to make satis hook into the plugin API and not break the standalone mode. When installed as a dependency, package resolution will ensure that the API versions used by satis matches the API versions provided by the underlying composer.

I don’t quite understand… What is the benefit?

By itself, this change provides little benefit. It is a feature enabling feature.

  • Satis can be further refactored to make compatibility bread less often
  • Satis can send and receive events from composer or other composer plugins. This enables running satis as part off a custom command. Think passing unit and integration tests of a project and then conditionally updating the staging or canary package repository.
  • Satis’ schema could be amended to make a project’s root package also function as an instruction to build a repository of all dependencies with almost zero configuration. Add this to a workflow or add a collaborator plugin that handles the necessary push/release and you have a powerful tool for CI and developer laptop alike.

But as I went along, I also re-aligned satis with the latest breaking changes inside composer 2.3/2.4. This will benefit users who do not care about the whole plugin story.

What’s next?

With satis 3.0-dev merging this initial change, the next steps are obvious, but not urgent.
Making the new plugin mode play nice with the latest composer was already easier than fixing the standalone mode. Satis still has an internal, now updated dependency copy of composer which is only run in standalone mode.

Standalone mode should be refactored to be just a thin wrapper around composer calling into its satis plugin. Keeping intrusion into composer internals to the bare minimum to hide the builtin commands and re-brand it as satis, this would make breakage on upcoming updates much less likely. Eventually, we can maybe stop carrying around a source code copy of composer at all.

Finally, there is reaping the benefits. I want to leverage composer/satis functionality inside the horde/components tool. Rolling out new versions of horde stuff could be so much easier.

Resources

bookmark_borderLet’s have a date. Revised horde/date ideas

It’s bad but not as bad as you think.

The way we write dates is very different among cultures and technologies. Even countries of the same language family might have totally different notions where to put the year, where to put the month, where to put the day when writing down a date in numbers. Apart from the order, we might use hyphen -, dot . or slash / to mark the sections. Leading zero yes or no. It gets much worse when writing out a date, like “Vlad was born on the 13th of June” or “Monday, October 10 I will have a barbecue”. Mix in different languages and different ways to abbreviate Monday as “Mo”, “Mon”, “M”.
The PHP-native ways to deal with date math and date formatting are a little quirky and best wrapped in some code that hides away the nasty details but exposes useful capabilities.

The horde/date library has been around for ages and it does its job. Due to changes in the PHP 8.1+ engine, it requires some internal re-engineering. As it is also due for a conversion to namespaces and PSR-4, I decided to think about the interface a little more. These are rough ideas and I am still figuring what will be fun and safe to work it and what is possibly over-thinking it.

It’s DateTime all right.

PHP provides internal data types to store and manipulate a date: DateTime and DateTimeImmutable. Horde_Date behaves similar to DateTimeImmutable but implements a lot of intricate math on its own. Still, it uses DateTime for some conversions. It makes sense to delegate most mutation math to the DateTimeImmutable class and make it also hold most state. No need to manually manage minutes, hours, days of weeks etc in most cases. Preferring Immutable safes us some clone magic.

Wrap it up or extend it?

There are two possible approaches to dealing with the builtin type: Either extend it or hide it in a wrapper. The third option would be to implement the interface but this is not possible.
Extending any of the two DateTime types would be handy as it is the lingua franca between different library ecosystems. We could simply inject our Horde Date type into any library that uses it. There are down sides to this approach though. While DateTimeInterface is reasonably small and mostly useful, DateTime and DateTimeImmutable expose a lot of functionality. Some of it is awkwardly named. Some of it would have to be wrapped in extending to avoid inconsistencies in our own object. Some of it possibly clashes with own naming schemes and blocks us from using preferred signatures. Some of it might not fit into our own notion of what belongs where. There is also a risk of exposing different functionality based on PHP versions. This is undesirable. Extending is not the way to go. Hiding away the DateTimeImmutable object and exposing it explicitly might seem a little verbose. It offers some interesting applications.

Clock Date – Now is the right time.

A clock date type always emits now. Asking it again at a later point will yield a later time without having to manipulate the object. This is useful for tracing duration of processes or for emitting status messages. We can also make this clock the second element of a time span. Its duration will automatically expand. We can add a Stop method to the clock which will return a regular, fixed date for further processing.

No Date – When it’s not right for you.

Sometimes we cannot rely on a date being present in the input. It might be optional. It might be required by the current data model but used to be optional or malformed. Traditional options would be expressing the non-date as null value, throwing an exception or silently assuming no date means now. These are appropriate, good solutions in many cases. Sometimes you may prefer to have the “no date” information behave a little like a date – until it reaches a point where it cannot. For example, a “not a date” can be formatted many ways. It can be serialized to the backend, provided the backend can deal with it. It can however not be calculated, mutated or cast to a DateTimeImmutable. I am not yet sure how to handle this. Maybe it should be confined to Formatters and Readers.

In The Zone.

PHP provides an own finite list of Time Zone identifiers. Wrapping PHP’s timezone objects allow dealing with well known but unsupported timezone names. We can map them to known names. We can safe the originally provided name for later usage. We might not carry a PHP timezone at all but signal the other parts of the library that some custom code must be applied.

In Good Format.

There are a plethora of ways to express a given date. There are three builtin date renderers in PHP, IntlDateFormatter (not always installed), strftrime (deprecated) and DateTimeInterface::format (English only). You might add your own. Each has its own dependencies, arguments, requirements.
It is much simpler if there is a Formatter type. Implementations can just configure it and load it with a date. Consumers have a simple interface to work with them regardless of how they are implemented.This also allows to keep the dependency footprint of the core date library low and makes adding more output formats very easy. The same is true for reading data. Reading values from various formats should not be the Date object’s concern. Another object should turn arbitrary string, integer or other data into dates – including legacy Horde_Date objects.

bookmark_borderHorde on PHP 8.1 and Composer: Update

Regular readers of this blog and many other are aware that PHP 7.4 will stop receiving security updates when PHP 8.2 comes out in November. This has made many horde admins question if they can continue to run Horde. Some events in life have made progress slower than originally planned. So where are we?

Confirmed running under PHP 8.1 and composer 2.4

  • horde/base in Browser
  • essential Horde Base CLI tools like horde-db-migrate and hordectl
  • horde/base portal blocks and admin area
  • horde/components developer tool
  • horde/turba Addressbook App Reading and writing contacts in the UI
  • horde/mnemo Notes App UI and webdav
  • horde/nag Tasks Apps UI, webdav, caldav
  • horde/kronolith Calendar App UI, webdav, CalDAV
  • horde/passwd Password App – Changing passwords worked with the hordeauth driver
  • horde/gollem File Manager App – very limited testing so far
  • horde/imp Webmail – very limited testing.

I run on a setup with openssl3 and a recent mariadb against dovecot and postfix. You can also consume the openSUSE 15.4 based containers built nightly. There is still considerable log spam from deprecation notices: Mostly tentative return types and signatures, also some use of deprecated functionality like strftime. Each night a few of these disappear. They don’t stop you from running horde apps.

I also have an eye on PHP 8.2 compatibility – So far, there should not be too many surprises. I also check most unit tests against the development version of PHPUnit 10.

This code is quite solid on PHP 7.4 – production users run on it.
On PHP 8.1 I consider it ready for adoption tests. Breakage is expected, feedback is welcome. Be sure to have a backup of the database and of any mail accounts you connect to it.
There is a lot to be done over the next few weeks.

If it does not run for your combination of drivers, please contact me via the horde mailing list.

Known caveats:

  • imp config SHOULD have an explicit cache setting: Set it to false to disable caching or to ‘cache’ to use Horde’s default cache. The ‘sql’ option also seems to work but I do not recommend it.
  • The RPC interface has seen very little testing. The json-rpc protocol should work. I have no desire to look into xmlrpc though unless somebody voices his needs. Beware, the xmlrpc extension has moved out of mainstream into pecl.
  • I do not have the necessary setup to comment on ActiveSync currently
  • Kolab integration is very likely broken. I don’t think anybody really uses recent horde with ancient kolab versions.
  • Most likely the SyncMl and PHPGroupware drivers are useless. If anybody really uses that bridge, please give feedback
  • I usually test against sabre/dav 4.4 – if you use anything else and see bugs, let me know
  • I don’t currently test against postgresql. MariaDB, MySQL, PerconaDB should work.
  • As PHP’s LDAP extension has moved from resources to objects, the LDAP authentication and addressbook drivers likely need an update. I do not currently test against LDAP but this is something I want to change
  • I know my former colleagues run LDAP and Redis so likely they will give some feedback in that area – Cannot comment on the timeline. I will offer a redis option for the maintaina container setup soonish.

bookmark_borderCode Generators: Bad, worse & ugly

Code generators have been invented and forgotten at least four times in software history. They have an appeal to developers like the sun to Daedalus’ son. Let’s not be Icarus, let’s keep them generators at a distance and watch them carefully.

Whenever a language, framework or paradigm forces developers to do the same thing over and over and over and over again they will try to get rid of that repetition. One approach is to automate writing code. It is not pretty but it saves time to concentrate on more interesting and useful things. Seasoned and reasonable software developers have resorted to that solution and many inexperienced have followed. Outside very narrow use cases I think generated code should better not exist at all.

Valid use case

Generated code and other kinds of boilerplate code are valid where avoiding them is not practical. This is often true for entry points. Depending on language, it might not be trivial for a the first piece of running code to find

  • its configuration
  • its collaborators, base classes, dependencies
  • useful data from previous runs

I have written a long, long piece on two-stage autoloaders and other two-stage bootstrapping topics and I keep rewriting it, breaking out separate topics. It is still unreleased for a reason. Any two-stage process that splits automated detection or definition of artifacts from the production run that uses them is essentially code generation. Avoiding it might be possible but impractical. Some level of repetition cannot be avoided at all and is best automated.

Another valid use case is generating code from code or transpiling. Nothing is wrong with that.

Unfortunate Use Cases

There are other use cases that should be avoided. Your framework follows convention over configuration so making magic work requires having some artifacts in the right places. Even if they have no natural place in your specific solution they are needed for technical reasons so you copy/paste or auto-generate the minimum sufficient implementation and make it fit. This is something to look for. Often there are ways around it. Another case is limits of the underlying language. You evolved from using magic properties and methods to implementing type safe, explicit equivalents but now you have to re-invent the type specific list or container type and you automate it. Bonus points if your ORM tool requires it. If your language does not support generics or another templating method, you are stuck between repetitive, explicit code and weakly typed magic. You end up using a code generator. Hopefully at one point somebody is annoyed enough and ventures to bring generics into your language. That would be the better solution but it is likely out of scope for your day to day work.

Stinking unnecessary use cases

Beyond that you are likely in the land of fluff where things just happen while they can and lines of code are generated just because it is customary. This is a foolish thing best avoided. Granted, automating code is better than hand-writing it. It does however not mean the code should exist at all. If you have no specific reason to repeat code, it is likely a design smell. This is not new, the Cunningham wiki had this thought a decade or more ago. Likely they were not even the first to recognize it. Refactoring, abstraction, configuring the specifics can help reduce the necessity for repetitive code.

My programming tools were full of wizards. Little dialog boxes waiting for me to click “Next” and “Next” and “Finish.” Click and drag and shazzam! — thousands of lines of working code. No need to get into the “hassle” of remembering the language. No need to even learn it. It is a powerful siren-song lure: You can make your program do all these wonderful and complicated things, and you don’t really need to understand.

Ellen Ullman: The dumbing-down of programming, Salon, May 1998 https://www.salon.com/1998/05/12/feature_321/

Let us take the input to a code generator and make it the input to abstracted, ready to run code instead. We will know when it is not practical, not performant or not possible. Then code generation is a blessed thing. Otherwise it is a sin.