use Psr\Container\ContainerInterface;
use RuntimeException;
use Wikimedia\Assert\Assert;
+use Wikimedia\ScopedCallback;
/**
* Generic service container.
*/
private $destroyed = false;
+ /**
+ * @var array Set of services currently being created, to detect loops
+ */
+ private $servicesBeingCreated = [];
+
/**
* @param array $extraInstantiationParams Any additional parameters to be passed to the
* instantiator function when creating a service. This is typically used to provide
* @param string $name
*
* @throws InvalidArgumentException if $name is not a known service.
+ * @throws RuntimeException if a circular dependency is detected.
* @return object
*/
private function createService( $name ) {
if ( isset( $this->serviceInstantiators[$name] ) ) {
+ if ( isset( $this->servicesBeingCreated[$name] ) ) {
+ throw new RuntimeException( "Circular dependency when creating service! " .
+ implode( ' -> ', array_keys( $this->servicesBeingCreated ) ) . " -> $name" );
+ }
+ $this->servicesBeingCreated[$name] = true;
+ $removeFromStack = new ScopedCallback( function () use ( $name ) {
+ unset( $this->servicesBeingCreated[$name] );
+ } );
+
$service = ( $this->serviceInstantiators[$name] )(
$this,
...$this->extraInstantiationParams
}
}
+ $removeFromStack->consume();
+
// NOTE: when adding more wiring logic here, make sure importWiring() is kept in sync!
} else {
throw new NoSuchServiceException( $name );