Skip to content

Latest commit

 

History

History
169 lines (130 loc) · 6.62 KB

File metadata and controls

169 lines (130 loc) · 6.62 KB

Service Providers

The backbone of Framework Factory is its versatile PSR-11 compliant IoC container. IoC containers typically allow services, or dependencies, to be loaded into them in many ways. Framework Factory, for instance, uses Service Providers to accomplish the installation, modification and customization of these services.

A service provider is a class or module that tells the container how to create, configure, and register services so they can be automatically resolved and injected into other parts of an application. Framework Factory uses a specific approach to creating service providers, using internal libraries to handle the heavy lifting of certain tasks such as the injection of the library, the implementation of the Context API and executing its lifecycle hooks.

For questions about creating and building services, please feel free to review the Services section of the docs before proceeding.

Building Providers

Each service provider must extend Framework Factory's base ServiceProvider class. The register() method of that class provides access to the containers binding methods.

<?php

namespace App\Providers;

use FrameworkFactory\Contracts\Providers\ServiceProvider;

class MessageProvider extends ServiceProvider
{
     public function register(): void
     {
        // ...
     }
}

Adding Services

Below is an example of the code required for your typical service provider class. Each service that is bound to the container is mounted by calling the bind() method.

<?php

namespace App\Providers;

use FrameworkFactory\Contracts\Providers\ServiceProvider;
use App\Services\Message as MessageService;

class MessageProvider extends ServiceProvider
{
     public function register(): void
     {
        $this->bind(MessageService::class, fn () => new MessageService());
     }
}

Using CreatesBinding

There is an alternative approach to binding a service to the container from within a service provider. This method uses the CreatesBinding attribute. This technique is useful when you want to register a non-deferred service and bind it to the container, or a service that has no arguments needing to be passed through its constructor.

When implementing the CreatesBinding attribute, the first parameter is the ID of the binding you're registering and the second parameter is the class you'd like to register.

<?php

namespace App\Providers;

use FrameworkFactory\Attributes\Providers\CreatesBinding;
use FrameworkFactory\Contracts\Providers\ServiceProvider;
use App\Services\Message as MessageService;

#[CreatesBinding('message', MessageService::class)]
class MessageProvider extends ServiceProvider {}

When using this approach, the register() method can be omitted since the CreatesBinding attribute foregoes this entirely, and bind()'s the new service to the container internally.

Lazy Loading

Lazy-loading is a programming technique where data, objects, modules, or resources are not loaded into memory until they are actually needed, rather than being loaded upfront. This can improve application startup time, reduce memory usage, lower network traffic, and make large applications feel more responsive because unnecessary work is deferred.

Common examples include loading database relationships only when accessed, importing JavaScript modules on demand, or loading images as a user scrolls through a page. However, lazy-loading also has disadvantages: it can introduce small delays when a resource is first requested, make application behavior less predictable, increase code complexity, and sometimes lead to issues such as excessive database queries (for example, the "N+1 query" problem in ORMs) if not implemented carefully. In general, lazy-loading trades immediate resource consumption for deferred execution, which can significantly improve efficiency when many resources may never actually be used.

Below is an example of the code required for lazy loading. This is done by calling the singleton() method. Additionally, we will want to implement the provides() method - which returns an array of ID's for singleton services that have been added to the container within that Service Provider class.

<?php

namespace App\Providers;

use FrameworkFactory\Contracts\Providers\ServiceProvider;
use App\Services\Message as MessageService;

class MessageProvider extends ServiceProvider
{
     public function register(): void
     {
        $this->singleton(MessageService::class, fn () => new MessageService());
     }
     
     public function provides(): array
     {
        return [
            MessageService::class
        ];
     }
}

Accessing Services

Accessing services can be done by calling the Application::get() method from anywhere within the application. This method returns the service that has been bound to the container, by using the assigned ID as an argument.

Using the Application

<?php

use App\Services\Message as MessageService;
use FrameworkFactory\Application;

$message = Application::get(MessageService::class);

echo $message->display('This is my message to the world!');

Using Accessors

Alternately, Accessors can be used to access services that have been bound to the container.

<?php

use App\Accessors\Message;

$message = Message::display('This is my message to the world!');

echo $message;

The boot() method

Once a dependency has been loaded into the container using a service provider, and the container has been built, we may want to interact with it prior to calling it from within the application. The boot() method of a service container is executed after the container has been built, and all dependencies have been loaded into it which allows them to be interacted with.

<?php

public function boot(): void
{
    // do something ...
}