ApnaPHP

Documentation

Performance Optimization

Learn how to optimize your ApnaPHP application for maximum performance, scalability, and efficiency.

Database Optimization

Query Optimization

Use Specific Columns

// Good: Select only needed columns
$users = User::select(['id', 'name', 'email'])->get();

// Bad: Select all columns
$users = User::all();

Use WHERE Clauses Efficiently

// Good: Indexed column in WHERE
$user = User::where('id', 1)->first();

// Bad: Function in WHERE (can't use index)
$users = User::where('LOWER(email)', strtolower($email))->get();

// Better: Store lowercase email
$users = User::where('email_lowercase', strtolower($email))->get();

Eager Loading for Relationships

// Good: Eager load relationships (1 query per relation)
$users = User::with('posts')->get();

// Bad: N+1 query problem
$users = User::all();
foreach ($users as $user) {
    $posts = $user->posts; // Separate query for each user
}

Database Indexing

Add Indexes to Frequently Queried Columns

protected array $schema = [
    'email' => 'required|unique|type:string|length:255|index',
    'username' => 'required|unique|type:string|length:100|index',
    'status' => 'type:string|default:active|length:50|index',
    'created_at' => 'type:timestamp|default:CURRENT_TIMESTAMP|index'
];

Composite Indexes

// For queries with multiple WHERE conditions
ALTER TABLE users ADD INDEX idx_status_role (status, role);

// Query will use this index
$admins = User::where('status', 'active')
              ->where('role', 'admin')
              ->get();

Query Caching

Cache Frequently Used Queries

// Cache expensive queries
$popularPosts = cache()->remember('popular_posts', 3600, function() {
    return Post::where('views', '>', 1000)
               ->orderBy('views', 'desc')
               ->limit(10)
               ->get();
});

Invalidate Cache on Updates

function POST(Request $request)
{
    $post = Post::create($request->all());
    
    // Clear relevant caches
    cache()->forget('popular_posts');
    cache()->forget('latest_posts');
    
    return Response::json($post, 201);
}

Connection Pooling

// Use persistent connections
$config = [
    'driver' => 'mysql',
    'host' => env('DB_HOST'),
    'persistent' => true,  // Enable persistent connections
    'pool_size' => 10      // Connection pool size
];

Caching Strategies

Page Caching

<?php
// app/products/page.apna.php

// Cache entire page output
$cacheKey = 'page_products';
$html = cache()->remember($cacheKey, 3600, function() {
    ob_start();
    include 'products_template.php';
    return ob_get_clean();
});

echo $html;

Fragment Caching

<?php
// Cache specific parts of a page

// Cache expensive sidebar
$sidebar = cache()->remember('sidebar_popular_posts', 3600, function() {
    return Post::orderBy('views', 'desc')->limit(5)->get();
});

// Cache user-specific data with user ID in key
$userId = auth()->id();
$userStats = cache()->remember("user_stats_{$userId}", 1800, function() use ($userId) {
    return [
        'posts_count' => Post::where('user_id', $userId)->count(),
        'comments_count' => Comment::where('user_id', $userId)->count()
    ];
});

Data Caching

class UserRepository
{
    public function findById($id)
    {
        return cache()->remember("user_{$id}", 3600, function() use ($id) {
            return User::find($id);
        });
    }

    public function findByEmail($email)
    {
        $cacheKey = "user_email_" . md5($email);
        return cache()->remember($cacheKey, 3600, function() use ($email) {
            return User::where('email', $email)->first();
        });
    }

    public function clearUserCache($id)
    {
        cache()->forget("user_{$id}");
    }
}

Cache Tags (Redis)

// Cache with tags for easier invalidation
cache()->tags(['users', 'profiles'])->put('user_1_profile', $data, 3600);

// Flush all caches with 'users' tag
cache()->tags('users')->flush();

Asset Optimization

Minification

// Minify CSS
function minifyCSS($css) {
    $css = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!', '', $css);
    $css = str_replace(["\r\n", "\r", "\n", "\t", '  ', '    ', '    '], '', $css);
    return $css;
}

// Minify JavaScript
function minifyJS($js) {
    $js = preg_replace('!/\*.*?\*/!s', '', $js);
    $js = preg_replace('/\s+/', ' ', $js);
    return $js;
}

Asset Concatenation

// Combine multiple CSS files
$cssFiles = ['style.css', 'theme.css', 'components.css'];
$combinedCSS = '';

foreach ($cssFiles as $file) {
    $combinedCSS .= file_get_contents("public/css/{$file}");
}

$minified = minifyCSS($combinedCSS);
file_put_contents('public/css/bundle.min.css', $minified);

Image Optimization

function optimizeImage($sourcePath, $destPath, $quality = 85)
{
    $info = getimagesize($sourcePath);
    
    switch ($info['mime']) {
        case 'image/jpeg':
            $image = imagecreatefromjpeg($sourcePath);
            imagejpeg($image, $destPath, $quality);
            break;
        case 'image/png':
            $image = imagecreatefrompng($sourcePath);
            imagepng($image, $destPath, floor($quality / 10));
            break;
        case 'image/gif':
            $image = imagecreatefromgif($sourcePath);
            imagegif($image, $destPath);
            break;
    }
    
    imagedestroy($image);
}

// Usage
optimizeImage('uploads/original.jpg', 'uploads/optimized.jpg', 85);

Lazy Loading Images

<!-- Use lazy loading for images -->
<img src="placeholder.jpg" 
     data-src="actual-image.jpg" 
     loading="lazy" 
     alt="Description">

<script>
// Lazy load images
document.addEventListener('DOMContentLoaded', function() {
    const images = document.querySelectorAll('img[data-src]');
    
    const imageObserver = new IntersectionObserver((entries) => {
        entries.forEach(entry => {
            if (entry.isIntersecting) {
                const img = entry.target;
                img.src = img.dataset.src;
                img.removeAttribute('data-src');
                imageObserver.unobserve(img);
            }
        });
    });
    
    images.forEach(img => imageObserver.observe(img));
});
</script>

Code Optimization

Opcode Caching

Enable OPcache in php.ini:

[opcache]
opcache.enable=1
opcache.enable_cli=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=10000
opcache.revalidate_freq=60
opcache.fast_shutdown=1

Autoloader Optimization

# Generate optimized autoloader
composer dump-autoload --optimize --classmap-authoritative

# For production
composer install --no-dev --optimize-autoloader

Reduce Function Calls

// Good: Store in variable
$count = count($array);
for ($i = 0; $i < $count; $i++) {
    // Process
}

// Bad: Call function repeatedly
for ($i = 0; $i < count($array); $i++) {
    // Process
}

Use Built-in Functions

// Good: Use built-in functions (faster)
$result = array_map('strtolower', $array);

// Bad: Custom loop
$result = [];
foreach ($array as $item) {
    $result[] = strtolower($item);
}

Memory Optimization

Unset Large Variables

function processLargeDataset()
{
    $largeData = fetchLargeData(); // 100MB
    
    $processed = processData($largeData);
    
    unset($largeData); // Free memory immediately
    
    return $processed;
}

Use Generators for Large Datasets

// Good: Use generator (low memory)
function getUsers() {
    $result = db()->query('SELECT * FROM users');
    
    while ($row = $result->fetch()) {
        yield $row;
    }
}

// Process one row at a time
foreach (getUsers() as $user) {
    processUser($user);
}

// Bad: Load all in memory
$users = User::all(); // Could be millions of rows
foreach ($users as $user) {
    processUser($user);
}

Batch Processing

// Process in chunks to limit memory usage
$chunkSize = 1000;
$offset = 0;

do {
    $users = User::limit($chunkSize)->offset($offset)->get();
    
    foreach ($users as $user) {
        processUser($user);
    }
    
    $offset += $chunkSize;
    
} while (count($users) === $chunkSize);

HTTP Optimization

Enable Gzip Compression

// Add to public/.htaccess (Apache)
<IfModule mod_deflate.c>
    AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css text/javascript application/javascript application/json
</IfModule>

// Or in PHP
if (extension_loaded('zlib')) {
    ob_start('ob_gzhandler');
}

Browser Caching

// Set cache headers
function GET(Request $request)
{
    $response = Response::json($data);
    
    // Cache for 1 hour
    $response->header('Cache-Control', 'public, max-age=3600');
    $response->header('Expires', gmdate('D, d M Y H:i:s', time() + 3600) . ' GMT');
    
    return $response;
}

ETags for Conditional Requests

function GET(Request $request)
{
    $data = getResourceData();
    $etag = md5(json_encode($data));
    
    // Check if client has cached version
    if ($request->header('If-None-Match') === $etag) {
        return Response::make('', 304); // Not Modified
    }
    
    $response = Response::json($data);
    $response->header('ETag', $etag);
    
    return $response;
}

CDN for Static Assets

<!-- Use CDN for common libraries -->
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css">

<!-- Your app assets -->
<link rel="stylesheet" href="/css/app.css">
<script src="/js/app.js"></script>

Session Optimization

Use Redis for Sessions

// config/session.php
return [
    'driver' => 'redis',
    'connection' => 'session',
    'lifetime' => 120,
    'expire_on_close' => false,
];

Minimize Session Data

// Good: Store only essential data
session(['user_id' => $user->id]);

// Bad: Store entire user object
session(['user' => $user]);

API Optimization

Rate Limiting

class RateLimiter
{
    public function check($key, $maxAttempts = 60, $decayMinutes = 1)
    {
        $attempts = cache()->get($key, 0);
        
        if ($attempts >= $maxAttempts) {
            return false;
        }
        
        cache()->increment($key);
        cache()->expire($key, $decayMinutes * 60);
        
        return true;
    }
}

// Usage
$limiter = new RateLimiter();
if (!$limiter->check('api:' . $request->ip(), 100, 1)) {
    return Response::json(['error' => 'Rate limit exceeded'], 429);
}

Response Compression

function GET(Request $request)
{
    $data = getLargeDataset();
    
    // Compress response if client supports it
    if (strpos($request->header('Accept-Encoding'), 'gzip') !== false) {
        $compressed = gzencode(json_encode($data), 6);
        
        return Response::make($compressed)
            ->header('Content-Encoding', 'gzip')
            ->header('Content-Type', 'application/json');
    }
    
    return Response::json($data);
}

Pagination

// Always paginate large datasets
function GET(Request $request)
{
    $page = $request->input('page', 1);
    $perPage = $request->input('per_page', 20);
    
    $users = User::paginate($perPage, $page);
    
    return Response::json([
        'data' => $users,
        'meta' => [
            'current_page' => $page,
            'per_page' => $perPage,
            'total' => User::count()
        ]
    ]);
}

Background Processing

Queue Heavy Tasks

// Instead of processing immediately
function POST(Request $request)
{
    // Bad: Process immediately (slow)
    $this->sendWelcomeEmail($user);
    $this->processImage($image);
    $this->generateReport($data);
    
    return Response::json($user, 201);
}

// Good: Queue for background processing
function POST(Request $request)
{
    $user = User::create($request->all());
    
    // Queue tasks
    Queue::push(new SendWelcomeEmail($user));
    Queue::push(new ProcessImage($image));
    Queue::push(new GenerateReport($data));
    
    return Response::json($user, 201);
}

Async Processing with Swoole

// Using Swoole for async operations
use Swoole\Coroutine;

function processAsync()
{
    Coroutine::create(function() {
        // Heavy task 1
        $result1 = heavyTask1();
    });
    
    Coroutine::create(function() {
        // Heavy task 2
        $result2 = heavyTask2();
    });
    
    // Both tasks run concurrently
}

Monitoring & Profiling

Query Logging

// Enable query logging in development
if (env('APP_DEBUG')) {
    db()->enableQueryLog();
    
    // After operations
    $queries = db()->getQueryLog();
    foreach ($queries as $query) {
        echo $query['sql'] . " - " . $query['time'] . "ms\n";
    }
}

Performance Profiling

class Profiler
{
    private static $timers = [];
    
    public static function start($name)
    {
        self::$timers[$name] = microtime(true);
    }
    
    public static function end($name)
    {
        if (isset(self::$timers[$name])) {
            $elapsed = microtime(true) - self::$timers[$name];
            logger()->info("{$name}: {$elapsed}s");
            return $elapsed;
        }
    }
}

// Usage
Profiler::start('database_query');
$users = User::all();
Profiler::end('database_query');

Profiler::start('processing');
processUsers($users);
Profiler::end('processing');

Memory Usage Tracking

function trackMemory($label)
{
    $usage = memory_get_usage(true);
    $peak = memory_get_peak_usage(true);
    
    logger()->info("{$label} - Current: " . formatBytes($usage) . 
                   ", Peak: " . formatBytes($peak));
}

function formatBytes($bytes)
{
    $units = ['B', 'KB', 'MB', 'GB'];
    $i = 0;
    
    while ($bytes >= 1024 && $i < count($units) - 1) {
        $bytes /= 1024;
        $i++;
    }
    
    return round($bytes, 2) . ' ' . $units[$i];
}

Best Practices

1. Use Caching Extensively

// Cache expensive operations
$config = cache()->remember('app_config', 3600, function() {
    return db()->table('settings')->get();
});

2. Optimize Database Queries

// Use indexes, select specific columns, eager load relations
$users = User::select(['id', 'name', 'email'])
             ->with('profile')
             ->where('status', 'active')
             ->get();

3. Minimize HTTP Requests

<!-- Combine and minify assets -->
<link rel="stylesheet" href="/css/bundle.min.css">
<script src="/js/bundle.min.js"></script>

4. Use Content Delivery Network (CDN)

// Serve static assets from CDN
$cdnUrl = env('CDN_URL', '');
$assetUrl = $cdnUrl . '/images/logo.png';

5. Enable Production Mode

APP_ENV=production
APP_DEBUG=false

This comprehensive guide covers all major performance optimization techniques for ApnaPHP applications!