8000 [DI] Add "_defaults" key to Yaml services configuration · symfony/symfony@7b4a18b · GitHub
[go: up one dir, main page]

Skip to content
10000

Commit 7b4a18b

Browse files
[DI] Add "_defaults" key to Yaml services configuration
1 parent 306a060 commit 7b4a18b

File tree

3 files changed

+122
-16
lines changed

3 files changed

+122
-16
lines changed

src/Symfony/Component/DependencyInjection/Loader/YamlFileLoader.php

Lines changed: 67 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -148,9 +148,56 @@ private function parseDefinitions($content, $file)
148148
if (!is_array($content['services'])) {
149149
throw new InvalidArgumentException(sprintf('The "services" key should contain an array in %s. Check your YAML syntax.', $file));
150150
}
151+
if (isset($content['services']['_defaults'])) {
152+
if (!is_array($defaults = $content['services']['_defaults'])) {
153+
throw new InvalidArgumentException(sprintf('Service defaults must be an array, "%s" given in "%s".', gettype($defaults), $file));
154+
}
155+
if (isset($defaults['alias']) || isset($defaults['class']) || isset($defaults['factory'])) {
156+
@trigger_error('Giving a service the "_defaults" name is deprecated since Symfony 3.3 and will be forbidden in 4.0. Rename your service.', E_USER_DEPRECATED);
157+
$defaults = array();
158+
} else {
159+
$defaultKeys = array('public', 'tags', 'autowire');
160+
unset($content['services']['_defaults']);
161+
162+
foreach ($defaults as $key => $default) {
163+
if (!in_array($key, $defaultKeys)) {
164+
throw new InvalidArgumentException(sprintf('The configuration key "%s" cannot be used to define a default value in "%s". Allowed keys are "%s".', $key, $file, implode('", "', $defaultKeys)));
165+
}
166+
}
167+
if (isset($defaults['tags'])) {
168+
if (!is_array($tags = $defaults['tags'])) {
169+
throw new InvalidArgumentException(sprintf('Parameter "tags" in "_defaults" must be an array in %s. Check your YAML syntax.', $file));
170+
}
171+
172+
foreach ($tags as $tag) {
173+
if (!is_array($tag)) {
174+
$tag = array('name' => $tag);
175+
}
176+
177+
if (!isset($tag['name'])) {
178+
throw new InvalidArgumentException(sprintf('A "tags" entry in "_defaults" is missing a "name" key in %s.', $file));
179+
}
180+
$name = $tag['name'];
181+
unset($tag['name']);
182+
183+
if (!is_string($name) || '' === $name) {
184+
throw new InvalidArgumentException(sprintf('The tag name in "_defaults" must be a non-empty string in %s.', $file));
185+
}
186+
187+
foreach ($tag as $attribute => $value) {
188+
if (!is_scalar($value) && null !== $value) {
189+
throw new InvalidArgumentException(sprintf('Tag "%s", attribute "%s" in "_defaults" must be of a scalar-type in %s. Check your YAML syntax.', $name, $attribute, $file));
190+
}
191+
}
192+
}
193+
}
194+
}
195+
} else {
196+
$defaults = array();
197+
}
151198

152199
foreach ($content['services'] as $id => $service) {
153-
$this->parseDefinition($id, $service, $file);
200+
$this->parseDefinition($id, $service, $file, $defaults);
154201
}
155202
}
156203

@@ -160,10 +207,11 @@ private function parseDefinitions($content, $file)
160207
* @param string $id
161208
* @param array $service
162209
* @param string $file
210+
* @param array $defaults
163211
*
164212
* @throws InvalidArgumentException When tags are invalid
165213
*/
166-
private function parseDefinition($id, $service, $file)
214+
private function parseDefinition($id, $service, $file, array $defaults)
167215
{
168216
if (is_string($service) && 0 === strpos($service, '@')) {
169217
$this->container->setAlias($id, substr($service, 1));
@@ -178,7 +226,7 @@ private function parseDefinition($id, $service, $file)
178226
static::checkDefinition($id, $service, $file);
179227

180228
if (isset($service['alias'])) {
181-
$public = !array_key_exists('public', $service) || (bool) $service['public'];
229+
$public = array_key_exists('public', $service) ? (bool) $service['public'] : !empty($defaults['public']);
182230
$this->container->setAlias($id, new Alias($service['alias'], $public));
183231

184232
foreach ($service as $key => $value) {
@@ -192,6 +240,7 @@ private function parseDefinition($id, $service, $file)
192240

193241
if (isset($service['parent'])) {
194242
$definition = new ChildDefinition($service['parent']);
243+
$defaults = array();
195244
} else {
196245
$definition = new Definition();
197246
}
@@ -212,8 +261,9 @@ private function parseDefinition($id, $service, $file)
212261
$definition->setLazy($service['lazy']);
213262
}
214263

215-
if (isset($service['public'])) {
216-
$definition->setPublic($service['public']);
264+
$public = isset($service['public']) ? $service['public'] : (isset($defaults['public']) ? $defaults['public'] : null);
265+
if (null !== $public) {
266+
$definition->setPublic($public);
217267
}
218268

219269
if (isset($service['abstract'])) {
@@ -262,27 +312,27 @@ private function parseDefinition($id, $service, $file)
262312
}
263313
}
264314

265-
if (isset($service['tags'])) {
266-
if (!is_array($service['tags'])) {
315+
$tags = isset($service['tags']) ? $service['tags'] : (isset($defaults['tags']) ? $defaults['tags'] : null);
316+
if (null !== $tags) {
317+
if (!is_array($tags)) {
267318
throw new InvalidArgumentException(sprintf('Parameter "tags" must be an array for service "%s" in %s. Check your YAML syntax.', $id, $file));
268319
}
269320

270-
foreach ($service['tags'] as $tag) {
321+
foreach ($tags as $tag) {
271322
if (!is_array($tag)) {
272323
$tag = array('name' => $tag);
273324
}
274325

275326
if (!isset($tag['name'])) {
276327
throw new InvalidArgumentException(sprintf('A "tags" entry is missing a "name" key for service "%s" in %s.', $id, $file));
277328
}
329+
$name = $tag['name'];
330+
unset($tag['name']);
278331

279-
if (!is_string($tag['name']) || '' === $tag['name']) {
332+
if (!is_string($name) || '' === $name) {
280333
throw new InvalidArgumentException(sprintf('The tag name for service "%s" in %s must be a non-empty string.', $id, $file));
281334
}
282335

283-
$name = $tag['name'];
284-
unset($tag['name']);
285-
286336
foreach ($tag as $attribute => $value) {
287337
if (!is_scalar($value) && null !== $value) {
288338
throw new InvalidArgumentException(sprintf('A "tags" attribute must be of a scalar-type for service "%s", tag "%s", attribute "%s" in %s. Check your YAML syntax.', $id, $name, $attribute, $file));
@@ -303,11 +353,12 @@ private function parseDefinition($id, $service, $file)
303353
$definition->setDecoratedService($service['decorates'], $renameId, $priority);
304354
}
305355

306-
if (isset($service['autowire'])) {
307-
if (is_array($service['autowire'])) {
308-
$definition->setAutowiredMethods($service['autowire']);
356+
$autowire = isset($service['autowire']) ? $service['autowire'] : (isset($defaults['autowire']) ? $defaults['autowire'] : null);
357+
if (null !== $autowire) {
358+
if (is_array($autowire)) {
359+
$definition->setAutowiredMethods($autowire);
309360
} else {
310-
$definition->setAutowired($service['autowire']);
361+
$definition->setAutowired($autowire);
311362
}
312363
}
313364

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
services:
2+
_defaults:
3+
public: false
4+
autowire: true
5+
tags:
6+
- name: foo
7+
8+
with_defaults:
9+
class: Foo
10+
11+
with_null:
12+ 10000
class: Foo
13+
public: ~
14+
autowire: ~
15+
tags: ~
16+
17+
no_defaults:
18+
class: Foo
19+
public: true
20+
autowire: false
21+
tags: []
22+
23+
no_defaults_child:
24+
parent: no_defaults
25+
autowire: ~
26+
tags:
27+
- name: bar
28+
29+
with_defaults_child:
30+
parent: with_defaults

src/Symfony/Component/DependencyInjection/Tests/Loader/YamlFileLoaderTest.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,31 @@ public function testClassFromId()
352352
$this->assertEquals(CaseSensitiveClass::class, $container->getDefinition(CaseSensitiveClass::class)->getClass());
353353
}
354354

355+
public function testDefaults()
356+
{
357+
$container = new ContainerBuilder();
358+
$loader = new YamlFileLoader($container, new FileLocator(self::$fixturesPath.'/yaml'));
359+
$loader->load('services28.yml');
360+
361+
$this->assertFalse($container->getDefinition('with_defaults')->isPublic());
362+
$this->assertFalse($container->getDefinition('with_null')->isPublic());
363+
$this->assertTrue($container->getDefinition('no_defaults')->isPublic());
364+
$this->assertTrue($container->getDefinition('no_defaults_child')->isPublic());
365+
$this->assertArrayNotHasKey('public', $container->getDefinition('no_defaults_child')->getChanges());
366+
367+
$this->assertSame(array('foo' => array(array())), $container->getDefinition('with_defaults')->getTags());
368+
$this->assertSame(array('foo' => array(array())), $container->getDefinition('with_null')->getTags());
369+
$this->assertSame(array(), $container->getDefinition('no_defaults')->getTags());
370+
$this->assertSame(array('bar' => array(array())), $container->getDefinition('no_defaults_child')->getTags());
371+
$this->assertSame(array(), $container->getDefinition('with_defaults_child')->getTags());
372+
373+
$this->assertTrue($container->getDefinition('with_defaults')->isAutowired());
374+
$this->assertTrue($container->getDefinition('with_null')->isAutowired());
375+
$this->assertFalse($container->getDefinition('no_defaults')->isAutowired());
376+
$this->assertFalse($container->getDefinition('no_defaults_child')->isAutowired());
377+
$this->assertArrayNotHasKey('autowire', $container->getDefinition('no_defaults_child')->getChanges());
378+
}
379+
355380
/**
356381
* @expectedException \Symfony\Component\DependencyInjection\Exception\InvalidArgumentException
357382
* @expectedExceptionMessage The value of the "decorates" option for the "bar" service must be the id of the service without the "@" prefix (replace "@foo" with "foo").

0 commit comments

Comments
 (0)
0