Components · v1.x

Service

The Service component registers application services in the container from config/services.yaml: whole directories, explicit arguments, method calls, factories and aliases. Interfaces implemented by a single registered class are bound automatically, and the whole file is compiled into a cache.

Autowiring

Every class can be injected without configuration: the container reads the constructor and gives each parameter the service of its type. Controllers, middlewares, commands, listeners and view helpers are built this way.

class NewsletterController extends AbstractController
{
    public function __construct(protected MailerInterface $mailer)
    {
    }
}

A service is shared: the same instance is given everywhere during the request. #[Autowire(shared: false)] on the class, or shared: false in services.yaml, creates a new instance each time.

#[Autowire] and #[Inject]

#[Autowire] on a constructor parameter and #[Inject] on a property say what to inject when the type is not enough. Both come from the Container component (see the Container documentation).

use NeoPHP\Component\Container\Attribute\Autowire;
use NeoPHP\Component\Container\Attribute\Inject;

#[Autowire(shared: true)]
class SmtpMailer implements MailerInterface
{
    #[Inject]
    protected LoggerInterface $logger;

    public function __construct(
        #[Autowire(service: 'mailer.transport')] protected TransportInterface $transport,
        #[Autowire(config: 'framework.app.name')] protected string $appName,
        #[Autowire(env: 'MAILER_DSN')] protected string $dsn,
        #[Autowire(param: 'kernel.debug')] protected bool $debug,
        #[Autowire('%kernel.root_path%/var/mails')] protected string $spool,
    ) {
    }
}
Argument Injected value
value (first argument) a value; placeholders (%env(...)%, %kernel.*%, %config.key%) are resolved
service the service with this id
config a configuration value (framework.app.name)
env an environment variable
param a kernel parameter (kernel.debug, kernel.root_path...)
shared #[Autowire] on the class only: false creates a new instance each time

Configuration file

config/services.yaml (or config/services.yml) only accepts the root key services.

services:
  _defaults:
    shared: true

  App\:
    resource: ../src/
    exclude:
      - ../src/Kernel.php

  App\Service\SmtpMailer:
    arguments:
      $host: '%env(MAIL_HOST)%'
      $logger: '@NeoPHP\Component\Logger\Contract\LoggerInterface'
    calls:
      - [setFrom, ['noreply@example.com']]

  App\Service\Slugger: ~

  mailer: '@App\Service\SmtpMailer'

  App\Service\NotifierInterface: '@App\Service\SmsNotifier'

  app.api_client:
    class: App\Service\ApiClient
    factory: ['@App\Service\ApiClientFactory', 'create']
    arguments:
      $baseUrl: 'https://api.example.com'
    shared: false
Entry Description
_defaults defaults of the file: shared (default true); autowire is accepted
Namespace\: { resource, exclude, shared } registers every instantiable class of a directory; exclude: files, directories or * patterns, relative to the file
id: ~ registers a class with its default values
id: '@other' / id: { alias: other } alias of another service
class class of the service (default: the id)
arguments arguments by name ($host) or by position; the others are autowired
calls methods called after the construction: [method, [arguments]]
factory ['@service', 'method'], ['Class', 'method'] or 'Class::method'; must return an object
shared false creates a new instance each time

The priority of shared is: the entry, then #[Autowire(shared: ...)] on the class, then _defaults.

Unknown keys, a missing class, a missing resource, a malformed call or factory, a method call on a missing method and a factory that does not return an object throw a ServiceException.

Arguments

Value Meaning
@id the service id
@?id the service id, or null when it does not exist
@@text the string @text
%...% a configuration placeholder, resolved when the service is built (see the Config documentation)

Arrays are resolved recursively.

Interfaces

When one registered class implements an interface, the interface is bound to this class automatically: a parameter typed NotifierInterface receives SmsNotifier.

When several registered classes implement it, resolving the interface throws a ServiceException that asks for an alias:

services:
  App\Service\NotifierInterface: '@App\Service\SmsNotifier'

An interface already bound by the framework, or defined as a service or alias in the file, is never replaced automatically.

Cache

services.yaml is compiled into var/cache/service/services.{env}.php. In debug, the cache is rebuilt when the file or a class of a resource changes. In production, clear it on every deployment:

php bin/neo cache:clear

Command

service:list lists the services, the aliases and the interfaces bound automatically, with their class, shared flag and source (definition, resource, alias, interface). An optional filter keeps the rows whose id or class contains the text.

php bin/neo service:list
php bin/neo service:list Repository

API

ServiceInterface

NeoPHP\Component\Service\Contract\ServiceInterface (implemented by ServiceManager, extending AbstractService):

Method Description
register(array $definitions): static registers compiled definitions (services, aliases, interfaces keys) in the container
getServices(): array service definitions by id (class, arguments, calls, factory, shared, source)
getAliases(): array aliases (alias => target)
getInterfaces(): array interfaces bound automatically (interface => id, or a list of ids when ambiguous)
<?php

declare(strict_types=1);

namespace App\Service;

use NeoPHP\Component\Service\Contract\ServiceInterface;

final class ServiceAudit
{
    public function __construct(private ServiceInterface $services)
    {
    }

    public function count(): int
    {
        return count($this->services->getServices());
    }
}

Other classes

Class Description
NeoPHP\Component\Service\Loader\YamlServiceLoader load(string $file): array compiles a services file; getResources(): array returns the watched files
NeoPHP\Component\Service\Provider\ServiceProvider registers the manager and loads services.yaml (constants SERVICE_FILES, CACHE_DIRECTORY)
NeoPHP\Component\Service\Exception\ServiceException invalid file or definition, ambiguous interface

Changelog

  • v1.8.0 — #[Autowire], #[Inject], config/services.yaml (resources, arguments, calls, factories, aliases), shared services by default, interfaces bound to their single implementation, service:list command.