Service Guarantor

Jet introduces the term Service Guarantor. What is it? Let's start with a concrete example of logging in Jet (although of course this is not just about logging):

namespace JetApplicationModule\Content\Articles\Admin;

use 
Jet\Logger;

use 
Jet\MVC_Controller_Default;

class 
Controller_Main extends MVC_Controller_Default
{
    
    public function 
add_Action(): void
    
{
        
//... ... ...
        
        
Logger::success(
            
event'article_created',
            
context_object_data$article
        
);
        
        
//... ... ...
    
}
}

No, don't be scared! It's not a service locator (for example). And everything makes perfect sense. Read on ;-)

In general, any service must have some defined interface (whether it is the interface it implements directly or an abstract class). Plain and simple, users of a service need to know what interface the service has and how to use it. This is true here as well. The logger in PHP Jet has an interface Jet\Logger_Interface (which in this case is really primitive). In this respect, nothing changes in PHP Jet at all, and it's firmly established that services have their given interface.

In other frameworks, however, what we call the one big pile syndrome works. The services are taken care of by the complex subsystem mentioned above. The services are thus on one heap.

PHP Jet does this by having the system define a service guarantor in addition to the service interface. The service guarantor is not just a static facade that is a bridge to that big service container (as one competing framework has). Yes, the convenience and straightforwardness of using the facade remains the same here. But the service guarantor in Jet is not just a facade, but also a container into which a specific service provider is embedded.

So if we continue with our logging example, you should know that the Jet\Logger facade itself does not perform any logging. It's just a service guarantor - a container having an interface reflecting the service, but a specific service provider had to be injected into that container (here, a logger that already knows how to perform the operation and provide the service). For example:

Logger::setLogger( new Logger_Admin() );

Thus, when the application was initialized, some higher authority (here base initializer, but it could be anything else) could have decided what specific service to use for logging.

So here you can have different logging for admin panel, different for REST API, different for web and so on. But the rest of the application doesn't care and just logs - just as it should under DI rules. At the same time, there is no dependency hell, no problems. And using everything is perfectly simple and straightforward.

Less code, less complexity, less problems. And at the same time, much more flexibility and better project sustainability.

Overview of service providers in Jet

To illustrate, we have shown logging as a very simple service. Of course, there are many more services and providers in Jet. Here is a list of them.

And of course there is nothing to stop you from creating your own services using a similar mechanism and philosophy. And you don't need a complex framework to do it ;-)

Service / subsystem Service guarantor Method for injecting a service provider
Authentication and authorization Jet\Auth Jet\Auth::setController(
Jet\Auth_Controller_Interface $controller
)
The service provider is the so-called Auth Controller implementing Jet\Auth_Controller_Interface. The role of this controller is to manage the logic of working with users, more specifically the logic of authentication and authorization.

There is no default Auth Controller. Similarly, there is no Auth Controller implementation included in the framework. These controllers are always part of the application space - the application. So it is entirely up to the developer how he implements the logic and nothing prevents any modifications. There are three controllers included in the sample application.

When using Jet MVC, the controller instance is injected into the service guarantor within the base initializer.
Autoloader Jet\Autoloader Jet\Autoloader::register(
Jet\Autoloader_Loader $loader
): void
The system for automatic class loading is actually made up of individual modules that must be injected when the application is initialized. Of course, there can be (and in practice are) more than one of these modules.

In the default configuration, initialization can be found in the script ~/application/Init/Autoloader.php
Autoloaderu Cahce Jet\Autoloader_Cache Jet\Autoloader_Cache::init(
Jet\Autoloader_Cache_Backend $backend
)
The backend cache of the automatic class loading system must be injected.

In the default configuration, the initialization can be found in the script ~/application/Init/Cache/Autoloader.php
Application Module Manager Jet\Application_Modules Jet\Application_Modules::setHandler(
Jet\Application_Modules_Handler $handler
) : void
The application module manager does not need to be injected. The system has a default implementation that is used if no other manager was injected during initialization.

Thus, it is possible to inject an alternative module manager, but in this case it is not necessary to inject it.

The application module manager is also uses the factory system.
Logger Jet\Logger Jet\Logger::setLogger(
Jet\Logger_Interface $logger
): void
System providing general logging. The implementation of the logger is always part of the application and not the framework. The sample application contains prebuilt loggers.

When using Jet MVC, the logger instance is injected into the service guarantor within the base initializer.
Sending emails Jet\Mailing Jet\Mailing::setBackend(
Mailing_Backend_Abstract $backend
): void
The email sending system needs a backend that ensures the actual sending of the email. The default backend will ensure sending before the standard means of PHP itself, and this default backend does not need to be injected. The default backend is used automatically if another backend is not injected into the service guarantor.

Again, it is possible to inject your own backend, but if you are happy with the default backend, then injecting it is not necessary.
Main MVC router Jet\MVC Jet\MVC::setRouter(
MVC_Router_Interface $router
): void
Also the main router Jet MVC does not need to be injected, but has its default implementation, but it is also possible to create your own router implementation and inject it. Interestingly, the Jet MVC router also uses factories.
Keš MVC Jet\MVC_Cache Jet\MVC_Cache::init(
MVC_Cache_Backend $backend
): void
The Jet MVC cache needs to be initialized and the backend inserted into the container/service guarantor at the designated location. That is, in the script:

~/application/Init/Cache/MVC.php

There are two ready-made backends using either files or Redis for storage. And of course there is nothing preventing you from implementing any other backend and injecting it into the system.
REST API server Jet\RESTServer Jet\RESTServer::setBackend(
RESTServer_Backend $backend
) : void
The backend REST API of the server is responsible for the specific implementation of HTTP requests read, in turn creating specific HTTP responses. So if you want to change the default behavior of the REST API server, it is of course not a problem.

Here again, there is a default implementation that is initialized automatically, so there is no need to inject the backend in the default situation.
Translator - dictionary storage Jet\Translator Jet\Translator::setBackend(
Translator_Backend $backend
) : void
The role of the translator backend is to load and store translation dictionaries.

Again, there is a default implementation that operates on files and this default backend is used automatically unless another one is injected.
Previous chapter
Dependency Injection in Jet framework
Next chapter
Factories