codebuds/webp-conversion-bundle
Installation
composer require codebuds/webp-conversion-bundle
Ensure your composer.json includes "require": {"php": "^8.1"} and Symfony 6.1+.
Enable the Bundle
Add to config/bundles.php:
return [
// ...
Codebuds\WebPConversionBundle\WebPConversionBundle::class => ['all' => true],
];
Basic Configuration
Create config/packages/webp_conversion.yaml:
web_p_conversion:
upload_path: '%kernel.project_dir%/public/uploads' # Default upload directory
quality: 80 # Default WebP quality (0-100)
First Use Case: Convert a Single Image
Use the WebPConverter service in a controller or command:
use Codebuds\WebPConversionBundle\Service\WebPConverter;
class ImageController extends AbstractController
{
public function convert(Image $image): Response
{
$converter = $this->container->get(WebPConverter::class);
$webpPath = $converter->convert($image->getPath(), $image->getExtension());
return $this->json(['webp_path' => $webpPath]);
}
}
Use Symfony’s EventSubscriber to trigger conversion after file uploads:
use Codebuds\WebPConversionBundle\Service\WebPConverter;
use Symfony\Component\HttpFoundation\File\UploadedFile;
class UploadSubscriber implements EventSubscriber
{
public function __construct(private WebPConverter $converter) {}
public function onUpload(UploadEvent $event): void
{
$file = $event->getFile();
if ($file instanceof UploadedFile) {
$webpPath = $this->converter->convert(
$file->getPathname(),
$file->guessExtension()
);
$event->setWebPPath($webpPath);
}
}
}
Create a command to convert all images in a directory:
use Codebuds\WebPConversionBundle\Service\WebPConverter;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class ConvertAllCommand extends Command
{
protected static $defaultName = 'app:convert-images';
public function __construct(private WebPConverter $converter) {}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$files = glob($this->getParameter('web_p_conversion.upload_path') . '/*.{jpg,jpeg,png,gif}');
foreach ($files as $file) {
$this->converter->convert($file, pathinfo($file, PATHINFO_EXTENSION));
$output->writeln("Converted: $file");
}
return Command::SUCCESS;
}
}
Override quality per image or request:
$webpPath = $converter->convert(
$originalPath,
$extension,
['quality' => 70] // Override default quality
);
Extend VichUploaderStorage to generate WebP alongside original uploads:
use Codebuds\WebPConversionBundle\Service\WebPConverter;
class CustomStorage extends VichUploaderStorage
{
public function __construct(
private WebPConverter $converter,
// ...
) {}
public function updateFile($object, $file): void
{
parent::updateFile($object, $file);
if ($file) {
$webpPath = $this->converter->convert(
$file->getPathname(),
$file->guessExtension()
);
$object->setWebPPath($webpPath);
}
}
}
File System Configuration
Ensure upload_path is writable and matches your storage setup (e.g., public/uploads for web-accessible files).
Symfony Cache Clear cache after configuration changes:
php bin/console cache:clear
Environment-Specific Configs
Use %kernel.environment% to define paths:
web_p_conversion:
upload_path: '%kernel.project_dir%/public/uploads/%kernel.environment%'
WebP Fallback
Serve WebP with <picture> tags in Twig:
<picture>
<source srcset="{{ asset(image.webPPath) }}" type="image/webp">
<img src="{{ asset(image.originalPath) }}" alt="{{ image.alt }}">
</picture>
Queue Delayed Conversions Use Symfony Messenger to avoid blocking requests:
$message = new ConvertImageMessage($originalPath, $extension);
$this->messageBus->dispatch($message);
Extension Mismatch
jpg, jpeg, png, and gif. Passing unsupported extensions (e.g., webp, svg) will fail silently or throw an error.$supportedExtensions = ['jpg', 'jpeg', 'png', 'gif'];
if (!in_array(strtolower($extension), $supportedExtensions)) {
throw new \InvalidArgumentException("Unsupported format: $extension");
}
Path Resolution
upload_path must be an absolute path (e.g., /var/www/uploads). Relative paths (e.g., ./uploads) will break.%kernel.project_dir% for portability:
upload_path: '%kernel.project_dir%/public/uploads'
Quality vs. Size Tradeoff
90) may not reduce file size significantly, while low quality (e.g., 50) can degrade visual fidelity.Overwriting Files
$webpPath = $this->converter->convert(
$originalPath,
$extension,
['filename' => 'custom_' . uniqid() . '.webp']
);
GD vs. Imagick
php -m | grep gd
Enable Imagick as a fallback in webp_conversion.yaml:
web_p_conversion:
use_imagick: true # Requires `pecl install imagick`
Enable Verbose Logging
Add to config/packages/monolog.yaml:
handlers:
webp:
type: stream
path: '%kernel.logs_dir%/webp_conversion.log'
level: debug
Check Conversion Output Temporarily log the conversion process:
$converter->convert($path, $extension, ['debug' => true]);
Validate WebP Files
Use file command to verify output:
file /path/to/converted.webp # Should output "WebP image data"
use Codebuds\WebPConversionBundle\Service\WebPConverterInterface;
class CustomWebPConverter implements WebPConverterInterface
{
public function convert(string $originalPath, string $extension, array $options = []): string
{
$filename = $this->generateCustomFilename($originalPath, $options);
// ... rest of the logic
}
private function generateCustomFilename(string $path, array $options): string
{
return 'custom_' . basename($path, '.' . $extension) . '_' . time() . '.webp';
}
}
Register as a service:
services:
Codebuds\WebPConversionBundle\Service\WebPConverter:
alias: 'app.custom
How can I help you explore Laravel packages today?