*/ private array $factories = array(); /** * Resolved instances. * * @var array */ private array $instances = array(); /** * Registers (or replaces) a service factory. The factory receives the container. * * @param string $id Service id. * @param callable $factory Factory: fn( Container $c ): mixed. */ public function set( string $id, callable $factory ): void { $this->factories[ $id ] = $factory; unset( $this->instances[ $id ] ); } /** * Whether the service is registered. * * @param string $id Service id. */ public function has( string $id ): bool { return isset( $this->factories[ $id ] ) || array_key_exists( $id, $this->instances ); } /** * Resolves a shared service. * * @param string $id Service id. * @return mixed * @throws \OutOfBoundsException When the service is not registered. */ public function get( string $id ): mixed { if ( array_key_exists( $id, $this->instances ) ) { return $this->instances[ $id ]; } if ( ! isset( $this->factories[ $id ] ) ) { throw new \OutOfBoundsException( sprintf( 'Service "%s" is not registered.', esc_html( $id ) ) ); } $this->instances[ $id ] = ( $this->factories[ $id ] )( $this ); return $this->instances[ $id ]; } /** * Decorates a registered service. The decorator receives the inner instance and the container. * * @param string $id Service id. * @param callable $decorator Decorator: fn( mixed $inner, Container $c ): mixed. * @throws \OutOfBoundsException When the service is not registered. */ public function extend( string $id, callable $decorator ): void { if ( ! isset( $this->factories[ $id ] ) ) { throw new \OutOfBoundsException( sprintf( 'Service "%s" is not registered.', esc_html( $id ) ) ); } $inner = $this->factories[ $id ]; $this->factories[ $id ] = static fn( Container $c ): mixed => $decorator( $inner( $c ), $c ); unset( $this->instances[ $id ] ); } }