Download the PHP package niktomo/kura without Composer
On this page you can find all versions of the php package niktomo/kura. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Package kura
Short Description A QueryBuilder-compatible APCu cache layer for Laravel reference data.
License MIT
Informations about the package kura
Japanese version: README-ja.md
[!WARNING] This package is currently under active development. APIs may change without notice before v1.0.0.
Kura
Kura (蔵 — storehouse) is a Laravel package that caches reference data in APCu and queries it with a Laravel QueryBuilder-compatible API.
Load data once from CSV or DB, store it in APCu, and query it with the same fluent API you already know — no database queries at runtime. Index-accelerated lookups keep response times sub-millisecond even with large datasets.
Kura provides a QueryBuilder-compatible API backed by APCu (in-process memory), serving reference data entirely without touching the database at query time. Index-based lookups deliver fast filtered queries; generator-based full scans keep per-request memory use low. The design assumes that the pks list and index data for each table fit within APCu's configured shared memory.
Why Kura?
- Familiar API —
where,orderBy,paginate,find,count,sum— same as Laravel's QueryBuilder - Sub-millisecond reads — APCu shared memory, no network round-trips (see benchmarks)
- Low memory footprint — Generator-based traversal; never loads entire datasets into PHP memory
- Smart indexes — Binary search indexes for range queries, composite index hashmaps for O(1) multi-column lookups, automatic chunk splitting for large datasets
- Self-Healing — Cache eviction? Kura automatically rebuilds from the data source — your app never sees stale or missing data
- Version management — Switch reference data versions seamlessly via DB or CSV
- Pluggable data sources —
LoaderInterfacelets you bring any backend: CSV, Eloquent, QueryBuilder, REST API, S3, etc. Built-in loaders included; swap or extend with 5 methods
Requirements
- PHP 8.2 / 8.3 / 8.4 (8.5+ expected to work)
- Laravel ^11.0 / ^12.0 / ^13.0
- APCu extension (
pecl install apcu)
Laravel Octane is not yet supported. Persistent processes require per-request state reset (version resolver, override) that is not yet implemented. Octane support is planned for v1.0.
Installation
Quick Start
1. Configure
Edit config/kura.php — the key sections for getting started:
2. Prepare your data
Kura supports two data sources: CSV files and Database (Eloquent).
Option A: CSV files
Organize CSV files by table, with a shared versions.csv:
versions.csv — controls which version is active:
The version with activated_at <= now and the latest timestamp is used.
stations/table.yaml — column types, index declarations, and primary key:
Supported types: int, float, bool, string
- Composite indexes (
[col1, col2]) enable O(1) multi-column equality lookups - If no indexes are needed, omit the
indexeskey entirely
Tip:
primary_keydefaults toid— only specify it if your table uses a different column name.
stations/data.csv — the actual data, with a version column:
The CsvLoader loads rows where version IS NULL (always loaded, shared across all versions) or version <= currentVersion (past and current versions). Rows where version > currentVersion are skipped (future versions not yet active).
Option B: Database (Eloquent)
No data CSV needed — load directly from your database. Column definitions and index declarations are read from table.yaml in the table directory:
Or with the version-managed resolver (recommended for production):
Option C: Custom Loader
Any data source works — implement LoaderInterface with 5 methods:
See Implementing a Custom Loader for a full example.
3. Register tables
Option A: Auto-discovery (CSV only)
The easiest approach when using CSV files — Kura scans a directory and registers every subdirectory that contains data.csv automatically. No AppServiceProvider code needed.
That's it — stations and lines are registered automatically. To override the primary key for a specific table:
Note: Adding a new table directory requires restarting the PHP process (
php artisan octane:restartfor Octane, or reloading PHP-FPM). The directory scan runs once at boot. Updating data inside an existing table (data.csv) also requires runningphp artisan kura:rebuild— there is no automatic file-change detection. Self-Healing only triggers on APCu TTL expiry, not on data.csv modification.
Option B: Manual registration
In your AppServiceProvider (or a dedicated service provider):
4. Build the cache
5. Query
How it works
When you call kura:rebuild, Kura loads data from your source (CSV or DB), stores each record in APCu, and builds search indexes. Subsequent queries read directly from shared memory — no database involved.
APCu key structure
Self-Healing
If APCu evicts cached data, Kura detects the loss at query time and automatically rebuilds from the data source. Your application always receives complete, correct results.
With rebuild.strategy = 'queue', the rebuild runs asynchronously — the current request gets data from the Loader directly while the cache is rebuilt in the background.
With rebuild.strategy = 'callback', you supply a custom callable (e.g. to dispatch to a Horizon priority queue). See Cache Architecture for details.
Supported Query Methods
Kura implements ~99 methods from Laravel's QueryBuilder. For the complete list, see Laravel Builder Coverage.
WHERE
where, orWhere, whereNot, whereIn, whereNotIn, whereBetween, whereNull, whereNotNull, whereLike, whereColumn, whereAll, whereAny, whereNone, whereExists, whereFilter, whereRowValuesIn, and more.
ORDER BY / LIMIT / PAGINATION
orderBy, orderByDesc, latest, oldest, inRandomOrder, limit, offset, paginate, simplePaginate
RETRIEVAL / AGGREGATES
get, first, find, sole, value, pluck, cursor, count, min, max, sum, avg, exists
Documentation
| Document | Description |
|---|---|
| 日本語 | Version switching, CSV/DB drivers, middleware |
| 日本語 | Index types, chunking, composite indexes, range queries |
| 日本語 | Common query patterns and examples |
| 日本語 | Internal design: TTL, self-healing, rebuild flow |
| 日本語 | Class structure and responsibilities |
| 日本語 | Full API compatibility table |
| 日本語 | APCu issues, slow queries, multi-server setup |
| 日本語 | Extension points, fixed behaviours, QueryBuilder rules |
Design Constraints & Extension Points
Kura is intentionally narrow in scope. Two operations are central: QueryBuilder-compatible filtering and index-based lookups. Everything else is either pluggable via interface/closure or fixed by design.
What you can extend
| Extension point | How |
|---|---|
| Data source | Implement LoaderInterface (5 methods: load, columns, indexes, primaryKey, version) |
| Version resolution | Bind VersionResolverInterface in the service container |
| Rebuild dispatch | strategy: callback with a \Closure(\Kura\CacheRepository): void |
| Per-table TTL | tables key in config/kura.php |
Fixed by design
| Behaviour | Reason |
|---|---|
APCu key format kura:{table}:{version}:{type} |
Self-healing and invalidation depend on this structure |
| Full-table load (no partial updates) | Ensures consistency; diff rebuilds are not supported |
| Self-healing is always active | Triggered automatically on missing pks key; cannot be disabled |
| Index types: unique, non-unique, composite | Declared by the Loader; no runtime registration API |
| QueryBuilder join / raw / cross-table subquery methods excluded | These have no meaning over in-memory flat data; closure-based condition grouping within a single table is supported |
See Design Constraints for extension patterns, memory model details, and contribution rules.
Configuration
All options are in config/kura.php. Below is the complete reference.
Benchmarks
Environment
| Host | Apple M4 Pro |
| Runtime | Docker linux/aarch64 |
| PHP | 8.4.19 (JIT disabled) |
| APCu | 5.1.28 (apc.shm_size=256M) |
| Iterations | 500 per scenario |
| Metric | p95 latency |
Dataset
Synthetic product records with the following schema and indexes:
| Column | Type | Cardinality |
|---|---|---|
id |
int | unique (1…N) |
name |
string | unique |
country |
string | 5 values (JP / US / GB / DE / FR), evenly distributed |
category |
string | 10 values (electronics / clothing / …), evenly distributed |
price |
float | 200 distinct values (1.99–200.99), cyclic |
active |
bool | 67% true / 33% false |
Indexes declared: country, price, country|category (composite).
Results (p95 latency)
| Scenario | 1K records | 10K records | 100K records |
|---|---|---|---|
find($id) — single record lookup |
0.9 µs | 1.0 µs | 1.0 µs |
where('country','JP') — indexed = (20% hit) |
119 µs | 1.08 ms | 12.60 ms |
where('country','JP')->where('category','electronics') — composite index (2% hit) |
99 µs | 885 µs | 9.68 ms |
whereBetween('price', [50,100]) — range index (25% hit) |
159 µs | 1.35 ms | 14.47 ms |
where('country','JP')->orderBy('price') — index walk |
170 µs | 1.40 ms | 17.49 ms |
where('active', true) — non-indexed full scan (67% hit) |
400 µs | 3.74 ms | 39.42 ms |
get() — all records |
436 µs | 3.71 ms | 38.74 ms |
Cache build (rebuild()) |
3.02 ms | 12.32 ms | 118.24 ms |
Index-accelerated queries (bold) are 3–5× faster than full scans at the same dataset size. At 100K records, indexed queries respond in under 18 ms; a non-indexed full scan takes ~39 ms.
orderBy on an indexed column uses a pre-sorted index walk — no PHP sort needed.
orderBy on a non-indexed column falls back to a PHP-side sort that collects all matching records into memory first (O(N)). For large tables, declare an index on any column you frequently sort by.
Run
php benchmarks/benchmark.phpin the Docker environment to reproduce.
With JIT + igbinary (100K records, p95)
Enabling PHP JIT and the igbinary APCu serializer delivers an additional 15–40% improvement across all query types.
| Scenario | Baseline | JIT + igbinary | Improvement |
|---|---|---|---|
find($id) — single record |
0.8 µs | 0.7 µs | -12% |
where country — indexed = |
12.93 ms | 10.04 ms | -22% |
| composite index | 9.96 ms | 7.50 ms | -25% |
whereBetween — range narrow |
14.58 ms | 11.82 ms | -19% |
orderBy price — index walk |
17.81 ms | 11.72 ms | -34% |
orderBy name — PHP sort |
42.05 ms | 31.12 ms | -26% |
count() |
12.77 ms | 8.96 ms | -30% |
| non-indexed full scan | 39.79 ms | 32.88 ms | -17% |
JIT accelerates closure evaluation in the WhereCompiler and PHP-side sort. igbinary reduces APCu deserialization overhead on every record fetch.
To enable, add the following to your php.ini:
ext-igbinary can be installed via pecl install igbinary.
Cache Warming
Pre-warm the APCu cache via HTTP after deployment (before traffic arrives).
Enable the warm endpoint
Generate a Bearer token
Endpoints
POST /kura/warm — rebuild cache for all registered tables
GET /kura/warm/status/{batchId} — check async rebuild progress
Testing without APCu
Use ArrayStore as a drop-in replacement for ApcuStore in tests and CI:
ArrayStore operates on plain PHP arrays — no APCu extension required.
License
MIT
All versions of kura with dependencies
ext-apcu Version *
illuminate/console Version ^11.0|^12.0|^13.0
illuminate/contracts Version ^11.0|^12.0|^13.0
illuminate/database Version ^11.0|^12.0|^13.0
illuminate/support Version ^11.0|^12.0|^13.0
symfony/yaml Version ^6.0|^7.0