PHPackages                             phptailors/context-interface - 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. phptailors/context-interface

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

phptailors/context-interface
============================

Context Manager Interfaces

0.1.1(3mo ago)06↓90%PHPPHP &gt;=8.0CI passing

Since Mar 27Pushed 3mo ago1 watchersCompare

[ Source](https://github.com/phptailors/context-interface)[ Packagist](https://packagist.org/packages/phptailors/context-interface)[ RSS](/packages/phptailors-context-interface/feed)WikiDiscussions master Synced 3w ago

READMEChangelogDependencies (1)Versions (4)Used By (0)

[![PHPUnit](https://github.com/phptailors/context-interface/actions/workflows/phpunit.yml/badge.svg)](https://github.com/phptailors/context-interface/actions/workflows/phpunit.yml)[![Composer Require Checker](https://github.com/phptailors/context-interface/actions/workflows/composer-require-checker.yml/badge.svg)](https://github.com/phptailors/context-interface/actions/workflows/composer-require-checker.yml)[![BC Check](https://github.com/phptailors/context-interface/actions/workflows/backward-compatibility-check.yml/badge.svg)](https://github.com/phptailors/context-interface/actions/workflows/backward-compatibility-check.yml)[![Psalm](https://github.com/phptailors/context-interface/actions/workflows/psalm.yml/badge.svg)](https://github.com/phptailors/context-interface/actions/workflows/psalm.yml)[![PHP CS Fixer](https://github.com/phptailors/context-interface/actions/workflows/php-cs-fixer.yml/badge.svg)](https://github.com/phptailors/context-interface/actions/workflows/php-cs-fixer.yml)

Context Manager Interfaces
=========================================================================

[](#context-manager-interfaces)

A set of PHP interfaces that support implementation of Context Managers.

Introduction
------------

[](#introduction)

The concept of Context Managers is borrowed from Python ([contextlib](https://docs.python.org/library/contextlib.html), [with statement](https://docs.python.org/reference/compound_stmts.html#with)) and [PHP RFC: Context Managers](https://wiki.php.net/rfc/context-managers).

Context Managers provide a way to abstract out common control-flow and variable-lifetime-management patterns, leading to simplified business logic code that can skip over a great deal of boilerplate. They place certain *guards around a section of code*, such that certain behaviors are guaranteed upon entering and leaving that section.

Commmon use cases include file management and database transactions, althrough there are many more.

PHP 8 does not provide Context Managers natively, but there is a [proposal](https://wiki.php.net/rfc/context-managers) and a [pull request](https://github.com/arnaud-lb/php-src/pull/26) for the feature, so it may appear in future versions. As for the time of this writting (March, 2026), the [RFC](https://wiki.php.net/rfc/context-managers) has status "In Discussion".

This specification defines PHP interfaces, which may be used to implement Context Managers in PHP 8. An implementation will be referred to as a **library** through the rest of this document.

1. Specification
----------------

[](#1-specification)

This specification defines four PHP interfaces. The first three formalize the core of the Context Managers concept. The fourth is a convenience interface.

- [Core Interfaces](#CoreInterfaces)
- [Convenience Interfaces](#ConvenienceInterfaces)

### 1.1 Core Interfaces

[](#11-core-interfaces)

The three core interfaces are

- [ExecutorInterface](#ExecutorInterface)
- [ExecutorFactoryInterface](#ExecutorFactoryInterface)
- [ContextManagerInterface](#ContextManagerInterface)

With these three interfaces implemented, an application shall be able to use the following syntax to execute a function with Context Managers in action:

```
/** @var ExecutorFactoryInterface $executor */
$executor->withContext([C1[, C2[, ...]]])->do(
    function ([$arg1,[ $arg2[, ...]]]) {
        /* code */
    }
);
```

The `C1`, `C2`, `...` are expressions, each of which MUST evaluate to an instance of [ContextManagerInterface](#ContextManagerInterface).

Using the above syntax, a cannonical example — a code for robust file handling that includes all necessary error management and automatic cleanup — could be written as follows:

```
/** @var ExecutorFactoryInterface $executor */
$line = $executor->withContext(new FileContextManager(fopen("foo.txt", "rt")))->do(
    function (mixed $handle): string {
        return fgets($handle);
    }
);
```

with `FileContextManger` being an implementation of [ContextManagerInterface](#ContextManagerInterface), such as

```
readonly class FileContextManager implements ContextMangerInterface {
    private mixed $handle;

    public function __construct(mixed $handle) {
        $this->handle = $handle;
    }

    public function enterContext(): mixed {
        return $this->handle;
    }

    public function exitContext(?\Throwable $exception): ?\Throwable {
        fclose($this->handle);
        return $exception;
    }
}
```

### 1.2 Convenience Interfaces

[](#12-convenience-interfaces)

The convenience interfaces include

- [ExecutorServiceInterface](#ExecutorServiceInterface)

The [ExecutorServiceInterface](#ExecutorServiceInterface) is a more abstract version of the [ExecutorFactoryInterface](#ExecutorFactoryInterface). It reduces the boilerplate required for wrapping Context Values with [ContextManagers](#ContextManager). The syntax introduced by [ExecutorServiceInterface](#ExecutorServiceInterface) is

```
/** @var ExecutorServiceInterface $executor */
$executor->with([V1[, V2[, ...]]])->do(
    function ([$arg1[, $arg2[, ...]]]) {
        /* code */
    }
);
```

The `V1`, `V2`, `...` are expressions, each of which evaluates to an arbitrary value, called a **Context Value**. It's the responsibility of [ExecutorService](#ExecutorService) to wrap the provided **Context Values**with appropriate [ContextManagers](#ContextManager).

With the above syntax, the cannonical example (robust file handling) would look like follows:

```
/** @var ExecutorServiceInterface $executor */
$line = $executor->with(fopen("foo.txt", "rb"))->do(
    function (mixed $handle): string {
        return fgets($handle);
    }
);
```

The difference is, that there is no call to `new FileContextManager(...)` in the above snippet.

### 1.3 ExecutorFactory

[](#13-executorfactory)

An implementing class for the [ExecutorFactoryInterface](#ExecutorFactoryInterface) will be called [ExecutorFactory](#ExecutorFactory) across this document, although implementors may chose a different name. A minimal implementation of the [ExecutorFactoryInterface](#ExecutorFactoryInterface) may look like:

```
use Taylors\Context\ExecutorFactoryInterface;
use Taylors\Context\ExecutorInterface;

final class ExecutorFactory implements ExecutorFactoryInterface {
    public function withContext(ContextManagerInterface ...$context): ExecutorInterface {
        return new Executor($context);
    }
}
```

provided there is a class [Executor](#Executor) that implements the [ExecutorInterface](#ExecutorInterface):

The purpose of the [ExecutorFactory](#ExecutorFactory) is to expose the [withContext()](#withContext) method, which is an entry point similar to the [using](https://wiki.php.net/rfc/context-managers) keyword from the [PHP RFC](https://wiki.php.net/rfc/context-managers).

#### 1.3.1 ExecutorFactory — the `withContext()` method

[](#131-executorfactory--the-withcontext-method)

The [withContext()](#withContext) method of [ExecutorFactory](#ExecutorFactory) returns an [Executor](#Executor) which adds the behavior specified by Context Managers to the execution of a user-provided callback. The returned [Executor](#Executor)MUST have access to the [ContextManagers](#ContextManager) passed as `...$context` to the [withContext()](#withContext) method.

### 1.4 Executor

[](#14-executor)

An implementing class of the [ExecutorInterface](#ExecutorInterface) will be called [Executor](#Executor) across this document, although implementors may chose a different name.

The [Executor](#Executor) is coupled with a **$context** — an ordered collection of zero or more [ContextManagers](#ContextManager). The **$context** is supposed to be (created out of) the array of `...$context` arguments passed to [ExecutorFactory::withContext()](#withContext). It must preserve element order and keys of `...$context`.

In a nutshell, the [Executor](#Executor) invokes a user-provided callback `$func` within [Executor's](#Executor) **$context** — it calls [enterContext()](#enterContext) on all the [ContextManagers](#ContextManager)from **$context**, then executes the user-provided callback `$func`, and then calls [exitContext()](#exitContext) on all the [ContextManagers](#ContextManager) in **$context**.

An implementation must ensure, that using [Executor](#Executor) with multiple [ContextManagers](#ContextManager) is equivalent to nesting [Executors](#Executors) with consecutive [ContextManagers](#ContextManager)from the same `...$context`, i.e. the effect of

```
/** @var ExecutorFactoryInterface */
$executor->withContext(E1, E2)->do(
    function ($arg1, $arg2) {
        /* code */
    }
);
```

is same as for

```
/** @var ExecutorFactoryInterface */
$executor->withContext(E1)->do(
    function ($arg1) use ($executor) {
        return $executor->withContext(E2)->do(
            function ($arg2) use ($arg1) {
                /* code */
            }
        );
    }
);
```

#### 1.4.1 Executor — the `do()` method

[](#141-executor--the-do-method)

An implementation of [ExecutorInterface::do()](#do), when invoked with a callable `$func`, MUST:

1. invoke [enterContext()](#enterContext) on each [Context Manager](#ContextManager) from `$context` in original order, and collect returned values, then
2. invoke user-provided function `$func` passing it the values collected in 1 as arguments, and then
3. invoke [exitContext()](#exitContext) on each [Context Manager](#ContextManager) from [Context](#Context) in reverse order,
4. return the value returned by `$func`.

If an exception is thrown during iteration 1:

- the method MUST NOT invoke `$func` and MUST proceed to 3.

In addition, if an exception is thrown during iteration 1 or from `$func`

- the reverse iteration in 3 MUST start from the last [ContextManager](#ContextManager) successfully visited in 1,
- if any of the [ContextManagers](#ContextManager) visited in 3 returns `null`, the [Executor](#Executor) MUST return `null` without throwing,
- otherwise, if all [ContextManagers](#ContextManager) returned `\Throwable`, the method MUST rethrow the exception returned by last visited [ContextManager](#ContextManager).

If any of the [ContextManagers](#ContextManager) returned `null` during the reverse iteration 3, the exception is assumed handled by that [ContextManager](#ContextManager) and is not passed to the remaining [ContextManagers](#ContextManager).

An example implementation of [Executor::do()](#do) may look like follows:

```
class Executor implements ExecutorInterface {
    /**
     * @var array
     */
    private array $context;

    /* ... */

    public function do(callable $func): mixed
    {
        $exception = null;
        $return = null;
        $entered = [];

        try {
            $args = [];
            foreach ($this->context as $name => $manager) {
                $args[$name] = $manager->enterContext();
                $entered[] = $manager;
            }
            $return = call_user_func_array($func, $args);
        } catch(\Throwable $e) {
            $exception = $e;
        }

        while (count($entered) > 0) {
            $manager = array_pop($entered);
            if (null === $exception) {
                $manager->exitContext();
            } else {
                $exception = $manager->exitContext($exception);
            }
        }

        return $return;
    }
}
```

### 1.4 ContextManager

[](#14-contextmanager)

An implementing class of the [ContextManagerInterface](#ContextManagerInterface) will be called [ContextManager](#ContextManager) across this document, although implementors may chose different names.

A [ContextManager](#ContextManager) object implements [enterContext()](#enterContext) and [exitContext()](#exitContext) for a single **Context Value**. In a typical scenario, multiple classes implementing [ContextManageInterface](#ContextManageInterface) will be provided by a **library** to handle e.g. different value types.

#### 1.4.1 ContextManager — the `enterContext()` method

[](#141-contextmanager--the-entercontext-method)

Enter the runtime context related to this object. The returned value will be passed by [Executor](#Executor) to an appropriate argument of the user-provide callback.

In most circumstances, the method returns the wrapped **Context Value** or a value derived from it.

#### 1.4.2 ContextManager — the `exitContext()` method

[](#142-contextmanager--the-exitcontext-method)

Exit the runtime context related to this object.

For a successful case, the [exitContext()](#exitContext) is called with no arguments. It may take arbitrary cleanup steps, and its return value if any is ignored.

If an exception is thrown in the course of the context block that propagates up to the context block, this is considered a failure. The [exitContext()](#exitContext) method is called with the exception as its only parameter. If [exitContext()](#exitContext) returns `null`, then no further action is taken. If [exitContext()](#exitContext) returns a `\Throwable`(either the one it was passed or a new one), it will be rethrown. [exitContext()](#exitContext) should NOT throw its own exceptions unless there is an error with the context manager object itself.

Becausu in a success case the method is passed `null`, that means always calling `return $exception` will result in the desired in-most-cases behavior (that is, rethrowing an exception if there was one, or just continuing if not).

2. Package
----------

[](#2-package)

The interfaces described are provided as part of the [phptailors/context-interface](https://packagist.org/packages/phptailors/context-interface)package.

3. `ExecutorInterface`
----------------------

[](#3-executorinterface)

```
namespace Tailors\Lib\Context;

interface ExecutorInterface
{
    public function do(callable $func): mixed;
}
```

4. `ExecutorFactoryInterface`
-----------------------------

[](#4-executorfactoryinterface)

```
namespace Tailors\Lib\Context;

interface ExecutorFactoryInterface
{
    public function withContext(ContextManagerInterface ...$context): ExecutorInterface;
}
```

5. `ContextManagerInterface`
----------------------------

[](#5-contextmanagerinterface)

```
namespace Tailors\Lib\Context;

interface ContextManagerInterface
{
    public function enterContext(): mixed;
    public function exitContext(?\Throwable $exception = null): ?\Throwable;
}
```

6. `ExecutorServiceInterface`
-----------------------------

[](#6-executorserviceinterface)

```
namespace Tailors\Lib\Context;
interface ExecutorServiceInterface
{
    public function with(mixed ...$args): ExecutorInterface;
}
```

8. References
-------------

[](#8-references)

- [Python: contextlib — Utilities for with-statement contexts](https://docs.python.org/library/contextlib.html)
- [Python: the with statement](https://docs.python.org/reference/compound_stmts.html#with)
- [PHP RFC: Context Managers](https://wiki.php.net/rfc/context-managers)

###  Health Score

32

—

LowBetter than 69% of packages

Maintenance82

Actively maintained with recent releases

Popularity4

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity31

Early-stage or recently created project

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

Total

3

Last Release

90d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/1761140?v=4)[Paweł Tomulik](/maintainers/ptomulik)[@ptomulik](https://github.com/ptomulik)

---

Top Contributors

[![ptomulik](https://avatars.githubusercontent.com/u/1761140?v=4)](https://github.com/ptomulik "ptomulik (3 commits)")

### Embed Badge

![Health badge](/badges/phptailors-context-interface/health.svg)

```
[![Health](https://phpackages.com/badges/phptailors-context-interface/health.svg)](https://phpackages.com/packages/phptailors-context-interface)
```

PHPackages © 2026

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