Composer aliases for fun and profit

I’ve used PHP Composer for package management for years now and I’m still learning new things. Today’s cool new capability is aliases. Let me start with a tip of the hat to Laurence Gellert; I stumbled across his blog while trying to extricate myself from a dependency problem. Somewhat akin to proxying Cloudfront with Nginx, I want to emphasize that I chose this solution after considering and rejecting other options and I do not intend to make a habit of it.

We use Composer to build our Moodle deployment packages. Moodle has about fifty different plugin types. Each plugin has a component name: mod for activity modules, qtype for question type, and so on. Each has a different installation path, which means that our build process depends on the Composer installers plugin supporting each type.

Moodle shipped a revamped TinyMCE integration beginning with Moodle 4.1. CLAMP did a thorough evaluation at the 2023 Winter Hack/Doc Fest at Swarthmore. While the new TinyMCE integration itself is of the editors type, any plugin that extends it would be a Tiny editor plugin (tiny). That means that we’ll need support for that plugin type added to the Composer installers project. Unfortunately, that project isn’t very active right now–no new releases since 2022, and no response to two separate pull requests to add that change.

My first thought was to fork the repository, cherry-pick the patch, and then override the composer.json in my project repository to use it:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
"type": "project",
"repositories": [
{
"type": "vcs",
"url": "https://github.com/LafColITS/installers"
},
{
"type": "composer",
"url": "https://packagist.lafayette.edu"
},
{
"type": "composer",
"url": "https://moodlegist.clamp-it.org"
}
],
...
"require": {
"composer/installers": "dev-moodle-tiny",
...
}
}

The problem with this approach is that we have about thirty separate plugins listed as dependencies, most of them direct from the Moodle plugins repository via Moodlegist, and they all have composer.json files that look like this:

1
2
3
4
5
6
7
8
9
10
{
"name": "moodle/report_roster",
"type": "moodle-report",
"require": {
"composer/installers": "^1.0 || ^2.0"
},
"extra": {
"installer-name": "roster"
}
}

Changing all of those isn’t an option; we need to satisfy the constraint. This is where aliases come in. You can use an inline alias to specify that a branch satisfies a constraint. In my example above, the pull request for the Composer installers plugin is based on version v2.2.0. So, I refactor a little using the as keyword:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
"type": "project",
"repositories": [
{
"type": "vcs",
"url": "https://github.com/LafColITS/installers"
},
{
"type": "composer",
"url": "https://packagist.lafayette.edu"
},
{
"type": "composer",
"url": "https://moodlegist.clamp-it.org"
}
],
...
"require": {
"composer/installers": "dev-moodle-tiny as v2.2.0",
...
}
}

I still need to fully rebuild my lock file, but I’m back in business and able to install TinyMCE plugins in my Moodle environment.

The Composer documentation helpfully includes a warning to not do this unless you really have to: “Inline aliasing should be avoided, especially for published packages/libraries. If you found a bug, try to get your fix merged upstream. This helps to avoid issues for users of your package.” They’re right. I think the risk is acceptable in this case: we’re not shipping a package, the upstream maintainer is unresponsive, and the change itself is trivial. I played around with using patches to solve this issue too, but I’m unsure about the order of operations in a fresh build.