Advanced Usage Examples

Advanced techniques and patterns for using the Crop library in production environments.

Custom Crop Strategies

Strategy Selection Based on Image Type

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
use drzippie\crop\{CropCenter, CropEntropy, CropBalanced};

class SmartCropper {
    public function cropByImageType($imagePath, $width, $height) {
        $imageInfo = getimagesize($imagePath);
        $aspectRatio = $imageInfo[0] / $imageInfo[1];
        
        // Choose strategy based on image characteristics
        if ($aspectRatio > 2.0) {
            // Wide images - use entropy to find focal point
            $crop = new CropEntropy($imagePath);
        } elseif ($aspectRatio < 0.5) {
            // Tall images - use center crop
            $crop = new CropCenter($imagePath);
        } else {
            // Square-ish images - use balanced approach
            $crop = new CropBalanced($imagePath);
        }
        
        return $crop->resizeAndCrop($width, $height);
    }
}

Multi-Strategy Comparison

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
use drzippie\crop\{CropCenter, CropEntropy, CropBalanced};

class CropComparator {
    public function generateComparison($imagePath, $width, $height) {
        $strategies = [
            'center' => new CropCenter($imagePath),
            'entropy' => new CropEntropy($imagePath),
            'balanced' => new CropBalanced($imagePath)
        ];
        
        $results = [];
        foreach ($strategies as $name => $crop) {
            $result = $crop->resizeAndCrop($width, $height);
            $result->writeImage("comparison_{$name}.jpg");
            $results[$name] = "comparison_{$name}.jpg";
        }
        
        return $results;
    }
}

Performance Optimization

Lazy Loading and Caching

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
use drzippie\crop\CropBalanced;

class CachedCropper {
    private $cacheDir;
    
    public function __construct($cacheDir) {
        $this->cacheDir = $cacheDir;
        if (!is_dir($cacheDir)) {
            mkdir($cacheDir, 0755, true);
        }
    }
    
    public function getCroppedImage($imagePath, $width, $height, $strategy = 'balanced') {
        $cacheKey = md5($imagePath . $width . $height . $strategy);
        $cachePath = $this->cacheDir . '/' . $cacheKey . '.jpg';
        
        if (file_exists($cachePath)) {
            return $cachePath;
        }
        
        $cropClass = $this->getCropClass($strategy);
        $crop = new $cropClass($imagePath);
        $result = $crop->resizeAndCrop($width, $height);
        $result->writeImage($cachePath);
        
        return $cachePath;
    }
    
    private function getCropClass($strategy) {
        switch ($strategy) {
            case 'center': return CropCenter::class;
            case 'entropy': return CropEntropy::class;
            case 'balanced': 
            default: return CropBalanced::class;
        }
    }
}

Parallel Processing

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
use drzippie\crop\CropCenter;

class ParallelCropper {
    private $workers = 4;
    
    public function processBatch($images, $width, $height) {
        $chunks = array_chunk($images, ceil(count($images) / $this->workers));
        $processes = [];
        
        foreach ($chunks as $i => $chunk) {
            $processes[$i] = new Process([
                'php',
                'worker.php',
                json_encode($chunk),
                $width,
                $height
            ]);
            $processes[$i]->start();
        }
        
        $results = [];
        foreach ($processes as $process) {
            $process->wait();
            $results = array_merge($results, json_decode($process->getOutput(), true));
        }
        
        return $results;
    }
}

Advanced Error Handling

Retry Mechanism

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
use drzippie\crop\CropEntropy;

class ResilientCropper {
    private $maxRetries = 3;
    private $retryDelay = 1000000; // 1 second in microseconds
    
    public function cropWithRetry($imagePath, $width, $height) {
        $attempts = 0;
        $lastException = null;
        
        while ($attempts < $this->maxRetries) {
            try {
                $crop = new CropEntropy($imagePath);
                return $crop->resizeAndCrop($width, $height);
            } catch (Exception $e) {
                $lastException = $e;
                $attempts++;
                
                if ($attempts < $this->maxRetries) {
                    usleep($this->retryDelay * $attempts);
                }
            }
        }
        
        throw new RuntimeException(
            "Failed to crop image after {$this->maxRetries} attempts",
            0,
            $lastException
        );
    }
}

Fallback Strategies

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
use drzippie\crop\{CropCenter, CropEntropy, CropBalanced};

class FallbackCropper {
    private $strategies = [
        CropEntropy::class,
        CropBalanced::class,
        CropCenter::class
    ];
    
    public function cropWithFallback($imagePath, $width, $height) {
        $lastException = null;
        
        foreach ($this->strategies as $strategyClass) {
            try {
                $crop = new $strategyClass($imagePath);
                return $crop->resizeAndCrop($width, $height);
            } catch (Exception $e) {
                $lastException = $e;
                continue;
            }
        }
        
        throw new RuntimeException(
            "All cropping strategies failed",
            0,
            $lastException
        );
    }
}

Custom Image Processing Pipeline

Multi-Stage Processing

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
use drzippie\crop\CropBalanced;

class ImagePipeline {
    private $stages = [];
    
    public function addStage(callable $stage) {
        $this->stages[] = $stage;
        return $this;
    }
    
    public function process($imagePath, $width, $height) {
        $crop = new CropBalanced($imagePath);
        $result = $crop->resizeAndCrop($width, $height);
        
        foreach ($this->stages as $stage) {
            $result = $stage($result);
        }
        
        return $result;
    }
}

// Usage
$pipeline = new ImagePipeline();
$pipeline
    ->addStage(function($image) {
        // Add watermark
        $watermark = new Imagick('watermark.png');
        $image->compositeImage($watermark, Imagick::COMPOSITE_OVER, 10, 10);
        return $image;
    })
    ->addStage(function($image) {
        // Adjust brightness
        $image->brightnessContrastImage(10, 5);
        return $image;
    })
    ->addStage(function($image) {
        // Apply filter
        $image->sepiaTonoImage(80);
        return $image;
    });

$result = $pipeline->process('input.jpg', 400, 300);

Quality-Based Processing

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
use drzippie\crop\{CropCenter, CropEntropy, CropBalanced};

class QualityAwareCropper {
    private $qualityThresholds = [
        'low' => 0.3,
        'medium' => 0.7,
        'high' => 1.0
    ];
    
    public function cropByQuality($imagePath, $width, $height, $quality = 'medium') {
        $imageInfo = getimagesize($imagePath);
        $originalSize = $imageInfo[0] * $imageInfo[1];
        $targetSize = $width * $height;
        $compressionRatio = $targetSize / $originalSize;
        
        // Choose strategy based on quality requirements and compression
        if ($quality === 'high' || $compressionRatio > $this->qualityThresholds['high']) {
            $crop = new CropEntropy($imagePath);
        } elseif ($quality === 'medium' || $compressionRatio > $this->qualityThresholds['medium']) {
            $crop = new CropBalanced($imagePath);
        } else {
            $crop = new CropCenter($imagePath);
        }
        
        return $crop
            ->setFilter($this->getFilterForQuality($quality))
            ->setBlur($this->getBlurForQuality($quality))
            ->resizeAndCrop($width, $height);
    }
    
    private function getFilterForQuality($quality) {
        switch ($quality) {
            case 'high': return Imagick::FILTER_LANCZOS;
            case 'medium': return Imagick::FILTER_CUBIC;
            case 'low': return Imagick::FILTER_POINT;
            default: return Imagick::FILTER_CUBIC;
        }
    }
    
    private function getBlurForQuality($quality) {
        switch ($quality) {
            case 'high': return 0.3;
            case 'medium': return 0.5;
            case 'low': return 0.8;
            default: return 0.5;
        }
    }
}

Integration Examples

Laravel Integration

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
use drzippie\crop\CropBalanced;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;

class ImageController extends Controller {
    public function crop(Request $request) {
        $request->validate([
            'image' => 'required|image|max:10240', // 10MB max
            'width' => 'required|integer|min:1|max:2000',
            'height' => 'required|integer|min:1|max:2000',
            'strategy' => 'in:center,entropy,balanced'
        ]);
        
        $file = $request->file('image');
        $tempPath = $file->store('temp');
        
        try {
            $cropClass = $this->getCropClass($request->input('strategy', 'balanced'));
            $crop = new $cropClass(Storage::path($tempPath));
            
            $result = $crop->resizeAndCrop(
                $request->input('width'),
                $request->input('height')
            );
            
            $outputPath = 'cropped/' . uniqid() . '.jpg';
            $result->writeImage(Storage::path($outputPath));
            
            return response()->json([
                'success' => true,
                'path' => Storage::url($outputPath)
            ]);
            
        } catch (Exception $e) {
            return response()->json([
                'success' => false,
                'error' => $e->getMessage()
            ], 500);
        } finally {
            Storage::delete($tempPath);
        }
    }
    
    private function getCropClass($strategy) {
        switch ($strategy) {
            case 'center': return CropCenter::class;
            case 'entropy': return CropEntropy::class;
            case 'balanced': 
            default: return CropBalanced::class;
        }
    }
}

Symfony Integration

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
use drzippie\crop\CropEntropy;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\JsonResponse;

class ImageService {
    private $uploadDir;
    
    public function __construct($uploadDir) {
        $this->uploadDir = $uploadDir;
    }
    
    public function processUpload(UploadedFile $file, $width, $height) {
        $originalName = $file->getClientOriginalName();
        $filename = pathinfo($originalName, PATHINFO_FILENAME);
        $extension = $file->getClientOriginalExtension();
        
        $uploadPath = $this->uploadDir . '/' . $filename . '.' . $extension;
        $file->move($this->uploadDir, $filename . '.' . $extension);
        
        try {
            $crop = new CropEntropy($uploadPath);
            $result = $crop->resizeAndCrop($width, $height);
            
            $croppedPath = $this->uploadDir . '/' . $filename . '_cropped.' . $extension;
            $result->writeImage($croppedPath);
            
            return new JsonResponse([
                'success' => true,
                'original' => $uploadPath,
                'cropped' => $croppedPath
            ]);
            
        } catch (Exception $e) {
            return new JsonResponse([
                'success' => false,
                'error' => $e->getMessage()
            ], 500);
        }
    }
}

Monitoring and Logging

Performance Monitoring

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
use drzippie\crop\CropBalanced;

class MonitoredCropper {
    private $logger;
    
    public function __construct($logger) {
        $this->logger = $logger;
    }
    
    public function cropWithMonitoring($imagePath, $width, $height) {
        $startTime = microtime(true);
        $startMemory = memory_get_usage();
        
        try {
            $crop = new CropBalanced($imagePath);
            $result = $crop->resizeAndCrop($width, $height);
            
            $endTime = microtime(true);
            $endMemory = memory_get_usage();
            
            $this->logger->info('Crop operation completed', [
                'image' => $imagePath,
                'dimensions' => "{$width}x{$height}",
                'duration' => round(($endTime - $startTime) * 1000, 2) . 'ms',
                'memory_used' => round(($endMemory - $startMemory) / 1024 / 1024, 2) . 'MB',
                'strategy' => 'balanced'
            ]);
            
            return $result;
            
        } catch (Exception $e) {
            $this->logger->error('Crop operation failed', [
                'image' => $imagePath,
                'dimensions' => "{$width}x{$height}",
                'error' => $e->getMessage()
            ]);
            
            throw $e;
        }
    }
}

Testing and Validation

Unit Testing Helper

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
use drzippie\crop\CropCenter;
use PHPUnit\Framework\TestCase;

class CropTestHelper extends TestCase {
    private $tempDir;
    
    protected function setUp(): void {
        $this->tempDir = sys_get_temp_dir() . '/crop_tests';
        if (!is_dir($this->tempDir)) {
            mkdir($this->tempDir, 0755, true);
        }
    }
    
    protected function tearDown(): void {
        $this->cleanupTempDir();
    }
    
    protected function createTestImage($width, $height) {
        $image = new Imagick();
        $image->newImage($width, $height, 'white');
        $image->setImageFormat('png');
        
        // Add some pattern
        $draw = new ImagickDraw();
        $draw->setFillColor('black');
        $draw->rectangle(10, 10, 30, 30);
        $image->drawImage($draw);
        
        return $image;
    }
    
    protected function assertImageDimensions($image, $expectedWidth, $expectedHeight) {
        $geometry = $image->getImageGeometry();
        $this->assertEquals($expectedWidth, $geometry['width']);
        $this->assertEquals($expectedHeight, $geometry['height']);
    }
    
    private function cleanupTempDir() {
        if (is_dir($this->tempDir)) {
            $files = glob($this->tempDir . '/*');
            foreach ($files as $file) {
                if (is_file($file)) {
                    unlink($file);
                }
            }
            rmdir($this->tempDir);
        }
    }
}

Next Steps