PHP Lesson 6: Deployment, DevOps & Advanced Architecture

PHP Lesson 6: Deployment, DevOps & Advanced Architecture

Oct 31, 2025 - 13:25
 0  137528
PHP Lesson 6: Deployment, DevOps & Advanced Architecture

PHP Lesson 6: Deployment, DevOps & Advanced Architecture

Welcome to Lesson 6! Now we're moving into professional deployment strategies, DevOps practices, and advanced architectural patterns used in enterprise PHP applications.

Part 1: Docker Containerization

1.1 Complete Docker Setup

dockerfile
# Dockerfile
FROM php:8.2-fpm

# Set working directory
WORKDIR /var/www

# Install system dependencies
RUN apt-get update && apt-get install -y \
    git \
    curl \
    libpng-dev \
    libonig-dev \
    libxml2-dev \
    zip \
    unzip \
    libzip-dev \
    default-mysql-client \
    nginx \
    supervisor

# Clear cache
RUN apt-get clean && rm -rf /var/lib/apt/lists/*

# Install PHP extensions
RUN docker-php-ext-install pdo_mysql mbstring exif pcntl bcmath gd zip

# Install Redis extension
RUN pecl install redis && docker-php-ext-enable redis

# Install Composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer

# Copy nginx configuration
COPY docker/nginx.conf /etc/nginx/sites-available/default

# Copy supervisor configuration
COPY docker/supervisord.conf /etc/supervisor/conf.d/supervisord.conf

# Copy application files
COPY . .

# Set permissions
RUN chown -R www-data:www-data /var/www/storage /var/www/bootstrap/cache

# Expose port
EXPOSE 80

# Start supervisor
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]
nginx
# docker/nginx.conf
server {
    listen 80;
    server_name localhost;
    root /var/www/public;
    index index.php index.html;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
        
        # Security headers
        add_header X-Frame-Options "SAMEORIGIN" always;
        add_header X-XSS-Protection "1; mode=block" always;
        add_header X-Content-Type-Options "nosniff" always;
        add_header Referrer-Policy "no-referrer-when-downgrade" always;
        add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;
    }

    location ~ /\.ht {
        deny all;
    }

    # Cache static files
    location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }

    # Security - hide PHP files
    location ~ /\.env$ {
        deny all;
    }

    # Gzip compression
    gzip on;
    gzip_vary on;
    gzip_min_length 1024;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_types
        application/atom+xml
        application/geo+json
        application/javascript
        application/x-javascript
        application/json
        application/ld+json
        application/manifest+json
        application/rdf+xml
        application/rss+xml
        application/xhtml+xml
        application/xml
        font/eot
        font/otf
        font/ttf
        image/svg+xml
        text/css
        text/javascript
        text/plain
        text/xml;
}
ini
; docker/supervisord.conf
[supervisord]
nodaemon=true
user=root

[program:php-fpm]
command=php-fpm
autostart=true
autorestart=true

[program:nginx]
command=nginx -g 'daemon off;'
autostart=true
autorestart=true

[program:queue-worker]
command=php /var/www/artisan queue:work --sleep=3 --tries=3
autostart=true
autorestart=true
user=www-data

[program:scheduler]
command=php /var/www/artisan schedule:work
autostart=true
autorestart=true
user=www-data
yaml
# docker-compose.yml
version: '3.8'

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    image: saas-app
    container_name: php_app
    restart: unless-stopped
    working_dir: /var/www
    volumes:
      - .:/var/www
      - ./docker/php.ini:/usr/local/etc/php/php.ini
    networks:
      - app-network

  webserver:
    image: nginx:alpine
    container_name: nginx_server
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - .:/var/www
      - ./docker/nginx.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      - app
    networks:
      - app-network

  database:
    image: mysql:8.0
    container_name: mysql_db
    restart: unless-stopped
    environment:
      MYSQL_DATABASE: ${DB_DATABASE}
      MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
      MYSQL_PASSWORD: ${DB_PASSWORD}
      MYSQL_USER: ${DB_USERNAME}
    volumes:
      - dbdata:/var/lib/mysql
      - ./docker/mysql.cnf:/etc/mysql/conf.d/custom.cnf
    ports:
      - "3306:3306"
    networks:
      - app-network

  redis:
    image: redis:alpine
    container_name: redis_cache
    restart: unless-stopped
    ports:
      - "6379:6379"
    volumes:
      - redisdata:/data
    networks:
      - app-network

  mailhog:
    image: mailhog/mailhog
    container_name: mailhog
    restart: unless-stopped
    ports:
      - "1025:1025"
      - "8025:8025"
    networks:
      - app-network

volumes:
  dbdata:
    driver: local
  redisdata:
    driver: local

networks:
  app-network:
    driver: bridge
bash
#!/bin/bash
# deploy.sh

#!/bin/bash
set -e

echo "Starting deployment..."

# Load environment variables
source .env

# Build and start containers
docker-compose down
docker-compose build --no-cache
docker-compose up -d

# Wait for MySQL to be ready
echo "Waiting for MySQL to be ready..."
while ! docker exec mysql_db mysqladmin ping -h"localhost" -u"root" -p"${DB_PASSWORD}" --silent; do
    sleep 1
done

# Run database migrations
docker exec -it php_app php artisan migrate --force

# Clear and cache configuration
docker exec -it php_app php artisan config:cache
docker exec -it php_app php artisan route:cache
docker exec -it php_app php artisan view:cache

# Install node dependencies and build assets (if needed)
# docker exec -it php_app npm install
# docker exec -it php_app npm run build

echo "Deployment completed successfully!"

Part 2: CI/CD Pipeline with GitHub Actions

2.1 Automated Testing & Deployment

yaml
# .github/workflows/deploy.yml
name: Deploy to Production

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  tests:
    runs-on: ubuntu-latest
    
    services:
      mysql:
        image: mysql:8.0
        env:
          MYSQL_ROOT_PASSWORD: password
          MYSQL_DATABASE: testing
        ports:
          - 3306:3306
        options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
      
      redis:
        image: redis:alpine
        ports:
          - 6379:6379
        options: --health-cmd="redis-cli ping" --health-interval=10s --health-timeout=5s --health-retries=3

    steps:
    - uses: actions/checkout@v3
    
    - name: Setup PHP
      uses: shivammathur/setup-php@v2
      with:
        php-version: '8.2'
        extensions: mbstring, xml, ctype, iconv, intl, pdo_mysql, gd, zip
        coverage: xdebug
        
    - name: Copy .env
      run: php -r "file_exists('.env') || copy('.env.example', '.env');"
      
    - name: Install dependencies
      run: composer install --prefer-dist --no-progress --no-interaction
      
    - name: Generate key
      run: php artisan key:generate
      
    - name: Execute tests
      run: vendor/bin/phpunit --coverage-text
      
    - name: Run static analysis
      run: vendor/bin/phpstan analyse

  security-scan:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
    
    - name: Run security check
      run: composer require --dev enlightn/security-checker && vendor/bin/security-checker security:check
    
    - name: Code quality
      uses: sonarsource/sonarcloud-github-action@master
      env:
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}

  build-and-push:
    needs: [tests, security-scan]
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
      
    steps:
    - name: Checkout code
      uses: actions/checkout@v3
      
    - name: Set up Docker Buildx
      uses: docker/setup-buildx-action@v2
      
    - name: Log in to Container Registry
      uses: docker/login-action@v2
      with:
        registry: ${{ env.REGISTRY }}
        username: ${{ github.actor }}
        password: ${{ secrets.GITHUB_TOKEN }}
        
    - name: Extract metadata
      id: meta
      uses: docker/metadata-action@v4
      with:
        images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
        tags: |
          type=ref,event=branch
          type=ref,event=pr
          type=semver,pattern={{version}}
          type=semver,pattern={{major}}.{{minor}}
          type=sha,prefix={{branch}}-
          
    - name: Build and push
      uses: docker/build-push-action@v4
      with:
        context: .
        push: ${{ github.event_name != 'pull_request' }}
        tags: ${{ steps.meta.outputs.tags }}
        labels: ${{ steps.meta.outputs.labels }}
        cache-from: type=gha
        cache-to: type=gha,mode=max

  deploy:
    needs: build-and-push
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    
    steps:
    - name: Deploy to production
      uses: appleboy/ssh-action@v0.1.7
      with:
        host: ${{ secrets.SERVER_HOST }}
        username: ${{ secrets.SERVER_USER }}
        key: ${{ secrets.SERVER_SSH_KEY }}
        script: |
          cd /var/www/saas-app
          git pull origin main
          docker-compose down
          docker-compose pull
          docker-compose up -d
          docker exec php_app php artisan migrate --force
          docker exec php_app php artisan config:cache
          docker exec php_app php artisan route:cache

Part 3: Advanced Architecture Patterns

3.1 Repository & Service Pattern

php
<?php
// app/Repositories/Contracts/UserRepositoryInterface.php
interface UserRepositoryInterface {
    public function all();
    public function find($id);
    public function create(array $data);
    public function update($id, array $data);
    public function delete($id);
    public function findByEmail($email);
    public function paginate($perPage = 15);
    public function with($relations);
}

// app/Repositories/UserRepository.php
class UserRepository implements UserRepositoryInterface {
    protected $model;
    
    public function __construct(User $user) {
        $this->model = $user;
    }
    
    public function all() {
        return $this->model->all();
    }
    
    public function find($id) {
        return $this->model->findOrFail($id);
    }
    
    public function create(array $data) {
        return $this->model->create($data);
    }
    
    public function update($id, array $data) {
        $user = $this->find($id);
        $user->update($data);
        return $user;
    }
    
    public function delete($id) {
        return $this->find($id)->delete();
    }
    
    public function findByEmail($email) {
        return $this->model->where('email', $email)->first();
    }
    
    public function paginate($perPage = 15) {
        return $this->model->paginate($perPage);
    }
    
    public function with($relations) {
        return $this->model->with($relations);
    }
}

// app/Services/UserService.php
class UserService {
    protected $userRepository;
    protected $emailService;
    protected $cache;
    
    public function __construct(
        UserRepositoryInterface $userRepository,
        EmailService $emailService,
        CacheManager $cache
    ) {
        $this->userRepository = $userRepository;
        $this->emailService = $emailService;
        $this->cache = $cache;
    }
    
    public function registerUser(array $userData) {
        // Validate business logic
        $this->validateUserData($userData);
        
        // Check if user exists
        if ($this->userRepository->findByEmail($userData['email'])) {
            throw new \Exception('User already exists with this email.');
        }
        
        // Hash password
        $userData['password'] = password_hash($userData['password'], PASSWORD_DEFAULT);
        
        // Create user
        $user = $this->userRepository->create($userData);
        
        // Send welcome email
        $this->emailService->sendWelcomeEmail($user);
        
        // Clear cache
        $this->cache->delete('users_list');
        
        // Log registration
        \Log::info("New user registered: {$user->email}");
        
        return $user;
    }
    
    public function updateUserProfile($userId, array $profileData) {
        $user = $this->userRepository->find($userId);
        
        // Business logic validation
        if (isset($profileData['email']) && $profileData['email'] !== $user->email) {
            if ($this->userRepository->findByEmail($profileData['email'])) {
                throw new \Exception('Email already taken.');
            }
            $profileData['email_verified_at'] = null;
        }
        
        $user = $this->userRepository->update($userId, $profileData);
        
        // Clear relevant caches
        $this->cache->delete("user_{$userId}");
        $this->cache->delete('users_list');
        
        return $user;
    }
    
    public function getUserWithStats($userId) {
        $cacheKey = "user_stats_{$userId}";
        
        return $this->cache->remember($cacheKey, 3600, function() use ($userId) {
            $user = $this->userRepository->with(['projects', 'tasks'])->find($userId);
            
            return [
                'user' => $user,
                'stats' => [
                    'project_count' => $user->projects->count(),
                    'task_count' => $user->tasks->count(),
                    'completed_tasks' => $user->tasks->where('status', 'completed')->count(),
                    'active_projects' => $user->projects->where('status', 'active')->count()
                ]
            ];
        });
    }
    
    private function validateUserData(array $data) {
        $validator = \Validator::make($data, [
            'name' => 'required|string|max:255',
            'email' => 'required|email|unique:users',
            'password' => 'required|min:8|confirmed'
        ]);
        
        if ($validator->fails()) {
            throw new \Illuminate\Validation\ValidationException($validator);
        }
    }
}

// app/Http/Controllers/UserController.php
class UserController extends Controller {
    protected $userService;
    
    public function __construct(UserService $userService) {
        $this->userService = $userService;
    }
    
    public function store(UserRequest $request) {
        try {
            $user = $this->userService->registerUser($request->validated());
            
            return response()->json([
                'message' => 'User registered successfully',
                'user' => $user
            ], 201);
            
        } catch (\Exception $e) {
            return response()->json([
                'message' => 'Registration failed',
                'error' => $e->getMessage()
            ], 400);
        }
    }
    
    public function updateProfile(ProfileUpdateRequest $request) {
        try {
            $user = $this->userService->updateUserProfile(
                auth()->id(),
                $request->validated()
            );
            
            return response()->json([
                'message' => 'Profile updated successfully',
                'user' => $user
            ]);
            
        } catch (\Exception $e) {
            return response()->json([
                'message' => 'Profile update failed',
                'error' => $e->getMessage()
            ], 400);
        }
    }
    
    public function show($id) {
        $userData = $this->userService->getUserWithStats($id);
        
        return response()->json($userData);
    }
}

3.2 Event-Driven Architecture

php
<?php
// app/Events/UserRegistered.php
class UserRegistered {
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public $user;

    public function __construct(User $user) {
        $this->user = $user;
    }
}

// app/Events/ProjectCreated.php
class ProjectCreated {
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public $project;

    public function __construct(Project $project) {
        $this->project = $project;
    }
}

// app/Listeners/SendWelcomeEmail.php
class SendWelcomeEmail {
    protected $emailService;

    public function __construct(EmailService $emailService) {
        $this->emailService = $emailService;
    }

    public function handle(UserRegistered $event) {
        $this->emailService->sendWelcomeEmail($event->user);
    }
}

// app/Listeners/CreateDefaultProject.php
class CreateDefaultProject {
    public function handle(UserRegistered $event) {
        // Create a default project for new users
        $project = Project::create([
            'name' => 'My First Project',
            'description' => 'Welcome to your first project!',
            'user_id' => $event->user->id,
            'status' => 'active'
        ]);

        // Create some sample tasks
        $tasks = [
            ['title' => 'Explore the dashboard', 'status' => 'todo'],
            ['title' => 'Create your first task', 'status' => 'todo'],
            ['title' => 'Invite team members', 'status' => 'todo']
        ];

        foreach ($tasks as $taskData) {
            $project->tasks()->create($taskData);
        }
    }
}

// app/Listeners/UpdateProjectStatistics.php
class UpdateProjectStatistics {
    protected $cache;

    public function __construct(CacheManager $cache) {
        $this->cache = $cache;
    }

    public function handle(ProjectCreated $event) {
        // Clear cached project statistics
        $this->cache->delete("user_{$event->project->user_id}_project_stats");
        $this->cache->delete("tenant_{$event->project->tenant_id}_projects_count");
    }
}

// app/Providers/EventServiceProvider.php
class EventServiceProvider extends ServiceProvider {
    protected $listen = [
        UserRegistered::class => [
            SendWelcomeEmail::class,
            CreateDefaultProject::class,
            NotifyAdmins::class,
        ],
        ProjectCreated::class => [
            UpdateProjectStatistics::class,
            LogProjectActivity::class,
        ],
        TaskCompleted::class => [
            UpdateProjectProgress::class,
            CheckProjectCompletion::class,
            NotifyTaskAssignee::class,
        ]
    ];

    public function boot() {
        //
    }
}

Part 4: Advanced Caching & Queue Systems

4.1 Redis Caching & Queue Implementation

php
<?php
// app/Services/CacheService.php
class CacheService {
    protected $redis;
    protected $prefix;

    public function __construct() {
        $this->redis = new Redis();
        $this->redis->connect(env('REDIS_HOST', '127.0.0.1'), env('REDIS_PORT', 6379));
        $this->prefix = env('REDIS_PREFIX', 'saas_app:');
    }

    public function get($key) {
        $value = $this->redis->get($this->prefix . $key);
        return $value ? unserialize($value) : null;
    }

    public function set($key, $value, $ttl = 3600) {
        return $this->redis->setex(
            $this->prefix . $key,
            $ttl,
            serialize($value)
        );
    }

    public function remember($key, $ttl, $callback) {
        $value = $this->get($key);
        
        if (!is_null($value)) {
            return $value;
        }
        
        $value = $callback();
        $this->set($key, $value, $ttl);
        
        return $value;
    }

    public function delete($key) {
        return $this->redis->del($this->prefix . $key);
    }

    public function flushPattern($pattern) {
        $keys = $this->redis->keys($this->prefix . $pattern);
        if (!empty($keys)) {
            return $this->redis->del($keys);
        }
        return 0;
    }

    public function increment($key, $value = 1) {
        return $this->redis->incrby($this->prefix . $key, $value);
    }

    public function decrement($key, $value = 1) {
        return $this->redis->decrby($this->prefix . $key, $value);
    }
}

// app/Jobs/SendBulkEmails.php
class SendBulkEmails implements ShouldQueue {
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public $users;
    public $subject;
    public $template;
    public $data;

    public function __construct($users, $subject, $template, $data = []) {
        $this->users = $users;
        $this->subject = $subject;
        $this->template = $template;
        $this->data = $data;
    }

    public function handle(EmailService $emailService) {
        foreach ($this->users as $user) {
            try {
                $emailService->sendTemplateEmail(
                    $user->email,
                    $this->subject,
                    $this->template,
                    array_merge($this->data, ['user' => $user])
                );
                
                // Log success
                \Log::info("Bulk email sent to: {$user->email}");
                
                // Small delay to prevent overwhelming the email service
                usleep(100000); // 0.1 second
                
            } catch (\Exception $e) {
                \Log::error("Failed to send email to {$user->email}: " . $e->getMessage());
                
                // Retry logic
                if ($this->attempts() < 3) {
                    $this->release(60); // Retry after 60 seconds
                }
            }
        }
    }

    public function failed(\Exception $exception) {
        \Log::error("Bulk email job failed: " . $exception->getMessage());
        
        // Notify administrators
        Mail::to('admin@example.com')->send(new JobFailedNotification(
            'SendBulkEmails',
            $exception->getMessage()
        ));
    }
}

// app/Jobs/ProcessProjectExport.php
class ProcessProjectExport implements ShouldQueue {
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public $projectId;
    public $userId;
    public $format;

    public function __construct($projectId, $userId, $format = 'pdf') {
        $this->projectId = $projectId;
        $this->userId = $userId;
        $this->format = $format;
    }

    public function handle() {
        // Get project data
        $project = Project::with(['tasks', 'users', 'files'])->find($this->projectId);
        
        if (!$project) {
            throw new \Exception("Project not found: {$this->projectId}");
        }

        // Generate export file
        $exportData = $this->prepareExportData($project);
        $filePath = $this->generateExportFile($exportData);
        
        // Store file information
        $file = File::create([
            'name' => "project_export_{$project->id}_{$this->format}." . $this->format,
            'path' => $filePath,
            'user_id' => $this->userId,
            'project_id' => $this->projectId
        ]);

        // Notify user
        Notification::send(
            User::find($this->userId),
            new ExportReadyNotification($file)
        );

        // Clean up old export files (older than 24 hours)
        $this->cleanupOldExports();
    }

    private function prepareExportData($project) {
        return [
            'project' => $project->toArray(),
            'tasks' => $project->tasks->toArray(),
            'team_members' => $project->users->toArray(),
            'statistics' => [
                'total_tasks' => $project->tasks->count(),
                'completed_tasks' => $project->tasks->where('status', 'completed')->count(),
                'total_hours' => $project->tasks->sum('estimated_hours'),
                'progress' => $project->tasks->count() > 0 ? 
                    ($project->tasks->where('status', 'completed')->count() / $project->tasks->count()) * 100 : 0
            ]
        ];
    }

    private function generateExportFile($data) {
        $filename = "exports/project_export_" . time() . ".{$this->format}";
        $fullPath = storage_path("app/{$filename}");
        
        // Ensure directory exists
        if (!file_exists(dirname($fullPath))) {
            mkdir(dirname($fullPath), 0755, true);
        }

        switch ($this->format) {
            case 'json':
                file_put_contents($fullPath, json_encode($data, JSON_PRETTY_PRINT));
                break;
            case 'csv':
                $this->generateCSV($data, $fullPath);
                break;
            case 'pdf':
                $this->generatePDF($data, $fullPath);
                break;
            default:
                throw new \Exception("Unsupported export format: {$this->format}");
        }

        return $filename;
    }

    private function cleanupOldExports() {
        $cutoff = now()->subHours(24);
        $oldFiles = File::where('created_at', '<', $cutoff)
                       ->where('path', 'like', 'exports/%')
                       ->get();
        
        foreach ($oldFiles as $file) {
            Storage::delete($file->path);
            $file->delete();
        }
    }
}

Part 5: Monitoring & Logging

5.1 Advanced Application Monitoring

php
<?php
// app/Services/MonitoringService.php
class MonitoringService {
    protected $redis;
    protected $db;

    public function __construct(Redis $redis, Connection $db) {
        $this->redis = $redis;
        $this->db = $db;
    }

    public function logRequest($request, $response, $executionTime) {
        $logData = [
            'method' => $request->method(),
            'url' => $request->fullUrl(),
            'ip' => $request->ip(),
            'user_agent' => $request->userAgent(),
            'user_id' => auth()->id(),
            'status_code' => $response->getStatusCode(),
            'execution_time' => $executionTime,
            'memory_usage' => memory_get_peak_usage(true),
            'timestamp' => now()->toISOString()
        ];

        // Store in Redis for real-time monitoring
        $this->redis->lpush('request_logs', json_encode($logData));
        
        // Keep only last 1000 requests
        $this->redis->ltrim('request_logs', 0, 999);

        // Log to database for long-term storage
        if ($this->shouldLogToDatabase($logData)) {
            DB::table('request_logs')->insert($logData);
        }
    }

    public function getPerformanceMetrics() {
        $recentLogs = $this->redis->lrange('request_logs', 0, 99);
        $logs = array_map('json_decode', $recentLogs);

        return [
            'average_response_time' => collect($logs)->avg('execution_time'),
            'requests_per_minute' => $this->calculateRPM(),
            'error_rate' => $this->calculateErrorRate($logs),
            'slow_requests' => collect($logs)->where('execution_time', '>', 1.0)->count(),
            'memory_usage' => memory_get_peak_usage(true) / 1024 / 1024 // MB
        ];
    }

    public function getDatabaseMetrics() {
        return [
            'connections' => $this->getDatabaseConnections(),
            'slow_queries' => $this->getSlowQueries(),
            'table_sizes' => $this->getTableSizes()
        ];
    }

    public function getSystemMetrics() {
        return [
            'cpu_usage' => $this->getCPUUsage(),
            'memory_usage' => $this->getMemoryUsage(),
            'disk_usage' => $this->getDiskUsage(),
            'uptime' => $this->getUptime()
        ];
    }

    private function calculateRPM() {
        $minuteAgo = now()->subMinute()->timestamp;
        $recentRequests = $this->redis->lrange('request_logs', 0, -1);
        
        return collect($recentRequests)
            ->map('json_decode')
            ->filter(function($log) use ($minuteAgo) {
                return strtotime($log->timestamp) > $minuteAgo;
            })
            ->count();
    }

    private function shouldLogToDatabase($logData) {
        // Only log slow requests or errors to database
        return $logData['execution_time'] > 1.0 || 
               $logData['status_code'] >= 400 ||
               $logData['method'] !== 'GET';
    }
}

// app/Http/Middleware/MonitoringMiddleware.php
class MonitoringMiddleware {
    protected $monitoringService;

    public function __construct(MonitoringService $monitoringService) {
        $this->monitoringService = $monitoringService;
    }

    public function handle($request, Closure $next) {
        $startTime = microtime(true);
        
        $response = $next($request);
        
        $executionTime = microtime(true) - $startTime;
        
        // Log the request
        $this->monitoringService->logRequest($request, $response, $executionTime);
        
        // Add performance headers
        $response->headers->set('X-Execution-Time', round($executionTime * 1000, 2) . 'ms');
        $response->headers->set('X-Memory-Usage', round(memory_get_peak_usage(true) / 1024 / 1024, 2) . 'MB');
        
        return $response;
    }
}

5.2 Advanced Logging Configuration

php
<?php
// config/logging.php
return [
    'default' => env('LOG_CHANNEL', 'stack'),
    
    'channels' => [
        'stack' => [
            'driver' => 'stack',
            'channels' => ['daily', 'slack'],
            'ignore_exceptions' => false,
        ],
        
        'single' => [
            'driver' => 'single',
            'path' => storage_path('logs/laravel.log'),
            'level' => env('LOG_LEVEL', 'debug'),
        ],
        
        'daily' => [
            'driver' => 'daily',
            'path' => storage_path('logs/laravel.log'),
            'level' => env('LOG_LEVEL', 'debug'),
            'days' => 14,
            'permission' => 0664,
        ],
        
        'slack' => [
            'driver' => 'slack',
            'url' => env('LOG_SLACK_WEBHOOK_URL'),
            'username' => 'Laravel Log',
            'emoji' => ':boom:',
            'level' => env('LOG_LEVEL', 'critical'),
        ],
        
        'papertrail' => [
            'driver' => 'monolog',
            'level' => env('LOG_LEVEL', 'debug'),
            'handler' => SyslogUdpHandler::class,
            'handler_with' => [
                'host' => env('PAPERTRAIL_URL'),
                'port' => env('PAPERTRAIL_PORT'),
            ],
        ],
        
        'stderr' => [
            'driver' => 'monolog',
            'level' => env('LOG_LEVEL', 'debug'),
            'handler' => StreamHandler::class,
            'formatter' => env('LOG_STDERR_FORMATTER'),
            'with' => [
                'stream' => 'php://stderr',
            ],
        ],
        
        'syslog' => [
            'driver' => 'syslog',
            'level' => env('LOG_LEVEL', 'debug'),
        ],
        
        'errorlog' => [
            'driver' => 'errorlog',
            'level' => env('LOG_LEVEL', 'debug'),
        ],
        
        'null' => [
            'driver' => 'monolog',
            'handler' => NullHandler::class,
        ],
        
        'emergency' => [
            'path' => storage_path('logs/laravel.log'),
        ],
        
        // Custom application logs
        'application' => [
            'driver' => 'daily',
            'path' => storage_path('logs/application.log'),
            'level' => 'info',
            'days' => 30,
        ],
        
        'security' => [
            'driver' => 'daily',
            'path' => storage_path('logs/security.log'),
            'level' => 'warning',
            'days' => 90,
        ],
        
        'performance' => [
            'driver' => 'daily',
            'path' => storage_path('logs/performance.log'),
            'level' => 'debug',
            'days' => 7,
        ],
    ],
];

Part 6: Deployment Scripts & Automation

6.1 Advanced Deployment Scripts

bash
#!/bin/bash
# scripts/deploy-production.sh

set -e

echo "???? Starting production deployment..."

# Load environment
source .env.production

# Maintenance mode
php artisan down --message="Deploying new version. Back soon!" --retry=60

# Get latest code
git fetch origin
git reset --hard origin/main

# Install PHP dependencies
composer install --no-dev --optimize-autoloader

# Install node dependencies
npm ci --production

# Build assets
npm run build

# Run database migrations
php artisan migrate --force

# Clear caches
php artisan config:cache
php artisan route:cache
php artisan view:cache

# Restart queues
php artisan queue:restart

# Reload PHP-FPM
sudo systemctl reload php8.2-fpm

# Bring application back up
php artisan up

echo "✅ Production deployment completed successfully!"

# Send deployment notification
curl -X POST -H 'Content-type: application/json' \
  --data "{\"text\":\"✅ Production deployment completed successfully!\"}" \
  $SLACK_WEBHOOK_URL

echo "???? Deployment notification sent"
bash
#!/bin/bash
# scripts/backup-database.sh

#!/bin/bash
set -e

# Configuration
BACKUP_DIR="/backups"
DATE=$(date +%Y%m%d_%H%M%S)
RETENTION_DAYS=7

echo "Starting database backup..."

# Create backup directory
mkdir -p $BACKUP_DIR

# Backup database
mysqldump -h $DB_HOST -u $DB_USERNAME -p$DB_PASSWORD $DB_DATABASE \
  --single-transaction \
  --quick \
  --lock-tables=false \
  > $BACKUP_DIR/backup_$DATE.sql

# Compress backup
gzip $BACKUP_DIR/backup_$DATE.sql

echo "Database backup completed: $BACKUP_DIR/backup_$DATE.sql.gz"

# Clean up old backups
find $BACKUP_DIR -name "backup_*.sql.gz" -mtime +$RETENTION_DAYS -delete

echo "Old backups cleanup completed"

# Upload to cloud storage (optional)
# aws s3 cp $BACKUP_DIR/backup_$DATE.sql.gz s3://your-backup-bucket/

Key Enterprise DevOps Features Covered:

  1. Containerization: Docker, multi-service architecture

  2. CI/CD Pipelines: Automated testing, security scanning, deployment

  3. Advanced Architecture: Repository pattern, service layers, event-driven

  4. Queue Systems: Background job processing, error handling

  5. Monitoring: Performance metrics, logging, alerting

  6. Deployment Automation: Zero-downtime deployments, rollback strategies

Production-Ready Practices:

  1. Security: Environment variables, secure headers, database encryption

  2. Performance: Caching strategies, query optimization, asset compression

  3. Reliability: Queue workers, database backups, monitoring

  4. Scalability: Load balancing, database replication, cache clusters

  5. Maintainability: Code quality tools, comprehensive logging, documentation

This lesson transforms your PHP application into a production-ready, enterprise-grade SaaS platform with professional DevOps practices!

What's Your Reaction?

like

dislike

love

funny

angry

sad

wow

MA Hussain Amjad Hussain: Architect of Truth in the Realm of Fact In the intricate tapestry of human experience, where shadow often meets light, Amjad Hussain stands as a dedicated chronicler of reality. His is a world built on the unshakeable foundations of fact, a pursuit he undertakes with the dual tools of a journalist's discerning eye and a writer's compelling narrative skill.