Last active
January 31, 2019 13:27
-
-
Save bnf/bbc431b6e3c31613c01fb685a6fa30cb to your computer and use it in GitHub Desktop.
Chained PSR-14 ListenerProviderInteface's, autoconfigured using container-interop/service-providers
This file contains hidden or 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
<?php | |
declare(strict_types=1); | |
require 'vendor/autoload.php'; | |
use Bnf\Di\Container; | |
use Crell\Tukio\Dispatcher; | |
use Interop\Container\ServiceProviderInterface; | |
use Psr\Container\ContainerInterface; | |
use Psr\EventDispatcher\ListenerProviderInterface; | |
use Psr\EventDispatcher\EventDispatcherInterface; | |
class Provider implements ListenerProviderInterface | |
{ | |
/** @var ?ListenerProviderInterface */ | |
private $previousProvider = null; | |
/** @var array */ | |
private $listeners = []; | |
public function __construct(ListenerProviderInterface $previous = null) | |
{ | |
$this->previousProvider = $previous; | |
} | |
public function getListenersForEvent(object $event) : iterable | |
{ | |
if ($this->previousProvider !== null) { | |
yield from $this->previousProvider->getListenersForEvent($event); | |
} | |
foreach ($this->listeners as $eventType => $listeners) { | |
foreach ($listeners as $listener) { | |
yield $listener; | |
} | |
} | |
} | |
public function attach(string $eventType, callable $listener): void { | |
$this->listeners[$eventType][] = $listener; | |
} | |
} | |
class Package1Provider extends Provider {}; | |
class Package2Provider extends Provider {}; | |
class Package3Provider extends Provider {}; | |
$core = new class implements ServiceProviderInterface { | |
public function getFactories() | |
{ | |
return [ | |
EventDispatcherInterface::class => function (ContainerInterface $container): EventDispatcherInterface { | |
return new Dispatcher($container->get(ListenerProviderInterface::class)); | |
}, | |
]; | |
} | |
public function getExtensions() | |
{ | |
return []; | |
} | |
}; | |
$package1 = new class implements ServiceProviderInterface { | |
public function getFactories() | |
{ | |
return []; | |
} | |
public function getExtensions() | |
{ | |
return [ | |
// Concatenate package2 ListenerProvider to (possibly) existing ListenerProviders | |
ListenerProviderInterface::class => function (ContainerInterface $container, ListenerProviderInterface $previous = null): ListenerProviderInterface { | |
$provider = new Package1Provider($previous); | |
$provider->attach(stdClass::class, function() { | |
echo 'a'; | |
}); | |
return $provider; | |
}, | |
]; | |
} | |
}; | |
$package2 = new class implements ServiceProviderInterface { | |
public function getFactories() | |
{ | |
return []; | |
} | |
public function getExtensions() | |
{ | |
return [ | |
// Concatenate package2 ListenerProvider to (possibly) existing ListenerProviders | |
ListenerProviderInterface::class => function (ContainerInterface $container, ListenerProviderInterface $previous = null): ListenerProviderInterface { | |
$provider = new Package2Provider($previous); | |
$provider->attach(stdClass::class, function() { | |
echo 'b'; | |
}); | |
return $provider; | |
}, | |
]; | |
} | |
}; | |
class ListenerProviderChain implements ListenerProviderInterface | |
{ | |
/** @var ListenerProviderInterface */ | |
protected $a, $b; | |
public function __construct(ListenerProviderInterface $a, ListenerProviderInterface $b) | |
{ | |
$this->a = $a; | |
$this->b = $b; | |
} | |
public function getListenersForEvent(object $event) : iterable | |
{ | |
yield from $this->a->getListenersForEvent($event); | |
yield from $this->b->getListenersForEvent($event); | |
} | |
} | |
$package3 = new class implements ServiceProviderInterface { | |
public function getFactories() | |
{ | |
return [ | |
Package3Provider::class => function (ContainerInterface $container): Package3Provider { | |
$provider = new Package3Provider(); | |
$provider->attach(stdClass::class, function() { | |
echo 'c'; | |
}); | |
return $provider; | |
}, | |
]; | |
} | |
public function getExtensions() | |
{ | |
return [ | |
// Concatenate package3 provider using a helper class to existing providers. | |
// That allows Package3Provider to be configured and instantiated separtely (if that's needed) | |
// This pattern means that using 5 providers would result in 4 classes that chain these: | |
// (C=chain class, P=provider class, D=dispatcher): | |
// D | |
// | | |
// C4 | |
// / \ | |
// C3 P5 | |
// / \ | |
// C2 P4 | |
// / \ | |
// C1 P3 | |
// / \ | |
// P1 P2 | |
ListenerProviderInterface::class => function (ContainerInterface $container, ListenerProviderInterface $previous = null): ListenerProviderInterface { | |
$provider = $container->get(Package3Provider::class); | |
return $previous === null ? $provider : new ListenerProviderChain($previous, $provider); | |
}, | |
]; | |
} | |
}; | |
// Will output 'abc' | |
(new Container([$core, $package1, $package2, $package3]))->get(EventDispatcherInterface::class)->dispatch(new stdClass); | |
echo PHP_EOL; | |
// Will output 'cba' | |
(new Container([$core, $package3, $package2, $package1]))->get(EventDispatcherInterface::class)->dispatch(new stdClass); | |
echo PHP_EOL; | |
// Will output 'a' | |
(new Container([$core, $package1]))->get(EventDispatcherInterface::class)->dispatch(new stdClass); | |
echo PHP_EOL; | |
// Will output 'b' | |
(new Container([$core, $package2]))->get(EventDispatcherInterface::class)->dispatch(new stdClass); | |
echo PHP_EOL; |
This file contains hidden or 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
{ | |
"name": "bnf/psr14-service-provider-example", | |
"private": true, | |
"require": { | |
"psr/event-dispatcher": "0.7.0", | |
"container-interop/service-provider": "^0.4.0", | |
"crell/tukio": "^0.7.2", | |
"bnf/di": "^0.1.2" | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment