Introducing the service manager

This is a mechanism to allow service functionality to be added, changed or removed by module installation. A named service is prepresented by a Contract (a PHP interface) that defines the methods a service provider implementation must supply. The available providers are enumerated through the XOOPS preload/event system with a core.service.locate.SERVICE_NAME event.

A request for a named service starts with a $xoops->service(SERVICE_NAME) call which returns a Provider object. The Provider communicates with any provider implementations, invoking the requested method, and communicating through a Response object.

The first contract implementation is for an Avatar service. This contract provides methods to get an avatar image URL, and to get a URL to create or update an avatar. The system components that interact with avatars do not need to know which provider, if any, is in use. A call to $xoops->service('Avatars') connects to the provider, and the caller only needs to know the Contract\AvatarInterface methods and how to interpret the Response object. There is no need for the caller to check for installed modules, or have any predefined dependencies to use the service.

This commit contains the service manager implementation. A subsequent commit will have the providers and the changes required to convert all avatar aware  code to use the service manager mechanism.
Deploy service manager conversion for avatar functions.