docs: consolidate documentation into organized structure

- Move 12 markdown files from root to docs/ subdirectories
- Organize documentation by category:
  • docs/troubleshooting/ (1 file)  - Technical troubleshooting guides
  • docs/deployment/      (4 files) - Deployment and security documentation
  • docs/guides/          (3 files) - Feature-specific guides
  • docs/planning/        (4 files) - Planning and improvement proposals

Root directory cleanup:
- Reduced from 16 to 4 markdown files in root
- Only essential project files remain:
  • CLAUDE.md (AI instructions)
  • README.md (Main project readme)
  • CLEANUP_PLAN.md (Current cleanup plan)
  • SRC_STRUCTURE_IMPROVEMENTS.md (Structure improvements)

This improves:
 Documentation discoverability
 Logical organization by purpose
 Clean root directory
 Better maintainability
This commit is contained in:
2025-10-05 11:05:04 +02:00
parent 887847dde6
commit 5050c7d73a
36686 changed files with 196456 additions and 12398919 deletions

View File

@@ -0,0 +1,108 @@
<?php
declare(strict_types=1);
namespace App\Framework\Performance\Entity;
use App\Framework\Core\ValueObjects\Byte;
use App\Framework\Core\ValueObjects\Duration;
use App\Framework\Database\Migration\MigrationVersion;
use App\Framework\Performance\PerformanceCategory;
use DateTimeImmutable;
final readonly class PerformanceMetric
{
public function __construct(
public ?int $id,
public string $operationId,
public string $operationType,
public PerformanceCategory $category,
public ?MigrationVersion $migrationVersion,
public Duration $executionTime,
public Byte $memoryStart,
public Byte $memoryEnd,
public Byte $memoryPeak,
public Byte $memoryDelta,
public bool $success,
public ?string $errorMessage,
public ?array $metadata,
public DateTimeImmutable $createdAt,
public ?DateTimeImmutable $updatedAt = null
) {
}
public static function fromPerformanceSnapshot(
string $operationId,
string $operationType,
PerformanceCategory $category,
Duration $executionTime,
Byte $memoryStart,
Byte $memoryEnd,
Byte $memoryPeak,
Byte $memoryDelta,
bool $success = true,
?string $errorMessage = null,
?MigrationVersion $migrationVersion = null,
?array $metadata = null
): self {
return new self(
id: null,
operationId: $operationId,
operationType: $operationType,
category: $category,
migrationVersion: $migrationVersion,
executionTime: $executionTime,
memoryStart: $memoryStart,
memoryEnd: $memoryEnd,
memoryPeak: $memoryPeak,
memoryDelta: $memoryDelta,
success: $success,
errorMessage: $errorMessage,
metadata: $metadata,
createdAt: new DateTimeImmutable(),
updatedAt: null
);
}
public function toArray(): array
{
return [
'id' => $this->id,
'operation_id' => $this->operationId,
'operation_type' => $this->operationType,
'category' => $this->category->value,
'migration_version' => $this->migrationVersion?->toString(),
'execution_time_ms' => $this->executionTime->toMilliseconds(),
'memory_start_bytes' => $this->memoryStart->toBytes(),
'memory_end_bytes' => $this->memoryEnd->toBytes(),
'memory_peak_bytes' => $this->memoryPeak->toBytes(),
'memory_delta_bytes' => $this->memoryDelta->toBytes(),
'success' => $this->success,
'error_message' => $this->errorMessage,
'metadata' => $this->metadata ? json_encode($this->metadata) : null,
'created_at' => $this->createdAt->format('Y-m-d H:i:s'),
'updated_at' => $this->updatedAt?->format('Y-m-d H:i:s'),
];
}
public static function fromArray(array $data): self
{
return new self(
id: $data['id'] ?? null,
operationId: $data['operation_id'],
operationType: $data['operation_type'],
category: PerformanceCategory::from($data['category']),
migrationVersion: $data['migration_version'] ? MigrationVersion::fromTimestamp($data['migration_version']) : null,
executionTime: Duration::fromMilliseconds($data['execution_time_ms']),
memoryStart: Byte::fromBytes($data['memory_start_bytes']),
memoryEnd: Byte::fromBytes($data['memory_end_bytes']),
memoryPeak: Byte::fromBytes($data['memory_peak_bytes']),
memoryDelta: Byte::fromBytes($data['memory_delta_bytes']),
success: (bool) $data['success'],
errorMessage: $data['error_message'],
metadata: $data['metadata'] ? json_decode($data['metadata'], true) : null,
createdAt: new DateTimeImmutable($data['created_at']),
updatedAt: $data['updated_at'] ? new DateTimeImmutable($data['updated_at']) : null
);
}
}

View File

@@ -4,6 +4,8 @@ declare(strict_types=1);
namespace App\Framework\Performance\Middleware;
use App\Framework\Config\Environment;
use App\Framework\Config\EnvKey;
use App\Framework\Http\Headers;
use App\Framework\Http\HttpMiddleware;
use App\Framework\Http\HttpResponse;
@@ -17,8 +19,6 @@ use App\Framework\Performance\Contracts\PerformanceCollectorInterface;
use App\Framework\Performance\EnhancedPerformanceCollector;
use App\Framework\Performance\PerformanceConfig;
use App\Framework\Performance\PerformanceReporter;
use App\Framework\Config\Environment;
use App\Framework\Config\EnvKey;
#[MiddlewarePriorityAttribute(MiddlewarePriority::LAST)]
final readonly class PerformanceDebugMiddleware implements HttpMiddleware
@@ -45,13 +45,13 @@ final readonly class PerformanceDebugMiddleware implements HttpMiddleware
// EMERGENCY SECURITY DISABLE: Force disable debug output immediately
// Until environment loading is fixed, completely disable debug output
return $context;
// SECURITY: Never output debug info in production, regardless of config
$appEnv = $this->environment->get(EnvKey::APP_ENV, 'production');
if ($appEnv === 'production') {
return $context;
}
// Check if performance tracking is enabled
if (! $this->config->enabled) {
return $context;

View File

@@ -18,6 +18,7 @@ enum PerformanceCategory: string
case SECURITY = 'security';
case BENCHMARK = 'benchmark';
case DISCOVERY = 'discovery';
case CONSOLE = 'console';
case CUSTOM = 'custom';
public static function fromString(string $value): self

View File

@@ -29,11 +29,11 @@ final readonly class PerformanceServiceInitializer
// Performance debugging should NEVER be enabled in production
$appEnv = $this->environment->get(EnvKey::APP_ENV, 'production');
$isDebugEnabled = $this->environment->getBool(EnvKey::APP_DEBUG, false);
// Strict check: Only enable in development AND debug mode
// Force disabled in production regardless of debug setting
$performanceEnabled = ($appEnv === 'development') && $isDebugEnabled;
$config = new PerformanceConfig(
enabled: $performanceEnabled,
detailedReports: $performanceEnabled, // Session info only in dev

View File

@@ -0,0 +1,218 @@
<?php
declare(strict_types=1);
namespace App\Framework\Performance\Repository;
use App\Framework\Core\ValueObjects\Duration;
use App\Framework\Database\ConnectionInterface;
use App\Framework\Database\Migration\MigrationVersion;
use App\Framework\Database\Query\QueryBuilder;
use App\Framework\Database\Query\QueryBuilderFactory;
use App\Framework\Performance\Entity\PerformanceMetric;
use App\Framework\Performance\PerformanceCategory;
use DateTimeImmutable;
final readonly class PerformanceMetricsRepository
{
private QueryBuilder $queryBuilder;
public function __construct(
private ConnectionInterface $connection,
private QueryBuilderFactory $queryBuilderFactory
) {
$this->queryBuilder = $this->queryBuilderFactory->create($connection);
}
public function save(PerformanceMetric $metric): int
{
$data = [
'operation_id' => $metric->operationId,
'operation_type' => $metric->operationType,
'category' => $metric->category->value,
'migration_version' => $metric->migrationVersion?->toString(),
'execution_time_ms' => $metric->executionTime->toMilliseconds(),
'memory_start_bytes' => $metric->memoryStart->toBytes(),
'memory_end_bytes' => $metric->memoryEnd->toBytes(),
'memory_peak_bytes' => $metric->memoryPeak->toBytes(),
'memory_delta_bytes' => $metric->memoryDelta->toBytes(),
'success' => $metric->success,
'error_message' => $metric->errorMessage,
'metadata' => $metric->metadata ? json_encode($metric->metadata) : null,
'created_at' => $metric->createdAt->format('Y-m-d H:i:s'),
'updated_at' => $metric->updatedAt?->format('Y-m-d H:i:s'),
];
return $this->queryBuilder
->table('performance_metrics')
->insert($data);
}
public function saveBatch(array $metrics): bool
{
if (empty($metrics)) {
return true;
}
$data = array_map(fn (PerformanceMetric $metric) => [
'operation_id' => $metric->operationId,
'operation_type' => $metric->operationType,
'category' => $metric->category->value,
'migration_version' => $metric->migrationVersion?->toString(),
'execution_time_ms' => $metric->executionTime->toMilliseconds(),
'memory_start_bytes' => $metric->memoryStart->toBytes(),
'memory_end_bytes' => $metric->memoryEnd->toBytes(),
'memory_peak_bytes' => $metric->memoryPeak->toBytes(),
'memory_delta_bytes' => $metric->memoryDelta->toBytes(),
'success' => $metric->success,
'error_message' => $metric->errorMessage,
'metadata' => $metric->metadata ? json_encode($metric->metadata) : null,
'created_at' => $metric->createdAt->format('Y-m-d H:i:s'),
'updated_at' => $metric->updatedAt?->format('Y-m-d H:i:s'),
], $metrics);
return $this->queryBuilder
->table('performance_metrics')
->insertBatch($data) > 0;
}
public function findById(int $id): ?PerformanceMetric
{
$result = $this->queryBuilder
->table('performance_metrics')
->where('id', '=', $id)
->first();
return $result ? PerformanceMetric::fromArray($result) : null;
}
public function findByOperationId(string $operationId): array
{
$results = $this->queryBuilder
->table('performance_metrics')
->where('operation_id', '=', $operationId)
->orderBy('created_at', 'DESC')
->get();
return array_map(
fn (array $row) => PerformanceMetric::fromArray($row),
$results
);
}
public function findByMigrationVersion(MigrationVersion $version): array
{
$results = $this->queryBuilder
->table('performance_metrics')
->where('migration_version', '=', $version->toString())
->orderBy('created_at', 'DESC')
->get();
return array_map(
fn (array $row) => PerformanceMetric::fromArray($row),
$results
);
}
public function findByCategory(PerformanceCategory $category, int $limit = 100): array
{
$results = $this->queryBuilder
->table('performance_metrics')
->where('category', '=', $category->value)
->orderBy('created_at', 'DESC')
->limit($limit)
->get();
return array_map(
fn (array $row) => PerformanceMetric::fromArray($row),
$results
);
}
public function findFailures(int $limit = 50): array
{
$results = $this->queryBuilder
->table('performance_metrics')
->where('success', '=', false)
->orderBy('created_at', 'DESC')
->limit($limit)
->get();
return array_map(
fn (array $row) => PerformanceMetric::fromArray($row),
$results
);
}
public function findSlowOperations(Duration $threshold, int $limit = 50): array
{
$results = $this->queryBuilder
->table('performance_metrics')
->where('execution_time_ms', '>', $threshold->toMilliseconds())
->orderBy('execution_time_ms', 'DESC')
->limit($limit)
->get();
return array_map(
fn (array $row) => PerformanceMetric::fromArray($row),
$results
);
}
public function getAverageExecutionTime(PerformanceCategory $category): ?Duration
{
$result = $this->queryBuilder
->table('performance_metrics')
->where('category', '=', $category->value)
->where('success', '=', true)
->average('execution_time_ms');
return $result ? Duration::fromMilliseconds((int) $result) : null;
}
public function getPerformanceStatistics(DateTimeImmutable $since): array
{
$results = $this->queryBuilder
->table('performance_metrics')
->select([
'category',
'COUNT(*) as total_operations',
'AVG(execution_time_ms) as avg_execution_time',
'MAX(execution_time_ms) as max_execution_time',
'MIN(execution_time_ms) as min_execution_time',
'SUM(CASE WHEN success = 1 THEN 1 ELSE 0 END) as successful_operations',
'AVG(memory_peak_bytes) as avg_memory_peak',
])
->where('created_at', '>=', $since->format('Y-m-d H:i:s'))
->groupBy('category')
->get();
return array_map(function (array $row) {
return [
'category' => PerformanceCategory::from($row['category']),
'total_operations' => (int) $row['total_operations'],
'avg_execution_time' => Duration::fromMilliseconds((int) $row['avg_execution_time']),
'max_execution_time' => Duration::fromMilliseconds((int) $row['max_execution_time']),
'min_execution_time' => Duration::fromMilliseconds((int) $row['min_execution_time']),
'successful_operations' => (int) $row['successful_operations'],
'success_rate' => (float) $row['successful_operations'] / (float) $row['total_operations'],
'avg_memory_peak' => \App\Framework\Core\ValueObjects\Byte::fromBytes((int) $row['avg_memory_peak']),
];
}, $results);
}
public function deleteOlderThan(DateTimeImmutable $cutoff): int
{
return $this->queryBuilder
->table('performance_metrics')
->where('created_at', '<', $cutoff->format('Y-m-d H:i:s'))
->delete();
}
public function count(): int
{
return $this->queryBuilder
->table('performance_metrics')
->count();
}
}