PHPackages                             lullabot/cmc - 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. lullabot/cmc

ActiveDrupal-module

lullabot/cmc
============

Tools for checking cacheability metadata in Drupal

27.9k↓34.3%[6 issues](https://github.com/Lullabot/cmc/issues)[3 PRs](https://github.com/Lullabot/cmc/pulls)PHP

Since Oct 13Pushed 6mo ago4 watchersCompare

[ Source](https://github.com/Lullabot/cmc)[ Packagist](https://packagist.org/packages/lullabot/cmc)[ RSS](/packages/lullabot-cmc/feed)WikiDiscussions main Synced 1mo ago

READMEChangelogDependenciesVersions (4)Used By (0)

Cacheability Metadata Checker (CMC)
===================================

[](#cacheability-metadata-checker-cmc)

A Drupal module that helps developers identify missing cache tags in their pages by tracking loaded entities and verifying their cache tags are properly bubbled up to the response.

Getting Started
---------------

[](#getting-started)

1. Add this repository to your composer.json:

    ```
    composer config repositories.cmc --json '{"type": "vcs", "url": "git@github.com:Lullabot/cmc.git"}'
    ```
2. Set minimum stability to dev (if needed):

    ```
    composer config minimum-stability dev
    ```
3. Require the module:

    ```
    composer require lullabot/cmc
    ```
4. Enable the module:

    ```
    drush en cmc
    ```

Features
--------

[](#features)

- Tracks loaded entities and their cache tags, compares with tags bubbled to the HTTP response object
- Configurable operation modes (disabled, display errors, strict)
- Option to check only front-end pages or include admin pages
- Ability to skip specific URLs
- API for other modules to exclude certain entities from tracking
- Custom PHPCS sniff for Twig template best practices

Configuration
-------------

[](#configuration)

Visit `/admin/config/development/cmc` to configure the module. The following options are available:

### Operation Mode

[](#operation-mode)

- **Disabled**: No cache tag checking (default)
- **Display errors**: Shows missing cache tags at the top of affected pages
- **Strict**: Throws exceptions when cache tags are missing (recommended for development)

### Skip Admin Pages

[](#skip-admin-pages)

When enabled (default), the module only checks pages using the default theme. When disabled, it also checks admin pages using the admin theme.

### Skip URLs

[](#skip-urls)

Define specific paths that should be excluded from cache tag checking.

API
---

[](#api)

### Hook: hook\_cmc\_skip\_tracking()

[](#hook-hook_cmc_skip_tracking)

Modules can implement this hook to exclude certain entities from cache tag tracking:

```
/**
 * Implements hook_cmc_skip_tracking().
 */
function mymodule_cmc_skip_tracking(EntityInterface $entity): bool {
  // Skip tracking for specific entity types or conditions.
  if ($entity->getEntityTypeId() === 'my_entity_type') {
    return TRUE;
  }
  return FALSE;
}
```

Best Practices
--------------

[](#best-practices)

1. Enable "Strict" mode during development to catch cache tag issues early.
2. Use "Display errors" mode on staging environments for visual feedback or to use tools such as VisualDiff tests.
3. Never enable the module in production environments.
4. Consider excluding admin pages to avoid unrelated issues when developing the front-end theme.

Coding Standards
----------------

[](#coding-standards)

The module includes a custom PHPCS sniff that helps maintain Drupal best practices in Twig templates:

### Direct Field Access Sniff

[](#direct-field-access-sniff)

This sniff detects direct field access in Twig templates using patterns like `node.field_*`, `content.field_*`, etc. This pattern is discouraged because it may introduce caching bugs by leaving out the proper cache tags in the top-level render array.

#### Installation

[](#installation)

1. Install development dependencies:

    ```
    composer require --dev drupal/coder squizlabs/php_codesniffer
    ```
2. Register the custom standard:

    ```
    vendor/bin/phpcs --config-set installed_paths /path/to/web/modules/custom/cmc/phpcs
    ```

    Note: the example above will set your `installed_paths` to include only the path to this module's sniffers, which is likely not what you want. You can pass a comma-separated list of paths to the above command, or include the line below in your `phpcs.xml.dist` configuration file:

    ```

    ```
3. Add the standard to your `phpcs.xml.dist`:

    ```

    ```

    Also make sure you allow `twig` as one of the extensions to be sniffed, in case you have an ``config value.
4. Run the sniffer:

    ```
    vendor/bin/phpcs /path/to/your/templates `--standard=CMC`
    ```

    If you included the standard in your `phpcs.xml.dist` configuration file, you can omit the `--standard=CMC` flag.

#### Skip Conditions

[](#skip-conditions)

The sniff will automatically skip the warning under these conditions:

1. If the template renders the full `content` variable:

    ```
    {{ content }}
    ```
2. If the template renders cache metadata explicitly:

    ```
    {{ content['#cache'] }}
    ```
3. If you explicitly opt-out using the special comment:

    ```
    {# cmc_direct_field_access_sniff_opt_out #}
    ```

#### Example Usage

[](#example-usage)

❌ Not recommended:

```
{# Direct field access will trigger warning #}
{{ node.field_image }}
{{ content.field_tags }}
{{ media.field_media_image }}
```

✅ Recommended:

```
{# Render the full content variable #}
{{ content }}

{# Render cache metadata & assets explicitly #}
{{ node.field_image }}
{{ {'#cache': content['#cache'], '#attached': content['#attached']} }}

{# Or opt-out if you know this is being handled elsewhere #}
{# cmc_direct_field_access_sniff_opt_out #}
{{ node.field_special_case }}
```

Example warning:

```
FILE: /path/to/template.html.twig
--------------------------------------------------------------------------------
FOUND 1 WARNING AFFECTING 1 LINE
--------------------------------------------------------------------------------
 15 | WARNING | Direct field access using "node.field_image" is not recommended.
    |         | Try to always render full render arrays that come from the backend.
--------------------------------------------------------------------------------

```

###  Health Score

22

—

LowBetter than 22% of packages

Maintenance27

Infrequent updates — may be unmaintained

Popularity28

Limited adoption so far

Community13

Small or concentrated contributor base

Maturity17

Early-stage or recently created project

 Bus Factor2

2 contributors hold 50%+ of commits

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.

### Community

Maintainers

![](https://www.gravatar.com/avatar/7f2b22c5ce2b0ee6b619c41efe1ddb93ac84d910f59539363009b76810469c72?d=identicon)[lullabot](/maintainers/lullabot)

---

Top Contributors

[![marcoscano](https://avatars.githubusercontent.com/u/5372312?v=4)](https://github.com/marcoscano "marcoscano (13 commits)")[![e0ipso](https://avatars.githubusercontent.com/u/1140906?v=4)](https://github.com/e0ipso "e0ipso (9 commits)")[![davereid](https://avatars.githubusercontent.com/u/62967?v=4)](https://github.com/davereid "davereid (3 commits)")[![andy-blum](https://avatars.githubusercontent.com/u/25532785?v=4)](https://github.com/andy-blum "andy-blum (2 commits)")

### Embed Badge

![Health badge](/badges/lullabot-cmc/health.svg)

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

PHPackages © 2026

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