Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feature #20943 [DependencyInjection] Use current class as default cla…
…ss for factory declarations (ogizanagi) This PR was merged into the 3.3-dev branch. Discussion ---------- [DependencyInjection] Use current class as default class for factory declarations | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | #20888 | License | MIT | Doc PR | Should update the notice about the "class" attribute on http://symfony.com/doc/current/service_container/factories.html #20888 makes sense to me, considering the following sample extracted from the documentation: ```xml <service id="app.newsletter_manager" class="AppBundle\Email\NewsletterManager"> <factory class="AppBundle\Email\NewsletterManager" method="create" /> </service> ``` The class is used as a factory to create itself, thus it can be simplified to: ```xml <service id="app.newsletter_manager" class="AppBundle\Email\NewsletterManager"> <factory method="create" /> </service> ``` However, it's not possible to provide the same feature for the YAML format, because it doesn't allow to distinct a function from a method call if the class is not provided explicitly under the `factory` key, whereas the xml format use a dedicated `function` attribute. Would this inconsistency between those two formats be a no-go for this feature? The doc notices: > When using a factory to create services, the value chosen for the class option has no effect on the resulting service. The actual class name only depends on the object that is returned by the factory. However, the configured class name may be used by compiler passes and therefore should be set to a sensible value. If this is merged, it should be updated wisely in order to not confuse everyone with this feature when using the xml format. UPDATE: The yaml format is now supported when the class is not provided in the factory array: ```yml services: my_factory: class: Bar\Baz factory: [~, 'create'] ``` Commits ------- e6d8570 [DependencyInjection] Use current class as default class for factory declarations
- Loading branch information
Showing
11 changed files
with
141 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
38 changes: 38 additions & 0 deletions
38
src/Symfony/Component/DependencyInjection/Compiler/ResolveFactoryClassPass.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Symfony package. | ||
* | ||
* (c) Fabien Potencier <fabien@symfony.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Symfony\Component\DependencyInjection\Compiler; | ||
|
||
use Symfony\Component\DependencyInjection\Definition; | ||
use Symfony\Component\DependencyInjection\Exception\RuntimeException; | ||
|
||
/** | ||
* @author Maxime Steinhausser <maxime.steinhausser@gmail.com> | ||
*/ | ||
class ResolveFactoryClassPass extends AbstractRecursivePass | ||
{ | ||
/** | ||
* {@inheritdoc} | ||
*/ | ||
protected function processValue($value, $isRoot = false) | ||
{ | ||
if ($value instanceof Definition && is_array($factory = $value->getFactory()) && null === $factory[0]) { | ||
if (null === $class = $value->getClass()) { | ||
throw new RuntimeException(sprintf('The "%s" service is defined to be created by a factory, but is missing the factory class. Did you forget to define the factory or service class?', $this->currentId)); | ||
} | ||
|
||
$factory[0] = $class; | ||
$value->setFactory($factory); | ||
} | ||
|
||
return parent::processValue($value, $isRoot); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
87 changes: 87 additions & 0 deletions
87
src/Symfony/Component/DependencyInjection/Tests/Compiler/ResolveFactoryClassPassTest.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
<?php | ||
|
||
/* | ||
* This file is part of the Symfony package. | ||
* | ||
* (c) Fabien Potencier <fabien@symfony.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Symfony\Component\DependencyInjection\Tests\Compiler; | ||
|
||
use Symfony\Component\DependencyInjection\Compiler\ResolveFactoryClassPass; | ||
use Symfony\Component\DependencyInjection\ContainerBuilder; | ||
use Symfony\Component\DependencyInjection\Definition; | ||
use Symfony\Component\DependencyInjection\Reference; | ||
|
||
class ResolveFactoryClassPassTest extends \PHPUnit_Framework_TestCase | ||
{ | ||
public function testProcess() | ||
{ | ||
$container = new ContainerBuilder(); | ||
|
||
$factory = $container->register('factory', 'Foo\Bar'); | ||
$factory->setFactory(array(null, 'create')); | ||
|
||
$pass = new ResolveFactoryClassPass(); | ||
$pass->process($container); | ||
|
||
$this->assertSame(array('Foo\Bar', 'create'), $factory->getFactory()); | ||
} | ||
|
||
public function testInlinedDefinitionFactoryIsProcessed() | ||
{ | ||
$container = new ContainerBuilder(); | ||
|
||
$factory = $container->register('factory'); | ||
$factory->setFactory(array((new Definition('Baz\Qux'))->setFactory(array(null, 'getInstance')), 'create')); | ||
|
||
$pass = new ResolveFactoryClassPass(); | ||
$pass->process($container); | ||
|
||
$this->assertSame(array('Baz\Qux', 'getInstance'), $factory->getFactory()[0]->getFactory()); | ||
} | ||
|
||
public function provideFulfilledFactories() | ||
{ | ||
return array( | ||
array(array('Foo\Bar', 'create')), | ||
array(array(new Reference('foo'), 'create')), | ||
array(array(new Definition('Baz'), 'create')), | ||
); | ||
} | ||
|
||
/** | ||
* @dataProvider provideFulfilledFactories | ||
*/ | ||
public function testIgnoresFulfilledFactories($factory) | ||
{ | ||
$container = new ContainerBuilder(); | ||
$definition = new Definition(); | ||
$definition->setFactory($factory); | ||
|
||
$container->setDefinition('factory', $definition); | ||
|
||
$pass = new ResolveFactoryClassPass(); | ||
$pass->process($container); | ||
|
||
$this->assertSame($factory, $container->getDefinition('factory')->getFactory()); | ||
} | ||
|
||
/** | ||
* @expectedException \Symfony\Component\DependencyInjection\Exception\RuntimeException | ||
* @expectedExceptionMessage The "factory" service is defined to be created by a factory, but is missing the factory class. Did you forget to define the factory or service class? | ||
*/ | ||
public function testNotAnyClassThrowsException() | ||
{ | ||
$container = new ContainerBuilder(); | ||
|
||
$factory = $container->register('factory'); | ||
$factory->setFactory(array(null, 'create')); | ||
|
||
$pass = new ResolveFactoryClassPass(); | ||
$pass->process($container); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters