PHPackages                             webiny/hrc - 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. [Caching](/categories/caching)
4. /
5. webiny/hrc

ActiveLibrary[Caching](/categories/caching)

webiny/hrc
==========

Cache management layer where cache rules are based on incoming HTTP request.

v1.0.0(8y ago)15982MITPHPPHP &gt;=7

Since Jul 28Pushed 8y ago9 watchersCompare

[ Source](https://github.com/Webiny/Hrc)[ Packagist](https://packagist.org/packages/webiny/hrc)[ Docs](http://www.webiny.com/)[ RSS](/packages/webiny-hrc/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (8)Dependencies (3)Versions (13)Used By (2)

HRC - Http Request Cache
========================

[](#hrc---http-request-cache)

This is a cache management system that provides control mechanisms around your cache. Caches are tied to the incoming HTTP request. In practice that means that you can create caches based on a combination of a request path, cookies, sent headers, query strings and some custom callbacks.

### ... but I use memcache (or redis)

[](#-but-i-use-memcache-or-redis)

Sure, but that's not a cache management system, that's just a storage ... managing cache requires a lot of knowledge and it's not an easy thing to do (especially with memcache).

A much smarter person than me, once stated:

> There are only two hard things in Computer Science: cache invalidation and naming things.

> \-- Phil Karlton

You can still use memcache, redis or any other cache storage system you want, just create a driver for it and hook it into Hrc. (see the guide down below)

Installation
------------

[](#installation)

```
composer require webiny/hrc

```

Requires PHP 7.0 or later.

How it works
------------

[](#how-it-works)

```
// define cache rules
$cacheRules = [
    'FooBar' => [
        'Ttl'   => 60,
        'Tags'  => ['cacheAll'],
        'Match' => [
            'Url' => '*'
        ]
    ]
];

// where and how to store cache files
$cacheStorage = new Webiny\Hrc\CacheStorage\FileSystem('/path/to/cache/folder/');
// where and how to keep a record of all stored cache files
$indexStorage = new Webiny\Hrc\IndexStorage\FileSystem('/path/to/index/folder');
// create Hrc instance
$hrc = new Hrc($cacheRules, $cacheStorage, $indexStorage);
// store something into cache
$hrc->save('entryName', 'some content');
// retrieve something from cache
$data = $hrc->read('entryName'); // some content
```

### 1. Define a set of cache rules

[](#1-define-a-set-of-cache-rules)

Cache rules define how and if something can be cached. For example, a simple cache rule looks something like this:

```
$cacheRules = [
    'SimpleRule'    => [
        'Ttl'   => 60,
        'Tags'  => ['simple', 'rule'],
        'Match' => [
            'Url' => '/some/*'
        ]
    ],
];
```

`Ttl` (Time-to-live) defines for how long that entry should be kept in cache.

`Tags` are used to tag the content, so you can invalidate it easier.

`Match` is a set of match criterias that the rule needs to satisfy in order for you to be able to store content. They also define what is used to create a cache key.

#### Match criteria

[](#match-criteria)

There are several things you can use in your match criteria:

- Url path
- Query string
- Request headers
- Cookies
- Custom callback

Match options:

- `true`: the parameter needs to be present, but only the parameter name, and not its value, will be used in the cache key.
- `false`: the parameter must not be preset for the rule to match.
- `*`: the parameter needs to exists and needs to have some value, and it's value will be used in the cache key.
- `?`: the parameter is optional, if it exists it's value is used in the cache key.
- any PHP regex `preg_match` standard
- any fixed string for exact match

Here are some match examples:

```
$mockRules = [
    'AdvancedMatch' => [
        'Ttl'   => 100,
        'Tags'  => ['advanced', 'one'],
        'Match' => [
            'Url'      => '/simple/url/([\w]+)/page/([\d]+)/(yes|no)/',
            'Cookie'   => [
                'X-Cart'          => 'cart value (\d+) currency ([\w]{2})',
                'X-CacheByCookie' => 'yes'
            ],
            'Header'   => [
                'X-Cache-Me'     => 'foo (\w+)',
                'X-Cache-Header' => '*'
            ],
            'Query'    => [
                'Cache'   => true,
                'foo'     => ?
            ],
            'Callback' => [
                'Webiny\Hrc\UnitTests\CacheRules\MockCallbacks::returnValue'
            ]
        ]
    ]
];
```

The `Callback` section is used to invoke a custom callback which is basically just an extension to the match rules. The callback method should return a value, that value will be used to build the cache key. If the callback returns boolean `false`, the rule will not match the request.

A callback method takes two parameters, `Webiny\Hrc\Request` and `Webiny\Hrc\CacheRules\CacheRule\CacheRule`:

```
class MockCallbacks
{
    public static function returnTrue(Webiny\Hrc\Request $r, Webiny\Hrc\CacheRules\CacheRule\CacheRule $cr)
    {
        // do you thing here
    }
}
```

Although not recommended, instead of strictly specifying match options for `Cookie`, `Header` and `Query`, you can put a `*`, to simply take all received parameters (eg. all query parameters), and build a cache key for any variation in a request.

```
$mockRules = [
    'GetAllParameters' => [
        'Ttl'   => 86400,
        'Tags'  => ['global', 'all'],
        'Match' => [
            'Url'      => '/match/all',
            'Cookie'   => '*',
            'Header'   => '*',
            'Query'   => '*'
        ]
    ]
];
```

### 2. Cache storage

[](#2-cache-storage)

Hrc is built in a way that you can store cache using any storage you want, from memcache to mongodb. By default we provide a filesystem storage and MongoDb. If you write a driver for any other storage mechanism, send over a pull request, and we will gladly merge it.

Creating a storage drive is rather simple, just create a class and implement `Webiny\Hrc\CacheStorage\CacheStorageInterface`. You have 3 simple methods to implement, and you're done.

### 3. Index storage

[](#3-index-storage)

Index storage is used to store additional cache information, you can look at it as a combination of cache metadata and taxonomy. The index is mainly used to achieve more possibilities around cache invalidation and faster cache invalidation times.

By default we provide a filesystem cache index and a MongoDb cache index, to create a custom-one, just implement `Webiny\Hrc\IndexStorage\IndexStorageInterface`.

#### Mongo storage and index

[](#mongo-storage-and-index)

If you plan to use the `Mongo` cache storage and cache index, make sure you run the `installCollections` method prior to using the driver, otherwise the required indexes won't be created and the performance will be slow.

```
$this->cacheStorage->installCollections();
$this->indexStorage->installCollections();
```

You need to run this only once. Alternative approach is to create the two collections and indexes manually:

- `HrcCacheStorage` collection should have the following indexes
    - `key` =&gt; unique index on the `key` field, sparse should be false
    - `ttl` =&gt; index on ttl field with expireAfterSeconds set to 0 seconds
- `HrcIndexStorage` collection should have the following indexes:
    - `key` =&gt; unique index on the `key` field, sparse should be false
    - `tags` =&gt; index on the `tags` field
    - `ttl` =&gt; index on ttl field with expireAfterSeconds set to 0 seconds

### 4. Matching a cache rule

[](#4-matching-a-cache-rule)

When you call the `read` or `save` method, if you don't provide the name of the cache rule, the class will run through all of defined cache rules, and will select the **first rule that matched the request**. However if you provide the cache rule name, the cache rule match patterns still must match, but the check will only be done on that particular rule. By providing a cache rule name, you can match multiple cache rules inside the same HTTP request.

```
// use the first matched cache rule
$hrc->save('entryName', 'some content');
$data = $hrc->read('entryName');

// use fooBar cache rule
$hrc->save('block 122', 'some content', 'fooBar');
$data = $hrc->read('block 122');
```

### 5. Callbacks

[](#5-callbacks)

There are two main callback events supported:

- `beforeSave(SavePayload)`
- `afterSave(SavePayload)`
- `beforeRead(ReadPayload)`
- `afterRead(ReadPayload)`

To register a callback for those events create a class that implements `\Webiny\Hrc\EventCallbackInterface`. You will have to implement all the callback methods. Both save methods receive 1 parameter, which is `SavePayload` instance. This instance contains all the relevant data about the current cache entry that is about to be created, or has been created. Similar is for the read methods, they receive the `ReadPayload` instance, which gives you access to the current cache entry as well as the option to set the purge flag, so that the content is actually purged and not retrieved from the database.

The callback methods don't need to return anything, but since the `SavePayload` instance is an object, on `beforeSave` you can use it to manipulate your cache entry, by changing the cache content, adding or removing tags and similar. On `afterSave` callback you will get back the same object, but this is just a confirmation that the object was successfully saved.

```
// your Hrc instance
$hrc = new Hrc($cacheRules, $cacheStorage, $indexStorage);

// my callback handler -> must implement \Webiny\Hrc\EventCallbackInterface
$handler = new MyCallbackHandler();

// register callbacks
$hrc->registerCallback($handler);
```

Cache purge
-----------

[](#cache-purge)

There are couple of ways you can purge cache:

### Purge by cache key

[](#purge-by-cache-key)

When you save a cache entry, the save method will return a cache key, using that key, you can purge that particular entry:

```
// save the cache and get back the cache key
$key = $hrc->save('entryName', 'some content');
// purge that cache entry
$hrc->purgeByCacheKey($key);
```

### Purge by tag

[](#purge-by-tag)

Every cache rule has one or more tags associated. Using the same tags, you can purge multiple cache entries. **Note:** when providing multiple tags, only entries that match ALL tags will be purged, or to put it in different words, we use logical AND condition between the tags.

```
$hrc->purgeByTag(['tag1', 'tag2']);
```

### Purge by request

[](#purge-by-request)

There is a flag that you can set, so that every matched cache entry, inside the current request, will automatically be purged.

```
// first set the purge flag to true
$hrc->setPurgeFlag(true);

// once the flag is set to true, every `read` call, that has a cache hit, will actually do a purge
$hrc->read('entryName');
```

Another way of doing this is by sending a special request header inside your request. That header is `X-HRC-Purge` and it just needs to be defined, there is no need to set any value for it. Sending that header has the same effect as setting the purge flag to true, but this way you don't need to set the flag, and it's actually only valid for that particular request.

#### Security

[](#security)

Based on the previous section, you might think that there is a big risk in having that header, because everybody can purge the cache and hit your database/backend on every request...and that's true, but there's a built-in mechanism to prevent that. You can set a `control key`, so only requests that have a valid key can actually purge via the header.

```
// set the control header
$hrc->setControlKey('someSecretString');
```

Once the control header is set, you now need to send a `X-HRC-Control-Key` request header, containing the same value. Only if both values match, the purge request will be executed.

Bugs and improvements
---------------------

[](#bugs-and-improvements)

Just report them under issues, or even better, send a pull request :)

License
-------

[](#license)

MIT

Resources
---------

[](#resources)

To run unit tests, you need to use the following command:

```
$ cd path/to/Webiny/Hrc/
$ composer install
$ phpunit

```

###  Health Score

33

—

LowBetter than 75% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity15

Limited adoption so far

Community17

Small or concentrated contributor base

Maturity70

Established project with proven stability

 Bus Factor1

Top contributor holds 85.3% 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 ~72 days

Recently: every ~30 days

Total

12

Last Release

3154d ago

Major Versions

0.3.x-dev → v1.0.02017-09-29

PHP version history (2 changes)v0.1.0PHP &gt;=5.5.9

v1.0.0PHP &gt;=7

### Community

Maintainers

![](https://www.gravatar.com/avatar/4440afa738ed146b05c06073a90345e0464c4f4d042b039532d881ca24859d77?d=identicon)[SvenAlHamad](/maintainers/SvenAlHamad)

---

Top Contributors

[![SvenAlHamad](https://avatars.githubusercontent.com/u/3808420?v=4)](https://github.com/SvenAlHamad "SvenAlHamad (29 commits)")[![adrians5j](https://avatars.githubusercontent.com/u/5121148?v=4)](https://github.com/adrians5j "adrians5j (4 commits)")[![Pavel910](https://avatars.githubusercontent.com/u/3920893?v=4)](https://github.com/Pavel910 "Pavel910 (1 commits)")

---

Tags

cache

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/webiny-hrc/health.svg)

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

###  Alternatives

[psr/simple-cache

Common interfaces for simple caching

8.1k727.3M2.1k](/packages/psr-simple-cache)[psr/cache

Common interface for caching libraries

5.2k686.9M1.3k](/packages/psr-cache)[react/cache

Async, Promise-based cache interface for ReactPHP

444112.4M40](/packages/react-cache)[beste/in-memory-cache

A PSR-6 In-Memory cache that can be used as a fallback implementation and/or in tests.

2512.2M6](/packages/beste-in-memory-cache)[anahkiasen/flatten

A package for the Illuminate framework that flattens pages to plain HTML

33113.0k](/packages/anahkiasen-flatten)[rtcamp/nginx-helper

Cleans nginx's fastcgi/proxy cache or redis-cache whenever a post is edited/published. Also provides cloudflare edge cache purging with Cache-Tags.

23517.0k1](/packages/rtcamp-nginx-helper)

PHPackages © 2026

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