Files
michaelschiemer/src/Framework/LiveComponents/DataProviderResolver.php
Michael Schiemer fc3d7e6357 feat(Production): Complete production deployment infrastructure
- 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.
2025-10-25 19:18:37 +02:00

196 lines
6.3 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Framework\LiveComponents;
use App\Framework\DI\Container;
use App\Framework\Discovery\Results\DiscoveryRegistry;
use App\Framework\LiveComponents\Attributes\DataProvider;
use ReflectionClass;
/**
* DataProviderResolver - Resolves DataProvider implementations
*
* Uses the Discovery system to find classes with #[DataProvider] attribute
* and resolves the correct provider instance based on interface and name.
*
* Example:
* ```php
* $provider = $resolver->resolve(ChartDataProvider::class, 'demo');
* // Returns instance of DemoChartDataProvider
* ```
*/
final class DataProviderResolver
{
/**
* Cache: interface + name => provider class
* @var array<string, class-string>
*/
private array $cache = [];
public function __construct(
private readonly DiscoveryRegistry $discoveryRegistry,
private readonly Container $container
) {
}
/**
* Resolve a data provider by interface and name
*
* @template T
* @param class-string<T> $interface The provider interface
* @param string $name The provider name (e.g., 'demo', 'database')
* @return T|null The resolved provider instance or null if not found
*/
public function resolve(string $interface, string $name): ?object
{
error_log("[DataProviderResolver::resolve] START: interface=$interface, name=$name");
// Check cache first
$cacheKey = $interface . '::' . $name;
if (isset($this->cache[$cacheKey])) {
error_log("[DataProviderResolver::resolve] Cache HIT: $cacheKey");
return $this->container->get($this->cache[$cacheKey]);
}
error_log("[DataProviderResolver::resolve] Cache MISS, calling findProviderClass");
// Find provider class via discovery
$providerClass = $this->findProviderClass($interface, $name);
error_log("[DataProviderResolver::resolve] findProviderClass returned: " . ($providerClass ?? 'NULL'));
if ($providerClass === null) {
return null;
}
// Cache the result
$this->cache[$cacheKey] = $providerClass;
// Resolve instance from container
return $this->container->get($providerClass);
}
/**
* Find provider class that implements interface with given name
*
* @param class-string $interface
* @param string $name
* @return class-string|null
*/
private function findProviderClass(string $interface, string $name): ?string
{
// Get all classes with DataProvider attribute from AttributeRegistry
$discoveredAttributes = $this->discoveryRegistry->attributes()->get(DataProvider::class);
// DEBUG: Write to file
file_put_contents('/tmp/resolver-debug.log', sprintf(
"[%s] Looking for: interface=%s, name=%s, found=%d attributes\n",
date('Y-m-d H:i:s'),
$interface,
$name,
count($discoveredAttributes)
), FILE_APPEND);
foreach ($discoveredAttributes as $discoveredAttribute) {
// Get class name as string from ClassName value object
$className = $discoveredAttribute->className->getFullyQualified();
file_put_contents('/tmp/resolver-debug.log', sprintf(
" Checking: class=%s\n",
$className
), FILE_APPEND);
// Get provider name from attribute arguments
$arguments = $discoveredAttribute->arguments;
if (! isset($arguments['name'])) {
file_put_contents('/tmp/resolver-debug.log', " SKIP: no 'name' argument\n", FILE_APPEND);
continue; // Skip if no name argument (shouldn't happen with our attribute)
}
file_put_contents('/tmp/resolver-debug.log', sprintf(
" name='%s' (looking for '%s')\n",
$arguments['name'],
$name
), FILE_APPEND);
// Check if name matches
if ($arguments['name'] !== $name) {
file_put_contents('/tmp/resolver-debug.log', " SKIP: name mismatch\n", FILE_APPEND);
continue;
}
// Check if class implements the interface
$reflection = new ReflectionClass($className);
$implements = $reflection->implementsInterface($interface);
file_put_contents('/tmp/resolver-debug.log', sprintf(
" implements %s? %s\n",
$interface,
$implements ? 'YES' : 'NO'
), FILE_APPEND);
if ($implements) {
file_put_contents('/tmp/resolver-debug.log', " MATCH! Returning $className\n", FILE_APPEND);
return $className;
}
}
return null;
}
/**
* Check if a provider exists
*
* @param class-string $interface
* @param string $name
*/
public function has(string $interface, string $name): bool
{
$cacheKey = $interface . '::' . $name;
if (isset($this->cache[$cacheKey])) {
return true;
}
return $this->findProviderClass($interface, $name) !== null;
}
/**
* Get all available provider names for an interface
*
* @param class-string $interface
* @return array<string> List of provider names
*/
public function getAvailableProviders(string $interface): array
{
$providers = [];
// Get all classes with DataProvider attribute from AttributeRegistry
$discoveredAttributes = $this->discoveryRegistry->attributes()->get(DataProvider::class);
foreach ($discoveredAttributes as $discoveredAttribute) {
// Get class name as string from ClassName value object
$className = $discoveredAttribute->className->getFullyQualified();
// Check if class implements the interface
$reflection = new ReflectionClass($className);
if (! $reflection->implementsInterface($interface)) {
continue;
}
// Get provider name from arguments
$arguments = $discoveredAttribute->arguments;
if (isset($arguments['name'])) {
$providers[] = $arguments['name'];
}
}
return array_unique($providers);
}
}