6.0.0-git
2024-03-19
Last Modified 2019-11-27 by Ralf Lang

No More Pear: Move to composer

Remove any hard runtime or installation dependency on pear and pearisms

Bugs

List any tickets on http://bugs.horde.org/ that cover this issue or are relevant to it.

People

Ralf Lang

Description

The PHP ecosystem has increasingly moved from pear to composer as the default installer. The changed paradigm (global versus local dependency management) attracts many users and developers.

Runtime dependencies

horde-db-migrate and web migrator

horde-db-migrate "forgets" to migrate or even acknowledge libraries in git checkouts where the pear channel is not present.
https://github.com/horde/Core/pull/2

l10n/translation tools?

Document .horde.yml format and how it relates to composer json and pear xml

See Developer Doc on .horde.yml format for both original format and proposed additions.

Installation

Generate composer json file from .horde.yml

[DONE] components/lib/Helper/Composer.php currently depends on conductor and gets its data from package.xml
anything handling package.xml is broken without pear though. Always need to register the horde channel

[DONE] rewrite all pear.horde.org dependencies to vcs type dependencies github/horde/

https://github.com/horde/components/pull/3

Other resources
Do we want to keep a whitelist of other channels/packages which should be pulled from packagist or github rather than pear?

Composer plugin: install apps below base rather than vendor/

(we don't want /vendor/ web-readable)
[DONE] horde apps are type "horde-application" and get installed to /$appname or base/appname
Installing/updating an app should clear cache and autoloader cache
[DONE] Check if the app provides a registry snippet in /doc/registry.d/
[DONE] write horde.local.php if necessary
[WONTDO SEE BELOW] installing base should provide some initialization
[DONE] The installer allows the deployment/bundle to provide pre-made configs for horde and apps

Composer plugin: copy, move or link js content from libraries to horde/js

[DONE] horde libs are type "horde-library" and get installed to /vendor/Horde/Foo
[DONE] js/ dir content of horde-library packages and the horde base app are copied or linked to web/js/
[DONE] automatically rewrite registry path of hordeJs fileroot and webroot to web/js/

Packagist

Add tool chain to create and update packages on packagist for easier consumption.

Autoloading and fixed loading

Examining horde base bootstrapping:

[NO TODO] horde/horde/index.php require_once lib/Application
[NO TODO]Horde/Application.php require_once core.php
[DONE] core.php checks for horde.local.php -- composer installer will write necessary autoloading instructions there.
[DONE] Either include_once Horde/Autoloader/Cache.php or require_once Horde/Autoloader/Default.php - fixed by adding a search path

Examining nag bootstrapping:

horde/nag/index.php and other client pages require_once nag/lib/Application.php
nag probes for horde_dir (either info from horde.local.php or directory above nag dir)
finally load (hordedir)/lib/core.php

-> both in base and apps, we can edit horde.local.php for hinting and autoloader ultimately comes from horde/lib/core.php

Issues with Horde_Autoloader_Default and Horde_Autoloader_Cache

Horde_Autoloader_Default has unconditional require_onces
Horde_Autoloader_Default registers unconditionally when loaded
Horde_Autoloader_Cache requires/registers Horde_Autoloader_Default unconditionally

[DONE] Fixed by providing suitable search paths in horde.local.php

Examining git-tools as a composer-native app:

git-tools binary detects the composer autoloader entrypoint vendor/autoload.php and require_once it.
the composer autoloader is configured to provide a psr-4 autoloader for the \\Horde\\GitTools namespace to ./lib

PSR-0, db migrations, classmaps and packages

Most horde library packages are simple to map to the psr-0 autoloader.
Package Horde_Foo would have all its classes in lib/Horde/Foo.

Some packages though have class names you would not expect from the package name
Horde_Core has Horde_ErrorHandler & friends
Horde_Form has multiple classes in one file
Horde_Util has Horde_String

These either need to be scanned for additional mappings or need "classmap" style autoload hints.
[UNSURE] Unit tests should be marked as autoload-dev (root package only autoloads)
[DONE] migrations should be autodetected by the composer writer.
[DONE] special case classmaps should be hinted by .horde.yml

Horde_Test

Horde_Test bundles phpunit. A Horde Library uses a copy of boilerplate code from horde_test to initialize autoloading. This boilerplate makes some PEARish assumptions (universal include hierarchy)

This pull request offers a new template and fixes other unconditional require_once instances https://github.com/horde/Test/pull/1
This should hopefully work for PEAR installs, composer installs and full git checkouts just the same.

[DONE] Running the unit tests via the components app, when installed into a composer deployment, is also possible without fixing AllTests & friends.

Proof of concept

There is a proof of concept in https://github.com/maintaina-com
This proof of concept includes all software patches which are part of this effort

components and git-tools

In this project, the components tool is used via git-tools as a frontend or as an installed app via composer exec horde-components.

Various patches have been applied. Most relevant is a rewrite of the Components_Helper_Composer class.

This class now writes a thoroughly different composer file than before and it takes its data from .horde.yml
rather than package.xml

To generate a new composer.json file for an app, run

composer exec -v horde-components web/$app/ composer

To generate a new composer.json file for a library, run

composer exec -v horde-components vendor/horde/$lib composer

Mind providing a useful config for horde-components first

config options govern
- if we want to build against dev-master dependencies or dependencies using the horde.yml version constraints
- if we assume all packages live on packagist, in a shared satis repo or a per-library git location
- how to translate non-horde pear dependencies into packagist dependencies (otherwise, pear channels will be used)

components release command

The components release command needs some additions to make this practical

  • [TODO] The composer file needs to be written with different options twice: It should use dev-master requirements in the master branch, but use versioned requirements as stated in the composer yaml file when writing a tagges release
  • [TODO] There should be configureable hooks to either trigger a satis rewrite or a packagist upload
  • [TODO] An easy way to perform downstream/inhouse releases omitting PEAR related stuff or upstream horde specific steps like updating the bug tracker. This should not involve lenghty command lines.

Known issues

There is currently a problem with discerning commandline options (-X and --something) for the composer exec command from those for the components cli.
https://github.com/composer/composer/issues/5632

If your use case depends on using these switches (most notoriously, --depend), run the command instead using ./vendor/bin/horde-components ...

Core, Base and Autoloader

Several patches were needed in places where horde just assumed files to always be in a certain location.
Most of these patches SHOULD NOT break git dev installs or pear installs - testing was very limited

Other relevant changes were detection of migration files in Horde_Core_Db_Migration and handling of Horde_Controller routes in
lib/Horde/Core/Controller/RequestMapper.php

Test

The Horde_Test suite had some hardcoded requires which were made condidional.

Other libraries

Unmentioned libraries SHOULD only have been modified by replacing the master branch composer file

Resources

https://getcomposer.org/doc/articles/custom-installers.md composer notes on custom installers and plugins
https://github.com/composer/installers multi-framework collection of installers
https://github.com/maglnet/ComposerRequireChecker Tool to find direct usage of indirect dependencies (Ensure you directly require all dependencies whose classes are referenced in own code)


Back to the Project List