*/ protected $instances = []; /** * Finds an entry of the container by its identifier and returns it. * * @param string $id Identifier of the entry to look for. * * @throws NotFoundExceptionInterface No entry was found for **this** identifier. * @throws ContainerExceptionInterface|ReflectionException Error while retrieving the entry. * * @return mixed Entry. */ public function get( string $key ): object { if ( ! $this->has( $key ) ) { // translators: %s is the name of the class that is not instantiable. throw new \Exception( sprintf( esc_html__( 'Service provider %s not found.', 'aisentic' ), esc_html( $key ) ) ); } $class_name = $this->services[ $key ]; return $this->resolve( $class_name ); } /** * Sets an entry of the container by its identifier * * @param string $id * @param string $class_name * * @return void */ public function add_service_provider( string $key, $class_name ): void { $this->services[ $key ] = $class_name; } /** * Returns true if the container can return an entry for the given identifier. * Returns false otherwise. * * `has($key)` returning true does not mean that `get($id)` will not throw an exception. * It does however mean that `get($id)` will not throw a `NotFoundExceptionInterface`. * * @param string $key Identifier of the entry to look for. * * @return bool */ public function has( string $key ): bool { return array_key_exists( $key, $this->services ); } /** * Resolves the service object by its name. * * @param string $class_name * * @throws DependencyHasNoDefaultValueException * @throws DependencyIsNotInstantiableException * @throws ReflectionException|Exception * * @return object */ public function resolve( string $class_name ): object { // Return the instance built on a previous resolution if we have one. if ( isset( $this->instances[ $class_name ] ) ) { return $this->instances[ $class_name ]; } if ( ! class_exists( $class_name ) ) { // translators: %s is the name of the class that is not instantiable. throw new Exception( sprintf( esc_html__( 'Class: %s does not exist.', 'aisentic' ), esc_html( $class_name ) ) ); } $reflection_class = new ReflectionClass( $class_name ); if ( ! $reflection_class->isInstantiable() ) { throw new DependencyIsNotInstantiableException( // translators: %s is the name of the class that is not instantiable. sprintf( esc_html__( 'Class: %s is not instantiable.', 'aisentic' ), esc_html( $class_name ) ) ); } if ( null === $reflection_class->getConstructor() ) { $instance = $reflection_class->newInstance(); } else { $parameters = $reflection_class->getConstructor()->getParameters(); $dependencies = $this->build_dependencies( $parameters ); $instance = $reflection_class->newInstanceArgs( $dependencies ); } $this->instances[ $class_name ] = $instance; return $instance; } /** * Builds the dependencies for the given parameters. * * @param ReflectionParameter[] $parameters * * @throws ReflectionException * @throws DependencyHasNoDefaultValueException * * @return array */ public function build_dependencies( array $parameters ): array { $dependencies = []; foreach ( $parameters as $parameter ) { $dependency = $parameter->getType() ? $parameter->getType()->getName() : null; if ( is_null( $dependency ) ) { if ( $parameter->isDefaultValueAvailable() ) { $dependencies[] = $parameter->getDefaultValue(); } else { throw new DependencyHasNoDefaultValueException( // translators: %s is the name of the class that is not instantiable. sprintf( esc_html__( 'Class: %s dependency cannot be resolved.', 'aisentic' ), esc_html( $parameter->name ) ) ); } } else { $dependencies[] = $this->get( $dependency ); } } return $dependencies; } }