The Cache component provides a simple, unified interface for storing and retrieving data quickly. It supports two drivers — file-based and SQLite — with identical method signatures, so switching drivers requires only a config change.
- Access via:
$app->cache - Drivers:
file(default),sqlite
Any PHP value can be cached — strings, integers, arrays, objects. Values are serialized automatically before storage and unserialized on retrieval.
Key behaviours:
- TTL of
nullmeans the item never expires - Expired items are deleted lazily on first read (no background process needed)
- Cache keys are hashed with MD5 internally — any string is a valid key
Set the driver in config/jiconfig.php:
return [
'cache_driver' => 'file', // 'file' or 'sqlite'
];
File driver — stores one .cache file per key in storage/Cache/. Zero setup, works everywhere PHP runs. Best for most applications.
SQLite driver — stores all keys in a single SQLite database file at storage/Cache/cache.db. Better for high read/write volume or when you want atomic increment/decrement across concurrent requests.
Both drivers are created automatically. No directories or tables need to be created manually.
set(string $key, mixed $value, int|null $ttl = null): bool
Stores a value in the cache. Returns true on success.
$key— (string) Unique identifier for this cache entry$value— (mixed) Any PHP value: string, int, array, object$ttl— (int|null, optional) Time-to-live in seconds. Passnullto cache forever. Default:null
// Cache a string forever
$app->cache->set('site_name', 'My Website');
// Cache an array for 10 minutes (600 seconds)
$app->cache->set('homepage_posts', $posts, 600);
// Cache a user object for 1 hour
$app->cache->set('user_' . $userId, $user, 3600);
// Cache a computed result for 24 hours
$app->cache->set('stats_summary', $stats, 86400);
get(string $key): mixed
Retrieves a cached value by key. Returns the original value, or null if the key does not exist or has expired. Expired items are deleted automatically on read.
$key— (string) The cache key to retrieve
$value = $app->cache->get('homepage_posts');
if ($value === null) {
// Not in cache - fetch from DB and store
$value = $app->db->table('posts')
->where('status', 'published')
->orderBy('created_at', 'DESC')
->limit(10)
->get();
$app->cache->set('homepage_posts', $value, 600);
}
// $value is now populated either way
The pattern above is the standard cache-aside pattern: check cache first, fall back to the source of truth, then store the result.
has(string $key): bool
Returns true if the key exists in cache and has not expired. Does not return the value. Use this when you need to branch on cache presence without fetching the value immediately.
$key— (string) The cache key to check
if ($app->cache->has('report_generated')) {
echo 'Your report is ready. ';
echo 'Download';
} else {
echo 'Report is still being generated. Please check back soon.';
}
// Guard against regenerating something expensive
if (!$app->cache->has('daily_email_sent')) {
sendDailySummaryEmail();
$app->cache->set('daily_email_sent', true, 86400);
}
delete(string $key): bool
Removes a specific key from the cache immediately. Returns true if the item was found and deleted, false if it did not exist.
$key— (string) The cache key to remove
// Invalidate cached user data after profile update
$app->db->table('users')
->where('id', $userId)
->update(['name' => $_POST['name']]);
$app->cache->delete('user_' . $userId);
// Invalidate homepage cache after a new post is published
$app->db->table('posts')->insert($newPost);
$app->cache->delete('homepage_posts');
increment(string $key, int $value = 1): int|false
decrement(string $key, int $value = 1): int|false
Increments or decrements a numeric cached value by the given amount. Returns the new value on success, or false if the key does not exist or is not numeric.
$key— (string) Cache key holding a numeric value$value— (int, optional) Amount to add or subtract. Default:1
// Track page views
$app->cache->set('page_views_home', 0);
$app->cache->increment('page_views_home'); // 1
$app->cache->increment('page_views_home'); // 2
$app->cache->increment('page_views_home', 5); // 7
// Count down remaining slots
$app->cache->set('seats_available', 50);
$remaining = $app->cache->decrement('seats_available'); // 49
// Simple rate-limiting counter
$key = 'api_calls_' . $userId;
if (!$app->cache->has($key)) {
$app->cache->set($key, 0, 3600); // reset every hour
}
$calls = $app->cache->increment($key);
if ($calls > 100) {
$app->exit(429, 'Rate limit exceeded.');
}
clear(): void
Removes all items from the cache, regardless of expiry. Use with care in production — this wipes the entire cache store, which will cause a burst of database or computation load as everything is rebuilt.
// Full cache wipe (e.g. after a deployment)
$app->cache->clear();
// Protect with an admin check first
if (!$app->auth->isAdminLoggedIn()) {
$app->exit(403);
}
$app->cache->clear();
echo 'Cache cleared successfully.';
gc(): void
Garbage collection — scans the entire cache store and deletes only expired items. Unlike clear(), valid items are kept. Run this periodically to reclaim disk space.
// Run on a random 1-in-100 request to avoid a dedicated cron job
if (rand(1, 100) === 1) {
$app->cache->gc();
}
// Or call from a dedicated maintenance endpoint
if (isset($_GET['gc']) && $app->auth->isAdminLoggedIn()) {
$app->cache->gc();
echo 'Expired cache items removed.';
}
Cache expensive or frequently read queries to reduce database load:
<?php
require __DIR__ . '/vendor/autoload.php';
$app = new App();
// Helper: get categories (rarely change, cache for 1 hour)
function getCategories($app) {
$key = 'all_categories';
$cached = $app->cache->get($key);
if ($cached !== null) {
return $cached;
}
$categories = $app->db->table('categories')
->orderBy('name')
->get();
$app->cache->set($key, $categories, 3600);
return $categories;
}
// Helper: get published posts for a category (cache 10 min)
function getPostsByCategory($app, $categoryId) {
$key = 'posts_cat_' . $categoryId;
$cached = $app->cache->get($key);
if ($cached !== null) {
return $cached;
}
$posts = $app->db->table('posts')
->where('category_id', $categoryId)
->where('status', 'published')
->orderBy('created_at', 'DESC')
->limit(20)
->get();
$app->cache->set($key, $posts, 600);
return $posts;
}
// When a post is published, invalidate its category cache
function publishPost($app, $post) {
$app->db->table('posts')
->where('id', $post['id'])
->update(['status' => 'published']);
// Invalidate so next request fetches fresh data
$app->cache->delete('posts_cat_' . $post['category_id']);
}
$categories = getCategories($app);
$posts = getPostsByCategory($app, 3);
Cache responses from slow external APIs to avoid hitting rate limits and improve response time:
cache->get($key);
if ($cached !== null) {
return $cached;
}
// Fetch from external API
$response = $app->httpRequest->get(
'https://api.weather.example.com/current',
['city' => $city, 'key' => 'YOUR_API_KEY']
);
if (!$response || !isset($response['temp'])) {
// API failed - return stale data if available, else null
return null;
}
$app->cache->set($key, $response, 900); // 15 minutes
return $response;
}
$weather = getWeather($app, 'London');
if ($weather) {
echo 'Temperature in London: ' . $weather['temp'] . '┬░C';
} else {
echo 'Weather data unavailable.';
}