So a while back I wanted to add native support for API versioning to FOSRestBundle. While working on this I began to realize that this would really require supporting proper content negotiation. Note that while there is a spec for how to express the preferences of the client, there is no spec on "negotiating" between what the client wants and what the server has to offer. My current implementation is rather simple .. well simple is probably a euphemism for "incomplete to the point of being more than half wrong". I began to search and didn't really find any decoupled library for PHP that could do the job. I then stumbled over mod_negotiation and thought that it would make a lot of sense to use their algorithm as I simply assumed that it would have gotten a fair bit if scrutiny preventing me from doing something stupid. More over it could also mean that we cut optionally allow users to leverage this apache module instead of our PHP implementation to improve performance. Now that idea sat there for a while until the Drupal guys brought up the topic again. I told them my idea and work began doing exactly this but unfortunately isn't moving forward as quickly as needed to make the December feature freeze of Drupal 8. There are two new people willing to work on this, but its still unclear if things will get ready in time. Since I know we are not the only ones who need this in the PHP community, I am sending this out as a rally cry to work on this together.
Now there are a few efforts in this area already that I am aware of and surely many I am not aware of. Heck maybe the perfect solution already exists out there and someone just as to point it out. First up there is Ben Ramsey's adaptation of an older library called mimeparse which provides at least part of what is needed. There is also an older implementation which is not really nicely decoupled and its using the LGPL which I am not a huge fan of. The Flow3 guys also recently beefed up their support for this, but it is not decoupled either. Finally I am sure that the EngineYard folks also have something inside FRAPI relating to content type negotiation.
Now wouldn't it be great of there would be a modern decoupled library that would prevent a solid implementation of a content negotiation algorithm. Maybe even pluggable to allow other flavors for negotiation as I can for example envision some people caring more about speed than completeness. Now I must admit that my personal plate is quite full with PHPCR and friends but I am willing to help to make this happen. But I am likely the wrong person to drive this. So I am hoping that with this post I can entice someone to step up. Maybe EngineYard wants to take the lead on this along the lines of the sponsorship of the Lithium framework? Heck I am sure the Lithium guys would also love to have something like this! But maybe someone will hear this call for help?
Indeed I should have mentioned those, especially as I am aware of them. Obviously since this extension isn't part of core there would still be a need of a userland implementation. But obviously its easier to reimplement this existing C code in PHP, then it is to start from scratch.
That being said, the API isn't really extensible. Most importantly it seems to read directly out of the headers send to PHP itself, meaning that I am not sure how one can easily create a request manually, f.e. for a sub-request or functional testing.
But I must admit I haven't played with it much yet, so please correct me if I am wrong ..
Could you briefly explain what the design goals would be? Maybe with some simple pseudo-code examples?
Just for the record here is the link I gave Nate via twitter:
Its not necessarily my personal ideal API, but its more or less what we need, ie. some way to pass in the clients accept headers, the servers preferences (how these are determined are beyond the cope of the library and will be different from framework to framework) and get out the "best" match .. and optionally an ordered list sorted by how good the match was.
Furthermore the implementation should use interfaces and dependency injection to make it easy to replace the algorithm. Some users might need a very sophisticated algorithm, for other use cases it can be simple but needs to be super fast etc.