Download the PHP package ifcastle/di without Composer

On this page you can find all versions of the php package ifcastle/di. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package di

Dependency Injection PHP Composer

Dependency Injection (DI) is a lightweight extensible PHP library for dependency injection for stateful application.

The library is designed for PHP 8.4 using the LazyProxy API.

Features

Installation

You can install Dependency Injector using Composer. Run the following command:

Basic Usage

Please read the Project Philosophy section before using the library.

The example below demonstrates how the library works with the SomeClass class, which implements the SomeInterface interface.

The class definition does not depend on the Dependency Injection implementation. Dependencies are injected via the class constructor.

The library automatically binds dependencies to their interfaces.

Special Attributes

Attributes provide a more precise way to describe features for dependency resolution. This library supports several attributes that can be used:

Lazy Loading

The library supports lazy loading of dependencies by special attribute:

Warning: Lazy dependencies are implemented using the PHP LazyProxy API, so the same dependencies in different classes will be different objects!

This means the === operation will return false, and spl_object_id() will return different values.

Circular Dependencies

The library allows resolving circular dependencies if the dependency is not used during resolution. If a circular dependency occurs, the library will create a LazyProxy object and return it.

Warning: Lazy or circular dependencies cannot be used BEFORE the process of resolving all dependencies is completed!

Custom attributes and Providers

You can create your own attributes to describe dependencies by implementing the DescriptorInterface.

To define a custom algorithm for dependency resolution, DescriptorInterface can implement the getProvider method, which returns the dependency.

Below is an example of a method that retrieves a value from the configuration:

This way, you can extend the DI logic without modifying the library's code.

Project Philosophy

This project implements a particular algorithm for dependency management, adhering to the following rule:

If Class A requires a dependency by the contract Interface, and Class B also requires a dependency Interface, both classes will receive the same dependency Class D within a single runtime environment.

The following statements are true:

Comparison of IfCastle DI vs. Symfony and other DI implementations

Aspect IfCastle DI Symfony
Dependency Resolution Single container for all dependencies in one runtime environment (Service Locator approach) Each service explicitly defines its dependencies, configured individually
Same Contract Handling One contract always linked to one dependency in a runtime environment Allows different implementations of the same interface through qualifiers, tags, or aliases
Runtime Environments Supports multiple runtime environments with hierarchical relationships (Parent -> Child) Global container; separate environments typically require separate configurations
Different Dependencies for Same Key Not possible within the same runtime environment Fully supported using tags, aliases, or contextual bindings
Approach to Dependency Management Service Locator-like: dependencies resolved from a shared container Strict DI Container approach: dependencies injected explicitly
Parent-Child Relationship Direct support for hierarchical environments with inheritance No direct concept of parent-child environments; containers are isolated

The approach of this library has several benefits:

Zero configuration principle

The task of dependency resolution is typically addressed by separating the information about how to locate dependencies from the objects that require them. For example:

Does this approach meet the SOLID principles? The TargetDependencies class must duplicate knowledge about the required dependencies of the Target class. However, the Target class is the single source of truth.

Another drawback of this solution is the increased code volume and complexity. The developer must now remember two points of definition related to initialization.

For this reason, modern DI approaches combine metadata for dependency resolution directly within the Target class.

To store metadata, attributes are used, which are directly placed in the Target class. Now the knowledge about how to resolve dependencies flows into the Target class, binding it to the DI implementation.

This coupling results in components requiring a specific library for dependency resolution and being unable to function independently. In the absence of a standard, this hinders code reusability.

The Zero configuration principle suggests avoiding the use of metadata within the Target class, making it independent of any specific DI implementation.

To reduce the coupling of Target classes, our library proposes moving dependency metadata knowledge to the contract domain. In other words, information about how to resolve dependencies can be stored in an interface rather than in the implementation. This keeps dependency definitions as transparent as possible.

Example:

In this case, the ClassWithDependencyContact class is a Target class, and the InterfaceWithDependencyContact interface is a Contract.

Using the DependencyContract attribute, the interface specifies how the dependency should be resolved. This solution is also not ideal, but in many cases, it results in cleaner code.

See more: Complex use cases

Performance considerations

The library does not include any compilers for dependency descriptors, although their implementation is possible. All dependency descriptors are resolved dynamically at application startup using the Reflection API.

Is this a performance issue? Yes, if you are using PHP in a stateless mode, where the PHP process terminates after each request.

For stateful applications, dependency resolution occurs once during the application's warm-up phase or on-demand, which aligns with the purpose of this library.

Architecture

The library consists of four core components that interact with each other:

All these components are interchangeable, and by modifying them, you can change the behavior.

Container

The dependency container follows the ServiceLocator/Environment pattern. This means that when attempting to resolve a dependency, the dependencies required by it will also be retrieved from the same container.

In other words,

All dependencies in the container share the container as a common execution environment.

You can leverage this fact to create multiple execution environments, each holding its unique dependencies. This allows you to implement Scope logic, where dependency initialization depends on the environment.

By default, the container is an immutable object in terms of associating keys with dependencies. However, the container's values change during execution, as dependencies are initialized on their first use, replacing dependency descriptors with the actual value.

This container behavior ensures that dependencies are single instances, meaning they are the same object.

Container Inheritance

To provide developers with a powerful tool for managing dependencies, the container supports inheritance based on the override principle. This means you can create two separate containers with different sets of dependencies and then link them as PARENT and CHILD containers.

In this case, an attempt to resolve a dependency in the child container will result in the following behavior:

Dereferencing a WeakReference.

The container supports dereferencing weak references if they are detected as a value.

Using weak references is typically useful when defining multiple aliases for dependencies or a reference to the container itself. In such cases, weak references help avoid additional work for the garbage collector and prevent memory leaks.

Initializer

Sometimes, it is necessary to initialize a dependency not directly through the constructor but with additional code. For example, loading a database driver based on configuration or context.

To solve this problem, an initializer can be used: a special object executed only once at the moment of dependency resolution.

To implement this approach, the library uses the InitializerInterface. If the container holds a value of type InitializerInterface, the container uses the executeInitializer method to obtain the dependency.

See SelfReferenceInitializer as an example of an initializer that resolves a dependency from the container.

Throwable values

Container values can be exception objects (Throwable). When attempting to resolve a dependency whose value is an exception object, that exception will be thrown.

This applies to both the resolveDependency method and the findDependency method. However, you can change this behavior by specifying the $returnThrowable flag for the findDependency.

Thus,

if a dependency was resolved with an error once, any later attempt to retrieve this dependency will result in the same error without attempting to resolve it again.

Builder

The container builder is responsible for constructing the container based on the specified dependencies.

The current implementation uses PHP Reflection to build the container and supports the following types of dependencies:

The builder creates a dependency descriptor where the key for the dependency is the data type. If a parameter has a default value, the dependency is marked as optional. If there is no default value, the dependency is marked as required.

A dependency can have a complex data type defined through PHP UNION or INTERSECTION expressions. In this case, the container will look for the dependency using multiple type keys.

Descriptor Provider

You can override the behavior of the Builder by implementing the DescriptorProviderInterface. There are two ways to do this:

DependencyContract lookup logic

The DependencyContract attribute is inherited according to specific rules:

  1. First, DependencyContract is checked in the current class or interface.
  2. If it is not found, DependencyContract is checked in the FIRST inherited interface of the class.
  3. If it is still not found, DependencyContract is checked further across all first descendants of the interfaces.
  4. If it is not found there either, DependencyContract is checked for the child class, and the algorithm loops back.

In other words, the first interface has priority in the DependencyContract inheritance logic.

This inheritance algorithm is chosen to reduce the complexity for developers when searching for the DependencyContract.

Initialization through a constructor

Initialization through the constructor assumes that all dependencies are defined as constructor parameters. In this case, the builder uses the PHP Reflection API to read the constructor's parameter list and their data types.

Initialization through a method

For method-based injection, a special interface InjectableInterface and a trait InjectorTrait are used, which implement the injection method.

In this case, dependencies are described using attributes above the class properties. The class can be constructed before the dependencies are resolved.

Resolver

The Resolver component is responsible for the dependency resolution process. It handles finding dependencies of a dependency and initializing the dependency's class.

Complex use cases

Let's consider a realistic example of using the library. Our goal is to develop a telemetry component (Prometheus-like) that allows defining counters as dependencies.

In the example above, we define two dependencies of type TelemetryCounterInterface. TelemetryCounterInterface is a telemetry counter contract that increments over time.

The initialization of such a counter is performed in two stages:

Let's define the TelemetryCounterInterface interface:

Now we can define telemetry counters in Target classes, fully hiding their implementation details as well as the method of dependency resolution from the Target object.

Environment and Scope concepts

The library's design is oriented towards usage following the Environment and Scope paradigm. The Container component acts as the environment, defining and limiting the set of dependencies that will be interconnected.

An application can define multiple containers, managing which dependencies will be applied in each case. Using the override mechanism, containers can inherit or override each other's dependencies.

By managing the logic of the ContainerBuilder and Resolver, which are unique to each container, the developer can modify the dependency resolution logic in different contexts without altering the code of Target classes.


All versions of di with dependencies

PHP Build Version
Package Version
Requires php Version >=8.4
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package ifcastle/di contains the following files

Loading the files please wait ...