Cache Savings Calculator — PHP source
See what prompt caching saves — uncached vs cached cost over N requests, with the write-premium break-even point.
This is the PHP implementation — the same logic the interactive tool runs, in a shareable, citable form.
<?php
/**
* cache_savings — uncached vs prompt-cached LLM cost comparison.
*
* Language: PHP (8.1+, standard library only)
* Source: CosmoDev polyglot showcase port of the Cache Savings Calculator
* tool, ported from src/lib/cacheSavings.ts (the canonical
* TypeScript implementation).
* Tool: https://dev.cosmolabs.org/tools/cache-savings-calculator
* License: display source — part of CosmoDev's polyglot tool pages.
*
* Design goals:
* - Pure + deterministic; never throws.
* - Functionally equivalent to the TS reference: same inputs -> same outputs.
* - Self-contained: stdlib only (no Composer packages).
*
* The TS original takes a full AiModel record but reads only its four pricing
* rates, so this port narrows the parameter to exactly those fields. Array
* keys keep the TS camelCase names (the established snippet convention):
* $model = ['inputPerM' => 10.0, 'outputPerM' => 50.0,
* 'cacheReadPerM' => 1.0, 'cacheWritePerM' => 12.5] // ?float each
* $input = ['promptTokens' => 10000, 'outputTokens' => 1000, 'hits' => 5]
* Any null rate makes every output null — the caller renders an explanatory
* empty state instead of partial math. All rates are per-1M-token USD,
* mirroring the cost conventions of llmCost.ts.
*
* Reference vectors (fixture model 10 / 50 / 1 / 12.5 — see
* cacheSavings.test.ts, the lock-step contract every port mirrors):
* 10k in / 1k out / 5 hits -> uncached 0.75, cached 0.425, savings 0.325,
* 43.333...% saved, break-even 13 hits. At 1 hit caching LOSES 0.035 (an
* honest negative saving). hits < 1 counts as 1. Zero tokens -> zero costs
* with 0%. cacheReadPerM 0 -> breakEvenHits null (write premium never repaid).
*/
declare(strict_types=1);
/**
* The all-null result used when any pricing rate is missing. Exposed as a
* function (not a constant) so every caller gets a fresh array.
*/
function cache_math_nulled(): array
{
return [
'uncached' => null,
'cached' => null,
'savings' => null,
'savingsPct' => null,
'breakEvenHits' => null,
];
}
/**
* Compare uncached vs prompt-cached cost for one model.
*
* @param array{inputPerM: ?float, outputPerM: ?float, cacheReadPerM: ?float, cacheWritePerM: ?float} $model
* The four per-1M-token USD pricing rates cacheMath reads from the TS AiModel.
* @param array{promptTokens: float, outputTokens: float, hits: float} $input
* Prompt/output tokens per request and the cache-hit count (values < 1 are treated as 1).
*
* @return array{uncached: ?float, cached: ?float, savings: ?float, savingsPct: ?float, breakEvenHits: ?int}
* uncached: hits × (prompt·in$/M + output·out$/M) / 1e6.
* cached: (prompt·write$/M + hits × (prompt·read$/M + output·out$/M)) / 1e6 —
* one cache write, `hits` cache reads, output billed every request.
* savings: uncached − cached (negative when caching costs more).
* savingsPct: savings / uncached × 100; 0 when uncached is 0.
* breakEvenHits: ceil(write$/M / read$/M) when read$/M > 0 — cache hits needed
* for cumulative READ spend to equal ONE write premium; null otherwise.
*/
function cache_math(array $model, array $input): array
{
$ipM = $model['inputPerM'];
$opM = $model['outputPerM'];
$cr = $model['cacheReadPerM'];
$cw = $model['cacheWritePerM'];
if ($ipM === null || $opM === null || $cr === null || $cw === null) {
return cache_math_nulled();
}
$hits = max(1, $input['hits']);
$inT = $input['promptTokens'];
$outT = $input['outputTokens'];
// One cache write, `hits` cache reads; output tokens are billed on every request.
$uncached = ($hits * ($inT * $ipM + $outT * $opM)) / 1000000;
$cached = ($inT * $cw + $hits * ($inT * $cr + $outT * $opM)) / 1000000;
$savings = $uncached - $cached;
// Loose comparison on purpose: PHP int(0) must equal float(0.0) exactly as
// TS's `uncached === 0` does across its single number type.
$savingsPct = $uncached == 0.0 ? 0 : ($savings / $uncached) * 100;
$breakEvenHits = $cr > 0 ? (int) ceil($cw / $cr) : null;
return [
'uncached' => $uncached,
'cached' => $cached,
'savings' => $savings,
'savingsPct' => $savingsPct,
'breakEvenHits' => $breakEvenHits,
];
}
Also available in 13 other languages
Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →