PhpRiot
News Archive
Related Articles
PhpRiot Newsletter
Your Email Address:

More information

Loose interface coupling

Note: This article was originally published at Planet PHP on 5 December 2011.
Planet PHP

OK, I think this is likely a stupid crazy idea, but it has so much practical benefits at first sight that I want to bring it up on the blog and not just twitter. As a rule of thumb when writing code one should always try to reference an interface in type hints, instanceof or is_a() checks. This in theory allows people to replace the implementation if only for unit tests. But more importantly it could also allow someone to switch to an entirely different lib for the given dependency. One practical issue here is that either both projects need to share a common interface library or forced the need to write adapters in order for a 3rd party lib to become compatible even if the relevant API methods have the exact same names and signatures. Now lets phttp://pooteeweet.org/blog/2008 imagine a world of collaboration] where some subset of the community (as big as possible, as small as necessary) agrees on some common interfaces, but they don't want to burden their code with one dependency for each of these interfaces. Especially as for different libraries a different subset of the community could end up collaborating. Here I see 3 options: 1) each library bundles the interfaces (even though they sit in some common namespace), 2) each project asks their users to fetch the common interfaces from some other place 3) runtime "coupling". Option 3) doesn't exist today and is what this blog post is about.

Actually maybe this is already possible using the runkit extension. This extension lets you mess around with essentially everything that is defined and usually can't be changed anymore: functions, classes, constants etc. Sounds harmful? It is! Right now the only valid use case I see is testing of untestable code (like being able to modify the behavior of date() at runtime). For option 3) what I am hoping for is a way to simply declare one interface to be implementing another interface. This would likely be run as part of your bootstrap or maybe the autoloader.

Here is some pseudo code that Ralph tweeted:

('My\ LoggerInterface', 'Your\LoggerInterface'); ?

Note this should effectively work a bit like the use statement in that it shouldn't trigger autoloading (maybe an optional parameter should trigger autoloading for debugging).

To give some historical context, I got the idea for this while listening in on a ZF2 IRC meeting. So I briefly mentioned this idea after the meeting and it seems like Ralph also saw some merit. Again neither of us was thinking about any way to alias method names let alone supporting reordering of signatures etc. So the use case would essentially in 99% (except for coincidences) of the cases still require direct communication between the authors of the libraries.

Of course another alternative to the "how to get the interfaces to my users" would be to make them part of PHP itself. Here I see a lot of practical issues:

  • the interfaces need to have to be mature, but right now each framework has their own interface, so each framework matures their own with their own names
  • getting them into core will add a lot of lead time for the collaboration to pay off, making it less interesting to collaborate to begin with
  • not everybody will necessarily want all the interfaces people decide to collaborate on but it also makes no sense to then start separating them into optional extensions as this would bring us back to square one
  • actually there could still be "competing" interface definitions as there could be different subsets of the community collaborating on interfaces for the same or overlapping "topics"

As this solution would allow collaboration between projects with minimal logistical pains it could also then be the way to mature interfaces to the point where they are sufficiently widely adopted and used that it makes sense to put them into core. And this solution would then also be an easy to use migration path for the original collaborators. So with the above approach we are just offering a practical solution to real world problem. It certainly feels hacky, but at least even after a few days I still haven't decided that its going to spell the end of the world either. So what do people think?