> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/shivammathur/setup-php/llms.txt
> Use this file to discover all available pages before exploring further.

# Caching

> Speed up your workflows by caching PHP extensions and Composer dependencies between runs.

Caching avoids repeating expensive setup work on every workflow run. There are two main things worth caching in a PHP workflow: compiled PHP extensions and Composer's package download cache.

## Extension caching

Some PHP extensions take a long time to compile and install. You can cache them using the [`shivammathur/cache-extensions`](https://github.com/shivammathur/cache-extensions) action together with `actions/cache`.

When the cache is warm, extensions are enabled directly from the cache instead of being compiled again, which significantly reduces setup time.

```yaml theme={null}
jobs:
  run:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup cache environment
        id: extcache
        uses: shivammathur/cache-extensions@v1
        with:
          php-version: '8.5'
          extensions: imagick, redis, swoole
          key: extensions-cache-v1

      - name: Cache extensions
        uses: actions/cache@v4
        with:
          path: ${{ steps.extcache.outputs.dir }}
          key: ${{ steps.extcache.outputs.key }}
          restore-keys: ${{ steps.extcache.outputs.key }}

      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: '8.5'
          extensions: imagick, redis, swoole
```

<Note>
  The `shivammathur/cache-extensions` action must run before `setup-php` so that the cache is restored before PHP setup begins. Refer to the [cache-extensions documentation](https://github.com/shivammathur/cache-extensions) for full configuration options.
</Note>

## Composer dependency caching

Composer maintains an internal download cache separate from the `vendor` directory. Caching this directory means packages are loaded from disk instead of being downloaded from Packagist on every run.

### Basic setup with composer.lock

When you commit `composer.lock`, use its hash as the cache key. This ensures the cache is invalidated whenever your locked dependencies change.

```yaml theme={null}
steps:
  - name: Checkout code
    uses: actions/checkout@v4

  - name: Setup PHP
    uses: shivammathur/setup-php@v2
    with:
      php-version: '8.5'

  - name: Get composer cache directory
    id: composer-cache
    run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT

  - name: Cache dependencies
    uses: actions/cache@v4
    with:
      path: ${{ steps.composer-cache.outputs.dir }}
      key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
      restore-keys: ${{ runner.os }}-composer-

  - name: Install dependencies
    run: composer install --prefer-dist
```

### When there is no composer.lock

If you do not commit `composer.lock` (common for libraries), use the hash of `composer.json` instead:

```yaml theme={null}
- name: Cache dependencies
  uses: actions/cache@v4
  with:
    path: ${{ steps.composer-cache.outputs.dir }}
    key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}
    restore-keys: ${{ runner.os }}-composer-
```

### Using prefer-lowest and prefer-stable in cache keys

If your matrix tests both minimum and stable dependency versions, include the preference in your cache key so the two variants do not share a cache:

```yaml theme={null}
strategy:
  matrix:
    php-versions: ['8.2', '8.3', '8.4']
    prefer: ['prefer-lowest', 'prefer-stable']

steps:
  - name: Get composer cache directory
    id: composer-cache
    run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT

  - name: Cache dependencies
    uses: actions/cache@v4
    with:
      path: ${{ steps.composer-cache.outputs.dir }}
      key: ${{ runner.os }}-composer-${{ matrix.prefer }}-${{ hashFiles('**/composer.lock') }}
      restore-keys: ${{ runner.os }}-composer-${{ matrix.prefer }}-

  - name: Install dependencies
    run: composer update --${{ matrix.prefer }} --prefer-dist
```

<Warning>
  Do not cache the `vendor` directory using `actions/cache`. Caching `vendor` can cause subtle issues because generated files, platform-specific binaries, and autoloader state inside `vendor` are not portable across runners. Always cache Composer's internal download cache (the path returned by `composer config cache-files-dir`) and run `composer install` to reconstruct `vendor`.
</Warning>

## Complete workflow example

The following workflow combines PHP setup, extension caching, and Composer dependency caching:

```yaml theme={null}
name: CI

on: [push, pull_request]

jobs:
  test:
    runs-on: ${{ matrix.operating-system }}
    strategy:
      matrix:
        operating-system: ['ubuntu-latest', 'windows-latest', 'macos-latest']
        php-versions: ['8.3', '8.4', '8.5']

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup cache environment
        id: extcache
        uses: shivammathur/cache-extensions@v1
        with:
          php-version: ${{ matrix.php-versions }}
          extensions: mbstring, intl, redis
          key: extensions-cache-v1

      - name: Cache extensions
        uses: actions/cache@v4
        with:
          path: ${{ steps.extcache.outputs.dir }}
          key: ${{ steps.extcache.outputs.key }}
          restore-keys: ${{ steps.extcache.outputs.key }}

      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: ${{ matrix.php-versions }}
          extensions: mbstring, intl, redis
          coverage: none

      - name: Get composer cache directory
        id: composer-cache
        run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT

      - name: Cache Composer dependencies
        uses: actions/cache@v4
        with:
          path: ${{ steps.composer-cache.outputs.dir }}
          key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
          restore-keys: ${{ runner.os }}-composer-

      - name: Install dependencies
        run: composer install --prefer-dist

      - name: Run tests
        run: vendor/bin/phpunit
```
