CDN Integration
Core PHP provides unified CDN integration for BunnyCDN and Cloudflare with automatic asset offloading, URL generation, and cache management.
Configuration
php
// config/cdn.php
return [
'driver' => env('CDN_DRIVER', 'bunnycdn'),
'bunnycdn' => [
'api_key' => env('BUNNY_API_KEY'),
'storage_zone' => env('BUNNY_STORAGE_ZONE'),
'storage_password' => env('BUNNY_STORAGE_PASSWORD'),
'cdn_url' => env('BUNNY_CDN_URL'),
'pull_zone_id' => env('BUNNY_PULL_ZONE_ID'),
],
'cloudflare' => [
'zone_id' => env('CLOUDFLARE_ZONE_ID'),
'api_token' => env('CLOUDFLARE_API_TOKEN'),
'cdn_url' => env('CLOUDFLARE_CDN_URL'),
],
'offload' => [
'enabled' => env('CDN_OFFLOAD_ENABLED', false),
'paths' => ['public/images', 'public/media', 'storage/app/public'],
],
];Basic Usage
Generating CDN URLs
php
use Core\Cdn\Facades\Cdn;
// Generate CDN URL
$url = Cdn::url('images/photo.jpg');
// https://cdn.example.com/images/photo.jpg
// With transformation parameters
$url = Cdn::url('images/photo.jpg', [
'width' => 800,
'quality' => 85,
]);Helper Function
php
// Global helper
$url = cdn_url('images/photo.jpg');
// In Blade templates
<img src="{{ cdn_url('images/photo.jpg') }}" alt="Photo">Storing Files
php
// Upload file to CDN
$path = Cdn::store($uploadedFile, 'media');
// Store with custom filename
$path = Cdn::store($uploadedFile, 'media', 'custom-name.jpg');
// Store from contents
$path = Cdn::put('path/file.txt', $contents);Deleting Files
php
// Delete single file
Cdn::delete('media/photo.jpg');
// Delete multiple files
Cdn::delete(['media/photo1.jpg', 'media/photo2.jpg']);
// Delete directory
Cdn::deleteDirectory('media/old');Cache Purging
Purge Single File
php
// Purge specific file from CDN cache
Cdn::purge('images/photo.jpg');Purge Multiple Files
php
// Purge multiple files
Cdn::purge([
'images/photo1.jpg',
'images/photo2.jpg',
]);Purge by Pattern
php
// Purge all images
Cdn::purge('images/*');
// Purge all JPEGs
Cdn::purge('**/*.jpg');Purge Everything
php
// Purge entire CDN cache (use sparingly!)
Cdn::purgeAll();Asset Offloading
Automatically offload existing assets to CDN:
bash
# Offload public disk
php artisan storage:offload --disk=public
# Offload specific path
php artisan storage:offload --path=public/images
# Dry run (preview without uploading)
php artisan storage:offload --dry-runProgrammatic Offloading
php
use Core\Cdn\Services\AssetPipeline;
$pipeline = app(AssetPipeline::class);
// Offload directory
$result = $pipeline->offload('public/images', [
'extensions' => ['jpg', 'png', 'gif', 'webp'],
'min_size' => 1024, // Only files > 1KB
]);
echo "Uploaded: {$result['uploaded']} files\n";
echo "Skipped: {$result['skipped']} files\n";URL Builder
Advanced URL construction with transformations:
php
use Core\Cdn\Services\CdnUrlBuilder;
$builder = app(CdnUrlBuilder::class);
$url = $builder->build('images/photo.jpg', [
// Dimensions
'width' => 800,
'height' => 600,
'aspect_ratio' => '16:9',
// Quality
'quality' => 85,
'format' => 'webp',
// Effects
'blur' => 10,
'brightness' => 1.2,
'contrast' => 1.1,
// Cropping
'crop' => 'center',
'gravity' => 'face',
]);BunnyCDN Specific
Pull Zone Management
php
use Core\Cdn\Services\BunnyCdnService;
$bunny = app(BunnyCdnService::class);
// Get pull zone info
$pullZone = $bunny->getPullZone($pullZoneId);
// Add/remove hostnames
$bunny->addHostname($pullZoneId, 'cdn.example.com');
$bunny->removeHostname($pullZoneId, 'cdn.example.com');
// Enable/disable cache
$bunny->setCacheEnabled($pullZoneId, true);Storage Zone Operations
php
use Core\Cdn\Services\BunnyStorageService;
$storage = app(BunnyStorageService::class);
// List files
$files = $storage->list('media/');
// Get file info
$info = $storage->getFileInfo('media/photo.jpg');
// Download file
$contents = $storage->download('media/photo.jpg');Cloudflare Specific
Zone Management
php
use Core\Cdn\Services\FluxCdnService;
$cloudflare = app(FluxCdnService::class);
// Purge cache by URLs
$cloudflare->purgePaths([
'https://example.com/images/photo.jpg',
'https://example.com/styles/app.css',
]);
// Purge by cache tags
$cloudflare->purgeTags(['images', 'media']);
// Purge everything
$cloudflare->purgeEverything();Testing
Fake CDN
php
use Core\Cdn\Facades\Cdn;
class UploadTest extends TestCase
{
public function test_uploads_file(): void
{
Cdn::fake();
$response = $this->post('/upload', [
'file' => UploadedFile::fake()->image('photo.jpg'),
]);
Cdn::assertStored('media/photo.jpg');
}
}Assert Operations
php
// Assert file was stored
Cdn::assertStored('path/file.jpg');
// Assert file was deleted
Cdn::assertDeleted('path/file.jpg');
// Assert cache was purged
Cdn::assertPurged('path/file.jpg');
// Assert nothing was stored
Cdn::assertNothingStored();Performance
URL Caching
CDN URLs are cached to avoid repeated lookups:
php
// URLs cached for 1 hour
$url = Cdn::url('images/photo.jpg'); // Generates URL + caches
$url = Cdn::url('images/photo.jpg'); // Returns from cacheBatch Operations
php
// Batch delete (single API call)
Cdn::delete([
'media/photo1.jpg',
'media/photo2.jpg',
'media/photo3.jpg',
]);
// Batch purge (single API call)
Cdn::purge([
'images/*.jpg',
'styles/*.css',
]);Best Practices
1. Use Helper in Blade
blade
{{-- ✅ Good --}}
<img src="{{ cdn_url('images/photo.jpg') }}" alt="Photo">
{{-- ❌ Bad - relative path --}}
<img src="/images/photo.jpg" alt="Photo">2. Offload Static Assets
php
// ✅ Good - offload after upload
public function store(Request $request)
{
$path = $request->file('image')->store('media');
// Offload to CDN immediately
Cdn::store($path);
return $path;
}3. Purge After Updates
php
// ✅ Good - purge on update
public function update(Request $request, Media $media)
{
$oldPath = $media->path;
$media->update($request->validated());
// Purge old file from cache
Cdn::purge($oldPath);
}4. Use Transformations
php
// ✅ Good - CDN transforms image
<img src="{{ cdn_url('photo.jpg', ['width' => 400, 'quality' => 85]) }}">
// ❌ Bad - transform server-side
<img src="{{ route('image.transform', ['path' => 'photo.jpg', 'width' => 400]) }}">Troubleshooting
Files Not Appearing
bash
# Verify CDN credentials
php artisan tinker
>>> Cdn::store(UploadedFile::fake()->image('test.jpg'), 'test')
# Check CDN dashboard for new filesPurge Not Working
bash
# Verify pull zone ID
php artisan tinker
>>> config('cdn.bunnycdn.pull_zone_id')
# Manual purge via dashboardURLs Not Resolving
php
// Check CDN URL configuration
echo config('cdn.bunnycdn.cdn_url');
// Verify file exists on CDN
$exists = Cdn::exists('path/file.jpg');