8000 [DI] Add "PHP fluent format" for configuring the container · symfony/symfony@8b6272e · GitHub
[go: up one dir, main page]

Skip to content

Commit 8b6272e

Browse files
[DI] Add "PHP fluent format" for configuring the container
1 parent fea348c commit 8b6272e

16 files changed

+1123
-1
lines changed

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

+60-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,15 @@
1111

1212
namespace Symfony\Component\DependencyInjection\Loader;
1313

14-
use Symfony\Component\Config\Resource\FileResource;
14+
require_once __DIR__.'/PhpFileLoader/functions.php';
15+
16+
use Symfony\Component\DependencyInjection\Definition;
17+
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
18+
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader\AliasConfigurator;
19+
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader\Internal\AbstractDefaultsConfigurator;
20+
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader\ParamConfigurator;
21+
use Symfony\Component\DependencyInjection\Loader\PhpFileLoader\ReferenceConfigurator;
22+
use Symfony\Component\DependencyInjection\Reference;
1523

1624
/**
1725
* PhpFileLoader loads service definitions from a PHP file.
@@ -23,6 +31,8 @@
2331
*/
2432
class PhpFileLoader extends FileLoader
2533
{
34+
private $defaults;
35+
2636
/**
2737
* {@inheritdoc}
2838
*/
@@ -35,6 +45,8 @@ public function load($resource, $type = null)
3545
$path = $this->locator->locate($resource);
3646
$this->setCurrentDir(dirname($path));
3747
$this->container->fileExists($path);
48+
$this->defaults = new Definition();
49+
$this->instanceof = array();
3850

3951
include $path;
4052
}
@@ -54,4 +66,51 @@ public function supports($resource, $type = null)
5466

5567
return 'php' === $type;
5668
}
69+
70+
/**
71+
* @internal
72+
*/
73+
public static function call(\Closure $callback)
74+
{
75+
$trace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT | DEBUG_BACKTRACE_IGNORE_ARGS, 4);
76+
if (!isset($trace[3]['object']) || !$trace[3]['object'] instanceof self) {
77+
throw new \ErrorException(sprintf('Function "%s()" must be called in a "%s" context.', $trace[1]['function'], __CLASS__), 0, E_ERROR, $trace[1]['file'], $trace[1]['line']);
78+
}
79+
$callback = $callback->bindTo($trace[3]['object'], __CLASS__);
80+
81+
return $callback($trace[3]['object']->defaults, $trace[1]['file']);
82+
}
83+
84+
/**
85+
* @internal
86+
*/
87+
public static function processValue($value)
88+
{
89+
if (is_array($value)) {
90+
foreach ($value as $k => $v) {
91+
$value[$k] = self::processValue($v);
92+
}
93+
94+
return $value;
95+
}
96+
97+
if ($value instanceof PhpFileLoader\ReferenceConfigurator) {
98+
static $refCast;
99+
100+
if (!$refCast) {
101+
$refCast = \Closure::bind(function ($ref) {
102+
return new Reference($ref->id, $ref->invalidBehavior);
103+
}, $value, $value);
104+
}
105+
106+
// cast ReferenceConfigurator to Reference
107+
return $refCast($value);
108+
}
109+
110+
if ($value instanceof AbstractDefaultsConfigurator || $value instanceof AliasConfigurator || $value instanceof ParamConfigurator) {
111+
throw new InvalidArgumentException(sprintf('"%s()" can be used only at the root of service configuration files.', $value::FACTORY));
112+
}
113+
114+
return $value;
115+
}
57116
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
13+
14+
use Symfony\Component\DependencyInjection\Alias;
15+
16+
/**
17+
* @author Nicolas Grekas <p@tchwork.com>
18+
*
19+
* @method $this public()
20+
* @method $this private()
21+
*/
22+
class AliasConfigurator
23+
{
24+
const FACTORY = 'alias';
25+
26+
private $alias;
27+
28+
public function __construct(Alias $alias)
29+
{
30+
$this->alias = $alias;
31+
}
32+
33+
/**
34+
* @return $this
35+
*/
36+
public function __call($method, $args)
37+
{
38+
if ('public' === strtolower($method)) {
39+
$this->alias->setPublic(true);
40+
} elseif ('private' === 10662 strtolower($method)) {
41+
$this->alias->setPublic(false);
42+
} else {
43+
throw new \BadMethodCallException(sprintf('Call to undefined method %s::%s()', get_class($this), $method));
44+
}
45+
46+
return $this;
47+
}
48+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
13+
14+
/**
15+
* @author Nicolas Grekas <p@tchwork.com>
16+
*
17+
* @method $this class()
18+
*/
19+
class ChildDefinitionConfigurator extends Internal\AbstractPrototypeConfigurator
20+
{
21+
const FACTORY = 'child';
22+
23+
/**
24+
* Sets the service that this service is decorating.
25+
*
26+
* @param null|string $id The decorated service id, use null to remove decoration
27+
* @param null|string $renamedId The new decorated service id
28+
* @param int $priority The priority of decoration
29+
*
30+
* @return $this
31+
*
32+
* @throws InvalidArgumentException In case the decorated service id and the new decorated service id are equals.
33+
*/
34+
public function decorate($id, $renamedId = null, $priority = 0)
35+
{
36+
$this->definition->setDecoratedService($id, $renamedId, $priority);
37+
38+
return $this;
39+
}
40+
41+
/**
42+
* @return $this
43+
*/
44+
public function __call($method, $args)
45+
{
46+
if ('class' !== strtolower($method)) {
47+
return parent::__call($method, $args);
48+
}
49+
if (!array_key_exists(0, $args)) {
50+
throw new \BadMethodCallException(sprintf('Missing argument 1 when calling method %s::%s()', get_class($this), $method));
51+
}
52+
$this->definition->setClass($args[0]);
53+
54+
return $this;
55+
}
56+
57+
/**
58+
* Sets a file to require before creating the service.
59+
*
60+
* @param string $file A full pathname to include
61+
*
62+
* @return $this
63+
*/
64+
public function file($file)
65+
{
66+
$this->definition->setFile($file);
67+
68+
return $this;
69+
}
70+
71+
/**
72+
* Sets whether this definition is synthetic, that is not constructed by the
73+
* container, but dynamically injected.
74+
*
75+
* @param bool $synthetic
76+
*
77+
* @return $this
78+
*/
79+
public function synthetic($synthetic = true)
80+
{
81+
$this->definition->setSynthetic($synthetic);
82+
83+
return $this;
84+
}
85+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
13+
14+
/**
15+
* @author Nicolas Grekas <p@tchwork.com>
16+
*/
17+
class DefaultsConfigurator extends Internal\AbstractDefaultsConfigurator
18+
{
19+
const FACTORY = '_defaults';
20+
21+
/**
22+
* Sets whether or not instanceof conditionals should be prepended with a global set.
23+
*
24+
* @return $this
25+
*/
26+
public function autoconfigure($autoconfigured = true)
27+
{
28+
$this->definition->setAutoconfigured($autoconfigured);
29+
30+
return $this;
31+
}
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
13+
14+
/**
15+
* @author Nicolas Grekas <p@tchwork.com>
16+
*/
17+
class DefinitionConfigurator extends ChildDefinitionConfigurator
18+
{
19+
const FACTORY = 'service';
20+
21+
/**
22+
* Sets whether or not instanceof conditionals should be prepended with a global set.
23+
*
24+
* @return $this
25+
*/
26+
public function autoconfigure($autoconfigured = true)
27+
{
28+
$this->definition->setAutoconfigured($autoconfigured);
29+
30+
return $this;
31+
}
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\DependencyInjection\Loader\PhpFileLoader;
13+
14+
/**
15+
* @author Nicolas Grekas <p@tchwork.com>
16+
*/
17+
class InstanceofConfigurator extends Internal\AbstractInstanceofConfigurator
18+
{
19+
const FACTORY = '_instanceof';
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\DependencyInjection\Loader\PhpFileLoader\Internal;
13+
14+
use Symfony\Component\DependencyInjection\Definition;
15+
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
16+
17+
/**
18+
* @author Nicolas Grekas <p@tchwork.com>
19+
*
20+
* @method $this public()
21+
* @method $this private()
22+
*
23+
* @internal
24+
*/
25+
abstract class AbstractDefaultsConfigurator
26+
{
27+
protected $definition;
28+
29+
public function __construct(Definition $definition)
30+
{
31+
$this->definition = $definition;
32+
}
33+
34+
/**
35+
* Adds a tag for this definition.
36+
*
37+
* @param string $name The tag name
38+
* @param array $attributes An array of attributes
39+
*
40+
* @return $this
41+
*/
42+
public function tag($name, array $attributes = array())
43+
{
44+
if (!is_string($name) || '' === $name) {
45+
throw new InvalidArgumentException(sprintf('The tag name in "_defaults" must be a non-empty string.'));
46+
}
47+
48+
foreach ($attributes as $attribute => $value) {
49+
if (!is_scalar($value) && null !== $value) {
50+
throw new InvalidArgumentException(sprintf('Tag "%s", attribute "%s" in "_defaults" must be of a scalar-type.', $name, $attribute));
51+
}
52+
}
53+
54+
$this->definition->addTag($name, $attributes);
55+
56+
return $this;
57+
}
58+
59+
/**
60+
* @return $this
61+
*/
62+
public function __call($method, $args)
63+
{
64+
if ('public' === strtolower($method)) {
65+
$this->definition->setPublic(true);
66+
} elseif ('private' === strtolower($method)) {
67+
$this->definition->setPublic(false);
68+
} else {
69+
throw new \BadMethodCallException(sprintf('Call to undefined method %s::%s()', get_class($this), $method));
70+
}
71+
72+
return $this;
73+
}
74+
75+
/**
76+
* Enables/disables autowiring.
77+
*
78+
* @param bool $autowired
79+
*
80+
* @return $this
81+
*/
82+
public function autowire($autowired = true)
83+
{
84+
$this->definition->setAutowired($autowired);
85+
86+
return $this;
87+
}
88+
}

0 commit comments

Comments
 (0)
0