Skip to content
This repository was archived by the owner on Feb 6, 2020. It is now read-only.

Commit 6ae6970

Browse files
author
fhein
committed
Started optimizing abstractFactories.
1 parent afd1d6f commit 6ae6970

File tree

1 file changed

+99
-143
lines changed

1 file changed

+99
-143
lines changed

src/ServiceManager.php

Lines changed: 99 additions & 143 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
use Zend\ServiceManager\Exception\ServiceNotCreatedException;
2222
use Zend\ServiceManager\Exception\ServiceNotFoundException;
2323

24-
use function array_intersect_key;
2524
use function array_keys;
2625
use function array_merge;
2726
use function array_merge_recursive;
@@ -333,6 +332,8 @@ public function getAllowOverride()
333332
*/
334333
public function configure(array $config)
335334
{
335+
// This is a bulk update/initial configuration
336+
// So we check all definitions
336337
$this->validateConfig($config);
337338

338339
if (isset($config['services'])) {
@@ -344,6 +345,7 @@ public function configure(array $config)
344345
$factories = $this->createFactoriesForInvokables($config['invokables']);
345346

346347
if (! empty($aliases)) {
348+
// @todo: This is wrong! These aliases are 'resolved' already
347349
$config['aliases'] = (isset($config['aliases']))
348350
? \array_merge($config['aliases'], $aliases)
349351
: $aliases;
@@ -366,10 +368,16 @@ public function configure(array $config)
366368
$this->shared = $config['shared'] + $this->shared;
367369
}
368370

371+
$aliases = [];
369372
if (isset($config['aliases'])) {
370-
$this->configureAliases($config['aliases']);
373+
$aliases = $config['aliases'];
371374
} elseif (! $this->configured && ! empty($this->aliases)) {
372-
$this->resolveAliases($this->aliases);
375+
$aliases = $this->aliases;
376+
}
377+
if (! empty($aliases)) {
378+
foreach ($aliases as $alias => $target) {
379+
$this->doSetAlias($alias, $target);
380+
}
373381
}
374382

375383
if (isset($config['shared_by_default'])) {
@@ -386,7 +394,11 @@ public function configure(array $config)
386394
// For abstract factories and initializers, we always directly
387395
// instantiate them to avoid checks during service construction.
388396
if (isset($config['abstract_factories'])) {
389-
$this->resolveAbstractFactories($config['abstract_factories']);
397+
$abstractFactories = $config['abstract_factories'];
398+
// $key not needed, but foreach faster
399+
foreach ($abstractFactories as $key => $abstractFactory) {
400+
$this->doAddAbstractFactory($abstractFactory);
401+
}
390402
}
391403

392404
if (isset($config['initializers'])) {
@@ -398,35 +410,6 @@ public function configure(array $config)
398410
return $this;
399411
}
400412

401-
/**
402-
* @param string[] $aliases
403-
*
404-
* @return void
405-
*/
406-
private function configureAliases(array $aliases)
407-
{
408-
if (! $this->configured) {
409-
$this->aliases = $aliases + $this->aliases;
410-
411-
$this->resolveAliases($this->aliases);
412-
413-
return;
414-
}
415-
416-
// Performance optimization. If there are no collisions, then we don't need to recompute loops
417-
$intersecting = $this->aliases && array_intersect_key($this->aliases, $aliases);
418-
$this->aliases = $this->aliases ? array_merge($this->aliases, $aliases) : $aliases;
419-
420-
if ($intersecting) {
421-
$this->resolveAliases($this->aliases);
422-
423-
return;
424-
}
425-
426-
$this->resolveAliases($aliases);
427-
$this->resolveNewAliasesWithPreviouslyResolvedAliases($aliases);
428-
}
429-
430413
/**
431414
* Add an alias.
432415
*
@@ -436,16 +419,8 @@ private function configureAliases(array $aliases)
436419
public function setAlias($alias, $target)
437420
{
438421
$this->validate($alias);
439-
$this->aliases[$alias] = $target;
422+
$this->doSetAlias($alias, $target);
440423

441-
$this->resolvedAliases[$alias] =
442-
isset($this->resolvedAliases[$target]) ? $this->resolvedAliases[$target] : $target;
443-
if (in_array($alias, $this->resolvedAliases)) {
444-
$r = array_intersect($this->resolvedAliases, [ $alias ]);
445-
foreach ($r as $name => $service) {
446-
$this->resolvedAliases[$name] = $target;
447-
}
448-
}
449424
}
450425

451426
/**
@@ -492,7 +467,7 @@ public function mapLazyService($name, $class = null)
492467
*/
493468
public function addAbstractFactory($factory)
494469
{
495-
$this->resolveAbstractFactory($factory);
470+
$this->doAddAbstractFactory($factory);
496471
}
497472

498473
/**
@@ -541,63 +516,6 @@ public function setShared($name, $flag)
541516
$this->shared[$name] = (bool) $flag;
542517
}
543518

544-
private function resolveAbstractFactory($abstractFactory)
545-
{
546-
if (\is_string($abstractFactory) && \class_exists($abstractFactory)) {
547-
//Cached string
548-
if (! isset($this->cachedAbstractFactories[$abstractFactory])) {
549-
$this->cachedAbstractFactories[$abstractFactory] = new $abstractFactory();
550-
}
551-
552-
$abstractFactory = $this->cachedAbstractFactories[$abstractFactory];
553-
}
554-
555-
if ($abstractFactory instanceof Factory\AbstractFactoryInterface) {
556-
$abstractFactoryObjHash = \spl_object_hash($abstractFactory);
557-
$this->abstractFactories[$abstractFactoryObjHash] = $abstractFactory;
558-
return;
559-
}
560-
561-
// Error condition; let's find out why.
562-
563-
// If we still have a string, we have a class name that does not resolve
564-
if (\is_string($abstractFactory)) {
565-
throw new InvalidArgumentException(
566-
sprintf(
567-
'An invalid abstract factory was registered; resolved to class "%s" ' .
568-
'which does not exist; please provide a valid class name resolving ' .
569-
'to an implementation of %s',
570-
$abstractFactory,
571-
AbstractFactoryInterface::class
572-
)
573-
);
574-
}
575-
576-
// Otherwise, we have an invalid type.
577-
throw new InvalidArgumentException(
578-
sprintf(
579-
'An invalid abstract factory was registered. Expected an instance of "%s", ' .
580-
'but "%s" was received',
581-
AbstractFactoryInterface::class,
582-
(is_object($abstractFactory) ? get_class($abstractFactory) : gettype($abstractFactory))
583-
)
584-
);
585-
}
586-
587-
/**
588-
* Instantiate abstract factories for to avoid checks during service construction.
589-
*
590-
* @param string[]|Factory\AbstractFactoryInterface[] $abstractFactories
591-
*
592-
* @return void
593-
*/
594-
private function resolveAbstractFactories(array $abstractFactories)
595-
{
596-
foreach ($abstractFactories as $abstractFactory) {
597-
$this->resolveAbstractFactory($abstractFactory);
598-
}
599-
}
600-
601519
/**
602520
* Instantiate initializers for to avoid checks during service construction.
603521
*
@@ -656,49 +574,6 @@ private function resolveInitializers(array $initializers)
656574
}
657575
}
658576

659-
/**
660-
* Resolve aliases to their canonical service names.
661-
*
662-
* @param string[] $aliases
663-
*
664-
* @returns void
665-
*/
666-
private function resolveAliases(array $aliases)
667-
{
668-
foreach ($aliases as $alias => $service) {
669-
$visited = [];
670-
$name = $alias;
671-
672-
while (isset($this->aliases[$name])) {
673-
if (isset($visited[$name])) {
674-
throw CyclicAliasException::fromAliasesMap($aliases);
675-
}
676-
677-
$visited[$name] = true;
678-
$name = $this->aliases[$name];
679-
}
680-
681-
$this->resolvedAliases[$alias] = $name;
682-
}
683-
}
684-
685-
/**
686-
* Rewrites the map of aliases by resolving the given $aliases with the existing resolved ones.
687-
* This is mostly done for performance reasons.
688-
*
689-
* @param string[] $aliases
690-
*
691-
* @return void
692-
*/
693-
private function resolveNewAliasesWithPreviouslyResolvedAliases(array $aliases)
694-
{
695-
foreach ($this->resolvedAliases as $name => $target) {
696-
if (isset($aliases[$target])) {
697-
$this->resolvedAliases[$name] = $this->resolvedAliases[$target];
698-
}
699-
}
700-
}
701-
702577
/**
703578
* Get a factory for the given service name
704579
*
@@ -1021,4 +896,85 @@ private function validateConfig(array $config)
1021896
$this->validateArray($config['lazy_services']['class_map']);
1022897
}
1023898
}
899+
900+
/**
901+
* Assuming that the alias name is valid (see above) resolve/add it.
902+
*
903+
* @param string $alias
904+
* @param string $target
905+
*/
906+
private function doSetAlias($alias, $target)
907+
{
908+
$this->aliases[$alias] = $target;
909+
$this->resolvedAliases[$alias] =
910+
isset($this->resolvedAliases[$target]) ? $this->resolvedAliases[$target] : $target;
911+
912+
if ($alias === $this->resolvedAliases[$alias]) {
913+
throw CyclicAliasException::fromAliasesMap([$alias]);
914+
}
915+
916+
if (in_array($alias, $this->resolvedAliases)) {
917+
$r = array_intersect($this->resolvedAliases, [ $alias ]);
918+
foreach ($r as $name => $service) {
919+
$this->resolvedAliases[$name] = $target;
920+
}
921+
}
922+
}
923+
924+
/**
925+
* Instantiate abstract factories for to avoid checks during service construction.
926+
*
927+
* @todo: To construct to avoid is not really an optimization, it's lazyness
928+
* AbstractFactories are shared services. Make sure that has() is guarded against
929+
* numerical parameters. Handle AbstractFactories as any other shared services.
930+
*
931+
* @todo: Implement a has() and get() logic which unifies the several arrays
932+
* It is not necessary to isset(blah) several times on a request.
933+
*
934+
* @param string[]|Factory\AbstractFactoryInterface[] $abstractFactories
935+
*
936+
* @return void
937+
*/
938+
private function doAddAbstractFactory($abstractFactory)
939+
{
940+
if (\is_string($abstractFactory) && \class_exists($abstractFactory)) {
941+
//Cached string
942+
if (! isset($this->cachedAbstractFactories[$abstractFactory])) {
943+
$this->cachedAbstractFactories[$abstractFactory] = new $abstractFactory();
944+
}
945+
946+
$abstractFactory = $this->cachedAbstractFactories[$abstractFactory];
947+
}
948+
949+
if ($abstractFactory instanceof Factory\AbstractFactoryInterface) {
950+
$abstractFactoryObjHash = \spl_object_hash($abstractFactory);
951+
$this->abstractFactories[$abstractFactoryObjHash] = $abstractFactory;
952+
return;
953+
}
954+
955+
// Error condition; let's find out why.
956+
957+
// If we still have a string, we have a class name that does not resolve
958+
if (\is_string($abstractFactory)) {
959+
throw new InvalidArgumentException(
960+
sprintf(
961+
'An invalid abstract factory was registered; resolved to class "%s" ' .
962+
'which does not exist; please provide a valid class name resolving ' .
963+
'to an implementation of %s',
964+
$abstractFactory,
965+
AbstractFactoryInterface::class
966+
)
967+
);
968+
}
969+
970+
// Otherwise, we have an invalid type.
971+
throw new InvalidArgumentException(
972+
sprintf(
973+
'An invalid abstract factory was registered. Expected an instance of "%s", ' .
974+
'but "%s" was received',
975+
AbstractFactoryInterface::class,
976+
(is_object($abstractFactory) ? get_class($abstractFactory) : gettype($abstractFactory))
977+
)
978+
);
979+
}
1024980
}

0 commit comments

Comments
 (0)