Search by

lekoala / kaly-di

lekoala

Package info

github.com/lekoala/kaly-di

pkg:composer/lekoala/kaly-di

Statistics

Installs: 82

Dependents: 1

Suggesters: 0

Stars: 0

Open Issues: 0

0.2.0 2026-09-13 05:29 UTC

This package is auto-updated.

Last update: 2026-09-13 05:34:04 UTC


README

Latest Version Total Downloads License PHP Version Require

Small PSR-11 autowiring container for PHP 8.3+

Kaly DI is a lightweight dependency injection container built around a strict separation of concerns:

  • Definitions — Kaly-specific configuration, used at the composition root.
  • Container — a PSR-11 container. At runtime, the only API is get()/has().
  • Injector — an independent utility for building fresh instances and invoking callables.

Application code never needs to depend on a proprietary container API.

Key Features

  • PSR-11 Compliance: interoperable with PHP standards.
  • No Attributes, No Magic: plain PHP configuration, no attributes or compilation.
  • Strongly Typed Definitions: define dependencies in PHP for full IDE support.
  • Autowiring: concrete classes are resolved automatically; bind interfaces when needed.
  • Exact has(): true only for internal entries, explicit definitions/bindings, or instantiable concrete classes.
  • Explicit Lifecycle: Container::get() returns shared services, Injector::make() instantiates fresh concrete classes.
  • Developer Friendly: typed error reporting and development-only assertions.

Installation

composer require lekoala/kaly-di

Quick Start

use Kaly\Di\Container;
use Kaly\Di\Definitions;
use Kaly\Di\Injector;

// 1. Configure the graph at the composition root
$definitions = Definitions::create()
    ->set(\PDO::class, fn () => new \PDO('sqlite::memory:'))
    ->bind(LoggerInterface::class, FileLogger::class);

// 2. Create the container
$container = new Container($definitions);

// 3. Resolve services (shared)
$pdo = $container->get(\PDO::class);
$logger = $container->get(LoggerInterface::class);

// 4. Build fresh instances with the Injector
$injector = new Injector($container);
$fresh = $injector->make(MyService::class);

get() resolves services, make() instantiates classes

$container->get(Foo::class);   // shared instance, configured by Definitions
$injector->make(Foo::class);   // fresh concrete instance, independent of Definitions
  • Container::get() resolves configured container entries (definitions, bindings, objects, factories). Entries are shared: two calls with the same id return the same object.
  • Injector::make() instantiates a concrete class independently of container definitions. It uses PSR-11 only to resolve the object dependencies of that class. An interface or abstract class cannot be built with make().

Documentation

Detailed guides are available in the docs/ directory:

  • Definitions: bindings, parameters, callbacks and merging.
  • Injector: building fresh instances and invoking callables.
  • Architecture: design decisions and the PSR-11 boundary.

Reflection Helpers

Pure, dependency-free utilities live in Kaly\Di\Reflection:

use Kaly\Di\Reflection;

Reflection::getShortClassName($object); // e.g. "MyService"
Reflection::getClassNamespace(MyService::class); // e.g. "App\Service"
Reflection::getParameterClass($reflectionParameter); // ?ReflectionClass

Parameter resolution (Parameters::resolveParameters(), valueMatchType(), flattenArguments()) is the internal engine of Container/Injector and is deliberately not part of the public API: unlike the legacy permissive resolver, it never invents ''/0/false/[] defaults and throws UnresolvableParameterException for required parameters that cannot be satisfied.

A Note on Assertions

Kaly DI distinguishes runtime guarantees from development-time validation:

  • Runtime guarantees (locking, the reserved ContainerInterface id) throw real exceptions and therefore always hold, even in production.
  • Development-time validation (class existence, binding compatibility, argument types) uses PHP assert(). These checks run in development (zend.assertions = 1) but are disabled in production (zend.assertions = -1) for zero overhead.

Ensure your test suite covers your DI configuration to catch mistakes before deployment.

Examples and Testing

Check the unit tests for comprehensive usage examples covering all features.