Repository navigation
Expand file tree
/
Copy pathservices.texy
More file actions
480 lines (352 loc) Β· 13 KB
/
Copy pathservices.texy
File metadata and controls
480 lines (352 loc) Β· 13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
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)
```
Access a service's public properties and constants via `@service::member`. Whether the name resolves to a property or to a constant is decided by its first letter - a lowercase initial means a public property, an uppercase one means a constant:
```neon
# public property of a service (starts with a lowercase letter)
@settings::apiUrl
# class constant of a service (starts with an uppercase letter)
@settings::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 .{data-version:3.0.5}
- `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.
Keep in mind that because the service is created later, errors in its configuration also manifest themselves later. For example, incorrect database credentials will not reveal themselves when the application starts, but only upon the first query.
Lazy creation also mitigates circular dependencies, i.e., a situation where service A requires service B and B at the same time requires A. Without it, the container reports the error `Circular reference detected`. With a lazy proxy, service A receives only a proxy of service B, which initializes itself when it is actually used, at a moment when A already exists. Nevertheless, a circular dependency signals a flawed design, and it is better to get rid of it.
.[note]
Lazy loading requires PHP 8.4 or newer and works only for services created by directly instantiating a class (e.g. `create: Foo`), not for those created by a factory method. It also cannot be used for classes that ultimately extend an internal PHP class. When lazy loading cannot be applied, the `lazy: true` flag is silently ignored.
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 with the [Inject |best-practices:inject-method-attribute#Inject Attributes] attribute 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 indicates that we are merely modifying an existing service. It also acts as a safeguard: if the service being modified doesn't exist, compilation fails with an exception.
We can also supplement the setup:
```neon
services:
application.application:
create: MyApplication
alteration: true
setup:
- '$onStartup[]' = [@resource, init]
```
You don't have to identify a service by its internal name - you can refer to it by type instead. The previous example can also be written as:
```neon
services:
@Nette\Application\Application:
create: MyApplication
```
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: true
setup: true
tags: true
```
If you want to remove a service added by an extension, you can do so as follows:
```neon
services:
cache.journal: false
```