[dev] Horde Libraries: Trying out Namespaces and PSR-4, PSR 11, PSR 12 without breaking user code

Ralf Lang lang at b1-systems.de
Sun Sep 20 19:46:46 UTC 2020


Hello,

Scope:

this weekend's project was an exploration on how to port parts of Horde
to PSR-4 autoloading, namespaces etc. The goal was making it run
together with unmodified Horde git master code.
I picked Horde_Injector for a candidate as I thought it might not be
trivial but manageable in scope.
https://github.com/maintaina-com/Injector/tree/refactor-psr4

Horde Injector is a very attractive, simple DI solution without any XML
or other overhead. It also has very few own dependencies into the Horde
Framework - It uses Horde_Exception and it suggests using Horde_Test.


Method:

I decided the library root should be /src and handle the namespace
Horde\Injector\.
I kept every class on its original level, with the exception of
Horde_Injector which became Horde\Injector\Injector.

I removed some patterns of doing very little, formal things in the
public methods and implementing the real action in some private _method().

I removed the leading dash from internal properties as advised by PSR-12.
Most changes were very formal and straight forward.

I only added a few lines of logic to handle autowiring as the original
implementation breaks (for unchanged user code) by the DependencyFinder
being scoped \Horde\Injector rather than \.

I added parameter and return type hints to most but not all method
signatures. I changed getInstance() and hasInstance() to get() and has()
to make it implement psr-11

I made lib\Horde\Injector* derived from namespaced classes and
interfaces in src\ and added \ to make the PSR-0 frontends explicitly
part of the global/main namespace. All PSR-0 classes required a new file
lib/_autoload.php which is a dependency-ordered unconditional list of
require_once to src/.

PSR-0 files under lib/ are mostly implement-nothing derived classes. Two
exceptions are the constructor and the child injector method as they
would return the namespaced version without the unnamespaced wrapper.
Also, the PSR-0 getInstance() and hasInstance() methods map to psr-4
get() and has()

I did not explicitly reference the PSR-11 interfaces. Although my code
satifies them through duck typing, I did not want to introduce
derivative questions like autoloading the PSR-11 interface definitions
in case of an autoloader unaware of the namespaced code behind the
unnamespaced, user-facing shim. If we do this for real, we should either
reference the interfaces or derive a class version which does.


Outcome:

Some unit tests are still failing due to signature mismatch. Within a
composer setup, Horde seems to run well from web and many CLI tools run,
including the quite demanding components app (extends Horde_Injector).


Discussion:

I think the hardest decision is whether to use modern type hints in
method signature or to skip these. It is a breaking change for code
which disrespects the phpdoc type hints or for derived classes.
I did not specifically address pear and git-tools installs but only
tested for composer. It should work for git-tools installations though.

If I wanted 100% signature compat I could either remove type hints or
move the actual implementation to traits and let both psr-4 and psr-0
code be independent frontends to the traits. This would look very much
like the Laravel code base.

I think this worked well . It is a path to gradual porting of many/most
horde libraries to PSR-4. It does not imply any need to convert
everything (or any using code of the respective library) within some
common timeline or project. It would be worhwhile to run some automated
mass edits to prepend un-namespaced class references with \ to avoid
ambiguities. That's a rather small investments which would prevent lots
of headaches.


Derivative: My team has experimented with partial PSR-4 conversions of
internal apps. While we kept the outside interface un-namespaced
(Api.php, Application.php, app/controllers/*, Ajax/*, ), most app
internal classes could be converted easily.
Anything wrapping/deriving from Core/ factories and services is a mess
though. All testing happened in a composer based setup.



-- 
Ralf Lang
Linux Consultant / Developer
Tel.: +49-170-6381563
Mail: lang at b1-systems.de
B1 Systems GmbH
Osterfeldstraße 7 / 85088 Vohburg / http://www.b1-systems.de
GF: Ralph Dehner / Unternehmenssitz: Vohburg / AG: Ingolstadt,HRB 3537

-------------- next part --------------
A non-text attachment was scrubbed...
Name: pEpkey.asc
Type: application/pgp-keys
Size: 2688 bytes
Desc: not available
URL: <https://lists.horde.org/archives/dev/attachments/20200920/2e4abc95/attachment.key>


More information about the dev mailing list