feat(Docker): Upgrade to PHP 8.5.0RC3 with native ext-uri support

BREAKING CHANGE: Requires PHP 8.5.0RC3

Changes:
- Update Docker base image from php:8.4-fpm to php:8.5.0RC3-fpm
- Enable ext-uri for native WHATWG URL parsing support
- Update composer.json PHP requirement from ^8.4 to ^8.5
- Add ext-uri as required extension in composer.json
- Move URL classes from Url.php85/ to Url/ directory (now compatible)
- Remove temporary PHP 8.4 compatibility workarounds

Benefits:
- Native URL parsing with Uri\WhatWg\Url class
- Better performance for URL operations
- Future-proof with latest PHP features
- Eliminates PHP version compatibility issues
This commit is contained in:
2025-10-27 09:31:28 +01:00
parent 799f74f00a
commit c8b47e647d
81 changed files with 6988 additions and 601 deletions

248
scripts/seed-ml-models.php Normal file
View File

@@ -0,0 +1,248 @@
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use App\Framework\MachineLearning\ModelManagement\ModelRegistry;
use App\Framework\MachineLearning\ModelManagement\ValueObjects\ModelMetadata;
use App\Framework\MachineLearning\ModelManagement\ValueObjects\ModelType;
use App\Framework\MachineLearning\ModelManagement\ModelPerformanceMonitor;
use App\Framework\Core\ValueObjects\Version;
use App\Framework\Core\ValueObjects\Timestamp;
use App\Framework\Core\ValueObjects\Duration;
use App\Framework\Core\AppBootstrapper;
use App\Framework\Performance\EnhancedPerformanceCollector;
use App\Framework\DateTime\SystemClock;
use App\Framework\DateTime\SystemHighResolutionClock;
use App\Framework\Performance\MemoryMonitor;
echo "🌱 ML Models Seeder\n";
echo "==================\n\n";
// Bootstrap application
$basePath = dirname(__DIR__);
$clock = new SystemClock();
$highResClock = new SystemHighResolutionClock();
$memoryMonitor = new MemoryMonitor();
$collector = new EnhancedPerformanceCollector($clock, $highResClock, $memoryMonitor, enabled: true);
$bootstrapper = new AppBootstrapper($basePath, $collector, $memoryMonitor);
$container = $bootstrapper->bootstrapWorker();
/** @var ModelRegistry $registry */
$registry = $container->get(ModelRegistry::class);
/** @var ModelPerformanceMonitor $performanceMonitor */
$performanceMonitor = $container->get(ModelPerformanceMonitor::class);
// Sample Models to Seed
$models = [
// 1. Fraud Detection Model (Supervised, Production)
[
'name' => 'fraud-detector',
'type' => ModelType::SUPERVISED,
'version' => '1.0.0',
'environment' => 'production',
'configuration' => [
'threshold' => 0.75,
'min_confidence' => 0.6,
'feature_count' => 15,
'algorithm' => 'random_forest',
],
'metrics' => [
'accuracy' => 0.94,
'precision' => 0.91,
'recall' => 0.89,
'f1_score' => 0.90,
'total_predictions' => 15234,
'average_confidence' => 0.87,
'confusion_matrix' => [
'true_positive' => 1345,
'true_negative' => 12789,
'false_positive' => 567,
'false_negative' => 533,
],
],
],
// 2. Spam Classifier (Supervised, Production - Degraded)
[
'name' => 'spam-classifier',
'type' => ModelType::SUPERVISED,
'version' => '2.0.0',
'environment' => 'production',
'configuration' => [
'threshold' => 0.80,
'min_confidence' => 0.7,
'feature_count' => 20,
'algorithm' => 'gradient_boosting',
],
'metrics' => [
'accuracy' => 0.78, // Degraded performance
'precision' => 0.82,
'recall' => 0.71,
'f1_score' => 0.76,
'total_predictions' => 8923,
'average_confidence' => 0.75,
'confusion_matrix' => [
'true_positive' => 892,
'true_negative' => 6051,
'false_positive' => 1234,
'false_negative' => 746,
],
],
],
// 3. User Segmentation (Unsupervised, Production)
[
'name' => 'user-segmentation',
'type' => ModelType::UNSUPERVISED,
'version' => '1.2.0',
'environment' => 'production',
'configuration' => [
'n_clusters' => 5,
'algorithm' => 'k_means',
'feature_count' => 12,
],
'metrics' => [
'accuracy' => 0.88,
'total_predictions' => 5678,
'average_confidence' => 0.83,
'silhouette_score' => 0.72,
],
],
// 4. Anomaly Detection (Unsupervised, Production)
[
'name' => 'anomaly-detector',
'type' => ModelType::UNSUPERVISED,
'version' => '1.5.0',
'environment' => 'production',
'configuration' => [
'contamination' => 0.1,
'algorithm' => 'isolation_forest',
'feature_count' => 10,
],
'metrics' => [
'accuracy' => 0.92,
'total_predictions' => 12456,
'average_confidence' => 0.85,
'anomaly_rate' => 0.08,
],
],
// 5. Recommendation Engine (Reinforcement, Development)
[
'name' => 'recommendation-engine',
'type' => ModelType::REINFORCEMENT,
'version' => '0.5.0',
'environment' => 'development',
'configuration' => [
'learning_rate' => 0.001,
'discount_factor' => 0.95,
'exploration_rate' => 0.1,
'algorithm' => 'q_learning',
],
'metrics' => [
'accuracy' => 0.67, // Still in development
'total_predictions' => 2345,
'average_confidence' => 0.62,
'average_reward' => 3.42,
],
],
// 6. Sentiment Analysis (Supervised, Staging)
[
'name' => 'sentiment-analyzer',
'type' => ModelType::SUPERVISED,
'version' => '2.1.0',
'environment' => 'staging',
'configuration' => [
'threshold' => 0.65,
'algorithm' => 'lstm',
'feature_count' => 50,
'max_sequence_length' => 100,
],
'metrics' => [
'accuracy' => 0.91,
'precision' => 0.89,
'recall' => 0.92,
'f1_score' => 0.90,
'total_predictions' => 7890,
'average_confidence' => 0.86,
],
],
];
echo "Registering " . count($models) . " ML models...\n\n";
foreach ($models as $index => $modelData) {
$modelNum = $index + 1;
echo "[$modelNum/" . count($models) . "] Registering {$modelData['name']} v{$modelData['version']}...\n";
try {
// Create ModelMetadata
$metadata = new ModelMetadata(
modelName: $modelData['name'],
modelType: $modelData['type'],
version: Version::fromString($modelData['version']),
configuration: $modelData['configuration'],
performanceMetrics: [],
createdAt: Timestamp::now(),
deployedAt: $modelData['environment'] === 'production' ? Timestamp::now() : null,
environment: $modelData['environment'],
metadata: [
'seeded_at' => date('Y-m-d H:i:s'),
'description' => "Sample {$modelData['type']->value} model for testing",
]
);
// Register model
$registry->register($metadata);
// Track performance metrics using trackPrediction
$performanceMonitor->trackPrediction(
modelName: $modelData['name'],
version: Version::fromString($modelData['version']),
prediction: 1, // Dummy prediction
actual: 1, // Dummy actual
confidence: $modelData['metrics']['average_confidence']
);
// Update metrics manually to match our sample data
if (isset($modelData['metrics']['confusion_matrix'])) {
$cm = $modelData['metrics']['confusion_matrix'];
// Track individual predictions to build up confusion matrix
for ($i = 0; $i < $cm['true_positive']; $i++) {
$performanceMonitor->trackPrediction(
modelName: $modelData['name'],
version: Version::fromString($modelData['version']),
prediction: 1,
actual: 1,
confidence: $modelData['metrics']['average_confidence']
);
}
}
echo " ✅ Successfully registered {$modelData['name']}\n";
echo " - Type: {$modelData['type']->value}\n";
echo " - Environment: {$modelData['environment']}\n";
echo " - Accuracy: " . round($modelData['metrics']['accuracy'] * 100, 2) . "%\n";
if ($modelData['metrics']['accuracy'] < 0.85) {
echo " ⚠️ Warning: Degraded performance\n";
}
echo "\n";
} catch (\Exception $e) {
echo " ❌ Error: {$e->getMessage()}\n\n";
}
}
echo "==================\n";
echo "✅ Seeding complete!\n\n";
echo "Next steps:\n";
echo "1. Visit https://localhost/admin/ml/dashboard to see the models\n";
echo "2. Check API endpoint: https://localhost/api/ml/dashboard\n";
echo "3. Verify foreach attribute rendering in Models Overview table\n";

View File

@@ -0,0 +1,247 @@
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use App\Framework\Core\AppBootstrapper;
use App\Framework\Performance\EnhancedPerformanceCollector;
use App\Framework\DateTime\SystemClock;
use App\Framework\DateTime\SystemHighResolutionClock;
use App\Framework\Performance\MemoryMonitor;
use App\Framework\Notification\Notification;
use App\Framework\Notification\Storage\NotificationRepository;
use App\Framework\Notification\ValueObjects\NotificationId;
use App\Framework\Notification\ValueObjects\NotificationPriority;
use App\Framework\Notification\ValueObjects\NotificationStatus;
use App\Framework\Notification\ValueObjects\NotificationChannel;
use App\Framework\Core\ValueObjects\Timestamp;
echo "🔔 Notifications Seeder\n";
echo "=====================\n\n";
// Bootstrap application
$basePath = dirname(__DIR__);
$clock = new SystemClock();
$highResClock = new SystemHighResolutionClock();
$memoryMonitor = new MemoryMonitor();
$collector = new EnhancedPerformanceCollector($clock, $highResClock, $memoryMonitor, enabled: true);
$bootstrapper = new AppBootstrapper($basePath, $collector, $memoryMonitor);
$container = $bootstrapper->bootstrapWorker();
/** @var NotificationRepository $repository */
$repository = $container->get(NotificationRepository::class);
// Sample notification types
final readonly class NotificationType implements App\Framework\Notification\ValueObjects\NotificationTypeInterface
{
public function __construct(private string $value) {}
public function toString(): string
{
return $this->value;
}
public function getDisplayName(): string
{
return match ($this->value) {
'ml_performance_degradation' => 'ML Performance Degradation',
'ml_model_deployed' => 'ML Model Deployed',
'ml_training_complete' => 'ML Training Complete',
'system_alert' => 'System Alert',
'security_alert' => 'Security Alert',
'info' => 'Information',
default => ucwords(str_replace('_', ' ', $this->value)),
};
}
public function isCritical(): bool
{
return in_array($this->value, [
'ml_performance_degradation',
'security_alert',
'system_alert'
], true);
}
public function equals($other): bool
{
return $other instanceof self && $this->value === $other->value;
}
}
// Sample notifications to create
$notifications = [
// ML Performance Alerts
[
'type' => 'ml_performance_degradation',
'title' => 'ML Model Performance Degradation Detected',
'body' => 'The spam-classifier model (v2.0.0) is experiencing performance degradation. Current accuracy: 78% (below threshold of 85%). Immediate attention recommended.',
'priority' => NotificationPriority::URGENT,
'action_url' => '/admin/ml/models/spam-classifier',
'action_label' => 'View Model Details',
'created_offset' => -7200, // 2 hours ago
],
[
'type' => 'ml_model_deployed',
'title' => 'New ML Model Deployed Successfully',
'body' => 'Fraud detector model v1.0.0 has been successfully deployed to production. Initial accuracy: 94%. Monitoring active.',
'priority' => NotificationPriority::NORMAL,
'action_url' => '/admin/ml/models/fraud-detector',
'action_label' => 'View Deployment',
'created_offset' => -3600, // 1 hour ago
],
[
'type' => 'ml_training_complete',
'title' => 'Model Training Completed',
'body' => 'Sentiment analyzer training completed successfully. New version 2.1.0 ready for deployment. Validation accuracy: 91%.',
'priority' => NotificationPriority::HIGH,
'action_url' => '/admin/ml/models/sentiment-analyzer',
'action_label' => 'Review & Deploy',
'created_offset' => -1800, // 30 minutes ago
],
// System Alerts
[
'type' => 'system_alert',
'title' => 'High Memory Usage Detected',
'body' => 'System memory usage exceeded 85% threshold. Current usage: 87%. Consider scaling resources or optimizing memory-intensive processes.',
'priority' => NotificationPriority::HIGH,
'action_url' => '/admin/performance',
'action_label' => 'View Metrics',
'created_offset' => -900, // 15 minutes ago
],
[
'type' => 'system_alert',
'title' => 'Queue Backlog Warning',
'body' => 'Job queue backlog detected. 1,234 pending jobs in queue. Processing rate: 45 jobs/minute. Estimated clearance time: 27 minutes.',
'priority' => NotificationPriority::NORMAL,
'action_url' => '/admin/queue',
'action_label' => 'View Queue',
'created_offset' => -600, // 10 minutes ago
],
// Security Alerts
[
'type' => 'security_alert',
'title' => 'Suspicious Login Attempts Detected',
'body' => 'Multiple failed login attempts detected from IP 203.0.113.42. Rate limiting applied. Review access logs for potential security threat.',
'priority' => NotificationPriority::URGENT,
'action_url' => '/admin/security/logs',
'action_label' => 'View Security Logs',
'created_offset' => -300, // 5 minutes ago
],
[
'type' => 'security_alert',
'title' => 'WAF Blocked Malicious Request',
'body' => 'Web Application Firewall blocked SQL injection attempt. Attack pattern detected: UNION SELECT. Source IP: 198.51.100.10.',
'priority' => NotificationPriority::HIGH,
'action_url' => '/admin/security/waf',
'action_label' => 'View WAF Logs',
'created_offset' => -120, // 2 minutes ago
],
// Info Notifications
[
'type' => 'info',
'title' => 'System Backup Completed',
'body' => 'Daily system backup completed successfully. Backup size: 2.3 GB. Next scheduled backup: Tomorrow at 2:00 AM.',
'priority' => NotificationPriority::LOW,
'action_url' => '/admin/backups',
'action_label' => 'View Backups',
'created_offset' => -86400, // 1 day ago
],
[
'type' => 'info',
'title' => 'Database Optimization Recommended',
'body' => 'Database performance analysis suggests optimizing 3 tables. Estimated performance improvement: 15%. Schedule maintenance window for optimization.',
'priority' => NotificationPriority::NORMAL,
'action_url' => '/admin/database/optimization',
'action_label' => 'View Recommendations',
'created_offset' => -172800, // 2 days ago
],
[
'type' => 'info',
'title' => 'Weekly Performance Report Available',
'body' => 'Weekly system performance report is now available. Key metrics: 99.8% uptime, 145ms avg response time, 1.2M requests processed.',
'priority' => NotificationPriority::LOW,
'action_url' => '/admin/reports/weekly',
'action_label' => 'View Report',
'created_offset' => -259200, // 3 days ago
],
];
echo "Creating " . count($notifications) . " sample notifications...\n\n";
$createdCount = 0;
$now = time();
foreach ($notifications as $index => $notificationData) {
$notificationNum = $index + 1;
echo "[$notificationNum/" . count($notifications) . "] Creating: {$notificationData['title']}\n";
try {
// Create notification timestamp (offset from now)
$createdAt = Timestamp::fromTimestamp($now + $notificationData['created_offset']);
// For recent notifications (< 1 hour ago), leave unread
// For older notifications, mark some as read
$isRecent = abs($notificationData['created_offset']) < 3600;
$shouldBeRead = !$isRecent && (($index % 3) === 0); // Mark every 3rd older notification as read
$notification = new Notification(
id: NotificationId::generate(),
recipientId: 'admin',
type: new NotificationType($notificationData['type']),
title: $notificationData['title'],
body: $notificationData['body'],
createdAt: $createdAt,
data: [],
channels: [NotificationChannel::DATABASE],
priority: $notificationData['priority'],
status: $shouldBeRead ? NotificationStatus::READ : NotificationStatus::SENT,
sentAt: $createdAt,
readAt: $shouldBeRead ? Timestamp::fromTimestamp($now + $notificationData['created_offset'] + 300) : null,
actionUrl: $notificationData['action_url'],
actionLabel: $notificationData['action_label']
);
$repository->save($notification);
$createdCount++;
$statusIcon = $shouldBeRead ? '✓' : '📬';
$priorityLabel = $notificationData['priority']->value;
echo " $statusIcon Successfully created ($priorityLabel priority, " . ($shouldBeRead ? 'read' : 'unread') . ")\n";
echo " - Created: " . $createdAt->format('Y-m-d H:i:s') . "\n";
if ($notificationData['action_url']) {
echo " - Action: {$notificationData['action_label']}{$notificationData['action_url']}\n";
}
echo "\n";
} catch (\Exception $e) {
echo " ❌ Error: {$e->getMessage()}\n\n";
}
}
echo "=====================\n";
echo "✅ Seeding complete!\n\n";
echo "Summary:\n";
echo "- Total notifications created: $createdCount\n";
// Get current stats
$unreadCount = $repository->countUnreadByUser('admin');
echo "- Unread notifications: $unreadCount\n";
$allNotifications = $repository->findByUser('admin', limit: 100);
echo "- Total notifications for admin: " . count($allNotifications) . "\n\n";
echo "Next steps:\n";
echo "1. Visit https://localhost/admin/notifications to view the notifications\n";
echo "2. Test mark as read functionality\n";
echo "3. Test mark all as read functionality\n";
echo "4. Verify unread badge updates in real-time\n";