flyokai/composition

Composition helpers

Maintainers

Package info

github.com/flyokai/composition

pkg:composer/flyokai/composition

Statistics

Installs: 3

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

0.1.0 2026-04-25 18:01 UTC

This package is auto-updated.

Last update: 2026-05-04 22:35:52 UTC


README

User docs → README.md · Agent quick-ref → CLAUDE.md · Agent deep dive → AGENTS.md

Topological-sort helpers for declaring before, after, and depends relationships between modules and pipeline items.

A thin layer on top of marcj/topsort that gives Flyokai a single, consistent way to order things — modules at bootstrap, ACL tuners, indexers, setup steps, anywhere a list needs to be deterministic.

Features

  • sortComposition(array $items) — sorts items by before/after/depends declarations
  • castCompositionArgument(array $items, string $name, string $key) — sort + validate, returns ordered class names
  • assertCompositionKeys() — ensures every item has the keys it must
  • mergeDepends() — merge & dedupe dependency arrays
  • ArraySortGroup — extends MJS\TopSort\Implementations\ArraySort with grouping for parallel-safe execution

Installation

composer require flyokai/composition

Quick start

use function Flyokai\Composition\sortComposition;

$items = [
    'cache'  => ['className' => Cache::class,  'after' => 'config'],
    'config' => ['className' => Config::class],
    'db'     => ['className' => Db::class,     'depends' => 'config'],
    'http'   => ['className' => Http::class,   'before' => 'db'],
];

$sorted = sortComposition($items);

// Returns: ['http', 'config', 'cache', 'db'] — keys preserved, order corrected

Item shape

[
    'itemName' => [
        'className' => 'Full\\Class\\Name',
        'before'    => 'item2,item3',     // string or array
        'after'     => 'item1',           // string or array
        'depends'   => 'other1,other2',   // string or array
    ]
]
Mechanism Semantics
before "this item comes before X" → X depends on this item
after "this item comes after X" → this item depends on X
depends direct dependency declaration

before is intentionally inverted — 'before' => 'cache' means cache depends on the current item, not the other way.

Casting to ordered class names

use function Flyokai\Composition\castCompositionArgument;

$ordered = castCompositionArgument($items, 'modules', 'className');
// ['Http\\Class', 'Config\\Class', 'Cache\\Class', 'Db\\Class']

assertCompositionKeys($items, ['className', 'tag'], 'pipelines') enforces that every entry contains the listed keys after sorting.

Group sorting

use Flyokai\Composition\TopSort\ArraySortGroup;

$sorter = new ArraySortGroup();
$sorter->add('a', []);
$sorter->add('b', ['a']);
$sorter->add('c', []);
$sorter->add('d', ['b']);

$sorter->sorted();  // ['a', 'c', 'b', 'd']
$sorter->grouped(); // [['a', 'c'], ['b'], ['d']] — items in a group are independent

grouped() returns dependency cohorts — every item in a cohort can run in parallel without violating ordering.

Gotchas

  • before is inverted. 'before' => 'cache' declares cache as depending on this item.
  • Missing dependencies throwElementNotFoundException. No silent omission.
  • Cycles throwCircularDependencyException via parent tracking.
  • Adding elements after sorting invalidates the cached result; a full re-sort runs on next access.
  • Accepts 'a,b', ['a','b'], or empty string interchangeably (empties are filtered out).

See also

  • flyokai/application uses this for module ordering and DB setup steps.
  • amphp-injector ships its own CompositionOrdered for ordered DI compositions, built on the same primitives.

License

MIT