Nette Documentation Preview

syntax
Service Definitions
*******************

.[perex]
Configuration is where we instruct the DI container how to create individual services and how to connect them with their dependencies. Nette offers a very clear and elegant way to achieve this.

The `services` section in the NEON configuration file is where we define our own services and their configurations. Let's look at a simple example defining a service named `database`, which represents an instance of the `PDO` class:

```neon
services:
	database: PDO('sqlite::memory:')
```

The configuration above results in the following factory method in the [DI container|container]:

```php
public function createServiceDatabase(): PDO
{
	return new PDO('sqlite::memory:');
}
```

Service names enable referencing them in other parts of the configuration file, using the `@serviceName` format. If there's no need to assign a name to the service, we can simply use a bullet point (`-`):

```neon
services:
	- PDO('sqlite::memory:')
```

To retrieve a service from the DI container, we can use the `getService()` method with the service name as a parameter, or the `getByType()` method with the service type:

```php
$database = $container->getService('database');
$database = $container->getByType(PDO::class);
```


Service Creation
================

Usually, we create a service simply by instantiating a specific class. For example:

```neon
services:
	database: PDO('mysql:host=127.0.0.1;dbname=test', root, secret)
```

If we need to expand the configuration with additional keys, the definition can be split across multiple lines:

```neon
services:
	database:
		create: PDO('sqlite::memory:')
		setup: ...
```

The `create` key has an alias `factory`; both variants are commonly used. However, we recommend using `create`.

Arguments for the constructor or the factory method can alternatively be specified using the `arguments` key:

```neon
services:
	database:
		create: PDO
		arguments: ['mysql:host=127.0.0.1;dbname=test', root, secret]
```

Services don't necessarily have to be created by simple class instantiation; they can also be the result of invoking static methods or methods of other services:

```neon
services:
	database: DatabaseFactory::create()
	router: @routerFactory::create()
```

Note that for simplicity, `::` is used instead of `->`, see [#Expression Language]. These factory methods will be generated:

```php
public function createServiceDatabase(): PDO
{
	return DatabaseFactory::create();
}

public function createServiceRouter(): RouteList
{
	return $this->getService('routerFactory')->create();
}
```

The DI container needs to know the type of the service being created. If we create a service using a method that lacks a specified return type, we must explicitly declare this type in the configuration:

```neon
services:
	database:
		create: DatabaseFactory::create()
		type: PDO
```


Arguments
=========

We pass arguments to constructors and methods in a way very similar to how it's done in PHP itself:

```neon
services:
	database: PDO('mysql:host=127.0.0.1;dbname=test', root, secret)
```

For better readability, we can list arguments on separate lines. In this case, using commas becomes optional:

```neon
services:
	database: PDO(
		'mysql:host=127.0.0.1;dbname=test'
		root
		secret
	)
```

You can also name the arguments, eliminating the need to worry about their order:

```neon
services:
	database: PDO(
		username: root
		password: secret
		dsn: 'mysql:host=127.0.0.1;dbname=test'
	)
```

If you want to omit certain arguments and use their default values or have a service injected via [autowiring|autowiring], use an underscore (`_`):

```neon
services:
	foo: Foo(_, %appDir%)
```

Arguments can include services, parameters, and much more, see [#Expression Language].


Setup
=====

In the `setup` section, we define methods that should be invoked upon service creation.

```neon
services:
	database:
		create: PDO(%dsn%, %user%, %password%)
		setup:
			- setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION)
```

This would look like this in PHP:

```php
public function createServiceDatabase(): PDO
{
	$service = new PDO('...', '...', '...');
	$service->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
	return $service;
}
```

In addition to method calls, values can also be assigned to properties. Adding elements to arrays is also supported, which requires enclosing the array access in quotes to avoid conflicts with NEON syntax:

```neon
services:
	foo:
		create: Foo
		setup:
			- $value = 123
			- '$onClick[]' = [@bar, clickHandler]
```

Which would look like the following in PHP code:

```php
public function createServiceFoo(): Foo
{
	$service = new Foo;
	$service->value = 123;
	$service->onClick[] = [$this->getService('bar'), 'clickHandler'];
	return $service;
}
```

However, in the setup, you can also invoke static methods or methods of other services. If you need to pass the current service itself as an argument, refer to it using `@self`:

```neon
services:
	foo:
		create: Foo
		setup:
			- My\Helpers::initializeFoo(@self)
			- @anotherService::setFoo(@self)
```

Note that for simplicity, `::` is used instead of `->`, see [#Expression Language]. Such a factory method will be generated:

```php
public function createServiceFoo(): Foo
{
	$service = new Foo;
	My\Helpers::initializeFoo($service);
	$this->getService('anotherService')->setFoo($service);
	return $service;
}
```


Expression Language
===================

Nette DI provides an exceptionally rich expression language, through which we can define almost anything. In configuration files, we can thus use [parameters |configuration#Parameters]:

```neon
# parameter
%wwwDir%

# value of a parameter under a key
%mailer.user%

# parameter inside a string
'%wwwDir%/images'
```

Furthermore, create objects, call methods and functions:

```neon
# create object
DateTime()

# call static method
Collator::create(%locale%)

# call PHP function
::getenv(DB_USER)
```

Refer to services either by their name or by type:

```neon
# service by name
@database

# service by type
@Nette\Database\Connection
```

Use first-class callable syntax: .{data-version:3.2.0}

```neon
# create callback, equivalent to [@user, logout]
@user::logout(...)
```

Use constants:

```neon
# class constant
FilesystemIterator::SKIP_DOTS

# get global constant using PHP function constant()
::constant(\PHP_VERSION)
```

Method calls can be chained just like in PHP. For simplicity, `::` is used instead of `->`:

```neon
DateTime()::format('Y-m-d')
# PHP: (new DateTime())->format('Y-m-d')

@http.request::getUrl()::getHost()
# PHP: $this->getService('http.request')->getUrl()->getHost()
```

You can use these expressions anywhere, when [creating services |#Service Creation], in [#arguments], in the [#setup] section, or in [parameters |configuration#Parameters]:

```neon
parameters:
	ipAddress: @http.request::getRemoteAddress()

services:
	database:
		create: DatabaseFactory::create( @anotherService::getDsn() )
		setup:
			- initialize( ::getenv('DB_USER') )
```


Special Functions
-----------------

In configuration files, you can use the following special functions:

- `not()` negates a value
- `bool()`, `int()`, `float()`, `string()` lossless casting to the specified type
- `typed()` creates an array of all services of the specified type
- `tagged()` creates an array of all services with the given tag

```neon
services:
	- Foo(
		id: int(::getenv('ProjectId'))
		productionMode: not(%debugMode%)
	)
```

Unlike standard PHP casting, such as `(int)`, lossless casting throws an exception for non-numeric values.

The `typed()` function creates an array of all services of the specified type (class or interface). It excludes services that have autowiring disabled. Multiple types can also be specified, separated by commas.

```neon
services:
	- BarsDependent( typed(Bar) )
```

An array of services of a certain type can also be passed as an argument automatically using [autowiring |autowiring#Collection of Services].

The `tagged()` function then creates an array of all services with a specific tag. Here too, you can specify multiple tags separated by commas.

```neon
services:
	- LoggersDependent( tagged(logger) )
```


Autowiring
==========

The `autowired` key allows you to influence the autowiring behavior for a specific service. For details, see the [chapter on autowiring|autowiring].

```neon
services:
	foo:
		create: Foo
		autowired: false     # the foo service is excluded from autowiring
```


Lazy Services .{data-version:3.2.4}
===================================

Lazy loading is a technique that defers the creation of a service until it is actually needed. In the global configuration, you can [enable lazy creation |configuration#Lazy Services] for all services at once. For individual services, you can then override this behavior:

```neon
services:
	foo:
		create: Foo
		lazy: false
```

When a service is defined as lazy, upon requesting it from the DI container, we receive a special proxy object. This proxy looks and behaves identically to the actual service, but the actual initialization (constructor invocation and setup calls) occurs only upon the first access to any of its methods or properties.

.[note]
Lazy loading can only be used for user-defined classes, not for internal PHP classes. It requires PHP 8.4 or newer.


Tags
====

Tags serve to add supplementary information to services. You can assign one or more tags to a service:

```neon
services:
	foo:
		create: Foo
		tags:
			- cached
```

Tags can also hold values:

```neon
services:
	foo:
		create: Foo
		tags:
			logger: monolog.logger.event
```

To retrieve all services associated with specific tags, you can use the `tagged()` function:

```neon
services:
	- LoggersDependent( tagged(logger) )
```

Within the DI container, you can retrieve the names of all services with a specific tag using the `findByTag()` method:

```php
$names = $container->findByTag('logger');
// $names is an array containing service names as keys and tag values as values
// e.g., ['foo' => 'monolog.logger.event', ...]
```


Inject Mode
===========

Using the `inject: true` flag enables dependency injection via public properties annotated with [inject |best-practices:inject-method-attribute#Inject Attributes] and [inject*() |best-practices:inject-method-attribute#inject Methods] methods.

```neon
services:
	articles:
		create: App\Model\Articles
		inject: true
```

By default, `inject` mode is enabled only for presenters.


Service Modifications
=====================

The DI container holds numerous services added via built-in or [user extensions|extensions]. You can modify the definitions of these existing services directly in the configuration. For example, you can change the class for the `application.application` service, which defaults to `Nette\Application\Application`, to a different one:

```neon
services:
	application.application:
		create: MyApplication
		alteration: true
```

The `alteration` flag is informative, indicating that we are merely modifying an existing service.

We can also supplement the setup:

```neon
services:
	application.application:
		create: MyApplication
		alteration: true
		setup:
			- '$onStartup[]' = [@resource, init]
```

When modifying a service, we might want to remove original arguments, setup items, or tags, using the `reset` key:

```neon
services:
	application.application:
		create: MyApplication
		alteration: true
		reset:
			- arguments
			- setup
			- tags
```

If you want to remove a service added by an extension, you can do so as follows:

```neon
services:
	cache.journal: false
```

Service Definitions

Configuration is where we instruct the DI container how to create individual services and how to connect them with their dependencies. Nette offers a very clear and elegant way to achieve this.

The services section in the NEON configuration file is where we define our own services and their configurations. Let's look at a simple example defining a service named database, which represents an instance of the PDO class:

services:
	database: PDO('sqlite::memory:')

The configuration above results in the following factory method in the DI container:

public function createServiceDatabase(): PDO
{
	return new PDO('sqlite::memory:');
}

Service names enable referencing them in other parts of the configuration file, using the @serviceName format. If there's no need to assign a name to the service, we can simply use a bullet point (-):

services:
	- PDO('sqlite::memory:')

To retrieve a service from the DI container, we can use the getService() method with the service name as a parameter, or the getByType() method with the service type:

$database = $container->getService('database');
$database = $container->getByType(PDO::class);

Service Creation

Usually, we create a service simply by instantiating a specific class. For example:

services:
	database: PDO('mysql:host=127.0.0.1;dbname=test', root, secret)

If we need to expand the configuration with additional keys, the definition can be split across multiple lines:

services:
	database:
		create: PDO('sqlite::memory:')
		setup: ...

The create key has an alias factory; both variants are commonly used. However, we recommend using create.

Arguments for the constructor or the factory method can alternatively be specified using the arguments key:

services:
	database:
		create: PDO
		arguments: ['mysql:host=127.0.0.1;dbname=test', root, secret]

Services don't necessarily have to be created by simple class instantiation; they can also be the result of invoking static methods or methods of other services:

services:
	database: DatabaseFactory::create()
	router: @routerFactory::create()

Note that for simplicity, :: is used instead of ->, see Expression Language. These factory methods will be generated:

public function createServiceDatabase(): PDO
{
	return DatabaseFactory::create();
}

public function createServiceRouter(): RouteList
{
	return $this->getService('routerFactory')->create();
}

The DI container needs to know the type of the service being created. If we create a service using a method that lacks a specified return type, we must explicitly declare this type in the configuration:

services:
	database:
		create: DatabaseFactory::create()
		type: PDO

Arguments

We pass arguments to constructors and methods in a way very similar to how it's done in PHP itself:

services:
	database: PDO('mysql:host=127.0.0.1;dbname=test', root, secret)

For better readability, we can list arguments on separate lines. In this case, using commas becomes optional:

services:
	database: PDO(
		'mysql:host=127.0.0.1;dbname=test'
		root
		secret
	)

You can also name the arguments, eliminating the need to worry about their order:

services:
	database: PDO(
		username: root
		password: secret
		dsn: 'mysql:host=127.0.0.1;dbname=test'
	)

If you want to omit certain arguments and use their default values or have a service injected via autowiring, use an underscore (_):

services:
	foo: Foo(_, %appDir%)

Arguments can include services, parameters, and much more, see Expression Language.

Setup

In the setup section, we define methods that should be invoked upon service creation.

services:
	database:
		create: PDO(%dsn%, %user%, %password%)
		setup:
			- setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION)

This would look like this in PHP:

public function createServiceDatabase(): PDO
{
	$service = new PDO('...', '...', '...');
	$service->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
	return $service;
}

In addition to method calls, values can also be assigned to properties. Adding elements to arrays is also supported, which requires enclosing the array access in quotes to avoid conflicts with NEON syntax:

services:
	foo:
		create: Foo
		setup:
			- $value = 123
			- '$onClick[]' = [@bar, clickHandler]

Which would look like the following in PHP code:

public function createServiceFoo(): Foo
{
	$service = new Foo;
	$service->value = 123;
	$service->onClick[] = [$this->getService('bar'), 'clickHandler'];
	return $service;
}

However, in the setup, you can also invoke static methods or methods of other services. If you need to pass the current service itself as an argument, refer to it using @self:

services:
	foo:
		create: Foo
		setup:
			- My\Helpers::initializeFoo(@self)
			- @anotherService::setFoo(@self)

Note that for simplicity, :: is used instead of ->, see Expression Language. Such a factory method will be generated:

public function createServiceFoo(): Foo
{
	$service = new Foo;
	My\Helpers::initializeFoo($service);
	$this->getService('anotherService')->setFoo($service);
	return $service;
}

Expression Language

Nette DI provides an exceptionally rich expression language, through which we can define almost anything. In configuration files, we can thus use parameters:

# parameter
%wwwDir%

# value of a parameter under a key
%mailer.user%

# parameter inside a string
'%wwwDir%/images'

Furthermore, create objects, call methods and functions:

# create object
DateTime()

# call static method
Collator::create(%locale%)

# call PHP function
::getenv(DB_USER)

Refer to services either by their name or by type:

# service by name
@database

# service by type
@Nette\Database\Connection

Use first-class callable syntax:

# create callback, equivalent to [@user, logout]
@user::logout(...)

Use constants:

# class constant
FilesystemIterator::SKIP_DOTS

# get global constant using PHP function constant()
::constant(\PHP_VERSION)

Method calls can be chained just like in PHP. For simplicity, :: is used instead of ->:

DateTime()::format('Y-m-d')
# PHP: (new DateTime())->format('Y-m-d')

@http.request::getUrl()::getHost()
# PHP: $this->getService('http.request')->getUrl()->getHost()

You can use these expressions anywhere, when creating services, in arguments, in the setup section, or in parameters:

parameters:
	ipAddress: @http.request::getRemoteAddress()

services:
	database:
		create: DatabaseFactory::create( @anotherService::getDsn() )
		setup:
			- initialize( ::getenv('DB_USER') )

Special Functions

In configuration files, you can use the following special functions:

  • not() negates a value
  • bool(), int(), float(), string() lossless casting to the specified type
  • typed() creates an array of all services of the specified type
  • tagged() creates an array of all services with the given tag
services:
	- Foo(
		id: int(::getenv('ProjectId'))
		productionMode: not(%debugMode%)
	)

Unlike standard PHP casting, such as (int), lossless casting throws an exception for non-numeric values.

The typed() function creates an array of all services of the specified type (class or interface). It excludes services that have autowiring disabled. Multiple types can also be specified, separated by commas.

services:
	- BarsDependent( typed(Bar) )

An array of services of a certain type can also be passed as an argument automatically using autowiring.

The tagged() function then creates an array of all services with a specific tag. Here too, you can specify multiple tags separated by commas.

services:
	- LoggersDependent( tagged(logger) )

Autowiring

The autowired key allows you to influence the autowiring behavior for a specific service. For details, see the chapter on autowiring.

services:
	foo:
		create: Foo
		autowired: false     # the foo service is excluded from autowiring

Lazy Services

Lazy loading is a technique that defers the creation of a service until it is actually needed. In the global configuration, you can enable lazy creation for all services at once. For individual services, you can then override this behavior:

services:
	foo:
		create: Foo
		lazy: false

When a service is defined as lazy, upon requesting it from the DI container, we receive a special proxy object. This proxy looks and behaves identically to the actual service, but the actual initialization (constructor invocation and setup calls) occurs only upon the first access to any of its methods or properties.

Lazy loading can only be used for user-defined classes, not for internal PHP classes. It requires PHP 8.4 or newer.

Tags

Tags serve to add supplementary information to services. You can assign one or more tags to a service:

services:
	foo:
		create: Foo
		tags:
			- cached

Tags can also hold values:

services:
	foo:
		create: Foo
		tags:
			logger: monolog.logger.event

To retrieve all services associated with specific tags, you can use the tagged() function:

services:
	- LoggersDependent( tagged(logger) )

Within the DI container, you can retrieve the names of all services with a specific tag using the findByTag() method:

$names = $container->findByTag('logger');
// $names is an array containing service names as keys and tag values as values
// e.g., ['foo' => 'monolog.logger.event', ...]

Inject Mode

Using the inject: true flag enables dependency injection via public properties annotated with inject and inject*() methods.

services:
	articles:
		create: App\Model\Articles
		inject: true

By default, inject mode is enabled only for presenters.

Service Modifications

The DI container holds numerous services added via built-in or user extensions. You can modify the definitions of these existing services directly in the configuration. For example, you can change the class for the application.application service, which defaults to Nette\Application\Application, to a different one:

services:
	application.application:
		create: MyApplication
		alteration: true

The alteration flag is informative, indicating that we are merely modifying an existing service.

We can also supplement the setup:

services:
	application.application:
		create: MyApplication
		alteration: true
		setup:
			- '$onStartup[]' = [@resource, init]

When modifying a service, we might want to remove original arguments, setup items, or tags, using the reset key:

services:
	application.application:
		create: MyApplication
		alteration: true
		reset:
			- arguments
			- setup
			- tags

If you want to remove a service added by an extension, you can do so as follows:

services:
	cache.journal: false