\documentclass{article}
\usepackage{ulem}
\usepackage{graphicx}
\usepackage{hyperref}
\pagestyle{headings}
\begin{document}
\part{Dependency Injection with Horde\textbackslash\{\}Injector}
Horde\textbackslash\{\}Injector is a lightweight PSR-11 dependency injection container for managing object dependencies and wiring. It allows loose coupling between application components. You can inject dependencies into the constructor or setter methods and don't need to use global state or singletons. The injector supports autowiring via reflection, declarative configuration through PHP 8 attributes and explicit binding for complex scenarios. For legacy scenarios it supports using annotations rather than attributes.

\section{Configuring the Injector}
The injector resolves dependencies in order of precedence: 1) Explicit bindings override attributes. 2)  Attributes override autowiring. Choose which approach matches your use case.

\subsection{Autowiring}
Autowiring means zero configuration. All necessary facts can be derived from type signatures in the constructor.

The injector automatically creates instances of classes with typed constructor parameters.<br />
The code example is fictional - a class <a href="https://wiki.horde.org/UserInterface">UserInterface</a> might exist in your code but not in Horde 6.

<pre><code class="language-php">
use Horde\textbackslash\{\}Db\textbackslash\{\}Adapter as DbAdapter;
use Psr\textbackslash\{\}Logger\textbackslash\{\}LoggerInterface;
class UserService
\{
    public function \_\_construct(
        private DbAdapter \$db,
        private LoggerInterface \$logger
    ) \{\}
\}

\$injector = new Horde\textbackslash\{\}Injector\textbackslash\{\}Injector(new Horde\textbackslash\{\}Injector\textbackslash\{\}TopLevel());
\$service = \$injector->get(UserService::class); // Dependencies auto-resolved
</code></pre>
Works when:

\begin{itemize}
\item All constructor parameters are type-hinted


\item All dependencies are concrete classes or already bound


\item No special construction logic required


\end{itemize}
\subsection{Factory Attributes (Recommended for Factories)}
Use PHP 8 \texttt{\#[Factory](Factory)} attributes to specify factory classes and methods. No manual binding calls are required:

<pre><code class="language-php">
use Horde\textbackslash\{\}Injector\textbackslash\{\}Attribute\textbackslash\{\}Factory;

class UserFactory
\{
    public function createUser(Horde\textbackslash\{\}Injector\textbackslash\{\}Injector \$injector): User
    \{
        return new User(
            \$injector->get(Database::class),
            \$injector->get('config.user.default\_role')
        );
    \}
\}

\#[Factory(factory: UserFactory::class, method: 'createUser')]
class User
\{
    public function \_\_construct(
        private Database \$db,
        private string \$defaultRole
    ) \{\}
\}

// No binding needed - factory auto-discovered

\$user = \$injector->get(User::class);
</code></pre>
Use attributes when:

\begin{itemize}
\item Factory logic is complex or needs configuration


\item You want self-documenting, type-safe factory registration


\item Refactoring tools should track factory relationships


\end{itemize}
\subsection{Setter Injection Attributes (Legacy)}
Mark optional dependencies for setter injection using \texttt{@inject} doc blocks (H5 compatibility) or \texttt{\#[Inject](Inject)} attributes (H6):

<pre><code class="language-php">
use Horde\textbackslash\{\}Injector\textbackslash\{\}Attribute\textbackslash\{\}Inject;

class Service
\{
    private ?CacheInterface \$cache = null;

    \#[Inject]
    public function setCache(CacheInterface \$cache): void
    \{
        \$this->cache = \$cache;
    \}
\}

\$injector->bindImplementation(Service::class, Service::class);
</code></pre>
\textbf{Note:} Setter injection happens after constructor. This means injected properties are \texttt{null} during \texttt{\_\_construct()}.<br />
This is a pattern for retrofitting code which cannot fix constructors for BC reasons. It's generally something to avoid otherwise.

\subsection{Explicit Binding (Full Control)}
For complex scenarios you can explicitly bind interfaces to implementations, factories, or closures:

\textbf{Factory binding:}

<pre><code class="language-php">
use Horde\textbackslash\{\}Cache\textbackslash\{\}Storage as CacheStorage;

\$injector->bindFactory(CacheStorage::class', 'CacheFactory', 'create');

class CacheFactory
\{
    public function create(Horde\textbackslash\{\}Injector\textbackslash\{\}Injector \$injector)
    \{
        \$conf = \$injector->getInstance('Horde\_Registry')->get('cache');
        return new Horde\textbackslash\{\}Cache\textbackslash\{\}Storage\textbackslash\{\}File(\$conf['dir']);
    \}
\}
</code></pre>
\textbf{Implementation binding:}

<pre><code class="language-php">
\$injector->bindImplementation('Psr\textbackslash\{\}Log\textbackslash\{\}LoggerInterface', 'Horde\textbackslash\{\}Log\textbackslash\{\}Logger');
</code></pre>
\textbf{Closure binding:}

<pre><code class="language-php">
\$injector->bindClosure('DatabaseConfig', function(\$inj) \{
    return new DatabaseConfig(\$inj->getInstance('Horde\_Registry')->get('sql'));
\});
</code></pre>
Explicit bindings always override attributes and autowiring.

\subsection{Instance Registration (Rare)}
Only use \texttt{setInstance()} for pre-constructed objects or the injector itself:

<pre><code class="language-php">
\$injector->setInstance('Horde\textbackslash\{\}Injector\textbackslash\{\}Injector', \$injector);
\$injector->setInstance('legacy\_db', \$existingDbConnection);
</code></pre>
\textbf{Warning:} Avoid \texttt{setInstance()} unless you really need it objects. Use factories instead to maintain lazy instantiation.

\section{Using the Injector}
\subsection{Getting Instances}
Retrieve objects from the container. Instances are cached (singleton per injector scope):

<pre><code class="language-php">
// PSR-11 interface (preferred in H6)
\$service = \$injector->get('ServiceInterface');

// H5 compatibility
\$service = \$injector->getInstance('ServiceInterface');
</code></pre>
\subsection{Creating New Instances}
Force creation of a new instance (not cached):

<pre><code class="language-php">
\$newService = \$injector->createInstance('ServiceClass');
</code></pre>
Dependencies may still be reused from the instance cache.

\subsection{Child Injectors (Scoping)}
Create isolated scopes for modules or request handling without polluting global scope:

<pre><code class="language-php">
\$appInjector->bindFactory('Logger', 'LoggerFactory', 'create');

\$moduleInjector = \$appInjector->createChildInjector();
\$moduleInjector->bindImplementation('Logger', 'ModuleSpecificLogger');

// Child sees parent bindings
\$x = \$moduleInjector->get('Logger'); // Uses ModuleSpecificLogger

// Parent doesn't see child bindings
\$y = \$appInjector->get('Logger'); // Uses LoggerFactory
</code></pre>
Use child injectors for:

\begin{itemize}
\item Per-module configuration


\item Request-scoped objects


\item Test isolation


\end{itemize}
\subsection{Checking Availability}
Test if the injector can provide an interface:

<pre><code class="language-php">
if (\$injector->has('OptionalService')) \{
    \$service = \$injector->get('OptionalService');
\}
</code></pre>
\texttt{has()} returns \texttt{true} if:

\begin{itemize}
\item An instance exists


\item A binding is registered


\item The class is autowireable (concrete with satisfiable dependencies)


\end{itemize}
\section{Migration from H5}
\textbf{H5 (Horde\_Injector):}

<pre><code class="language-php">
\$injector = new Horde\_Injector(new Horde\_Injector\_TopLevel());
\$injector->bindFactory('User', 'UserFactory', 'create');
\$user = \$injector->getInstance('User');
</code></pre>
\textbf{H6 (Horde\textbackslash\{\}Injector with attributes):}

<pre><code class="language-php">
use Horde\textbackslash\{\}Injector\textbackslash\{\}Attribute\textbackslash\{\}Factory;

\#[Factory(factory: UserFactory::class, method: 'create')]
class User \{ \}

\$injector = new Horde\textbackslash\{\}Injector\textbackslash\{\}Injector(new Horde\textbackslash\{\}Injector\textbackslash\{\}TopLevel());
\$user = \$injector->get(User::class); // PSR-11, factory auto-discovered
</code></pre>
\textbf{Key changes:}

\begin{itemize}
\item \texttt{getInstance()} ? \texttt{get()} (PSR-11 compliance)


\item Manual \texttt{bindFactory()} ? \texttt{\#[Factory](Factory)} attribute (optional)


\item \texttt{Horde\_Injector} ? \texttt{Horde\textbackslash\{\}Injector\textbackslash\{\}Injector} (namespaced)


\end{itemize}
\section{Best Practices}
\begin{itemize}
\item \textbf{Prefer autowiring} - Let type hints drive dependency resolution


\item \textbf{Use attributes for factories} - Self-documenting, refactoring-safe


\item \textbf{Avoid \texttt{setInstance()} for business objects} - Breaks lazy loading


\item \textbf{Use child injectors for scopes} - Module/request isolation


\item \textbf{Constructor injection over setter injection} - Dependencies available immediately


\item \textbf{Type-hint everything} - Enables autowiring and IDE support


\end{itemize}
\end{document}
