PHPackages                             dhii/services - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. dhii/services

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

dhii/services
=============

A collection of useful DI service implementations.

v0.1.0(4y ago)349.9k↑63.9%2[2 issues](https://github.com/Dhii/services/issues)[3 PRs](https://github.com/Dhii/services/pulls)MITPHPPHP ^7.1 | ^8.0CI passing

Since May 9Pushed 1mo ago2 watchersCompare

[ Source](https://github.com/Dhii/services)[ Packagist](https://packagist.org/packages/dhii/services)[ RSS](/packages/dhii-services/feed)WikiDiscussions 0.2.x Synced 1mo ago

READMEChangelog (6)Dependencies (5)Versions (16)Used By (0)

Dhii - Services
===============

[](#dhii---services)

[![Continuous Integration](https://github.com/Dhii/services/actions/workflows/ci.yml/badge.svg)](https://github.com/Dhii/services/actions/workflows/ci.yml)[![Latest Stable Version](https://camo.githubusercontent.com/ad9dbb6b935a689d6ed360b38ee21c9720eadbd6febbba31bdbc6ed75312d7cc/68747470733a2f2f706f7365722e707567782e6f72672f646869692f73657276696365732f76)](//packagist.org/packages/dhii/services)[![Latest Unstable Version](https://camo.githubusercontent.com/24e4a4a66c9b1130375f16308c1d0d0e65eda145f428f03d4eed3f8306595464/68747470733a2f2f706f7365722e707567782e6f72672f646869692f73657276696365732f762f756e737461626c65)](//packagist.org/packages/dhii/services)

This package provides a collection of service factory and extension definition implementations that can be used with [PSR-11 containers](https://github.com/php-fig/container), as well as the experimental [service provider spec](https://github.com/container-interop/service-provider), to replace the anonymous functions that are typically used for definitions.

---

- [Requirements](#requirements)
- [Installation](#installation)
- [Classes](#classes)
    - [`Factory`](#factory)
    - [`Extension`](#extension)
    - [`Constructor`](#constructor)
    - [`ServiceList`](#servicelist)
    - [`ArrayExtension`](#arrayextension)
    - [`FuncService`](#funcservice)
    - [`Others`](#others)
- [Mutation](#mutation)
- [Multi-Boxing](#multi-boxing)
- [Static Analysis](#static-analysis)

Requirements
============

[](#requirements)

- PHP &gt;= 8.1

Installation
============

[](#installation)

**With [Composer](https://getcomposer.org/)**:

```
composer require dhii/services

```

**Without Composer**:

1. Go [here](https://getcomposer.org/).
2. Install it.
3. See "With Composer"

Classes
=======

[](#classes)

All implementations in this package inherit from [`Service`](src/Service.php); an [invocable](https://www.php.net/manual/en/language.oop5.magic.php#object.invoke) object with a [`getDependencies`](https://github.com/Dhii/services/blob/initial-version/src/Service.php#L49) method that returns an array of keys.

[`Factory`](src/Factory.php)
----------------------------

[](#factory)

A simple implementation that uses a callback to construct its service.

Unlike a normal anonymous function, the callback given to the `Factory` does not get a `ContainerInterface` argument, but rather the services that match the given dependency keys. This allows omitting a lot of trivial service retrieval code, and most importantly type-hinting the services:

```
new Factory(['dep1', 'dep2'], function(int $dep1, SomeInterface $dep2) {
  // ...
});
```

Roughly equivalent to:

```
function (ContainerInterface $c) {
  $dep1 = $c->get('dep1');
  if (!is_int($dep1)) {
    throw new TypeError(sprintf('Parameter $dep1 must be of type int; %1$s given', is_object($dep1) ? get_class($dep1) : gettype($dep1));
  }

  $dep2 = $c->get('dep2');
  if (!($dep2 instanceof SomeInterface)) {
    throw new TypeError(sprintf('Parameter $dep2 must be of type int; %1$s given', is_object($dep2) ? get_class($dep2) : gettype($dep2));
  }
  // ...
}
```

This is true for all implementations that use a similar mechanic of passing already resolved services to other definitions. Therefore, type-checking code will be henceforth omitted for brevity.

[`Extension`](src/Extension.php)
--------------------------------

[](#extension)

Very similar to `Factory`, but the callback also receives the service instance from the original factory or previous extension as the first argument.

```
new Extension(['dep1', 'dep2'], function($prev, $dep1, $dep2) {
  // ...
});
```

Equivalent to:

```
function (ContainerInterface $c, $prev) {
  $dep1 = $c->get('dep1');
  $dep2 = $c->get('dep2');
  // ...
}
```

[`Constructor`](src/Factories/Constructor.php)
----------------------------------------------

[](#constructor)

A variant of `Factory` that invokes a constructor, rather than a callback function. Very useful in cases where a class is only constructed using other services.

```
new Constructor(MyClass::class, ['dep1', 'dep2']);
```

Equivalent to:

```
function (ContainerInterface $c) {
  $dep1 = $c->get('dep1');
  $dep2 = $c->get('dep2');

  return new MyClass($dep1, $dep2);
}
```

Consequently, it also works without any dependencies, which is useful when the constructor is parameterless:

```
new Constructor(MyClass::class);
```

Equivalent to:

```
function (ContainerInterface $c) {
  return new MyClass();
}
```

[`ServiceList`](src/Factories/ServiceList.php)
----------------------------------------------

[](#servicelist)

Creates an array that contains the services indicated by its dependencies. Very useful for managing registration of instances when coupled with [`ArrayExtension`](src/Extensions/ArrayExtension.php).

```
new ServiceList(['service1', 'service2']);
```

Equivalent to:

```
function (ContainerInterface $c) {
  return [
    $c->get('service1'),
    $c->get('service2'),
  ];
}
```

[`ArrayExtension`](src/Extensions/ArrayExtension.php)
-----------------------------------------------------

[](#arrayextension)

An extension implementation that adds its dependencies to the previous value. Very useful for registering new instances to a list.

```
new ArrayExtension(['dep1', 'dep2'])
```

Equivalent to:

```
function (ContainerInterface $c, array $prev) {
  return array_merge($prev, [
    $c->get('dep1'),
    $c->get('dep2'),
  ]);
}
```

[`FuncService`](src/Factories/FuncService.php)
----------------------------------------------

[](#funcservice)

A variant of `Factory`, but it returns the callback rather than invoking it. Invocation arguments will be passed before the injected dependencies. Very useful for declaring callback services.

```
new FuncService(['dep1', 'dep2'], function($arg1, $arg2, $dep1, $dep2) {
  // ...
});
```

Equivalent to:

```
function (ContainerInterface $c) {
  $dep1 = $c->get('dep1');
  $dep2 = $c->get('dep2');

  return function ($arg1, $arg2) use ($dep1, $dep2) {
    // ...
  };
}
```

Others
------

[](#others)

- [`StringService`](src/Factory/StringService.php) - For services that return strings that are interpolated with other services.
- [`Value`](src/Factories/Value.php) - For services that always return a static value.
- [`Alias`](src/Factory/Alias.php) - An alias for another service, with defaulting capabilities for when the original does not exist.
- [`GlobalVar`](src/Factory/GlobalVar.php) - For services that return global variables.

Mutation
========

[](#mutation)

The [`withDependencies()`](https://github.com/Dhii/services/blob/initial-version/src/Service.php#L63) method allows all service instances to be copied with different dependencies, while leaving the original instances unaffected.

```
$service = new Factory(['database'], function ($database) {
  // ...
});

$service2 = $service->withDependencies(['db']);
```

This makes it possible to modify service dependencies at run-time, or even during a build process, which can be especially useful when dealing with 3rd party service providers that need to be rewired.

Multi-boxing
============

[](#multi-boxing)

One of the benefits of being able to derive new services with different dependencies is the ability to use the same provider multiple times. Let's look at an example.

Consider a service provider for a logger that writes to a file.

```
class LoggerProvider implements ServiceProviderInterface
{
  public function getFactories()
  {
    return [
      'logger' => new Constructor(FileLogger::class, ['file_path']),
      'file_path' => new Value(sys_get_tmp_dir() . '/log.txt')
    ];
  }

  public function getExtensions ()
  {
    return [];
  }
}
```

Our application needs to keep 2 different log files: one for errors and one for debugging.

Simply using the above service provider twice won't work; we'd be re-declaring the `logger` and `file_path` services.

Prefixing the factories *would* allow us to have two instances of the service provider, but that would break the dependencies. If we prefixed the factories from one logger such that they become `debug_logger` and `debug_file_path`, the `debug_logger` factory would still be depending on `file_path`, which would no longer exist after prefixing.

This where mutation of dependencies comes in. We can write a `PrefixingProvider` decorator that not only prefixes all services in a provider, but also prefixes any dependencies.

*(The below class is incomplete for the sake of brevity. Assume that a constructor exists and that it initializes its `$prefix` and `$provider` properties)*.

```
class PrefixingProvider implements ServiceProviderInterface {
  public function getFactories() {
    $factories = [];

    foreach ($this->provider->getFactories() as $key => $factory) {
      $deps = $factory->getDependencies();
      $newDeps = array_map(fn($dep) => $this->prefix . $dep, $deps);

      $factories[$this->prefix . $key] = $factory->withDependencies($newDeps);
    }

    return $factories;
  }
}
```

We can now create two different versions of the same service provider:

```
$debugLogProvider = new PrefixingProvider('debug_', new LoggerProvider);
$errorLogProvider = new PrefixingProvider('error_', new LoggerProvider);
```

The first one will provide `debug_logger` and `debug_file_path`, while the second will provide `error_logger` and `error_file_path`.

Static Analysis
===============

[](#static-analysis)

By having all services declare their dependencies, we open up the possibility to create an inspection tool that statically analyzes a list of services to build a dependency graph. This graph can help uncover various potential problems without needing to run the code. These insights can reveal:

- Circular dependency
- Dependencies that do not exist
- Factories that override each other, rather than using extensions
- Dependency chains that are too deep
- Unused services

No such tool exists at the time of writing, but I *do* plan on taking on this task.

###  Health Score

41

—

FairBetter than 89% of packages

Maintenance45

Moderate activity, may be stable

Popularity34

Limited adoption so far

Community13

Small or concentrated contributor base

Maturity58

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 82.3% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~261 days

Recently: every ~384 days

Total

8

Last Release

58d ago

PHP version history (3 changes)v0.1.0-alpha1PHP ^7.0

v0.1.0-alpha2PHP ^7.1 | ^8.0

0.2.x-devPHP ^8.1

### Community

Maintainers

![](https://www.gravatar.com/avatar/3a5e9c220209499fbe84cad3f5c6aac842e76bd9bf498445e0a317b94f461903?d=identicon)[mecha](/maintainers/mecha)

---

Top Contributors

[![XedinUnknown](https://avatars.githubusercontent.com/u/1428973?v=4)](https://github.com/XedinUnknown "XedinUnknown (116 commits)")[![mecha](https://avatars.githubusercontent.com/u/5425482?v=4)](https://github.com/mecha "mecha (24 commits)")[![Biont](https://avatars.githubusercontent.com/u/4208996?v=4)](https://github.com/Biont "Biont (1 commits)")

###  Code Quality

TestsPHPUnit

Static AnalysisPsalm

Type Coverage Yes

### Embed Badge

![Health badge](/badges/dhii-services/health.svg)

```
[![Health](https://phpackages.com/badges/dhii-services/health.svg)](https://phpackages.com/packages/dhii-services)
```

###  Alternatives

[symfony/dependency-injection

Allows you to standardize and centralize the way objects are constructed in your application

4.2k431.1M7.5k](/packages/symfony-dependency-injection)[illuminate/contracts

The Illuminate Contracts package.

704122.9M10.1k](/packages/illuminate-contracts)[illuminate/container

The Illuminate Container package.

31278.1M2.0k](/packages/illuminate-container)[ecotone/ecotone

Supporting you in building DDD, CQRS, Event Sourcing applications with ease.

558549.8k17](/packages/ecotone-ecotone)[civicrm/civicrm-core

Open source constituent relationship management for non-profits, NGOs and advocacy organizations.

728272.9k20](/packages/civicrm-civicrm-core)[internal/dload

Downloads binaries.

98142.7k10](/packages/internal-dload)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
