Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit ec1e7ca

Browse files
committed
[DependencyInjection] added a way to automatically update scoped services
A service can now be marked as synchronized; when set, all method calls involving this service will be called each time this service is set. When in a scope, methods are also called to restore the previous version of the service.
1 parent 469330d commit ec1e7ca

22 files changed

+366
-24
lines changed

src/Symfony/Component/DependencyInjection/Container.php

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,10 @@ public function set($id, $service, $scope = self::SCOPE_CONTAINER)
206206
}
207207

208208
$this->services[$id] = $service;
209+
210+
if (method_exists($this, $method = 'synchronize'.strtr($id, array('_' => '', '.' => '_')).'Service')) {
211+
$this->$method();
212+
}
209213
}
210214

211215
/**
@@ -221,7 +225,7 @@ public function has($id)
221225
{
222226
$id = strtolower($id);
223227

224-
return isset($this->services[$id]) || method_exists($this, 'get'.strtr($id, array('_' => '', '.' => '_')).'Service');
228+
return array_key_exists($id, $this->services) || method_exists($this, 'get'.strtr($id, array('_' => '', '.' => '_')).'Service');
225229
}
226230

227231
/**
@@ -247,7 +251,7 @@ public function get($id, $invalidBehavior = self::EXCEPTION_ON_INVALID_REFERENCE
247251
{
248252
$id = strtolower($id);
249253

250-
if (isset($this->services[$id])) {
254+
if (array_key_exists($id, $this->services)) {
251255
return $this->services[$id];
252256
}
253257

@@ -263,7 +267,7 @@ public function get($id, $invalidBehavior = self::EXCEPTION_ON_INVALID_REFERENCE
263267
} catch (\Exception $e) {
264268
unset($this->loading[$id]);
265269

266-
if (isset($this->services[$id])) {
270+
if (array_key_exists($id, $this->services)) {
267271
unset($this->services[$id]);
268272
}
269273

@@ -289,7 +293,7 @@ public function get($id, $invalidBehavior = self::EXCEPTION_ON_INVALID_REFERENCE
289293
*/
290294
public function initialized($id)
291295
{
292-
return isset($this->services[strtolower($id)]);
296+
return array_key_exists(strtolower($id), $this->services);
293297
}
294298

295299
/**
@@ -393,8 +397,11 @@ public function leaveScope($name)
393397
$services = $this->scopeStacks[$name]->pop();
394398
$this->scopedServices += $services;
395399

396-
array_unshift($services, $this->services);
397-
$this->services = call_user_func_array('array_merge', $services);
400+
foreach ($services as $array) {
401+
foreach ($array as $id => $service) {
402+
$this->set($id, $service, $name);
403+
}
404+
}
398405
}
399406
}
400407

src/Symfony/Component/DependencyInjection/ContainerBuilder.php

Lines changed: 60 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ class ContainerBuilder extends Container implements TaggedContainerInterface
4646
*/
4747
private $definitions = array();
4848

49+
/**
50+
* @var Definition[]
51+
*/
52+
private $obsoleteDefinitions = array();
53+
4954
/**
5055
* @var Alias[]
5156
*/
@@ -351,14 +356,28 @@ public function set($id, $service, $scope = self::SCOPE_CONTAINER)
351356

352357
if ($this->isFrozen()) {
353358
// setting a synthetic service on a frozen container is alright
354-
if (!isset($this->definitions[$id]) || !$this->definitions[$id]->isSynthetic()) {
359+
if (
360+
(!isset($this->definitions[$id]) && !isset($this->obsoleteDefinitions[$id]))
361+
||
362+
(isset($this->definitions[$id]) && !$this->definitions[$id]->isSynthetic())
363+
||
364+
(isset($this->obsoleteDefinitions[$id]) && !$this->obsoleteDefinitions[$id]->isSynthetic())
365+
) {
355366
throw new BadMethodCallException(sprintf('Setting service "%s" on a frozen container is not allowed.', $id));
356367
}
357368
}
358369

370+
if (isset($this->definitions[$id])) {
371+
$this->obsoleteDefinitions[$id] = $this->definitions[$id];
372+
}
373+
359374
unset($this->definitions[$id], $this->aliases[$id]);
360375

361376
parent::set($id, $service, $scope);
377+
378+
if (isset($this->obsoleteDefinitions[$id]) && $this->obsoleteDefinitions[$id]->isSynchronized()) {
379+
$this->synchronize($id);
380+
}
362381
}
363382

364383
/**
@@ -885,19 +904,7 @@ private function createService(Definition $definition, $id)
885904
}
886905

887906
foreach ($definition->getMethodCalls() as $call) {
888-
$services = self::getServiceConditionals($call[1]);
889-
890-
$ok = true;
891-
foreach ($services as $s) {
892-
if (!$this->has($s)) {
893-
$ok = false;
894-
break;
895-
}
896-
}
897-
898-
if ($ok) {
899-
call_user_func_array(array($service, $call[0]), $this->resolveServices($parameterBag->resolveValue($call[1])));
900-
}
907+
$this->callMethod($service, $call);
901908
}
902909

903910
$properties = $this->resolveServices($parameterBag->resolveValue($definition->getProperties()));
@@ -999,4 +1006,43 @@ public static function getServiceConditionals($value)
9991006

10001007
return $services;
10011008
}
1009+
1010+
/**
1011+
* Synchronizes a service change.
1012+
*
1013+
* This method updates all services that depend on the given
1014+
* service by calling all methods referencing it.
1015+
*
1016+
* @param string $id A service id
1017+
*/
1018+
private function synchronize($id)
1019+
{
1020+
foreach ($this->definitions as $definitionId => $definition) {
1021+
// only check initialized services
1022+
if (!$this->initialized($definitionId)) {
1023+
continue;
1024+
}
1025+
1026+
foreach ($definition->getMethodCalls() as $call) {
1027+
foreach ($call[1] as $argument) {
1028+
if ($argument instanceof Reference && $id == (string) $argument) {
1029+
$this->callMethod($this->get($definitionId), $call);
1030+
}
1031+
}
1032+
}
1033+
}
1034+
}
1035+
1036+
private function callMethod($service, $call)
1037+
{
1038+
$services = self::getServiceConditionals($call[1]);
1039+
1040+
foreach ($services as $s) {
1041+
if (!$this->has($s)) {
1042+
return;
1043+
}
1044+
}
1045+
1046+
call_user_func_array(array($service, $call[0]), $this->resolveServices($this->getParameterBag()->resolveValue($call[1])));
1047+
}
10021048
}

src/Symfony/Component/DependencyInjection/Definition.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ class Definition
3636
private $public;
3737
private $synthetic;
3838
private $abstract;
39+
private $synchronized;
3940

4041
protected $arguments;
4142

@@ -56,6 +57,7 @@ public function __construct($class = null, array $arguments = array())
5657
$this->tags = array();
5758
$this->public = true;
5859
$this->synthetic = false;
60+
$this->synchronized = false;
5961
$this->abstract = false;
6062
$this->properties = array();
6163
}
@@ -569,6 +571,34 @@ public function isPublic()
569571
return $this->public;
570572
}
571573

574+
/**
575+
* Sets the synchronized flag of this service.
576+
*
577+
* @param Boolean $boolean
578+
*
579+
* @return Definition The current instance
580+
*
581+
* @api
582+
*/
583+
public function setSynchronized($boolean)
584+
{
585+
$this->synchronized = (Boolean) $boolean;
586+
587+
return $this;
588+
}
589+
590+
/**
591+
* Whether this service is synchronized.
592+
*
593+
* @return Boolean
594+
*
595+
* @api
596+
*/
597+
public function isSynchronized()
598+
{
599+
return $this->synchronized;
600+
}
601+
572602
/**
573603
* Sets whether this definition is synthetic, that is not constructed by the
574604
* container, but dynamically injected.

src/Symfony/Component/DependencyInjection/Dumper/PhpDumper.php

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -567,7 +567,7 @@ protected function get{$name}Service()
567567
*/
568568
private function addServices()
569569
{
570-
$publicServices = $privateServices = $aliasServices = '';
570+
$publicServices = $privateServices = $aliasServices = $synchronizers = '';
571571
$definitions = $this->container->getDefinitions();
572572
ksort($definitions);
573573
foreach ($definitions as $id => $definition) {
@@ -576,6 +576,8 @@ private function addServices()
576576
} else {
577577
$privateServices .= $this->addService($id, $definition);
578578
}
579+
580+
$synchronizers .= $this->addServiceSynchronizer($id, $definition);
579581
}
580582

581583
$aliases = $this->container->getAliases();
@@ -584,7 +586,60 @@ private function addServices()
584586
$aliasServices .= $this->addServiceAlias($alias, $id);
585587
}
586588

587-
return $publicServices.$aliasServices.$privateServices;
589+
return $publicServices.$aliasServices.$synchronizers.$privateServices;
590+
}
591+
592+
/**
593+
* Adds synchronizer methods.
594+
*
595+
* @param string $id A service identifier
596+
* @param Definition $definition A Definition instance
597+
*/
598+
private function addServiceSynchronizer($id, Definition $definition)
599+
{
600+
if (!$definition->isSynchronized()) {
601+
return;
602+
}
603+
604+
$code = '';
605+
foreach ($this->container->getDefinitions() as $definitionId => $definition) {
606+
foreach ($definition->getMethodCalls() as $call) {
607+
foreach ($call[1] as $argument) {
608+
if ($argument instanceof Reference && $id == (string) $argument) {
609+
$arguments = array();
610+
foreach ($call[1] as $value) {
611+
$arguments[] = $this->dumpValue($value);
612+
}
613+
614+
$call = $this->wrapServiceConditionals($call[1], sprintf("\$this->get('%s')->%s(%s);", $definitionId, $call[0], implode(', ', $arguments)));
615+
616+
$code .= <<<EOF
617+
if (\$this->initialized('$definitionId')) {
618+
$call
619+
}
620+
621+
EOF;
622+
}
623+
}
624+
}
625+
}
626+
627+
if (!$code) {
628+
return;
629+
}
630+
631+
$name = Container::camelize($id);
632+
633+
return <<<EOF
634+
635+
/**
636+
* Updates the '$id' service.
637+
*/
638+
protected function synchronize{$name}Service()
639+
{
640+
$code }
641+
642+
EOF;
588643
}
589644

590645
private function addNewInstance($id, Definition $definition, $return, $instantiation)

src/Symfony/Component/DependencyInjection/Dumper/XmlDumper.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,12 @@ private function addService($definition, $id, \DOMElement $parent)
127127
if (!$definition->isPublic()) {
128128
$service->setAttribute('public', 'false');
129129
}
130+
if ($definition->isSynthetic()) {
131+
$service->setAttribute('synthetic', 'true');
132+
}
133+
if ($definition->isSynchronized()) {
134+
$service->setAttribute('synchronized', 'true');
135+
}
130136

131137
foreach ($definition->getTags() as $name => $tags) {
132138
foreach ($tags as $attributes) {
@@ -239,6 +245,9 @@ private function convertParameters($parameters, $type, \DOMElement $parent, $key
239245
} elseif ($behaviour == ContainerInterface::IGNORE_ON_INVALID_REFERENCE) {
240246
$element->setAttribute('on-invalid', 'ignore');
241247
}
248+
if (!$value->isStrict()) {
249+
$element->setAttribute('strict', 'false');
250+
}
242251
} elseif ($value instanceof Definition) {
243252
$element->setAttribute('type', 'service');
244253
$this->addService($value, null, $element);

src/Symfony/Component/DependencyInjection/Dumper/YamlDumper.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,14 @@ private function addService($id, $definition)
9494
$code .= sprintf(" file: %s\n", $definition->getFile());
9595
}
9696

97+
if ($definition->isSynthetic()) {
98+
$code .= sprintf(" synthetic: true\n");
99+
}
100+
101+
if ($definition->isSynchronized()) {
102+
$code .= sprintf(" synchronized: true\n");
103+
}
104+
97105
if ($definition->getFactoryMethod()) {
98106
$code .= sprintf(" factory_method: %s\n", $definition->getFactoryMethod());
99107
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ private function parseDefinition($id, $service, $file)
148148
$definition = new Definition();
149149
}
150150

151-
foreach (array('class', 'scope', 'public', 'factory-class', 'factory-method', 'factory-service', 'synthetic', 'abstract') as $key) {
151+
foreach (array('class', 'scope', 'public', 'factory-class', 'factory-method', 'factory-service', 'synthetic', 'synchronized', 'abstract') as $key) {
152152
if (isset($service[$key])) {
153153
$method = 'set'.str_replace('-', '', $key);
154154
$definition->$method((string) $service->getAttributeAsPhp($key));

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,10 @@ private function parseDefinition($id, $service, $file)
153153
$definition->setSynthetic($service['synthetic']);
154154
}
155155

156+
if (isset($service['synchronized'])) {
157+
$definition->setSynchronized($service['synchronized']);
158+
}
159+
156160
if (isset($service['public'])) {
157161
$definition->setPublic($service['public']);
158162
}

src/Symfony/Component/DependencyInjection/Loader/schema/dic/services/services-1.0.xsd

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@
8686
<xsd:attribute name="scope" type="xsd:string" />
8787
<xsd:attribute name="public" type="boolean" />
8888
<xsd:attribute name="synthetic" type="boolean" />
89+
<xsd:attribute name="synchronized" type="boolean" />
8990
<xsd:attribute name="abstract" type="boolean" />
9091
<xsd:attribute name="factory-class" type="xsd:string" />
9192
<xsd:attribute name="factory-method" type="xsd:string" />

0 commit comments

Comments
 (0)