21
21
use Zend \ServiceManager \Exception \ServiceNotCreatedException ;
22
22
use Zend \ServiceManager \Exception \ServiceNotFoundException ;
23
23
24
- use function \array_keys ;
25
24
use function \array_merge ;
26
25
use function \array_merge_recursive ;
27
26
use function \class_exists ;
@@ -222,7 +221,7 @@ public function get($name)
222
221
$ object = $ this ->doCreate ($ resolvedName );
223
222
224
223
// Cache the object for later, if it is supposed to be shared.
225
- if (( $ sharedService) ) {
224
+ if ($ sharedService ) {
226
225
$ this ->services [$ resolvedName ] = $ object ;
227
226
}
228
227
@@ -334,8 +333,8 @@ public function getAllowOverride()
334
333
public function configure (array $ config )
335
334
{
336
335
// This is a bulk update/initial configuration
337
- // So we check all definitions
338
- $ this ->validateServiceNameConfig ($ config );
336
+ // So we check all definitions upfront
337
+ $ this ->validateServiceNames ($ config );
339
338
340
339
if (isset ($ config ['services ' ])) {
341
340
$ this ->services = $ config ['services ' ] + $ this ->services ;
@@ -368,16 +367,15 @@ public function configure(array $config)
368
367
$ this ->shared = $ config ['shared ' ] + $ this ->shared ;
369
368
}
370
369
371
- $ aliases = [];
372
370
if (isset ($ config ['aliases ' ])) {
371
+ // @todo: Shouldn't that be $config['aliases'] + $this->aliases (or array_merge?)
372
+ // Will have to examine.
373
373
$ aliases = $ config ['aliases ' ];
374
374
} elseif (! $ this ->configured && ! empty ($ this ->aliases )) {
375
375
$ aliases = $ this ->aliases ;
376
376
}
377
377
if (! empty ($ aliases )) {
378
- foreach ($ aliases as $ alias => $ target ) {
379
- $ this ->mapAliasToTarget ($ alias , $ target );
380
- }
378
+ $ this ->mapAliasesToTargets ($ aliases );
381
379
}
382
380
383
381
if (isset ($ config ['shared_by_default ' ])) {
@@ -418,8 +416,11 @@ public function configure(array $config)
418
416
*/
419
417
public function setAlias ($ alias , $ target )
420
418
{
421
- $ this ->validateServiceName ($ alias );
422
- $ this ->mapAliasToTarget ($ alias , $ target );
419
+ if (! isset ($ this ->services [$ alias ]) || $ this ->allowOverride ) {
420
+ $ this ->mapAliasToTarget ($ alias , $ target );
421
+ return ;
422
+ }
423
+ throw new ContainerModificationsNotAllowedException ($ alias );
423
424
}
424
425
425
426
/**
@@ -443,8 +444,11 @@ public function setInvokableClass($name, $class = null)
443
444
*/
444
445
public function setFactory ($ name , $ factory )
445
446
{
446
- $ this ->validateServiceName ($ name );
447
- $ this ->factories [$ name ] = $ factory ;
447
+ if (! isset ($ this ->services [$ name ]) || $ this ->allowOverride ) {
448
+ $ this ->factories [$ name ] = $ factory ;
449
+ return ;
450
+ }
451
+ throw new ContainerModificationsNotAllowedException ($ name );
448
452
}
449
453
450
454
/**
@@ -499,8 +503,11 @@ public function addInitializer($initializer)
499
503
*/
500
504
public function setService ($ name , $ service )
501
505
{
502
- $ this ->validateServiceName ($ name );
503
- $ this ->services [$ name ] = $ service ;
506
+ if (! isset ($ this ->services [$ name ]) || $ this ->allowOverride ) {
507
+ $ this ->services [$ name ] = $ service ;
508
+ return ;
509
+ }
510
+ throw new ContainerModificationsNotAllowedException ($ name );
504
511
}
505
512
506
513
/**
@@ -511,8 +518,11 @@ public function setService($name, $service)
511
518
*/
512
519
public function setShared ($ name , $ flag )
513
520
{
514
- $ this ->validateServiceName ($ name );
515
- $ this ->shared [$ name ] = (bool ) $ flag ;
521
+ if (! isset ($ this ->services [$ name ]) || $ this ->allowOverride ) {
522
+ $ this ->shared [$ name ] = (bool ) $ flag ;
523
+ return ;
524
+ }
525
+ throw new ContainerModificationsNotAllowedException ($ name );
516
526
}
517
527
518
528
/**
@@ -537,8 +547,8 @@ private function resolveInitializer($initializer)
537
547
538
548
if (is_string ($ initializer )) {
539
549
throw new InvalidArgumentException (sprintf (
540
- 'An invalid initializer was registered; resolved to class or function "%s" '
541
- . 'which does not exist; please provide a valid function name or class '
550
+ 'An invalid initializer was registered; resolved to class or function "%s" '
551
+ . 'which does not exist; please provide a valid function name or class '
542
552
. 'name resolving to an implementation of %s ' ,
543
553
$ initializer ,
544
554
Initializer \InitializerInterface::class
@@ -547,8 +557,8 @@ private function resolveInitializer($initializer)
547
557
548
558
// Otherwise, we have an invalid type.
549
559
throw new InvalidArgumentException (sprintf (
550
- 'An invalid initializer was registered. Expected a callable, or an instance of '
551
- . '(or string class name resolving to) "%s", '
560
+ 'An invalid initializer was registered. Expected a callable, or an instance of '
561
+ . '(or string class name resolving to) "%s", '
552
562
. 'but "%s" was received ' ,
553
563
Initializer \InitializerInterface::class,
554
564
(is_object ($ initializer ) ? get_class ($ initializer ) : gettype ($ initializer ))
@@ -793,61 +803,6 @@ private function createFactoriesForInvokables(array $invokables)
793
803
return $ factories ;
794
804
}
795
805
796
- /**
797
- * Determine if a service instance for a given name already exists,
798
- * and if it exists, determine if is it allowed to get overriden.
799
- *
800
- * Validation in the context of this class means, that for
801
- * a given service name we do not have a service instance
802
- * in the cache OR override is explicitly allowed.
803
- *
804
- * @param string $service
805
- * @throws ContainerModificationsNotAllowedException if the
806
- * provided service name is invalid.
807
- */
808
- private function validateServiceName ($ service )
809
- {
810
- // Important: Next three lines must kept equal to the three
811
- // lines of validateServiceNameArray (see below) which are marked as code
812
- // duplicate!
813
- if (! isset ($ this ->services [$ service ]) || $ this ->allowOverride ) {
814
- return ;
815
- }
816
- throw new ContainerModificationsNotAllowedException ($ service );
817
- }
818
-
819
- /**
820
- * Determine if a service instance for any of the provided array's
821
- * keys already exists, and if it exists, determine if is it allowed
822
- * to get overriden.
823
- *
824
- * Validation in the context of this class means, that for
825
- * a given service name we do not have a service instance
826
- * in the cache OR override is explicitly allowed.
827
- *
828
- * @param string[] $services
829
- * @param string $type Type of service being checked.
830
- * @throws ContainerModificationsNotAllowedException if any
831
- * array keys is invalid.
832
- */
833
- private function validateServiceNameArray (array $ services )
834
- {
835
- foreach (array_keys ($ services ) as $ service ) {
836
- // This is a code duplication from validateServiceName (see above).
837
- // validateServiceName is almost a one liner, so we reproduce it
838
- // here for the sake of performance of aggregated service
839
- // manager configurations (we save the overhead the function
840
- // call would produce)
841
- //
842
- // Important: Next three lines MUST kept equal to the first
843
- // three lines of validateServiceName!
844
- if (! isset ($ this ->services [$ service ]) ?: $ this ->allowOverride ) {
845
- return ;
846
- }
847
- throw new ContainerModificationsNotAllowedException ($ service );
848
- }
849
- }
850
-
851
806
/**
852
807
* Determine if a service for any name provided by a service
853
808
* manager configuration(services, aliases, factories, ...)
@@ -862,22 +817,73 @@ private function validateServiceNameArray(array $services)
862
817
* @throws ContainerModificationsNotAllowedException if any
863
818
* service key is invalid.
864
819
*/
865
- private function validateServiceNameConfig (array $ config )
820
+ private function validateServiceNames (array $ config )
866
821
{
867
822
if ($ this ->allowOverride || ! $ this ->configured ) {
868
823
return ;
869
824
}
870
825
871
- $ sections = ['services ' , 'aliases ' , 'invokables ' , 'factories ' , 'delegators ' , 'shared ' ];
826
+ if (isset ($ config ['services ' ])) {
827
+ foreach ($ config ['services ' ] as $ service => $ _ ) {
828
+ if (! isset ($ this ->services [$ service ]) || $ this ->allowOverride ) {
829
+ continue ;
830
+ }
831
+ throw new ContainerModificationsNotAllowedException ($ service );
832
+ }
833
+ }
834
+
835
+ if (isset ($ config ['aliases ' ])) {
836
+ foreach ($ config ['aliases ' ] as $ service => $ _ ) {
837
+ if (! isset ($ this ->services [$ service ]) || $ this ->allowOverride ) {
838
+ continue ;
839
+ }
840
+ throw new ContainerModificationsNotAllowedException ($ service );
841
+ }
842
+ }
872
843
873
- foreach ($ sections as $ section ) {
874
- if (isset ($ config [$ section ])) {
875
- $ this ->validateServiceNameArray ($ config [$ section ]);
844
+ if (isset ($ config ['invokables ' ])) {
845
+ foreach ($ config ['invokables ' ] as $ service => $ _ ) {
846
+ if (! isset ($ this ->services [$ service ]) || $ this ->allowOverride ) {
847
+ continue ;
848
+ }
849
+ throw new ContainerModificationsNotAllowedException ($ service );
850
+ }
851
+ }
852
+
853
+ if (isset ($ config ['factories ' ])) {
854
+ foreach ($ config ['factories ' ] as $ service => $ _ ) {
855
+ if (! isset ($ this ->services [$ service ]) || $ this ->allowOverride ) {
856
+ continue ;
857
+ }
858
+ throw new ContainerModificationsNotAllowedException ($ service );
859
+ }
860
+ }
861
+
862
+ if (isset ($ config ['delegators ' ])) {
863
+ foreach ($ config ['delegators ' ] as $ service => $ _ ) {
864
+ if (! isset ($ this ->services [$ service ]) || $ this ->allowOverride ) {
865
+ continue ;
866
+ }
867
+ throw new ContainerModificationsNotAllowedException ($ service );
868
+ }
869
+ }
870
+
871
+ if (isset ($ config ['shared ' ])) {
872
+ foreach ($ config ['shared ' ] as $ service => $ _ ) {
873
+ if (! isset ($ this ->services [$ service ]) || $ this ->allowOverride ) {
874
+ continue ;
875
+ }
876
+ throw new ContainerModificationsNotAllowedException ($ service );
876
877
}
877
878
}
878
879
879
880
if (isset ($ config ['lazy_services ' ]['class_map ' ])) {
880
- $ this ->validateServiceNameArray ($ config ['lazy_services ' ]['class_map ' ]);
881
+ foreach ($ config ['lazy_services ' ]['class_map ' ] as $ service => $ _ ) {
882
+ if (! isset ($ this ->services [$ service ]) || $ this ->allowOverride ) {
883
+ continue ;
884
+ }
885
+ throw new ContainerModificationsNotAllowedException ($ service );
886
+ }
881
887
}
882
888
}
883
889
@@ -909,6 +915,40 @@ private function mapAliasToTarget($alias, $target)
909
915
}
910
916
}
911
917
918
+ /**
919
+ * Assuming that all provided alias keys are valid resolve them.
920
+ *
921
+ * This as an adaptation of Tarjan's strongly connected components
922
+ * algorithm. We detect cycles as well reduce the graph so that
923
+ * each alias key gets associated with the resolved service.
924
+ *
925
+ * @param string[][] array of alias definitions
926
+ */
927
+ private function mapAliasesToTargets ($ aliases )
928
+ {
929
+ $ tagged = [];
930
+ foreach ($ aliases as $ alias => $ target ) {
931
+ if (! isset ($ tagged [$ alias ])) {
932
+ $ tCursor = $ aliases [$ alias ];
933
+ $ aCursor = $ alias ;
934
+ $ stack = [];
935
+ while (isset ($ aliases [$ tCursor ])) {
936
+ $ stack [] = $ aCursor ;
937
+ $ aCursor = $ tCursor ;
938
+ if (isset ($ tagged [$ aCursor ])) {
939
+ throw CyclicAliasException::fromCyclicAlias ($ alias , $ aliases );
940
+ }
941
+ $ tagged [$ aCursor ] = true ;
942
+ $ tCursor = $ aliases [$ tCursor ];
943
+ }
944
+ foreach ($ stack as $ alias ) {
945
+ $ aliases [$ alias ] = $ tCursor ;
946
+ }
947
+ }
948
+ }
949
+ $ this ->aliases = $ aliases ;
950
+ }
951
+
912
952
/**
913
953
* Instantiate abstract factories in order to avoid checks during service construction.
914
954
*
@@ -938,8 +978,8 @@ private function resolveAbstractFactoryInstance($abstractFactory)
938
978
// If we still have a string, we have a class name that does not resolve
939
979
if (is_string ($ abstractFactory )) {
940
980
throw new InvalidArgumentException (sprintf (
941
- 'An invalid abstract factory was registered; resolved to class "%s" '
942
- . 'which does not exist; please provide a valid class name resolving '
981
+ 'An invalid abstract factory was registered; resolved to class "%s" '
982
+ . 'which does not exist; please provide a valid class name resolving '
943
983
. 'to an implementation of %s ' ,
944
984
$ abstractFactory ,
945
985
AbstractFactoryInterface::class
@@ -948,7 +988,7 @@ private function resolveAbstractFactoryInstance($abstractFactory)
948
988
949
989
// Otherwise, we have an invalid type.
950
990
throw new InvalidArgumentException (sprintf (
951
- 'An invalid abstract factory was registered. Expected an instance of "%s", '
991
+ 'An invalid abstract factory was registered. Expected an instance of "%s", '
952
992
. 'but "%s" was received ' ,
953
993
AbstractFactoryInterface::class,
954
994
(is_object ($ abstractFactory ) ? get_class ($ abstractFactory ) : gettype ($ abstractFactory ))
0 commit comments