PHPackages                             caseyamcl/sortable-tasks - 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. caseyamcl/sortable-tasks

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

caseyamcl/sortable-tasks
========================

Abstraction to compile and sort tasks on the fly

v0.6.1(5y ago)2271[1 PRs](https://github.com/caseyamcl/sortable-tasks/pulls)MITPHPPHP ^7.4|^8.0

Since Oct 31Pushed 5y ago1 watchersCompare

[ Source](https://github.com/caseyamcl/sortable-tasks)[ Packagist](https://packagist.org/packages/caseyamcl/sortable-tasks)[ Docs](https://github.com/caseyamcl/sortable-tasks)[ RSS](/packages/caseyamcl-sortable-tasks/feed)WikiDiscussions main Synced today

READMEChangelog (7)Dependencies (3)Versions (10)Used By (0)

Sortable Tasks
==============

[](#sortable-tasks)

[![Latest Version on Packagist](https://camo.githubusercontent.com/1667fdd96356af82a48c207b093c38e02e28c715760ade257577917c4c49da6b/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6361736579616d636c2f736f727461626c652d7461736b732e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/caseyamcl/sortable-tasks)[![Software License](https://camo.githubusercontent.com/55c0218c8f8009f06ad4ddae837ddd05301481fcf0dff8e0ed9dadda8780713e/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d627269676874677265656e2e7376673f7374796c653d666c61742d737175617265)](LICENSE.md)[![Build](https://github.com/caseyamcl/sortable-tasks/workflows/Build/badge.svg)](https://github.com/caseyamcl/sortable-tasks/workflows/Build/badge.svg)[![Coverage Status](https://camo.githubusercontent.com/c84f6c368cc33cafc93d995e9a531e24bedeaba45e0d19e91891cb94e62b5743/68747470733a2f2f696d672e736869656c64732e696f2f7363727574696e697a65722f636f7665726167652f672f6361736579616d636c2f736f727461626c652d7461736b732e7376673f7374796c653d666c61742d737175617265)](https://scrutinizer-ci.com/g/caseyamcl/sortable-tasks/code-structure)[![Quality Score](https://camo.githubusercontent.com/b32928a2fd5e6947b28288e18b1476dbb85a89b19cded34ed03df45091d2289b/68747470733a2f2f696d672e736869656c64732e696f2f7363727574696e697a65722f672f6361736579616d636c2f736f727461626c652d7461736b732e7376673f7374796c653d666c61742d737175617265)](https://scrutinizer-ci.com/g/caseyamcl/sortable-tasks)[![Total Downloads](https://camo.githubusercontent.com/3b2a23af63aa28dd0ef8b9fd3b0b170033ecd12f7e802c08b3a3ed8b4a6348a3/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f6361736579616d636c2f736f727461626c652d7461736b732e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/caseyamcl/sortable-tasks)

A simple, un-opinionated PHP 7.4+ abstraction library to allow for the ordering of tasks. Features:

- Tasks are service class instances that can define other tasks as dependencies
- Useful for libraries like setup routines, ensuring that HTTP middleware runs in-order, and other libraries where the ordering of tasks is determined in an arbitrary order, but need to be run deterministically
- Builds on the [`marcj/topsort` library](https://packagist.org/packages/marcj/topsort) to enable sorting of tasks, each defined in their own class
- Un-opinionated about how tasks actually run; just concerned with sorting them based on their dependency and running them in-order
- PSR-4 and PSR-12 compliant

Structure
---------

[](#structure)

This library only contains two files:

| `SortableTask.php` | Interface for a task; contains two methods to define dependencies, `depeondsOn` and `mustRunBefore` | | `SortableTasksIterator.php` | Iterator class that does the sorting and other work; implements `Iterable` and `Countable` |

Install
-------

[](#install)

Via Composer

```
$ composer require caseyamcl/sortable-tasks
```

Usage
-----

[](#usage)

> Refer to the `tests/Fixture` directory for a fully functional example.

The best way to demonstrate this library is with an example, so we'll use a setup application. In our hypothetical application, setup tasks can be registered in any order, but they will run in a particular order based on a set of explicitly defined dependencies.

First, we must define a class that implements the `SortableTask` interface:

```
use SortableTasks\SortableTask;

abstract class SetupStep implements SortableTask
{
    abstract public function __invoke(): bool;
}

```

Notice that we do not implement the `dependsOn()` and `mustRunBefore()` methods in the abstract `SetupStep` class. This means that each concrete implementation step must define its dependencies. This is optional, and is up to the library that implements SortableTasks.

If most steps do not require ordering, then we could do the following:

```
use SortableTasks\SortableTask;

abstract class SetupStep implements SortableTask
{
    abstract public function __invoke(): bool;

    // Provide default implementations of `dependsOn` and `mustRunBefore` that return empty arrays
    public static function dependsOn() : iterable
    {
        return [];
    }
    public static function mustRunBefore() : iterable
    {
        return [];
    }
}
```

Let's create some setup steps now:

```
class CheckConfigStep extends SetupStep
{
    public static function dependsOn(): iterable
    {
       return []; // depends on nothing; can run anytime in the order of operations
    }

    public function __invoke(): bool
    {
        // do stuff, then..
        return true;
    }
}

class CheckDbConnectionStep extends SetupStep
{
    private DbConnector $dbConnector;

    public function __construct(DbConnector $dbConnector)
    {
        $this->dbConnector = $dbConnector;
    }

    public static function dependsOn(): iterable
    {
        return [CheckConfigStep::class];
    }

    public static function mustRunBefore(): iterable
    {
        return [BuildContainerStep::class];
    }

    public function __invoke(): bool
    {
        // do stuff, then..
        return $this->dbConnector->checkConnection();
    }
}

class BuildContainerStep extends SetupStep
{
    private ContainerBuilder $containerBuilder;

    public function __construct(ContainerBuilder $containerBuilder)
    {
        $this->containerBuilder = $containerBuilder;
    }

    public static function dependsOn(): iterable
    {
        return [CheckConfigStep::class, CheckDbConnection::class];
    }

    public function __invoke(): bool
    {
        // do stuff, then..
        return $this->containerBuilder->buildContainer();
    }
}
```

Now that we have some concrete classes, let's add them to a SortableTasksIterator:

```
use SortableTasks\SortableTasksIterator;

$iterator = new SortableTasksIterator();

// Notice that it doesn't matter in what order we add the steps; they will get sorted at runtime
$iterator->add(new BuildContainerStep());
$iterator->add(new CheckDbConnectionStep());
$iterator->add(new CheckConfigStep());

// Tasks are sorted upon calling the iterator
// Class names are the keys
foreach ($iterator as $setupStepClassName => $setupStep) {
    if (! $setupStep()->__invoke()) {
        throw new SetupFailedException('Setup failed on step: ' . $setupStepClassName);
    }
}
```

### Handling errors

[](#handling-errors)

There are two issues that are likely to occur during task execution, both of them throw excpetions:

1. Circular dependencies; e.g., Task "A" depends on Task "B", which depends on Task "A". In this situation, a `MJS\TopSort\CircularDependecyException` is thrown.
2. Non-existent dependencies; e.g., Task "A" depends on Task "B", but task "B" is not defined. In this situation, a `MJS\TopSort\ElementNotFoundException` is thrown.

Change log
----------

[](#change-log)

Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.

Testing
-------

[](#testing)

```
$ composer test
```

Contributing
------------

[](#contributing)

Please see [CONTRIBUTING](CONTRIBUTING.md) for details.

Security
--------

[](#security)

If you discover any security related issues, please email  instead of using the issue tracker.

Credits
-------

[](#credits)

- [Casey McLaughlin](https://github.com/caseyamcl)
- [All Contributors](../../contributors)

License
-------

[](#license)

The MIT License (MIT). Please see [License File](LICENSE.md) for more information.

###  Health Score

26

—

LowBetter than 43% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity11

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity54

Maturing project, gaining track record

 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 ~17 days

Recently: every ~29 days

Total

8

Last Release

1894d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/8db51cd614310a5de4822be9602de578e6d6dd2af7949d52fcd375ba2a8d1c74?d=identicon)[caseyamcl](/maintainers/caseyamcl)

---

Top Contributors

[![caseyamcl](https://avatars.githubusercontent.com/u/53035?v=4)](https://github.com/caseyamcl "caseyamcl (29 commits)")

---

Tags

caseyamclsortable-tasks

###  Code Quality

TestsPHPUnit

Code StylePHP\_CodeSniffer

### Embed Badge

![Health badge](/badges/caseyamcl-sortable-tasks/health.svg)

```
[![Health](https://phpackages.com/badges/caseyamcl-sortable-tasks/health.svg)](https://phpackages.com/packages/caseyamcl-sortable-tasks)
```

###  Alternatives

[civicrm/civicrm-core

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

728272.9k17](/packages/civicrm-civicrm-core)[rs/laravel-version-control

Foundations for making your app version controlled. Provides migration, blueprint and base models. Will make your app GxP compliant if you exclusively use the VC models and table structure as set out in this package.

1227.5k](/packages/rs-laravel-version-control)[mad-web/laravel-seoable

Easy to map your eloquent fields to seo properties

407.6k](/packages/mad-web-laravel-seoable)

PHPackages © 2026

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