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!
