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,153 @@
<?php
declare(strict_types=1);
namespace App\Framework\Database\Serialization;
use App\Framework\Database\Metadata\MetadataRegistry;
use App\Framework\Database\TypeConverter;
use ReflectionClass;
/**
* Serializer for database entities with support for complex types
*/
final class EntitySerializer
{
public function __construct(
private readonly MetadataRegistry $metadataRegistry,
private readonly TypeConverter $typeConverter
) {
}
/**
* Serialize entity to array format suitable for caching
*/
public function serialize(object $entity): array
{
$entityClass = $entity::class;
$metadata = $this->metadataRegistry->getMetadata($entityClass);
$reflection = new ReflectionClass($entityClass);
$data = [
'__class' => $entityClass,
'__properties' => [],
];
foreach ($metadata->getProperties() as $propertyMetadata) {
$property = $reflection->getProperty($propertyMetadata->name);
$value = $property->getValue($entity);
// Convert to database format for consistent serialization
$convertedValue = $this->typeConverter->convertToDatabaseValue(
$value,
$propertyMetadata->type
);
$data['__properties'][$propertyMetadata->name] = [
'value' => $convertedValue,
'type' => $propertyMetadata->type,
'nullable' => $propertyMetadata->nullable,
];
}
return $data;
}
/**
* Deserialize array data back to entity object
*/
public function deserialize(array $data, string $expectedClass): object
{
if (! isset($data['__class']) || $data['__class'] !== $expectedClass) {
throw new SerializationException("Serialized data does not match expected class {$expectedClass}");
}
if (! isset($data['__properties'])) {
throw new SerializationException("Invalid serialized entity data - missing properties");
}
$reflection = new ReflectionClass($expectedClass);
$entity = $reflection->newInstanceWithoutConstructor();
foreach ($data['__properties'] as $propertyName => $propertyData) {
if (! $reflection->hasProperty($propertyName)) {
continue; // Skip properties that no longer exist
}
$property = $reflection->getProperty($propertyName);
// Convert from database format back to PHP value
$value = $this->typeConverter->convertToPHPValue(
$propertyData['value'],
$propertyData['type']
);
$property->setValue($entity, $value);
}
return $entity;
}
/**
* Serialize array of entities
*/
public function serializeCollection(array $entities): array
{
return array_map([$this, 'serialize'], $entities);
}
/**
* Deserialize array of entities
*/
public function deserializeCollection(array $serializedEntities, string $entityClass): array
{
return array_map(
fn (array $data) => $this->deserialize($data, $entityClass),
$serializedEntities
);
}
/**
* Get cache-friendly hash of entity for versioning
*/
public function getEntityHash(object $entity): string
{
$serialized = $this->serialize($entity);
// Remove class info for hash calculation
unset($serialized['__class']);
return md5(serialize($serialized));
}
/**
* Check if serialized data is compatible with current class structure
*/
public function isCompatible(array $data, string $entityClass): bool
{
if (! isset($data['__class']) || $data['__class'] !== $entityClass) {
return false;
}
if (! isset($data['__properties'])) {
return false;
}
$metadata = $this->metadataRegistry->getMetadata($entityClass);
$reflection = new ReflectionClass($entityClass);
// Check if all required properties exist
foreach ($metadata->getProperties() as $propertyMetadata) {
if (! $propertyMetadata->nullable && ! isset($data['__properties'][$propertyMetadata->name])) {
return false;
}
if (! $reflection->hasProperty($propertyMetadata->name)) {
return false;
}
}
return true;
}
}

View File

@@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
namespace App\Framework\Database\Serialization;
use App\Framework\Exception\ExceptionContext;
use App\Framework\Exception\FrameworkException;
final class SerializationException extends FrameworkException
{
public static function invalidData(string $reason): self
{
return new self(
message: "Invalid serialized data: {$reason}",
context: ExceptionContext::forOperation('serialization_decode', 'Database')
->withData(['reason' => $reason])
);
}
public static function classMismatch(string $expected, string $actual): self
{
return new self(
message: "Class mismatch: expected {$expected}, got {$actual}",
context: ExceptionContext::forOperation('serialization_class_match', 'Database')
->withData([
'expected_class' => $expected,
'actual_class' => $actual,
])
);
}
public static function missingProperty(string $class, string $property): self
{
return new self(
message: "Missing required property {$property} in class {$class}",
context: ExceptionContext::forOperation('serialization_property_check', 'Database')
->withData([
'class' => $class,
'missing_property' => $property,
])
);
}
}