Download the PHP package vatvit/freshen without Composer

On this page you can find all versions of the php package vatvit/freshen. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package freshen

⚠️ Read-only mirror. This repository is the Packagist publish mirror of packages/php in the Freshen monorepo. Source, issues, and pull requests live there — please don't open them here. Install with composer require vatvit/freshen-php.


Freshen (PHP)

Finally, full control over your caching — in one library.

Packagist Version PHP Version License

Freshen brings together the caching pieces you normally wire up by hand: single-flight recompute so exactly one worker rebuilds a hot key while everyone else is served the last good value (no cache-stampede); preemptive refresh that recomputes an entry before it goes stale, on TTLs and jitter you control; structured keys and effective delete — genuinely evict one exact key or a whole prefix, atomically and in one round-trip; and built-in metrics on every hit, miss, and rebuild. Wrap any expensive read — a database query, an API call, a rendered fragment — and every cache-related decision is explicit and yours. Runs natively on PHP 8.1 → 8.4 (COMPATIBILITY.md).

Features

Framework bridges

On Symfony or Laravel? Skip the wiring — a drop-in bridge gives you everything above from declarative config (composer require and you're done):

Framework Package Docs
Symfony ^6.4 \|\| ^7.0 vatvit/freshen-symfony bridge README
Laravel ^11 \|\| ^12 (PHP 8.2+) vatvit/freshen-laravel bridge README

At a glance

Once a cache is wired (a few lines — see Usage), reading is two lines and you never touch the store, a stampede, or serialisation:

On a miss, $topSellersCache calls your loader, stores the result, and returns it — no "check store → query → write back" dance, no stampede handling. value() gives back exactly what the loader returned (an array stays an array; Stash (de)serialises for you), and isMiss() even tells a cached null apart from an absent entry.

This directory is the source of truth. It is subtree-split by CI into a read-only mirror repository that Packagist serves.

Install

Requires a PSR-6 cache pool (Stash). ext-redis is suggested for a Redis backend.

Usage

1. Wire a backend

Freshen\Cache reads and writes through a Stash pool. For the full guarantees (atomic single-flight recompute and exact, non-hierarchical invalidation) use Freshen's Redis driver, Freshen\Driver\Redis, in place of Stash\Driver\Redis. Two equivalent ways to build the pool:

$pool is a plain PSR-6 pool (\Stash\Pool implements Psr\Cache\CacheItemPoolInterface) — Freshen consumes it, it isn't a Freshen type; if you already cache with Stash it's the pool you already have. Cache wires its own item class (Freshen\Item, for deterministic TTLs and exact delete) onto the pool automatically — you don't set it yourself. Strong single-flight needs a backend with a conditional write: Redis (SET NX) today; other Stash drivers work but fall back to Stash's best-effort lock (see docs/PARITY.md §12, §14).

2. Build a cache for one dataset, and read

A Freshen\Cache is not a global cache — it wraps one loader, i.e. one dataset, with its own TTLs. The loader is the heart of the library: Freshen calls it to (re)compute the authoritative value for a key (your DB query, an API call, a heavy computation). On a read you never write values yourself — a get() on a cold or due key invokes the loader and Freshen stores the result. That's the whole point: read, and the cache keeps itself fresh, stampede-free.

Need another dataset (say, categories)? That's another loader and another Cache, with its own TTLs — see Framework integration for the per-dataset wiring.

A Key is a structured, immutable identity — domain / facet [ / schemaVersion ] [ / locale ] / id:

Why domain + facet? Together they form the key's prefix — a hierarchy, not just a namespace. domain is the bounded context / entity type (product, user, order); facet is the specific view or query within it (top-sellers, profile, detail). Grouping entries under a shared prefix is what makes hierarchical invalidation work: invalidate() on a prefix drops every entry beneath it in one call (e.g. clear all product/top-sellers/* variants at once), while invalidateExact() removes just the single entry.

Why the id can be complex (a map). A cached value is usually a function of several inputs — filters, pagination, options — not one scalar. Rather than make you hand-build and normalise a string, Key takes the whole parameter map and canonicalises it: keys are deep-sorted so logically-equal inputs produce the same key regardless of order (['brand' => 'Apple', 'category' => 456] is the same key as the example above), then serialised to a deterministic, separator-safe, cross-language-stable token. So multi-dimensional keys stay correct and collision-free with no effort on your side.

Now just read. On a miss the loader fills the cache; within the precompute window one caller refreshes while the rest are served the current value; under a recompute a follower is served the previous (stale) value — all automatic:

value() throws a RuntimeException on a miss — guard with isMiss() (or isHit() / isStale()) first.

To update an entry, pick by whether you already hold the value. If you don't have it, drive the loader: invalidate() drops the entry (next get() recomputes) and refresh() recomputes it now (§3/§4). If you already have a fresh value — you just computed it, or a write path produced it — put($key, $value) stores it directly and skips the loader; using refresh() there would waste a recompute.

A cache is a domain object — wrap it like a repository

This is the architectural intent of the class layout: a Freshen\Cache instance is the complete logic for one piece of business data — how it's loaded (the loader), how long it lives (TTLs), how it's refreshed and invalidated. It is not a generic key-value bucket you reach into from everywhere.

You already do this for the database: raw SQL doesn't get sprinkled across the app, it's wrapped in a ProductRepository. Cached data is no different — so isolate each dataset behind a small business object that owns its cache and hides the keys:

Callers write $topSellers->forCategory(456, 'en') — they never touch a Key, a TTL, or the word "cache". Each dataset (top sellers, categories, a user profile) is its own such object with its own Cache, loader, and TTLs. Split and isolate your cached data; treat it as the first-class domain concept it is.

3. Invalidate & refresh

Three write-side operations. Each defaults to async (§4); pass SyncMode::SYNC to act inline against the backend:

Prefix selector. invalidate() also accepts a Freshen\Interface\KeyPrefixInterface — clear a whole subtree without a concrete Key:

Batching. All three accept a list to act on many selectors in one call:

4. Invalidate & refresh — asynchronous (the default)

invalidate() / invalidateExact() / refresh() default to SyncMode::ASYNC: instead of touching the backend inline they emit a per-operation event through a PSR-14 dispatcher, and a subscribed Freshen\AsyncHandler performs the equivalent SYNC operation later. Each operation has its own event class — Freshen\InvalidateEvent, Freshen\InvalidateExactEvent, Freshen\RefreshEvent (all extend the abstract Freshen\AsyncEvent) — so a listener provider routes each op to the right handler by event class alone. Give the cache a dispatcher, then wire the handler:

Calling an async operation when no dispatcher was configured throws a LogicException.

5. Observability — metrics, built in

Freshen emits a named metric on every read/write path, so hit / stale / fill / miss / invalidate visibility is built in, not bolted on. Implement MetricsInterface to forward them to StatsD, Prometheus, your logger — anything. No sink? Metrics are simply off, at zero cost.

Emitted set: cache_hit{state: fresh|stale|fresh_after_sleep}, cache_fill, cache_put, cache_miss{cause: …}, cache_invalidate, cache_invalidate_hierarchical. Fire-and-forget — a sink must not throw into the cache path. See docs/PARITY.md §10.

6. Fail-open

failOpen (constructor, default true) is the last-resort behaviour under contention when there is no value to serve: true recomputes via the loader and returns it without caching (a HIT) — availability over a cold store; false returns a MISS instead. See docs/PARITY.md §7.

How it wires into your app (PSR + DI)

Freshen ships no framework of its own — it's a small core that plugs into standards you already use, so wire it like any other service:

Framework integration

Prefer a drop-in bridge on Symfony or Laravel — it wires all of this for you. Three principles hold whichever path you take: (1) Freshen needs a Stash pool (not the framework's own PSR-6 pool); (2) async needs a PSR-14 dispatcher — Symfony's is PSR-14, Laravel's is not (its bridge ships a PSR-14 adapter + queue); and (3) a Cache is per-dataset — one loader, its own TTLs. A second dataset is a second loader + a second cache, each with its own config.

If you're not on those frameworks (or want to wire it by hand), see Manual wiring below.

Manual wiring

On Symfony or Laravel, prefer the bridge (above) — it does all of this for you. Wire it by hand only for another framework, a plain PSR-6 setup, or full control. A Cache composes four things; build them as shared services and inject a cache per dataset:

For async invalidation, register one Freshen\AsyncHandler($cache) per cache on a PSR-14 dispatcher, routing each event class to its method (InvalidateEvent → handleInvalidation, InvalidateExactEvent → handleInvalidateExact, RefreshEvent → handleRefresh). Symfony's event_dispatcher is PSR-14; Laravel's is not — which is exactly what the bridges handle (Symfony natively, Laravel via a PSR-14 adapter + queue). A second dataset is a second loader + a second Cache reusing the same shared pool.

Escape hatch & limitations

$cache->asPool() exposes the underlying Stash PSR-6 pool for advanced/host use. Note that whole-store clear is intentionally unsupported: $cache->asPool()->clear() throws a RuntimeException. Stash's pool-wide clear maps to Redis FLUSHDB, which wipes the entire database — every key, not just cached ones — so Freshen does not expose it. Clear cached data by key or prefix with invalidate() / invalidateExact() instead.

The cross-language behaviour contract is docs/PARITY.md.

Security

Tracked by the Packagist security advisory database — run composer audit to check your install. Report vulnerabilities privately via GitHub Security Advisories; the full policy is in SECURITY.md.

Develop / contribute

The test and quality tooling runs across the full PHP version matrix in Docker (each script spins up the right container) — install Docker, then from the repo root:

If you have PHP 8.1+ and Composer locally, you can also run the suite directly inside packages/php:

License

MIT


All versions of freshen with dependencies

PHP Build Version
Package Version
Requires php Version >=8.1
psr/event-dispatcher Version ^1.0
psr/log Version ^3.0
tedivm/stash Version v1.2.1
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package vatvit/freshen contains the following files

Loading the files please wait ...