PHPackages                             tommyknocker/chain - 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. tommyknocker/chain

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

tommyknocker/chain
==================

Professional-grade fluent method chaining library with advanced features: conditional execution, timeout protection, error handling, extension system, and PSR-11 container integration.

v1.2.0(8mo ago)18MITPHPPHP ^8.0CI passing

Since Nov 15Pushed 8mo ago1 watchersCompare

[ Source](https://github.com/tommyknocker/chain)[ Packagist](https://packagist.org/packages/tommyknocker/chain)[ RSS](/packages/tommyknocker-chain/feed)WikiDiscussions master Synced 2d ago

READMEChangelog (1)Dependencies (4)Versions (7)Used By (0)

Chain
=====

[](#chain)

[![CI](https://github.com/tommyknocker/chain/actions/workflows/ci.yml/badge.svg)](https://github.com/tommyknocker/chain/actions)[![License: MIT](https://camo.githubusercontent.com/7013272bd27ece47364536a221edb554cd69683b68a46fc0ee96881174c4214c/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d626c75652e737667)](LICENSE)[![PHP Version](https://camo.githubusercontent.com/4223e9c84cef1248f4f014965931832e9c2079d3b772bfddc72b69964a2587d1/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048502d382e302b2d3737376262332e737667)](https://www.php.net/)[![PHPStan Level 8](https://camo.githubusercontent.com/d117944b58da8146f96b4ef7403807610a20eeb3fbcaaaf95157bbcdad1686eb/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048505374616e2d6c6576656c253230382d627269676874677265656e2e737667)](https://phpstan.org/)

Fluent method chaining &amp; context switching utility for PHP. Call methods across objects, transform or branch chains, and conditionally execute logic in a concise style.

Quick Example
-------------

[](#quick-example)

```
use tommyknocker\chain\Chain;

// Process user data through multiple transformations
$orderTotal = Chain::of(new User('Alice', 25))
    ->setEmail('alice@example.com')
    ->tap(fn($u) => logger()->info("Processing order for: " . $u->getName()))
    ->map(fn($user) => new Order(strlen($user->getName()) * 10))
    ->when(
        fn($order) => $order->getTotal() > 30,
        fn($chain) => $chain->applyDiscount(5)
    )
    ->getTotal()
    ->get();

echo $orderTotal; // 45 (50 - 5 discount)
```

Installation
------------

[](#installation)

```
composer require tommyknocker/chain
```

Core Concepts
-------------

[](#core-concepts)

- Start with `Chain::of($object)` or `Chain::of(ClassName::class, ...$args)`
- Every method call stays fluent; if a method returns an object, the chain context switches to it automatically
- Use `change($idOrObject)` to jump to another object (resolved via PSR-11 container if configured)
- Use `get()` to read the last result (or the current instance if there is no last result)

API Overview
------------

[](#api-overview)

**Core Methods:**

- `of(string|object $target, ...$args): Chain` - Start a chain from an object or instantiate a class
- `get(): mixed` - Get final result
- `value(): mixed` - Alias for get(), more semantic
- `instance(): object` - Get current wrapped object

**Transformation:**

- `tap(callable $fn): Chain` - Execute side effects
- `map(callable $fn): Chain` - Transform to another object
- `pipe(callable ...$pipes): Chain` - Functional pipeline

**Enhanced Control Flow:**

- `whenAll(callable ...$conditions): Chain` - Execute when ALL conditions are true
- `whenAny(callable ...$conditions): Chain` - Execute when ANY condition is true
- `whenNone(callable ...$conditions): Chain` - Execute when NO conditions are true
- `when(bool|callable $cond, callable $cb, ?callable $else = null): Chain` - Conditional execution
- `unless(bool|callable $cond, callable $cb, ?callable $else = null): Chain` - Inverse conditional
- `clone(): Chain` - Branch immutably

**Resilience:**

- `rescue(callable $callback, callable $handler): Chain` - Handle exceptions with fallback
- `catch(string $exceptionClass, callable $callback, callable $handler): Chain` - Catch specific exceptions
- `retry(int $times, callable $callback, int $delayMs = 0): Chain` - Retry with backoff
- `timeout(int $seconds, callable $callback): Chain` - Timeout protection

**Iteration &amp; Debugging:**

- `each(callable $fn): Chain` - Iterate over collections
- `dump(string $label = ''): Chain` - Debug output, continues chain
- `dd(string $label = ''): never` - Dump and die

**Container Integration:**

- `change(string|object $target): Chain` - Switch to another object (PSR-11)

**Configuration &amp; Extensions:**

- `Chain::configure(ChainConfig $config): void` - Configure Chain behavior
- `addExtension(ChainExtensionInterface $extension): Chain` - Add extension for monitoring/logging

Examples
--------

[](#examples)

### Basic Chaining

[](#basic-chaining)

```
// Create instance and chain
$result = Chain::of(new User('Alice', 25))
    ->getName()
    ->map(fn($user) => new Order(strlen($user->getName()) * 10))
    ->getTotal()
    ->get();

// Or instantiate via of()
$result = Chain::of(StringBuilder::class, 'Hello')
    ->append(' World')
    ->uppercase()
    ->toString()
    ->get();
```

### Conditional Logic

[](#conditional-logic)

```
// Smart banking: apply bonus for high-value accounts, charge fees for low balances
$finalBalance = Chain::of(new Account())
    ->deposit(500)
    ->when(
        fn($acc) => $acc->getBalance() > 300,
        fn($chain) => $chain->addBonus()  // +100 bonus
    )
    ->unless(
        fn($acc) => $acc->getBalance() < 100,
        fn($chain) => $chain->withdraw(50)  // maintenance fee
    )
    ->getBalance()
    ->get();

echo $finalBalance; // 550 (500 + 100 bonus - 50 fee)
```

### Enhanced Conditional Logic

[](#enhanced-conditional-logic)

```
// Multiple condition checking
$result = Chain::of(new User('Alice', 25))
    ->whenAll(
        fn($u) => $u->isAdult(),
        fn($u) => strlen($u->getName()) > 3,
        fn($u) => $u->getAge() < 50
    )
    ->tap(fn($u) => $u->setEmail('alice@example.com'))
    ->getEmail()
    ->get();

// Any condition can be true
$result = Chain::of(new User('Bob', 16))
    ->whenAny(
        fn($u) => $u->isAdult(),
        fn($u) => $u->getAge() > 15,
        fn($u) => strlen($u->getName()) > 2
    )
    ->tap(fn($u) => $u->addRole('verified'))
    ->getRoles()
    ->get();

// No conditions should be true
$result = Chain::of(new User('Charlie', 25))
    ->whenNone(
        fn($u) => $u->getAge() > 30,
        fn($u) => $u->getAge() < 18,
        fn($u) => strlen($u->getName()) < 3
    )
    ->tap(fn($u) => $u->addRole('special'))
    ->getRoles()
    ->get();
```

```
// Complex order processing with business rules
$total = Chain::of(new Order(100.0))
    ->when(
        fn($order) => $order->getTotal() > 50,
        fn($chain) => $chain->applyDiscount(10)  // $10 off for orders > $50
    )
    ->unless(
        fn($order) => $order->getTotal() < 20,
        fn($chain) => $chain->addTax(0.08)  // 8% tax unless small order
    )
    ->getTotal()
    ->get();

echo $total; // 97.20 (100 - 10 discount + 8% tax on 90)
```

### Pipeline

[](#pipeline)

```
// Calculate price with multiple transformations
$finalPrice = Chain::of(new Calculator(100))
    ->subtract(20)  // Apply discount
    ->multiply(1.08)  // Add 8% tax
    ->pipe(
        fn($c) => $c->getValue(),
        fn($v) => round($v, 2),  // Round to 2 decimals
        fn($v) => max($v, 0)  // Ensure non-negative
    )
    ->get();

echo $finalPrice; // 86.4
```

### Pipelines with pipe()

[](#pipelines-with-pipe)

```
// Text processing pipeline for user input sanitization
$sanitized = Chain::of(new StringBuilder('  Hello@World123  '))
    ->pipe(
        fn($b) => $b->toString(),
        fn($text) => trim($text),
        fn($text) => strtolower($text),
        fn($text) => preg_replace('/[^a-z0-9]/', '', $text)
    )
    ->get();

echo $sanitized; // 'helloworld123'
```

### Tap for Side Effects

[](#tap-for-side-effects)

```
// Using tap for logging without breaking the chain
$logger = Chain::of(new Logger())
    ->log('Starting process')
    ->log('Loading data')
    ->tap(fn($l) => print("Current logs: " . $l->count() . "\n"))
    ->log('Processing')
    ->tap(fn($l) => print("Logs so far: " . implode(', ', $l->getLogs()) . "\n"))
    ->log('Completed')
    ->instance();

echo "Total logs: " . $logger->count() . "\n";
```

### Branching

[](#branching)

```
// Calculate different pricing scenarios from same base
$baseCalc = Chain::of(new Calculator(100));

$retailPrice = $baseCalc->clone()->multiply(1.5)->getValue()->get();  // 150
$wholesalePrice = $baseCalc->clone()->multiply(1.2)->getValue()->get();  // 120
$memberPrice = $baseCalc->clone()->multiply(0.9)->getValue()->get();  // 90
```

### Immutable Branching with clone()

[](#immutable-branching-with-clone)

```
// Explore different account scenarios without mutating original
$baseAccount = Chain::of(new Account());

$scenario1 = $baseAccount->clone()
    ->deposit(1000)
    ->withdraw(200)
    ->getBalance()->get();  // 800

$scenario2 = $baseAccount->clone()
    ->deposit(500)
    ->addBonus()
    ->getBalance()->get();  // 600 (500 + 100 bonus)

$original = $baseAccount->getBalance()->get();  // 0 (unchanged)
```

### Composite Scenario

[](#composite-scenario)

```
// Complex data processing workflow with multiple stages
$report = Chain::of(new DataProcessor())
    ->addItem(100)
    ->addItem(250)
    ->addItem(75)
    ->addItem(300)
    ->tap(fn($p) => logger()->info("Processing " . $p->count() . " items"))
    ->filter(fn($x) => $x >= 100)  // Only items >= 100
    ->transform(fn($x) => $x * 1.08)  // Add 8% markup
    ->pipe(
        fn($p) => ['total' => $p->sum(), 'count' => $p->count()],
        fn($stats) => new Report($stats['total'], $stats['count']),
        fn($report) => $report->format()
    )
    ->get();

echo $report; // "Total: 702.00, Count: 3, Average: 234.00"
```

### Timeout Protection

[](#timeout-protection)

```
// Protect against slow operations
try {
    $result = Chain::of(new Calculator(10))
        ->timeout(2, function ($calc) {
            // Simulate slow operation
            usleep(1000000); // 1 second
            return $calc->add(5);
        })
        ->getValue()
        ->get();

    echo "Result: $result\n";
} catch (\tommyknocker\chain\Exception\ChainTimeoutException $e) {
    echo "Operation timed out: " . $e->getMessage() . "\n";
}
```

### Configuration &amp; Extensions

[](#configuration--extensions)

```
// Configure Chain behavior
Chain::configure(ChainConfig::performance());

// Add monitoring extension
class LoggingExtension implements ChainExtensionInterface
{
    private array $logs = [];

    public function beforeMethodCall(string $method, array $args): void
    {
        $this->logs[] = "Before: {$method}";
    }

    public function afterMethodCall(string $method, mixed $result): void
    {
        $this->logs[] = "After: {$method}";
    }

    public function getLogs(): array
    {
        return $this->logs;
    }
}

$logger = new LoggingExtension();

$result = Chain::of(new Calculator(10))
    ->addExtension($logger)
    ->add(5)
    ->multiply(2)
    ->getValue()
    ->get();

// Check logs
foreach ($logger->getLogs() as $log) {
    echo "$log\n";
}
```

```
// PSR-11 Container integration with change()
$container = new SimpleContainer();
$container->set('email', new EmailService());
$container->set('notification', new NotificationService());

Chain::setResolver($container);

// Switch between services dynamically
$result1 = Chain::of($container->get('email'))
    ->send('user@example.com')
    ->get();

$result2 = Chain::of($container->get('email'))
    ->change('notification')  // Switch to notification service
    ->notify('Important update')
    ->get();

echo "$result1\n";  // "Email sent to user@example.com"
echo "$result2\n";  // "Notification: Important update"
```

Development
-----------

[](#development)

### Installation

[](#installation-1)

```
composer install
```

### Testing

[](#testing)

```
composer test              # Run all tests
composer test:coverage     # Run tests with coverage report
composer test:ci          # Run tests for CI (with JUnit output)
```

### Code Quality

[](#code-quality)

```
composer phpstan          # Static analysis
composer phpstan:baseline # Generate PHPStan baseline
composer cs:fix           # Fix code style issues
composer cs:check         # Check code style (dry-run)
composer quality          # Run all quality checks
composer quality:fix      # Run quality checks and fix issues
```

### Examples

[](#examples-1)

```
composer examples         # Test all examples
```

### Release Management

[](#release-management)

```
composer release          # Create new release
```

Examples
--------

[](#examples-2)

See the [`examples/`](examples/) directory for working examples:

- [`workflow.php`](examples/workflow.php) - **Complete workflow** with User→Profile context switching
- [`conditionals.php`](examples/conditionals.php) - Conditional execution with when/unless
- [`branching.php`](examples/branching.php) - Clone chains for independent branches
- [`pipeline.php`](examples/pipeline.php) - Functional pipelines with pipe()
- [`processing.php`](examples/processing.php) - Data processing with each(), dump(), value()
- [`resilience.php`](examples/resilience.php) - Error handling with rescue(), catch(), retry()
- [`container.php`](examples/container.php) - PSR-11 container integration
- [`advanced-features.php`](examples/advanced-features.php) - **NEW!** All enhanced features demo

Run examples:

```
php examples/advanced-features.php  # Run specific example
composer examples                   # Test all examples
```

License
-------

[](#license)

MIT. See LICENSE file.

Changelog
---------

[](#changelog)

See [CHANGELOG.md](CHANGELOG.md) for a list of changes and version history.

###  Health Score

39

—

LowBetter than 85% of packages

Maintenance61

Regular maintenance activity

Popularity6

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity71

Established project with proven stability

 Bus Factor1

Top contributor holds 100% 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 ~507 days

Recently: every ~4 days

Total

6

Last Release

249d ago

Major Versions

0.0.1 → v1.0.02025-10-09

PHP version history (2 changes)v1.0.0PHP ^8.4

v1.0.1PHP ^8.0

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/7640707?v=4)[Vasiliy Krivoplyas](/maintainers/tommyknocker)[@tommyknocker](https://github.com/tommyknocker)

---

Top Contributors

[![tommyknocker](https://avatars.githubusercontent.com/u/7640707?v=4)](https://github.com/tommyknocker "tommyknocker (15 commits)")

---

Tags

chainingphpwrapperwrapperfluentchainingChainfluent-interfacemethod-chainingobject-wrapper

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/tommyknocker-chain/health.svg)

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

###  Alternatives

[symfony/dependency-injection

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

4.2k455.6M9.3k](/packages/symfony-dependency-injection)[illuminate/contracts

The Illuminate Contracts package.

706130.3M12.9k](/packages/illuminate-contracts)[illuminate/container

The Illuminate Container package.

31182.0M2.3k](/packages/illuminate-container)[ecotone/ecotone

Enterprise architecture layer for Laravel and Symfony — CQRS, Event Sourcing, Durable Workflows (Sagas, Orchestrators), Projections, and Outbox messaging via PHP attributes.

564576.7k48](/packages/ecotone-ecotone)[symfony/type-info

Extracts PHP types information.

20069.8M263](/packages/symfony-type-info)[civicrm/civicrm-core

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

751291.4k39](/packages/civicrm-civicrm-core)

PHPackages © 2026

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