*/ private array $factories = array(); /** * Built instances keyed by service id. * * @var array */ private array $instances = array(); /** * Registers a factory. Replacing a service that was already built is not allowed. * * @param string $id Service id, normally the class name. * @param callable $factory Builds the service. * @phpstan-param callable(Container):object $factory * @throws \LogicException When the service was already built. */ public function set( string $id, callable $factory ): void { if ( isset( $this->instances[ $id ] ) ) { throw new \LogicException( esc_html( sprintf( 'Service %s is already in use and cannot be replaced.', $id ) ) ); } $this->factories[ $id ] = $factory; } /** * Whether a service is registered. * * @param string $id Service id. */ public function has( string $id ): bool { return isset( $this->factories[ $id ] ) || isset( $this->instances[ $id ] ); } /** * Returns the shared instance of a service. * * @template T of object * @param string $id Service class name. * @phpstan-param class-string $id * @return T * @throws \OutOfBoundsException When the service is unknown. * @throws \UnexpectedValueException When the factory returns the wrong type. */ public function get( string $id ): object { if ( ! isset( $this->instances[ $id ] ) ) { if ( ! isset( $this->factories[ $id ] ) ) { throw new \OutOfBoundsException( esc_html( sprintf( 'Unknown service %s.', $id ) ) ); } $this->instances[ $id ] = ( $this->factories[ $id ] )( $this ); } $instance = $this->instances[ $id ]; if ( ! $instance instanceof $id ) { throw new \UnexpectedValueException( esc_html( sprintf( 'Service %s has the wrong type.', $id ) ) ); } return $instance; } }