# Context Managers

Context managers enrich the global `Timber::context()` with shared data that's available in every template. They're the quartermaster's store — making sure every template has the supplies it needs before setting sail.

## How They Work

Context managers implement the `ContextManagerInterface` and are registered in `config/context-managers.php`. During boot, the `TimberServiceProvider` instantiates each one and hooks them into the `timber/context` filter.

Every time `Timber::context()` is called (typically once per request, in a controller's constructor), each registered context manager has a chance to add its data.

### The Interface

{% code title="src/ContextManagers/ContextManagerInterface.php" %}

```php
namespace PressGang\ContextManagers;

interface ContextManagerInterface {
    /**
     * @param array<string, mixed> $context
     * @return array<string, mixed>
     */
    public function add_to_context(array $context): array;
}
```

{% endcode %}

## Built-in Context Managers

PressGang ships with five context managers out of the box:

### SiteContextManager

Adds the Timber `Site` object and a cache-busted stylesheet URL to the context.

**Context keys:** `site`, `site.stylesheet`

{% code title="views/layout.twig" %}

```twig
<link rel="stylesheet" href="{{ site.stylesheet }}">
<h1>{{ site.name }}</h1>
```

{% endcode %}

The stylesheet URL is filterable via `pressgang_stylesheet`.

### MenuContextManager

Adds all registered WordPress navigation menus as Timber `Menu` objects, keyed by location.

**Context keys:** `menu_{location}` (e.g. `menu_primary`, `menu_footer`)

{% code title="views/partials/nav.twig" %}

```twig
{% for item in menu_primary.items %}
    <a href="{{ item.link }}">{{ item.title }}</a>
{% endfor %}
```

{% endcode %}

Each menu is filterable via `pressgang_context_menu_{location}`.

### ThemeModsContextManager

Adds all WordPress Customizer theme modifications to the `theme` object.

**Context keys:** properties on `theme` (e.g. `theme.header_image`, `theme.custom_logo`)

{% code title="views/partials/header.twig" %}

```twig
{% if theme.custom_logo %}
    <img src="{{ theme.custom_logo }}" alt="{{ site.name }}">
{% endif %}
```

{% endcode %}

Each value is filterable via `pressgang_theme_mod_{key}`.

### AcfOptionsContextManager

Adds ACF (Advanced Custom Fields) options page fields to the context, converting values to Timber objects where appropriate. Results are cached via `wp_cache`.

**Context key:** `options`

{% code title="views/partials/footer.twig" %}

```twig
{{ options.company_name }}
{{ options.logo.src }}
```

{% endcode %}

{% hint style="info" %}
This manager only runs when ACF is active and `config/acf-options.php` is configured.
{% endhint %}

### WooCommerceContextManager

Adds WooCommerce-specific data to the context when WooCommerce is active.

## Creating a Custom Context Manager

{% stepper %}
{% step %}

#### Create the class

{% code title="src/ContextManagers/SocialLinksContextManager.php" lineNumbers="true" %}

```php
namespace MyTheme\ContextManagers;

use PressGang\ContextManagers\ContextManagerInterface;

class SocialLinksContextManager implements ContextManagerInterface {

    public function add_to_context(array $context): array {
        $context['social_links'] = [
            'twitter'  => get_option('social_twitter'),
            'facebook' => get_option('social_facebook'),
            'instagram' => get_option('social_instagram'),
        ];

        return $context;
    }
}
```

{% endcode %}
{% endstep %}

{% step %}

#### Register in config

Add it to your child theme's `config/context-managers.php`:

{% code title="config/context-managers.php" %}

```php
return [
    \PressGang\ContextManagers\SiteContextManager::class,
    \PressGang\ContextManagers\MenuContextManager::class,
    \PressGang\ContextManagers\ThemeModsContextManager::class,
    \PressGang\ContextManagers\AcfOptionsContextManager::class,
    \MyTheme\ContextManagers\SocialLinksContextManager::class,
];
```

{% endcode %}
{% endstep %}

{% step %}

#### Use in Twig

{% code title="views/partials/social.twig" %}

```twig
<a href="{{ social_links.twitter }}">Twitter</a>
```

{% endcode %}
{% endstep %}
{% endstepper %}

## Important Guidelines

{% hint style="danger" %}
Context managers run on **every request** — frontend, admin, AJAX, and CLI. Keep them lightweight!
{% endhint %}

* **Cache non-trivial queries.** If your context manager fetches data from the database, wrap it in `wp_cache_get()`/`wp_cache_set()`.
* **Only add data needed across many templates.** Data specific to a single page should live in the controller, not a context manager.
* **Keep it side-effect free.** Context managers must not write to the database, send emails, or perform remote requests.
* **Don't overwrite built-in Timber context keys** like `site`, `request`, or `user`.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.pressgang.dev/context-managers.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
