Enable Discovery debug logging for production troubleshooting

- Add DISCOVERY_LOG_LEVEL=debug
- Add DISCOVERY_SHOW_PROGRESS=true
- Temporary changes for debugging InitializerProcessor fixes on production
This commit is contained in:
2025-08-11 20:13:26 +02:00
parent 59fd3dd3b1
commit 55a330b223
3683 changed files with 2956207 additions and 16948 deletions

View File

@@ -0,0 +1,132 @@
<?php
declare(strict_types=1);
namespace App\Framework\DI\Dependency;
use App\Framework\Core\ValueObjects\ClassName;
/**
* Type-safe cache for constructor dependency information
*/
final class ConstructorCache
{
/**
* @var array<string, ConstructorCacheEntry>
*/
private array $entries = [];
/**
* Check if class is cached
*/
public function has(ClassName $className): bool
{
return isset($this->entries[$className->getFullyQualified()]);
}
/**
* Get cache entry for class
*/
public function get(ClassName $className): ?ConstructorCacheEntry
{
return $this->entries[$className->getFullyQualified()] ?? null;
}
/**
* Store cache entry for class
*/
public function set(ConstructorCacheEntry $entry): void
{
$this->entries[$entry->className->getFullyQualified()] = $entry;
}
/**
* Remove cache entry for specific class
*/
public function remove(ClassName $className): void
{
unset($this->entries[$className->getFullyQualified()]);
}
/**
* Clear all cache entries
*/
public function clear(): void
{
$this->entries = [];
}
/**
* Get all cached class names
* @return array<ClassName>
*/
public function getCachedClasses(): array
{
return array_map(
fn (ConstructorCacheEntry $entry) => $entry->className,
array_values($this->entries)
);
}
/**
* Get cache statistics
*/
public function getStats(): array
{
$totalEntries = count($this->entries);
$emptyConstructors = 0;
$withDependencies = 0;
$totalParameters = 0;
foreach ($this->entries as $entry) {
if ($entry->hasParameters()) {
$withDependencies++;
$totalParameters += $entry->getParameterCount();
} else {
$emptyConstructors++;
}
}
return [
'total_entries' => $totalEntries,
'empty_constructors' => $emptyConstructors,
'with_dependencies' => $withDependencies,
'total_parameters' => $totalParameters,
'average_parameters' => $withDependencies > 0 ? round($totalParameters / $withDependencies, 2) : 0,
];
}
/**
* Find all entries that depend on a specific class
* @return array<ConstructorCacheEntry>
*/
public function getDependentsOf(ClassName $className): array
{
$dependents = [];
foreach ($this->entries as $entry) {
$dependencies = $entry->getClassDependencies();
foreach ($dependencies as $dependency) {
if ($dependency->equals($className)) {
$dependents[] = $entry;
break;
}
}
}
return $dependents;
}
/**
* Get entries that use specific dependency type
* @return array<ConstructorCacheEntry>
*/
public function getEntriesWithDependencyType(DependencyType $type): array
{
return array_filter(
$this->entries,
fn (ConstructorCacheEntry $entry) => $entry->usesDependencyType($type)
);
}
}

View File

@@ -0,0 +1,119 @@
<?php
declare(strict_types=1);
namespace App\Framework\DI\Dependency;
use App\Framework\Core\ValueObjects\ClassName;
/**
* Represents a cached constructor parameter with its dependency information
*/
final readonly class ConstructorCacheEntry
{
private function __construct(
public ClassName $className,
public ParameterCacheCollection $parameters
) {
}
/**
* Create cache entry for a class with no constructor parameters
*/
public static function empty(ClassName $className): self
{
return new self($className, ParameterCacheCollection::empty());
}
/**
* Create cache entry from resolved dependencies
* @param array<array<string, mixed>> $cacheInfo
*/
public static function fromCacheInfo(ClassName $className, array $cacheInfo): self
{
return new self($className, ParameterCacheCollection::fromArray($cacheInfo));
}
/**
* Create cache entry from ParameterCacheInfo objects
*/
public static function fromParameters(ClassName $className, ParameterCacheInfo ...$parameters): self
{
return new self($className, ParameterCacheCollection::from(...$parameters));
}
/**
* Check if constructor has parameters
*/
public function hasParameters(): bool
{
return ! $this->parameters->isEmpty();
}
/**
* Get number of parameters
*/
public function getParameterCount(): int
{
return $this->parameters->count();
}
/**
* Get parameter cache info at specific position
*/
public function getParameter(int $position): ?ParameterCacheInfo
{
return $this->parameters->get($position);
}
/**
* Get parameter cache collection
*/
public function getParameters(): ParameterCacheCollection
{
return $this->parameters;
}
/**
* Get parameters as legacy array format (for backward compatibility)
* @return array<array<string, mixed>>
*/
public function getParametersAsArray(): array
{
return $this->parameters->toLegacyArray();
}
/**
* Get dependency types used in this constructor
* @return array<DependencyType>
*/
public function getDependencyTypes(): array
{
return $this->parameters->getDependencyTypes();
}
/**
* Check if constructor uses specific dependency type
*/
public function usesDependencyType(DependencyType $dependencyType): bool
{
return $this->parameters->usesDependencyType($dependencyType);
}
/**
* Get all class dependencies referenced in this constructor
* @return array<ClassName>
*/
public function getClassDependencies(): array
{
return $this->parameters->getClassDependencies();
}
/**
* Check if constructor depends on specific class
*/
public function dependsOn(ClassName $className): bool
{
return $this->parameters->dependsOn($className);
}
}

View File

@@ -0,0 +1,68 @@
<?php
declare(strict_types=1);
namespace App\Framework\DI\Dependency;
use App\Framework\Core\ValueObjects\ClassName;
/**
* Represents a resolved dependency with caching information
*/
final readonly class Dependency
{
private function __construct(
public DependencyType $type,
public mixed $value,
public ?ClassName $className = null
) {
}
public static function default(mixed $value): self
{
return new self(DependencyType::DEFAULT, $value);
}
public static function null(): self
{
return new self(DependencyType::NULL, null);
}
public static function dependency(object $value, ClassName $className): self
{
return new self(DependencyType::DEPENDENCY, $value, $className);
}
public static function nullableDependency(object $value, ClassName $className): self
{
return new self(DependencyType::NULLABLE_DEPENDENCY, $value, $className);
}
public function getCacheInfo(): array
{
return match ($this->type) {
DependencyType::DEFAULT => [
'type' => $this->type->value,
'value' => $this->value,
],
DependencyType::NULL => [
'type' => $this->type->value,
],
DependencyType::DEPENDENCY, DependencyType::NULLABLE_DEPENDENCY => [
'type' => $this->type->value,
'class' => $this->className?->getFullyQualified(),
],
};
}
public static function fromCacheInfo(array $cacheInfo): ?ClassName
{
$type = DependencyType::from($cacheInfo['type']);
return match ($type) {
DependencyType::DEPENDENCY, DependencyType::NULLABLE_DEPENDENCY =>
isset($cacheInfo['class']) ? ClassName::create($cacheInfo['class']) : null,
default => null
};
}
}

View File

@@ -0,0 +1,86 @@
<?php
declare(strict_types=1);
namespace App\Framework\DI\Dependency;
/**
* Collection of resolved dependencies with type safety and iteration support
*/
final readonly class DependencyCollection implements \IteratorAggregate, \Countable
{
/**
* @param array<mixed> $dependencies
*/
private function __construct(
private array $dependencies
) {
}
/**
* Create collection from resolved dependency values
* @param array<mixed> $dependencies
*/
public static function fromValues(array $dependencies): self
{
return new self($dependencies);
}
/**
* Create empty collection
*/
public static function empty(): self
{
return new self([]);
}
/**
* Get all dependency values as array (for backward compatibility)
* @return array<mixed>
*/
public function toArray(): array
{
return $this->dependencies;
}
/**
* Get dependency at specific index
*/
public function get(int $index): mixed
{
return $this->dependencies[$index] ?? null;
}
/**
* Check if collection has dependencies
*/
public function isEmpty(): bool
{
return empty($this->dependencies);
}
/**
* Count dependencies
*/
public function count(): int
{
return count($this->dependencies);
}
/**
* Get iterator for foreach loops
* @return \ArrayIterator<int, mixed>
*/
public function getIterator(): \ArrayIterator
{
return new \ArrayIterator($this->dependencies);
}
/**
* Create collection with additional dependency
*/
public function with(mixed $dependency): self
{
return new self([...$this->dependencies, $dependency]);
}
}

View File

@@ -0,0 +1,13 @@
<?php
declare(strict_types=1);
namespace App\Framework\DI\Dependency;
enum DependencyType: string
{
case DEFAULT = 'default';
case NULL = 'null';
case DEPENDENCY = 'dependency';
case NULLABLE_DEPENDENCY = 'nullable_dependency';
}

View File

@@ -0,0 +1,205 @@
<?php
declare(strict_types=1);
namespace App\Framework\DI\Dependency;
use App\Framework\Core\ValueObjects\ClassName;
use App\Framework\DI\Container;
/**
* Type-safe collection of parameter cache information
*/
final readonly class ParameterCacheCollection implements \IteratorAggregate, \Countable
{
/**
* @param array<ParameterCacheInfo> $parameters
*/
private function __construct(
private array $parameters
) {
}
/**
* Create collection from ParameterCacheInfo objects
* @param ParameterCacheInfo ...$parameters
*/
public static function from(ParameterCacheInfo ...$parameters): self
{
return new self($parameters);
}
/**
* Create collection from legacy cache array format
* @param array<array<string, mixed>> $cacheArray
*/
public static function fromArray(array $cacheArray): self
{
$parameters = array_map(
fn (array $info) => ParameterCacheInfo::fromArray($info),
$cacheArray
);
return new self($parameters);
}
/**
* Create empty collection
*/
public static function empty(): self
{
return new self([]);
}
/**
* Get iterator for foreach loops
* @return \ArrayIterator<int, ParameterCacheInfo>
*/
public function getIterator(): \ArrayIterator
{
return new \ArrayIterator($this->parameters);
}
/**
* Count parameters
*/
public function count(): int
{
return count($this->parameters);
}
/**
* Check if collection is empty
*/
public function isEmpty(): bool
{
return empty($this->parameters);
}
/**
* Get parameter at specific position
*/
public function get(int $position): ?ParameterCacheInfo
{
return $this->parameters[$position] ?? null;
}
/**
* Get all parameters as array
* @return array<ParameterCacheInfo>
*/
public function toArray(): array
{
return $this->parameters;
}
/**
* Convert to legacy array format (for backward compatibility)
* @return array<array<string, mixed>>
*/
public function toLegacyArray(): array
{
return array_map(
fn (ParameterCacheInfo $param) => $param->toArray(),
$this->parameters
);
}
/**
* Get all dependency types used
* @return array<DependencyType>
*/
public function getDependencyTypes(): array
{
$types = [];
foreach ($this->parameters as $param) {
if (! in_array($param->type, $types, true)) {
$types[] = $param->type;
}
}
return $types;
}
/**
* Check if collection uses specific dependency type
*/
public function usesDependencyType(DependencyType $type): bool
{
foreach ($this->parameters as $param) {
if ($param->type === $type) {
return true;
}
}
return false;
}
/**
* Get all class dependencies
* @return array<ClassName>
*/
public function getClassDependencies(): array
{
$dependencies = [];
foreach ($this->parameters as $param) {
if ($param->hasClassDependency()) {
$dependencies[] = $param->className;
}
}
return $dependencies;
}
/**
* Check if any parameter depends on specific class
*/
public function dependsOn(ClassName $className): bool
{
foreach ($this->parameters as $param) {
if ($param->dependsOn($className)) {
return true;
}
}
return false;
}
/**
* Filter parameters by dependency type
*/
public function filterByType(DependencyType $type): self
{
$filtered = array_filter(
$this->parameters,
fn (ParameterCacheInfo $param) => $param->type === $type
);
return new self(array_values($filtered));
}
/**
* Get parameters that have class dependencies
*/
public function getClassDependencyParameters(): self
{
$filtered = array_filter(
$this->parameters,
fn (ParameterCacheInfo $param) => $param->hasClassDependency()
);
return new self(array_values($filtered));
}
/**
* Resolve all parameter values using container
* @return array<mixed>
*/
public function resolveValues(Container $container): array
{
return array_map(
fn (ParameterCacheInfo $param) => $param->resolveValue($container),
$this->parameters
);
}
}

View File

@@ -0,0 +1,117 @@
<?php
declare(strict_types=1);
namespace App\Framework\DI\Dependency;
use App\Framework\Core\ValueObjects\ClassName;
use App\Framework\DI\Container;
/**
* Represents cached information for a single constructor parameter
*/
final readonly class ParameterCacheInfo
{
private function __construct(
public DependencyType $type,
public mixed $value = null,
public ?ClassName $className = null
) {
}
/**
* Create cache info for default value parameter
*/
public static function default(mixed $value): self
{
return new self(DependencyType::DEFAULT, $value);
}
/**
* Create cache info for null parameter
*/
public static function null(): self
{
return new self(DependencyType::NULL);
}
/**
* Create cache info for dependency parameter
*/
public static function dependency(ClassName $className): self
{
return new self(DependencyType::DEPENDENCY, null, $className);
}
/**
* Create cache info for nullable dependency parameter
*/
public static function nullableDependency(ClassName $className): self
{
return new self(DependencyType::NULLABLE_DEPENDENCY, null, $className);
}
/**
* Create from legacy cache array format
*/
public static function fromArray(array $cacheInfo): self
{
$type = DependencyType::from($cacheInfo['type']);
return match ($type) {
DependencyType::DEFAULT => self::default($cacheInfo['value']),
DependencyType::NULL => self::null(),
DependencyType::DEPENDENCY => self::dependency(ClassName::create($cacheInfo['class'])),
DependencyType::NULLABLE_DEPENDENCY => self::nullableDependency(ClassName::create($cacheInfo['class'])),
};
}
/**
* Convert to legacy array format (for backward compatibility)
*/
public function toArray(): array
{
return match ($this->type) {
DependencyType::DEFAULT => [
'type' => $this->type->value,
'value' => $this->value,
],
DependencyType::NULL => [
'type' => $this->type->value,
],
DependencyType::DEPENDENCY, DependencyType::NULLABLE_DEPENDENCY => [
'type' => $this->type->value,
'class' => $this->className?->getFullyQualified(),
],
};
}
/**
* Check if this parameter has a class dependency
*/
public function hasClassDependency(): bool
{
return $this->className !== null;
}
/**
* Check if this parameter has a specific class dependency
*/
public function dependsOn(ClassName $className): bool
{
return $this->className !== null && $this->className->equals($className);
}
/**
* Get the resolved value for this parameter
*/
public function resolveValue(Container $container): mixed
{
return match ($this->type) {
DependencyType::DEFAULT => $this->value,
DependencyType::NULL => null,
DependencyType::DEPENDENCY, DependencyType::NULLABLE_DEPENDENCY =>
$container->get($this->className->getFullyQualified()),
};
}
}

View File

@@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace App\Framework\DI\Dependency;
/**
* Represents a resolved parameter with its value and cache information
*/
final readonly class ResolvedParameter
{
public function __construct(
public mixed $value,
public Dependency $dependency
) {
}
/**
* Get cache information for this parameter
*/
public function getCacheInfo(): array
{
return $this->dependency->getCacheInfo();
}
}