Status: Accepted Date: 2024-02-15 Applies to: KaririCode\Dotenv 4.2+
In production, .env files are static — they change only during deployments. Parsing the same file on every request wastes CPU cycles. The ideal cache format should:
- Eliminate file I/O and string parsing entirely after first load.
- Leverage existing PHP infrastructure (no external cache servers).
- Be atomic — no partial reads during deployment.
- Self-invalidate when source files change.
The cache is a plain PHP file that returns an associative array:
<?php
// Auto-generated by KaririCode\Dotenv — do not edit manually.
// Regenerate with: php vendor/bin/kariricode-dotenv cache:dump
return array (
'__metadata' =>
array (
'generated_at' => '2024-02-15T10:30:00+00:00',
'source_hash' => 'a1b2c3d4e5f6...',
'generator' => 'KaririCode\\Dotenv v4.x',
),
'DB_HOST' => 'localhost',
'DB_PORT' => '5432',
'APP_DEBUG' => 'true',
);When OPcache is enabled, PHP compiles this file once into shared memory. Subsequent include calls load directly from shared memory — no disk I/O, no parsing, no lexing. This is the fastest possible read path in PHP.
$tmpFile = $path . '.tmp.' . getmypid();
file_put_contents($tmpFile, $content);
rename($tmpFile, $path);
opcache_invalidate($path, true);- Write to a temporary file (PID-suffixed to avoid collisions).
- Atomic
rename()— POSIX guarantees this is atomic on the same filesystem. - Invalidate OPcache for the target path so the new version is picked up.
The __metadata.source_hash is computed from file modification times and sizes of all source .env files:
$context = hash_init('md5');
foreach ($filePaths as $filePath) {
hash_update($context, (string) filemtime($filePath));
hash_update($context, (string) filesize($filePath));
}
return hash_final($context);On load, if the stored hash doesn't match the current source hash, the cache is considered stale and bypassed. This avoids content hashing (expensive for large files) while still detecting changes.
load() called
→ cachePath configured?
→ No: parse .env files normally
→ Yes: compute source hash → load cache file → hash matches?
→ Yes: use cached variables (zero parse cost)
→ No: parse .env files normally (cache is stale)
The cache stores raw string values, not typed values. Type detection and casting run after cache load, ensuring that custom detectors/casters registered at runtime are applied consistently regardless of whether the source was cache or file.
Positive:
- Zero parse cost in production when OPcache is enabled — variables load from shared memory.
- No external cache dependency (Redis, Memcached, APCu).
- Atomic writes prevent partial reads during deployment.
- Automatic staleness detection without manual cache clearing.
var_export()produces valid PHP that survives OPcache restarts.
Negative:
- Requires filesystem write access in the deployment step.
- OPcache must be enabled for the full performance benefit (still faster than parsing without OPcache due to
includevs string parsing). - MD5-based hash is not cryptographically secure — but it only needs collision resistance for cache invalidation, not security.
Rejected alternatives:
- APCu cache: Requires ext-apcu and doesn't survive OPcache resets.
- JSON cache: Requires
json_decode()on every request — slower thaninclude. - Serialized PHP:
unserialize()is slower thanincludefor array data and has security implications. - Content hashing (SHA-256 of file contents): Correct but expensive — reading entire file contents to check if cache is valid defeats the purpose.