Skip to content

Commit f2cba65

Browse files
committed
[DependencyInjection] Added closure service factories
1 parent 9b2195c commit f2cba65

20 files changed

+681
-5
lines changed

composer.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
"symfony/security-bundle": "self.version",
5858
"symfony/serializer": "self.version",
5959
"symfony/stopwatch": "self.version",
60+
"symfony/superclosure-bridge": "self.version",
6061
"symfony/swiftmailer-bridge": "self.version",
6162
"symfony/templating": "self.version",
6263
"symfony/translation": "self.version",
@@ -76,7 +77,8 @@
7677
"propel/propel1": "1.6.*",
7778
"ircmaxell/password-compat": "1.0.*",
7879
"ocramius/proxy-manager": ">=0.3.1,<0.6-dev",
79-
"egulias/email-validator": "~1.2"
80+
"egulias/email-validator": "~1.2",
81+
"jeremeamia/superclosure": "~1.0"
8082
},
8183
"autoload": {
8284
"psr-0": { "Symfony\\": "src/" },
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
vendor/
2+
composer.lock
3+
phpunit.xml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
CHANGELOG
2+
=========
3+
4+
2.6.0
5+
-----
6+
7+
* Added the `Symfony\Bridge\SuperClosure` bridge
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
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\Bridge\SuperClosure\ClosureDumper;
13+
14+
use Jeremeamia\SuperClosure\ClosureParser;
15+
use Symfony\Component\DependencyInjection\Dumper\ClosureDumper\ClosureDumperInterface;
16+
use Symfony\Component\DependencyInjection\Exception\DumpingClosureException;
17+
18+
/**
19+
* @author Nikita Konstantinov <unk91nd@gmail.com>
20+
*/
21+
final class SuperClosureDumper implements ClosureDumperInterface
22+
{
23+
/**
24+
* {@inheritdoc}
25+
*/
26+
public function dump(\Closure $closure)
27+
{
28+
$parser = ClosureParser::fromClosure($closure);
29+
30+
if ($parser->getUsedVariables()) {
31+
throw new DumpingClosureException($closure);
32+
}
33+
34+
try {
35+
// Remove trailing ";"
36+
return substr($parser->getCode(), 0, -1);
37+
} catch (\Exception $e) {
38+
throw new DumpingClosureException($closure);
39+
}
40+
}
41+
}
+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Copyright (c) 2004-2014 Fabien Potencier
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy
4+
of this software and associated documentation files (the "Software"), to deal
5+
in the Software without restriction, including without limitation the rights
6+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
copies of the Software, and to permit persons to whom the Software is furnished
8+
to do so, subject to the following conditions:
9+
10+
The above copyright notice and this permission notice shall be included in all
11+
copies or substantial portions of the Software.
12+
13+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
THE SOFTWARE.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
<?php
2+
3+
use Symfony\Component\DependencyInjection\ContainerInterface;
4+
use Symfony\Component\DependencyInjection\Container;
5+
use Symfony\Component\DependencyInjection\Exception\InactiveScopeException;
6+
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
7+
use Symfony\Component\DependencyInjection\Exception\LogicException;
8+
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
9+
use Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag;
10+
11+
/**
12+
* ProjectServiceContainerWithClosures
13+
*
14+
* This class has been auto-generated
15+
* by the Symfony Dependency Injection Component.
16+
*/
17+
class ProjectServiceContainerWithClosures extends Container
18+
{
19+
private static $parameters = array(
20+
'baz' => 42,
21+
);
22+
23+
/**
24+
* Constructor.
25+
*/
26+
public function __construct()
27+
{
28+
$this->services =
29+
$this->scopedServices =
30+
$this->scopeStacks = array();
31+
32+
$this->set('service_container', $this);
33+
34+
$this->scopes = array();
35+
$this->scopeChildren = array();
36+
$this->methodMap = array(
37+
'bar' => 'getBarService',
38+
'foo' => 'getFooService',
39+
);
40+
41+
$this->aliases = array();
42+
}
43+
44+
/**
45+
* {@inheritdoc}
46+
*/
47+
public function compile()
48+
{
49+
throw new LogicException('You cannot compile a dumped frozen container.');
50+
}
51+
52+
/**
53+
* Gets the 'bar' service.
54+
*
55+
* This service is shared.
56+
* This method always returns the same instance of the service.
57+
*
58+
* @return \stdClass A stdClass instance.
59+
*/
60+
protected function getBarService()
61+
{
62+
return $this->services['bar'] = call_user_func(function (\stdClass $foo) {
63+
$bar = clone $foo;
64+
$bar->bar = 'bar';
65+
return $bar;
66+
}, $this->get('foo'));
67+
}
68+
69+
/**
70+
* Gets the 'foo' service.
71+
*
72+
* This service is shared.
73+
* This method always returns the same instance of the service.
74+
*
75+
* @return \stdClass A stdClass instance.
76+
*/
77+
protected function getFooService()
78+
{
79+
return $this->services['foo'] = call_user_func(function (\Symfony\Component\DependencyInjection\ContainerInterface $container) {
80+
$foo = new \stdClass();
81+
$foo->foo = $container->getParameter('baz');
82+
return $foo;
83+
}, $this);
84+
}
85+
86+
/**
87+
* {@inheritdoc}
88+
*/
89+
public function getParameter($name)
90+
{
91+
$name = strtolower($name);
92+
93+
if (!(isset(self::$parameters[$name]) || array_key_exists($name, self::$parameters))) {
94+
throw new InvalidArgumentException(sprintf('The parameter "%s" must be defined.', $name));
95+
}
96+
97+
return self::$parameters[$name];
98+
}
99+
100+
/**
101+
* {@inheritdoc}
102+
*/
103+
public function hasParameter($name)
104+
{
105+
$name = strtolower($name);
106+
107+
return isset(self::$parameters[$name]) || array_key_exists($name, self::$parameters);
108+
}
109+
110+
/**
111+
* {@inheritdoc}
112+
*/
113+
public function setParameter($name, $value)
114+
{
115+
throw new LogicException('Impossible to call set() on a frozen ParameterBag.');
116+
}
117+
118+
/**
119+
* {@inheritdoc}
120+
*/
121+
public function getParameterBag()
122+
{
123+
if (null === $this->parameterBag) {
124+
$this->parameterBag = new FrozenParameterBag(self::$parameters);
125+
}
126+
127+
return $this->parameterBag;
128+
}
129+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
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\Bridge\SuperClosure\Tests;
13+
14+
use Symfony\Bridge\SuperClosure\ClosureDumper\SuperClosureDumper;
15+
use Symfony\Component\DependencyInjection\ContainerBuilder;
16+
use Symfony\Component\DependencyInjection\ContainerInterface;
17+
use Symfony\Component\DependencyInjection\Dumper\PhpDumper;
18+
use Symfony\Component\DependencyInjection\Reference;
19+
20+
/**
21+
* Integration tests of {@see \Symfony\Component\DependencyInjection\Dumper\PhpDumper} and SuperClosureDumper
22+
*
23+
* @author Nikita Konstantinov <unk91nd@gmail.com>
24+
*/
25+
class PhpDumperTest extends \PHPUnit_Framework_TestCase
26+
{
27+
public function testThatPhpDumperCanDumpClosure()
28+
{
29+
$container = new ContainerBuilder();
30+
31+
$container->setParameter('baz', 42);
32+
33+
$container
34+
->register('foo', 'stdClass')
35+
->setFactory(function (ContainerInterface $container) {
36+
$foo = new \stdClass();
37+
$foo->foo = $container->getParameter('baz');
38+
39+
40+
return $foo;
41+
})
42+
;
43+
44+
$container
45+
->register('bar', 'stdClass')
46+
->setFactory(function (\stdClass $foo) {
47+
$bar = clone $foo;
48+
$bar->bar = 'bar';
49+
50+
51+
return $bar;
52+
})
53+
->addArgument(new Reference('foo'))
54+
;
55+
56+
$container->compile();
57+
58+
$dumper = new PhpDumper($container);
59+
$dumper->setClosureDumper(new SuperClosureDumper());
60+
61+
$options = array('class' => 'ProjectServiceContainerWithClosures');
62+
63+
$this->assertStringEqualsFile(__DIR__.'/Fixtures/php/services_with_closure_factory.php', $dumper->dump($options));
64+
}
65+
66+
public function testThatDumpedContainerWorks()
67+
{
68+
require_once __DIR__.'/Fixtures/php/services_with_closure_factory.php';
69+
70+
$container = new \ProjectServiceContainerWithClosures();
71+
72+
$expectedBar = new \stdClass();
73+
$expectedBar->foo = 42;
74+
$expectedBar->bar = 'bar';
75+
76+
$this->assertEquals($expectedBar, $container->get('bar'));
77+
}
78+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
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\Bridge\SuperClosure\Tests;
13+
14+
use Symfony\Bridge\SuperClosure\ClosureDumper\SuperClosureDumper;
15+
use Symfony\Component\DependencyInjection\ContainerInterface;
16+
17+
/**
18+
* @author Nikita Konstantinov <unk91nd@gmail.com>
19+
*/
20+
class SuperClosureDumperTest extends \PHPUnit_Framework_TestCase
21+
{
22+
public function testThatClosureDumps()
23+
{
24+
$dumper = new SuperClosureDumper();
25+
26+
$expectedCode = <<<'CODE'
27+
function (\Symfony\Component\DependencyInjection\ContainerInterface $container) {
28+
return $container->get('foo');
29+
}
30+
CODE;
31+
32+
$actualCode = $dumper->dump(function (ContainerInterface $container) {
33+
return $container->get('foo');
34+
});
35+
36+
$this->assertSame($expectedCode, $actualCode);
37+
}
38+
39+
/**
40+
* @expectedException \Symfony\Component\DependencyInjection\Exception\DumpingClosureException
41+
*/
42+
public function testThatContextDependentClosureCannotBeDumped()
43+
{
44+
$dumper = new SuperClosureDumper();
45+
46+
$dumper->dump(function () use ($dumper) {
47+
return new \stdClass();
48+
});
49+
}
50+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{
2+
"name": "symfony/superclosure-bridge",
3+
"type": "symfony-bridge",
4+
"description": "Symfony SuperClosure Bridge",
5+
"keywords": [],
6+
"homepage": "http://symfony.com",
7+
"license": "MIT",
8+
"authors": [
9+
{
10+
"name": "Fabien Potencier",
11+
"email": "fabien@symfony.com"
12+
},
13+
{
14+
"name": "Symfony Community",
15+
"homepage": "http://symfony.com/contributors"
16+
}
17+
],
18+
"require": {
19+
"php": ">=5.3.3",
20+
"symfony/dependency-injection": "~2.6",
21+
"jeremeamia/superclosure": "~1.0"
22+
},
23+
"autoload": {
24+
"psr-0": { "Symfony\\Bridge\\SuperClosure\\": "" }
25+
},
26+
"target-dir": "Symfony/Bridge/SuperClosure",
27+
"minimum-stability": "dev",
28+
"extra": {
29+
"branch-alias": {
30+
"dev-master": "2.6-dev"
31+
}
32+
}
33+
}

0 commit comments

Comments
 (0)