Skip to content

Performance Optimization

Rumen Damyanov edited this page Sep 22, 2025 · 1 revision

Performance Optimization

Learn how to optimize your SEO analysis performance with caching, lazy loading, and efficient content processing strategies.

Overview

The PHP-SEO package is designed for performance, but when dealing with large amounts of content or high-traffic applications, optimization becomes crucial. This guide covers various strategies to maximize performance while maintaining SEO effectiveness.

Performance Bottlenecks

Common Performance Issues

  1. Large HTML Content: Analyzing large HTML documents
  2. Repeated Analysis: Analyzing the same content multiple times
  3. External Requests: Fetching remote resources for validation
  4. Memory Usage: Processing content-heavy pages
  5. Regex Operations: Complex pattern matching in content analysis

Caching Strategies

Result Caching

Cache analysis results to avoid reprocessing:

use Rumenx\PhpSeo\SeoManager;
use Psr\SimpleCache\CacheInterface;

class CachedSeoManager
{
    private SeoManager $seoManager;
    private CacheInterface $cache;
    private int $ttl = 3600; // 1 hour

    public function __construct(SeoManager $seoManager, CacheInterface $cache)
    {
        $this->seoManager = $seoManager;
        $this->cache = $cache;
    }

    public function analyze(string $content, array $options = []): SeoAnalysis
    {
        $cacheKey = $this->generateCacheKey($content, $options);
        
        $result = $this->cache->get($cacheKey);
        if ($result !== null) {
            return unserialize($result);
        }

        $analysis = $this->seoManager->analyze($content, $options);
        $this->cache->set($cacheKey, serialize($analysis), $this->ttl);

        return $analysis;
    }

    private function generateCacheKey(string $content, array $options): string
    {
        return 'seo_analysis_' . md5($content . serialize($options));
    }
}

Laravel Caching Integration

// In your Laravel service provider
use Illuminate\Support\Facades\Cache;

public function analyzePage(Request $request): array
{
    $content = $request->input('content');
    $cacheKey = 'seo_analysis_' . md5($content);

    return Cache::remember($cacheKey, 3600, function() use ($content) {
        return app(SeoManager::class)->analyze($content)->toArray();
    });
}

Symfony Caching Integration

// Using Symfony Cache component
use Symfony\Component\Cache\Adapter\FilesystemAdapter;

$cache = new FilesystemAdapter('seo_analysis', 3600);

$cacheItem = $cache->getItem($cacheKey);
if (!$cacheItem->isHit()) {
    $analysis = $seoManager->analyze($content);
    $cacheItem->set($analysis);
    $cache->save($cacheItem);
}

$analysis = $cacheItem->get();

Lazy Loading

Deferred Analysis Components

Only analyze components when needed:

use Rumenx\PhpSeo\Config\SeoConfig;

$config = new SeoConfig([
    'performance' => [
        'lazy_loading' => true,
        'deferred_components' => [
            'structured_data',
            'social_media_tags',
            'content_analysis'
        ]
    ]
]);

$seo = new SeoManager($config);

// Analysis components are loaded only when accessed
$analysis = $seo->analyze($content);
$metaTags = $analysis->getMetaTags(); // Lightweight operation
$structuredData = $analysis->getStructuredData(); // Loaded on demand

Conditional Analysis

Skip unnecessary analysis based on content type:

class SmartSeoManager extends SeoManager
{
    public function analyze(string $content, array $options = []): SeoAnalysis
    {
        $contentType = $options['content_type'] ?? 'webpage';
        
        $config = $this->getOptimizedConfig($contentType);
        
        return parent::analyze($content, array_merge($options, [
            'config' => $config
        ]));
    }

    private function getOptimizedConfig(string $contentType): array
    {
        switch ($contentType) {
            case 'api_response':
                return [
                    'analyze_content' => false,
                    'generate_structured_data' => false,
                    'check_images' => false
                ];
            
            case 'product_page':
                return [
                    'analyze_content' => true,
                    'generate_structured_data' => true,
                    'schema_types' => ['Product']
                ];
            
            case 'blog_post':
                return [
                    'analyze_content' => true,
                    'generate_structured_data' => true,
                    'schema_types' => ['Article']
                ];
            
            default:
                return [];
        }
    }
}

Content Processing Optimization

HTML Parsing Optimization

use Rumenx\PhpSeo\Config\SeoConfig;

$config = new SeoConfig([
    'html_parser' => [
        'method' => 'dom', // Use DOM instead of regex for complex HTML
        'encoding' => 'UTF-8',
        'preserve_whitespace' => false,
        'load_external_entities' => false
    ],
    'content_analysis' => [
        'max_content_length' => 50000, // Limit content analysis length
        'skip_large_text_blocks' => true,
        'optimize_text_extraction' => true
    ]
]);

Text Analysis Optimization

class OptimizedContentAnalyzer
{
    private array $stopWords;
    private array $commonWords;

    public function analyzeContent(string $content): array
    {
        // Limit content length for analysis
        if (strlen($content) > 100000) {
            $content = $this->extractRelevantContent($content);
        }

        // Use pre-compiled regex patterns
        static $patterns = [
            'headings' => '/<h[1-6][^>]*>(.*?)<\/h[1-6]>/is',
            'paragraphs' => '/<p[^>]*>(.*?)<\/p>/is',
            'links' => '/<a[^>]*href=["\']([^"\']*)["\'][^>]*>(.*?)<\/a>/is'
        ];

        $analysis = [];
        foreach ($patterns as $type => $pattern) {
            preg_match_all($pattern, $content, $matches);
            $analysis[$type] = $matches[1] ?? [];
        }

        return $analysis;
    }

    private function extractRelevantContent(string $content): string
    {
        // Extract main content area, skip navigation, footer, etc.
        $dom = new DOMDocument();
        @$dom->loadHTML($content);
        
        $xpath = new DOMXPath($dom);
        
        // Try to find main content
        $mainSelectors = [
            '//main',
            '//article',
            '//*[@id="content"]',
            '//*[@class="content"]',
            '//body'
        ];

        foreach ($mainSelectors as $selector) {
            $elements = $xpath->query($selector);
            if ($elements->length > 0) {
                return $dom->saveHTML($elements->item(0));
            }
        }

        return $content;
    }
}

Memory Management

Memory-Efficient Processing

class MemoryEfficientSeoManager
{
    private int $memoryLimit;
    
    public function __construct(int $memoryLimit = 128 * 1024 * 1024) // 128MB
    {
        $this->memoryLimit = $memoryLimit;
    }

    public function analyzeContent(string $content): array
    {
        $initialMemory = memory_get_usage();
        
        try {
            // Process content in chunks if it's large
            if (strlen($content) > 1000000) { // 1MB
                return $this->analyzeInChunks($content);
            }

            $result = $this->performAnalysis($content);
            
            // Clean up if memory usage is high
            if (memory_get_usage() - $initialMemory > $this->memoryLimit / 2) {
                $this->cleanup();
            }

            return $result;
            
        } catch (Exception $e) {
            $this->cleanup();
            throw $e;
        }
    }

    private function analyzeInChunks(string $content): array
    {
        $chunkSize = 100000; // 100KB chunks
        $chunks = str_split($content, $chunkSize);
        $results = [];

        foreach ($chunks as $chunk) {
            $chunkResult = $this->performAnalysis($chunk);
            $results[] = $chunkResult;
            
            // Clean up after each chunk
            unset($chunkResult);
            if (function_exists('gc_collect_cycles')) {
                gc_collect_cycles();
            }
        }

        return $this->mergeResults($results);
    }

    private function cleanup(): void
    {
        if (function_exists('gc_collect_cycles')) {
            gc_collect_cycles();
        }
    }
}

Database Optimization

Efficient SEO Data Storage

// Laravel Migration Example
Schema::create('seo_cache', function (Blueprint $table) {
    $table->id();
    $table->string('content_hash', 64)->unique();
    $table->json('analysis_result');
    $table->timestamp('expires_at');
    $table->timestamps();
    
    $table->index(['content_hash', 'expires_at']);
});

Batch Processing

class BatchSeoProcessor
{
    private SeoManager $seoManager;
    private int $batchSize = 50;

    public function processBatch(array $pages): array
    {
        $results = [];
        $batches = array_chunk($pages, $this->batchSize);

        foreach ($batches as $batch) {
            $batchResults = $this->processBatchItems($batch);
            $results = array_merge($results, $batchResults);
            
            // Small delay to prevent overwhelming the system
            usleep(100000); // 0.1 seconds
        }

        return $results;
    }

    private function processBatchItems(array $items): array
    {
        $results = [];
        
        foreach ($items as $item) {
            try {
                $analysis = $this->seoManager->analyze($item['content']);
                $results[$item['id']] = $analysis->toArray();
            } catch (Exception $e) {
                $results[$item['id']] = ['error' => $e->getMessage()];
            }
        }

        return $results;
    }
}

Configuration Optimization

Production Configuration

use Rumenx\PhpSeo\Config\SeoConfig;

// Optimized configuration for production
$config = new SeoConfig([
    'performance' => [
        'cache_enabled' => true,
        'cache_ttl' => 3600,
        'lazy_loading' => true,
        'memory_limit' => '256M',
        'max_execution_time' => 30
    ],
    'analysis' => [
        'deep_content_analysis' => false, // Disable for faster processing
        'check_external_links' => false,  // Skip external link validation
        'generate_suggestions' => false,  // Skip suggestion generation
        'detailed_metrics' => false       // Only basic metrics
    ],
    'html_parser' => [
        'method' => 'dom',
        'encoding' => 'UTF-8',
        'preserve_whitespace' => false
    ]
]);

Development Configuration

// Detailed configuration for development/debugging
$config = new SeoConfig([
    'performance' => [
        'cache_enabled' => false,        // Disable cache for testing
        'debug_mode' => true,           // Enable debugging
        'profile_performance' => true   // Track performance metrics
    ],
    'analysis' => [
        'deep_content_analysis' => true,
        'check_external_links' => true,
        'generate_suggestions' => true,
        'detailed_metrics' => true,
        'validate_html' => true
    ]
]);

Monitoring and Profiling

Performance Monitoring

class PerformanceMonitor
{
    private array $metrics = [];

    public function startTimer(string $operation): void
    {
        $this->metrics[$operation] = [
            'start_time' => microtime(true),
            'start_memory' => memory_get_usage()
        ];
    }

    public function endTimer(string $operation): array
    {
        if (!isset($this->metrics[$operation])) {
            return [];
        }

        $metric = $this->metrics[$operation];
        $endTime = microtime(true);
        $endMemory = memory_get_usage();

        return [
            'operation' => $operation,
            'execution_time' => $endTime - $metric['start_time'],
            'memory_usage' => $endMemory - $metric['start_memory'],
            'peak_memory' => memory_get_peak_usage()
        ];
    }

    public function getMetrics(): array
    {
        return $this->metrics;
    }
}

// Usage
$monitor = new PerformanceMonitor();
$monitor->startTimer('seo_analysis');

$analysis = $seoManager->analyze($content);

$metrics = $monitor->endTimer('seo_analysis');
error_log("SEO Analysis took {$metrics['execution_time']} seconds");

Laravel Performance Monitoring

// Using Laravel's built-in monitoring
use Illuminate\Support\Facades\Log;

class SeoController extends Controller
{
    public function analyze(Request $request)
    {
        $startTime = microtime(true);
        $startMemory = memory_get_usage();

        $analysis = app(SeoManager::class)->analyze($request->input('content'));

        $executionTime = microtime(true) - $startTime;
        $memoryUsage = memory_get_usage() - $startMemory;

        Log::info('SEO Analysis Performance', [
            'execution_time' => $executionTime,
            'memory_usage' => $memoryUsage,
            'content_length' => strlen($request->input('content'))
        ]);

        return response()->json($analysis->toArray());
    }
}

Best Practices

General Performance Guidelines

  1. Use Caching: Always implement caching for repeated operations
  2. Limit Content Size: Set reasonable limits for content analysis
  3. Profile Regularly: Monitor performance in production
  4. Optimize Queries: Use efficient database queries for SEO data
  5. Memory Management: Clean up resources after processing

Framework-Specific Optimizations

Laravel

// Use Laravel's cache tags for better cache management
Cache::tags(['seo', 'content'])->put($cacheKey, $analysis, 3600);

// Clear related caches when content changes
Cache::tags(['seo'])->flush();

Symfony

// Use Symfony's cache pools for different cache types
$seoCache = $cachePool->getItem('seo.' . $cacheKey);
$structuredDataCache = $cachePool->getItem('structured_data.' . $cacheKey);

Production Deployment

  1. Enable OPcache: Use PHP OPcache for better performance
  2. Use CDN: Serve static assets from CDN
  3. Database Optimization: Optimize database queries and indexes
  4. Load Balancing: Distribute load across multiple servers
  5. Monitoring: Set up application performance monitoring

Troubleshooting Performance Issues

Common Performance Problems

  1. Slow Analysis: Large content or complex HTML structure

    • Solution: Implement content chunking or lazy loading
  2. High Memory Usage: Processing large amounts of data

    • Solution: Use streaming processing or memory limits
  3. Cache Misses: Poor cache hit ratio

    • Solution: Optimize cache keys and TTL settings
  4. Database Bottlenecks: Slow SEO data queries

    • Solution: Add indexes and optimize queries

Performance optimization is crucial for maintaining fast response times while providing comprehensive SEO analysis. Regular monitoring and profiling will help identify bottlenecks and optimization opportunities.

Clone this wiki locally