Skip to content

[DependencyInjection] Make method (setter) autowiring configurable #20167

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 13, 2016

Conversation

dunglas
Copy link
Member

@dunglas dunglas commented Oct 5, 2016

Q A
Branch? master
Bug fix? no
New feature? yes
BC breaks? no
Deprecations? maybe?
Tests pass? yes
Fixed tickets #19631
License MIT
Doc PR symfony/symfony-docs#7041

Follow up of #19631. Implements #19631 (comment):

Edit: the last supported format:

services:
    foo:
        class: Foo
        autowire: ['__construct', 'set*'] # Autowire constructor and all setters
        autowire: true # Converted by loaders in `autowire: ['__construct']` for BC
        autowire: ['foo', 'bar'] # Autowire only `foo` and `bar` methods

Outdated:

autowire: true # constructor autowiring
autowire: [__construct, setFoo, setBar] # autowire whitelisted methods only
autowire: '*' # autowire constructor + every setters (following existing rules for setters autowiring)
  • Allow to specify the list of methods in the XML loader
  • Add tests for the YAML loader

@@ -662,13 +662,28 @@ public function setAutowiringTypes(array $types)
*/
public function isAutowired()
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should I deprecate this method?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure it's worth it.

@fabpot
Copy link
Member

fabpot commented Oct 5, 2016

👍

@dunglas
Copy link
Member Author

dunglas commented Oct 6, 2016

The syntax for the XML loader is:

<?xml version="1.0" encoding="utf-8"?>
<container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
    <services>
        <service id="autowire_star" class="Foo" autowire="*" />
        <service id="autowire_array" class="Foo">
            <autowire>setFoo</autowire>
            <autowire>bar</autowire>
        </service>
    </services>
</container>

Does it looks good to everybody?

$methods = array();
foreach ($autowired as $methodName) {
if ($reflectionClass->hasMethod($methodName)) {
$methods[] = $reflectionClass->getMethod($methodName);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This won't work when using ['__construct'] right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It works fine: https://3v4l.org/Lr78t

Copy link
Contributor

@GuilhemN GuilhemN Oct 7, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure but it will add its arguments as a call instead of constructor arguments. And it may create weird bugs and not work at all if there are mandatory constructor arguments.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch I'll update that.

@@ -41,8 +41,8 @@ public function process(ContainerBuilder $container)
try {
$this->container = $container;
foreach ($container->getDefinitions() as $id => $definition) {
if ($definition->isAutowired()) {
$this->completeDefinition($id, $definition);
if (false !== $autowired = $definition->getAutowired()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about keep using $definition->isAutowired() here (as it stay valid), remove the newly added $autowired argument from completeDefinition() and use $autowired = $defintion->getAutowired(); directly into?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It adds some extra method calls for no reason. I prefer to keep it as is.

Copy link
Member

@chalasr chalasr Oct 8, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current adds an extra argument to completeDefinition for no reason, it can be done in the method directly as you have the Definition, and the check is doing exactly what the method call would do thus ... Up to you.

@@ -72,8 +72,10 @@ public static function createResourceForClass(\ReflectionClass $reflectionClass)
$metadata['__construct'] = self::getResourceMetadataForMethod($constructor);
}

foreach (self::getSetters($reflectionClass) as $reflectionMethod) {
$metadata[$reflectionMethod->name] = self::getResourceMetadataForMethod($reflectionMethod);
foreach ($reflectionClass->getMethods(\ReflectionMethod::IS_PUBLIC) as $reflectionMethod) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@weaverryan can you take a look at this change?

Because it's now possible to autowire any method (not just only setters), I think that there is now other solution than watching all defined methods.

Copy link
Contributor

@GuilhemN GuilhemN Oct 8, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about allowing masks? (eg set*).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can indeed allow masks in the configuration, but it's not related to this code block (it is not aware of the configuration so it needs to watch all methods anyway).

@dunglas
Copy link
Member Author

dunglas commented Oct 13, 2016

ping @symfony/deciders. We need to choose between merging this one or reverting setter autowiring before the 3.2 release.

*/
public function getMethods(\ReflectionClass $reflectionClass, array $autowired)
{
if (is_array($autowired)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's always an array as it is typehinted

*
* @return \ReflectionMethod[]
*/
public function getMethods(\ReflectionClass $reflectionClass, array $autowired)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is meant to be private

@Tobion
Copy link
Contributor

Tobion commented Oct 13, 2016

The XML loader should probably forbid to use both the autowire attribute and elements at the same time. Stuff like that doesn't make sense:

<?xml version="1.0" encoding="utf-8"?>
<container xmlns="http://symfony.com/schema/dic/services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
    <services>
        <service id="autowire_star" class="Foo" autowire="*">
           <autowire>setFoo</autowire>
            <autowire>bar</autowire>
        </service>
        <service id="autowire_array" class="Foo"  autowire="false">
            <autowire>setFoo</autowire>
            <autowire>bar</autowire>
        </service>
    </services>
</container>


<xsd:simpleType name="boolean_or_star">
<xsd:restriction base="xsd:string">
<xsd:pattern value="(%.+%|true|false|\*)" />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A boolean is XML schema is also allowed to be 1 and 0. So this might break BC.

Copy link
Member Author

@dunglas dunglas Oct 13, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't a BC break because it was already not using the boolean type of XML schema but the one locally defined. I've just copied, pasted and extended the current type definition to allow using a * character:

  <xsd:simpleType name="boolean">
    <xsd:restriction base="xsd:string">
      <xsd:pattern value="(%.+%|true|false)" />
    </xsd:restriction>
  </xsd:simpleType>

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, it's to allow parameters. I'm pretty sure allowing dynamic values for autowiring should not be allowed and makes no sense after all. I don't see the point of it.

Copy link
Member Author

@dunglas dunglas Oct 13, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean by dynamic values? It allows to write:

<service class="Foo" autowire="true"/> <!-- Autowire constructor only (BC) -->
<!-- or -->
<service class="Foo" autowire="*"/> <!-- Autowire constructor + setters -->

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean parameters: <service class="Foo" autowire="%param%"/>

Copy link
Member Author

@dunglas dunglas Oct 13, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok got it. But we cannot change it because it was already allowed since 2.8 (because it was using the local boolean type like all others parameters).

@Tobion
Copy link
Contributor

Tobion commented Oct 13, 2016

So if you explicitly autowire a method that has no arguments it is basically the same as a normal call configuration to this method? I suspect people will then misuse autowiring just to call certain methods since there are now 2 ways to do that.

@dunglas
Copy link
Member Author

dunglas commented Oct 13, 2016

@Tobion thanks for the review, comments fixed now.

So if you explicitly autowire a method that has no arguments it is basically the same as a normal call configuration to this method? I suspect people will then misuse autowiring just to call certain methods since there are now 2 ways to do that.

I think that it's not the same intent:

  • When you use <autowire>, it means "always fill parameters of this method, if I add or remove some, it must not fail"
  • When you use <call>, it's the old good call without any magic

But again, and as pointed in the doc, I would discourage end users to use setter autowiring. It's a feature designed for creators of RAD frameworks and bundles.

@Tobion
Copy link
Contributor

Tobion commented Oct 13, 2016

When you use , it means "always fill parameters of this method, if I add or remove some, it must not fail"

Fine for me.

One more thing. I think we should make the programmatic Definition methods more explicit and not use a multivalued @param bool|string|string[] $autowired parameter. IMO we need to distinguish between configuration and programmatic API.

autowire: true # constructor autowiring
autowire: [__construct, setFoo, setBar] # autowire whitelisted methods only
autowire: '*' # autowire constructor + every setters (following existing rules for setters autowiring)

makes sense as @dunglas and @nicolas-grekas pointed out. But using a magic * in a programmers API like

$fooDefinition = new Definition('Foo');
$fooDefinition->setAutowired('*');

does not makse sense.

So I'd prefer to change the methods on the Definition class similar to what @ro0NL suggested in #19631 (comment).

$definition->setAutowired(Definition::CONSTRUCTOR|Definition::SETTERS);
$definition->setAutowiredMethods(array('setFoo', 'notASetter'));
$definition->disableAutowiring();

The current yaml/xml loaders would just map to this explicit API.

@dunglas
Copy link
Member Author

dunglas commented Oct 14, 2016

It will be difficult to make this new API backward compatible with the old one. Currently autowired is a boolean.

@ro0NL
Copy link
Contributor

ro0NL commented Oct 16, 2016

@dunglas what about this approach?

$def->setAutowired(false); // no autowiring
$def->setAutorwired(true/**Def::CONSTRUCTOR*/); // constructor autowiring
$def->setAutowired(Def::SETTERS); // setter autowiring
$def->setAutorwired(array('foo')); // whitelist autowiring
$def->setAutorwired(Def::CONSTRUCTOR|Def::SETTERS); // combined autowiring

// optional: combined + whitelist autowiring
$def->setAutorwired(Def::CONSTRUCTOR|Def::SETTERS|Def::WHITELIST, array('foo'));
autowire: false # no autowiring
autowire: true | 'constructor' | constant('Def::CONSTRUCTOR') # constructor autowiring
autowire: 'setters' | constant('Def::SETTERS') # setter autowiring
autowire: # whitelist autowiring
  - foo
autowire: # combined autowiring
  constructor: true
  setters: true

# optional: combined + whitelist autowiring
autowire:
  constructor: true
  setters: true
  whitelist:
    - foo

@dunglas
Copy link
Member Author

dunglas commented Oct 16, 2016

@ro0NL I don't want to change the format for XML and YAML because we already have reached a consensus.

Your proposal for the PHP API doesn't fix the issue raised by @Tobion: the setAutowire method still accept several types of parameters (string, bool and string[]); exactly like the current implementation.

Using a bit field isn't the right choice here: you cannot accumulate options. You can only choose one option between constructor autowiring, constructor + setter autowiring or a list of methods to autowire.

IMO the current implementation is good enough even if I would prefer accepting only one type for setAutowire. It's expressive, easy to learn and flexible. All loaders (PHP, XML and YAML) also have a similar API (better learning curve).

Maybe can we just introduce a Definition::SETTER_AUTOWIRING = '*' to avoid using a special raw string in the PHP API.

@ro0NL
Copy link
Contributor

ro0NL commented Oct 16, 2016

Fair enough. Just saying im not sure * meaning constructor + setters is intuitive.. especially as we shouldnt promote setter injection.. right? This is making it more or less special.

autowire: '*' vs. autowire: { constructor: true, setters: true }

I'd prefer the latter in terms of less magic, extensibility and verbose configuration. But you're right, we've already reached consensus before. Just thought of this today :)

@dunglas dunglas force-pushed the autowire_setter_config branch from a3da23a to 77c8359 Compare October 25, 2016 08:36
@@ -24,6 +24,9 @@
*/
class AutowirePass implements CompilerPassInterface
{
/**
* @var ContainerBuilder
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

really required? isn't the type hint on the constructor enough?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without it PHPStorm's autocompletion doesn't work, but I can remove it :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no constructor btw (it's why it's required).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh right, the function I looked at is process
ok then

*
* @throws RuntimeException
*/
private function completeDefinition($id, Definition $definition, $autowired)
private function completeDefinition($id, Definition $definition, array $autowired = array('__construct'))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since this is private, let's make the arg mandatory to make the calling code easier to read?

@@ -41,7 +44,7 @@ public function process(ContainerBuilder $container)
try {
$this->container = $container;
foreach ($container->getDefinitions() as $id => $definition) {
if (false !== $autowired = $definition->getAutowired()) {
if (!empty($autowired = $definition->getAutowired())) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a bug fan of using empty. I'd prefer if ($autowired = $definition->getAutowired()) {

@nicolas-grekas
Copy link
Member

nicolas-grekas commented Nov 1, 2016 via email

@ro0NL
Copy link
Contributor

ro0NL commented Nov 1, 2016

Perhaps should be avoided, but in this case (to me) it makes most sense from a configuring perspective.

Eventually a YAML/XML/PHP definition represents the same thing, and therefore should have similar (if not the same) schema/API imo.

edit: on 2nd thought :):) maybe choose the desired PHP API now (eg. setAutowiredMethods(array $methods)) and extract PHP configuration (eg. return [];) later on? Does that make sense?

@nicolas-grekas
Copy link
Member

My vote would be for reverting #17608 and merge this one for 3.3.
This one has a high impact on DX, yet DX needs some time to mature IMHO, and this maybe be too fresh for 3.2.

@dunglas
Copy link
Member Author

dunglas commented Nov 2, 2016

I agree, it's too late for 3.2.

dunglas added a commit that referenced this pull request Nov 2, 2016
… add setter injection support (dunglas)" (nicolas-grekas)

This PR was merged into the 3.2-dev branch.

Discussion
----------

Revert "feature #17608 [DependencyInjection] Autowiring: add setter injection support (dunglas)"

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | -
| License       | MIT
| Doc PR        | -

This reverts commit 7eab6b9, reversing
changes made to 35f201f.

As discussed in #20167

Commits
-------

bf91eda Revert "feature #17608 [DependencyInjection] Autowiring: add setter injection support (dunglas)"
@dunglas dunglas force-pushed the autowire_setter_config branch from 9d7e8b3 to 48ab62f Compare November 2, 2016 14:21
@dunglas
Copy link
Member Author

dunglas commented Nov 2, 2016

I've rebased the PR and updated method's names.

I've added two new methods setAutowiredMethods(array $autowiredMethods) and getAutowiredMethods() : array because the naming is better and it allows to remove the union type.

However I've kept isAutowired() : bool and setAutowired(bool $autowired) (without deprecating them) because it doesn't hurt and it looks better in term of DX when not using method autowiring. And method autowiring should almost never be used when not developing a library or a framework.

@@ -239,6 +239,19 @@ private function parseDefinition(\DOMElement $service, $file)
$definition->addAutowiringType($type->textContent);
}

$autowireTags = array();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$autowiredMethods?

Copy link
Member Author

@dunglas dunglas Nov 3, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer to keep the current name here because this var contains the value of the XML tag.

@@ -41,8 +44,8 @@ public function process(ContainerBuilder $container)
try {
$this->container = $container;
foreach ($container->getDefinitions() as $id => $definition) {
if ($definition->isAutowired()) {
$this->completeDefinition($id, $definition);
if ($autowired = $definition->getAutowiredMethods()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$autowiredMethods?

*
* @throws RuntimeException
*/
private function completeDefinition($id, Definition $definition)
private function completeDefinition($id, Definition $definition, array $autowired)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$autowiredMethods?

$found = array();
$regexList = array();
foreach ($autowired as $pattern) {
$regexList[] = '/^'.str_replace('\*', '.*', preg_quote($pattern, '/')).'$/i';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps use .+, but it depends if set* should match set(). Not sure :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would say that set() is ok but I'm not sure too.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

* comes from glob patterns, and there, * => /.*/, we shouldn't be the one redefining old semantics people are used to since years.

*
* @return Definition The current instance
*/
public function setAutowired($autowiredMethods)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$autowired

*/
public function setAutowired($autowiredMethods)
{
$this->autowiredMethods = $autowiredMethods ? array('__construct') : array();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would call setAutowiredMethods here..

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why? This method is just a mutator, we can avoid an extra function call and potential unwanted side effects in the future.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough.

$autowireTags[] = $type->textContent;
}

if (!empty($autowireTags)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (0 !== count($autowiredTags)) {?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even better: if ($autowiredTags) {
I'm fighting against the idea that using count() provides any type information. It does not.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right.. got confused by ! 😞

*
* @param string $id
* @param \ReflectionClass $reflectionClass
* @param string[] $autowired
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$autowiredMethods?

{
if ($isConstructor = $reflectionMethod->isConstructor()) {
$arguments = $definition->getArguments();
} else {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about removing the else?

$arguments = array();
if ($isConstructor = $reflectionMethod->isConstructor()) {
    $arguments = $definition->getArguments();
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It adds an extra assignation. Not sure it worth it.

Copy link
Contributor

@GuilhemN GuilhemN Nov 5, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As you want, that's just a detail after all ☺

@@ -111,7 +180,11 @@ private function completeDefinition($id, Definition $definition)
if (!$typeHint = $parameter->getClass()) {
// no default value? Then fail
if (!$parameter->isOptional()) {
throw new RuntimeException(sprintf('Unable to autowire argument index %d ($%s) for the service "%s". If this is an object, give it a type-hint. Otherwise, specify this argument\'s value explicitly.', $index, $parameter->name, $id));
if ($isConstructor) {
throw new RuntimeException(sprintf('Unable to autowire argument index %d ($%s) for the service "%s". If this is an object, give it a type-hint. Otherwise, specify this argument\'s value explicitly.', $index, $parameter->name, $id));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about reversing the if condition here to make the exception message more readable?

if (!$isConstructor) {
    return;
}

// ...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But I prefer using non-negative condition when possible (and it's possible here).

$arguments = array();
}

$addMethodCall = false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add a small comment here? I don't think it's easy to understand what contains this variable without looking deeply the code.
Maybe // Whether the method should be added to the definition as a call.

At the same time, I don't remember if it's written somewhere in the code what's this pass behavior (e.g. only autowire methods when at least one if their arguments is not to its default value), it could be worth adding it if not.

$pass = new AutowirePass();
$pass->process($container);

$this->assertEquals(array('Symfony\Component\DependencyInjection\Compiler\AutowirePass: Autowiring\'s patterns "not", "exist*" for service "foo" don\'t match any method.'), $container->getCompiler()->getLog());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AutowirePass::class?

@@ -290,6 +290,16 @@ public function testAutowired()
$this->assertFalse($def->isAutowired());
$def->setAutowired(true);
$this->assertTrue($def->isAutowired());
$this->assertEquals(array('__construct'), $def->getAutowiredMethods());

$def->setAutowired(array('foo'));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

setAutowiredMethods?

$found = array();
$regexList = array();
foreach ($autowiredMethods as $pattern) {
$regexList[] = '/^'.str_replace('\*', '.*', preg_quote($pattern, '/')).'$/i';
Copy link
Contributor

@ro0NL ro0NL Nov 5, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be consider allowing some other basic glob patterns, ie. ? => . Could be convenient in some cases...

https://en.wikipedia.org/wiki/Glob_(programming)#Syntax

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need IMHO

@dunglas
Copy link
Member Author

dunglas commented Dec 5, 2016

@fabpot can I merge this one? It will help for #18193 and #20738.

@nicolas-grekas
Copy link
Member

👍 for merging asap, so that we can play with the feat asap and tweak it if needed in the coming months.

@nicolas-grekas nicolas-grekas modified the milestones: 3.3, 3.x Dec 12, 2016
} catch (RuntimeException $e) {
if ($parameter->allowsNull()) {
$value = null;
} elseif ($parameter->isDefaultValueAvailable()) {
$value = $parameter->getDefaultValue();
} else {
throw $e;
// The exception code is set to 1 if the exception must be thrown even if it's a setter
if (1 === $e->getCode() || $isConstructor) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use a specific getter/setter instead of a magic 1 value for the code?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done (but I find the use of a specific code less intrusive than using accessors, here we add methods to a generic exception for an implementation detail of the AutowirePass).

* Allowed values:
* - true: constructor autowiring, same as $this->setAutowiredMethods(array('__construct'))
* - false: no autowiring, same as $this->setAutowiredMethods(array())
*
* @param bool $autowired
*
* @return Definition The current instance
*/
public function setAutowired($autowired)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one could be deprecated, right?

Copy link
Member Author

@dunglas dunglas Dec 13, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I propose to deprecate it in another PR (if it's worth it) because we need to determine how we deal with #20648

{
public function setFoo(Foo $foo)
{
// should be called
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about throwing an exception here instead of the comment?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This setter should be called (and shouldn't throw anything).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nevermind :)

@fabpot
Copy link
Member

fabpot commented Dec 13, 2016

Thank you @dunglas.

@fabpot fabpot merged commit 6dd53c7 into symfony:master Dec 13, 2016
fabpot added a commit that referenced this pull request Dec 13, 2016
…configurable (dunglas)

This PR was merged into the 3.3-dev branch.

Discussion
----------

[DependencyInjection] Make method (setter) autowiring configurable

| Q | A |
| --- | --- |
| Branch? | master |
| Bug fix? | no |
| New feature? | yes |
| BC breaks? | no |
| Deprecations? | maybe? |
| Tests pass? | yes |
| Fixed tickets | #19631 |
| License | MIT |
| Doc PR | symfony/symfony-docs#7041 |

Follow up of #19631. Implements #19631 (comment):

Edit: the last supported format:

``` yaml
services:
    foo:
        class: Foo
        autowire: ['__construct', 'set*'] # Autowire constructor and all setters
        autowire: true # Converted by loaders in `autowire: ['__construct']` for BC
        autowire: ['foo', 'bar'] # Autowire only `foo` and `bar` methods
```

Outdated:

``` yaml
autowire: true # constructor autowiring
autowire: [__construct, setFoo, setBar] # autowire whitelisted methods only
autowire: '*' # autowire constructor + every setters (following existing rules for setters autowiring)
```
- [x] Allow to specify the list of methods in the XML loader
- [x] Add tests for the YAML loader

Commits
-------

6dd53c7 [DependencyInjection] Introduce method injection for autowiring
@fabpot fabpot mentioned this pull request May 1, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants