- Add comprehensive health check system with multiple endpoints - Add Prometheus metrics endpoint - Add production logging configurations (5 strategies) - Add complete deployment documentation suite: * QUICKSTART.md - 30-minute deployment guide * DEPLOYMENT_CHECKLIST.md - Printable verification checklist * DEPLOYMENT_WORKFLOW.md - Complete deployment lifecycle * PRODUCTION_DEPLOYMENT.md - Comprehensive technical reference * production-logging.md - Logging configuration guide * ANSIBLE_DEPLOYMENT.md - Infrastructure as Code automation * README.md - Navigation hub * DEPLOYMENT_SUMMARY.md - Executive summary - Add deployment scripts and automation - Add DEPLOYMENT_PLAN.md - Concrete plan for immediate deployment - Update README with production-ready features All production infrastructure is now complete and ready for deployment.
170 lines
4.8 KiB
PHP
170 lines
4.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Framework\LiveComponents\Caching;
|
|
|
|
use App\Framework\Cache\CacheKey;
|
|
|
|
/**
|
|
* Cache Key Builder
|
|
*
|
|
* Builds intelligent cache keys for LiveComponents with varyBy support.
|
|
*
|
|
* Key Format: livecomponent:{component_name}:{base_key}:{vary_suffix}
|
|
*
|
|
* Examples:
|
|
* - livecomponent:user-stats:total-count:u:123:l:en
|
|
* - livecomponent:product-list:category-5:l:de:f:new-ui
|
|
* - livecomponent:dashboard:widgets:global
|
|
*
|
|
* Framework Pattern: Readonly service class
|
|
*/
|
|
final readonly class CacheKeyBuilder
|
|
{
|
|
/**
|
|
* Build cache key with varyBy support
|
|
*
|
|
* @param string $componentName - Component name (e.g., "user-stats")
|
|
* @param string $baseKey - Base cache key from component
|
|
* @param VaryBy|null $varyBy - VaryBy specification
|
|
* @param CacheContext|null $context - Current cache context
|
|
* @return CacheKey - Complete cache key
|
|
*/
|
|
public function build(
|
|
string $componentName,
|
|
string $baseKey,
|
|
?VaryBy $varyBy = null,
|
|
?CacheContext $context = null
|
|
): CacheKey {
|
|
// Start with component prefix
|
|
$parts = ['livecomponent', $componentName, $baseKey];
|
|
|
|
// Add varyBy suffix if specified
|
|
if ($varyBy && $context) {
|
|
$varySuffix = $varyBy->apply($context);
|
|
$parts[] = $varySuffix;
|
|
} else {
|
|
$parts[] = 'global';
|
|
}
|
|
|
|
$keyString = implode(':', $parts);
|
|
|
|
return CacheKey::fromString($keyString);
|
|
}
|
|
|
|
/**
|
|
* Build cache key from callable varyBy
|
|
*
|
|
* Allows dynamic varyBy logic per component.
|
|
*
|
|
* @param string $componentName - Component name
|
|
* @param string $baseKey - Base cache key
|
|
* @param callable $varyByCallable - Callable that returns VaryBy
|
|
* @param CacheContext|null $context - Current cache context
|
|
* @return CacheKey - Complete cache key
|
|
*/
|
|
public function buildFromCallable(
|
|
string $componentName,
|
|
string $baseKey,
|
|
callable $varyByCallable,
|
|
?CacheContext $context = null
|
|
): CacheKey {
|
|
$varyBy = $varyByCallable($context);
|
|
|
|
if (! $varyBy instanceof VaryBy) {
|
|
throw new \InvalidArgumentException(
|
|
'varyBy callable must return VaryBy instance'
|
|
);
|
|
}
|
|
|
|
return $this->build($componentName, $baseKey, $varyBy, $context);
|
|
}
|
|
|
|
/**
|
|
* Build cache key with array-based varyBy
|
|
*
|
|
* Convenience method for simple varyBy arrays.
|
|
*
|
|
* @param string $componentName - Component name
|
|
* @param string $baseKey - Base cache key
|
|
* @param array $varyByArray - Array like ['userId', 'locale']
|
|
* @param CacheContext|null $context - Current cache context
|
|
* @return CacheKey - Complete cache key
|
|
*/
|
|
public function buildFromArray(
|
|
string $componentName,
|
|
string $baseKey,
|
|
array $varyByArray,
|
|
?CacheContext $context = null
|
|
): CacheKey {
|
|
$varyBy = $this->varyByFromArray($varyByArray);
|
|
|
|
return $this->build($componentName, $baseKey, $varyBy, $context);
|
|
}
|
|
|
|
/**
|
|
* Convert array to VaryBy
|
|
*
|
|
* Supports: 'userId', 'locale', 'roles', ['featureFlags' => [...]], ['custom' => [...]]
|
|
*/
|
|
private function varyByFromArray(array $array): VaryBy
|
|
{
|
|
$userId = in_array('userId', $array, true);
|
|
$locale = in_array('locale', $array, true);
|
|
$roles = in_array('roles', $array, true);
|
|
|
|
$featureFlags = [];
|
|
$custom = [];
|
|
|
|
foreach ($array as $key => $value) {
|
|
if ($key === 'featureFlags' && is_array($value)) {
|
|
$featureFlags = $value;
|
|
} elseif ($key === 'custom' && is_array($value)) {
|
|
$custom = $value;
|
|
}
|
|
}
|
|
|
|
return new VaryBy(
|
|
userId: $userId,
|
|
locale: $locale,
|
|
roles: $roles,
|
|
featureFlags: $featureFlags,
|
|
custom: $custom
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Parse cache key to extract components
|
|
*
|
|
* Useful for debugging and introspection.
|
|
*
|
|
* @param CacheKey $cacheKey - Cache key to parse
|
|
* @return array{component: string, base_key: string, vary_suffix: string}|null
|
|
*/
|
|
public function parse(CacheKey $cacheKey): ?array
|
|
{
|
|
$parts = explode(':', $cacheKey->toString());
|
|
|
|
if (count($parts) < 4 || $parts[0] !== 'livecomponent') {
|
|
return null;
|
|
}
|
|
|
|
return [
|
|
'component' => $parts[1],
|
|
'base_key' => $parts[2],
|
|
'vary_suffix' => implode(':', array_slice($parts, 3)),
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Check if cache key belongs to specific component
|
|
*/
|
|
public function isComponentKey(CacheKey $cacheKey, string $componentName): bool
|
|
{
|
|
$parsed = $this->parse($cacheKey);
|
|
|
|
return $parsed && $parsed['component'] === $componentName;
|
|
}
|
|
}
|